doc: update architecture, evolution, README for toolsrv 9P migration

- README: toolsrv shown as separate child process in architecture diagram,
  cmd/toolsrv added to repository layout table
- architecture.md: repository structure reflects cmd/{olliesrv,toolsrv}
  internal packages, package table updated for two-server model
- evolution.md: add Phase 20 (toolsrv 9P migration & process isolation),
  update topology diagram, add to gantt chart
This commit is contained in:
Ollie Agent 2026-08-11 07:24:53 +02:00
parent df74c3fa60
commit 4b7d5cdd79
3 changed files with 135 additions and 63 deletions

View File

@ -70,7 +70,8 @@ Single Go module with one Git submodule (kde).
|-----------|----------|-------------|
| `agent/` | Go | Agent loop, history, prompt resolution, context compaction |
| `backend/` | Go | LLM providers (Anthropic, OpenAI, Ollama, Gemini, Copilot, CodeWhisperer) |
| `toolsrv/` | Go | Tool server, dynamic tool dispatch, sandboxed execution, remote execution |
| `toolsrv/` | Go | Tool server client library (9P client for toolsrv) |
| `cmd/toolsrv/` | Go | Tool execution server (separate 9P server, sandboxed execution) |
| `tools/lsp/` | Go | LSP bridge daemon + client library (gopls, clangd, intelephense) |
| `session/` | Go | Session lifecycle, config, persistence |
| `fs/` | Go | 9P filesystem tree (EDSL-declared namespace, ctl dispatch) |
@ -78,7 +79,8 @@ Single Go module with one Git submodule (kde).
| `bypass/` | Go | Bypass broker (sandbox escape approval) |
| `sandbox/` | Go | Landlock sandbox config |
| `lib9p/` | Go | 9P protocol library + native C client |
| `cmd/olliesrv/` | Go | The main binary |
| `cmd/olliesrv/` | Go | The main server binary (session/agent 9P namespace) |
| `cmd/toolsrv/` | Go | Tool execution server (spawned by olliesrv, separate 9P server) |
| `cmd/ollie-9p/` | Go | 9P client CLI |
| `cmd/ollie-remote/` | Go | Remote execution binary |
| `kde/` | C++/Qt6 | KDE plasmoid, GUI, Kate plugin, KRunner *(submodule)* |
@ -109,7 +111,11 @@ graph TB
P9[9P Filesystem<br>session/ namespace]
SESS[session.Session<br>tool server conn · persistence]
AG[agent.Agent<br>loop · compaction<br>prompt assembly]
TOOLS[toolsrv.Server<br>dynamic dispatch · sandbox]
end
subgraph "toolsrv (child process, 9P)"
TOOLS[9P Filesystem<br>ctl · tools · proc/]
EXEC[Tool Scripts<br>· shell, reasoning_think<br>· file_*, lsp_*, memory_*<br>· gui_*, subagent_*]
end
subgraph "LLM Backends"
@ -120,16 +126,12 @@ graph TB
KIRO[Kiro]
end
subgraph Execution
EXEC[Tool Scripts<br>loaded via ctl<br>· shell, reasoning_think<br>· file_*, lsp_*, memory_*<br>· gui_*, subagent_*]
end
TUI & SCRIPTS & ACME --> O
O --> P9
KDE & KATE & EMACS --> P9
P9 --> SESS
SESS --> AG
SESS -->|shared RPC conn| TOOLS
SESS -->|9P over Unix socket| TOOLS
AG -->|streaming| OLL & OAI & ANT & COP & KIRO

View File

@ -15,31 +15,19 @@ Design principles:
Single Go module (`ollie`) with one Git submodule (`kde/`) for the KDE frontend:
```
ollie/
├── agent/ Agent loop, history, prompt resolution, context compaction
├── backend/ LLM providers (Anthropic, OpenAI, Ollama, Gemini, Copilot, CodeWhisperer)
├── toolsrv/ Tool server: sandboxed execution, tool registry, skill management
├── session/ Session lifecycle (config, creation, persistence)
├── fs/ 9P filesystem: EDSL spec + handlers (flat package)
│ ├── spec.go Namespace declaration (single source of truth)
│ ├── handlers.go All file handlers (root, session, agent)
│ ├── ctl.go rdwrHandler type + rdwrDispatch (shared by all ctl files)
│ ├── agent_log.go AgentLog (chat streaming, chatCond)
│ ├── session_node.go SessionNode wrapper
│ ├── lifecycle.go RootState, session node cache
│ ├── newroot.go NewRoot — tree construction config
│ └── cache.go ModelCache
├── cmd/
│ ├── olliesrv/ 9P server (sessions, agents, backends)
│ │ └── internal/ agent/, backend/, bypass/, fs/, session/, prompts/, toolclient/
│ ├── toolsrv/ 9P tool execution server (separate process)
│ │ └── internal/ fs/, exec/, registry/, sandbox/
│ ├── ollie-9p/ 9P client CLI
│ └── ollie-remote/ Remote execution server
├── toolsrv/ Client library (9P client for toolsrv)
├── fsedsl/ Filesystem declaration EDSL (generic, reusable)
├── bypass/ Bypass broker (sandbox escape approval)
├── sandbox/ Landlock sandbox config YAML
├── lib9p/ 9P protocol library + native C client
├── env/ Environment variable loading
├── log/ Structured logging
├── paths/ XDG path resolution
├── format/ Chat log formatting constants
├── cmd/ Binaries:
│ ├── olliesrv/ 9P server
│ ├── ollie-9p/ 9P client CLI
│ └── ollie-remote/ Remote execution server
├── kde/ KDE integration (submodule) — standalone GUI, Kate plugin, KRunner
├── data/agents/ Agent config JSONs (default, coding, orchestrator, worker, ...)
├── data/prompts/ Prompt templates (markdown)
@ -71,7 +59,9 @@ flowchart TB
subgraph Core["Agent Engine (per session)"]
LOOP["Agent Loop\n(agent/loop.go)"]
SESS["Session State\n(agent/history.go)"]
TRV["toolsrv.Server\n· dynamic tool dispatch\n· landlock sandboxed execution\n· remote execution via SSH"]
end
subgraph ToolSrv["toolsrv (child process)"]
TRV["9P Server\n· dynamic tool dispatch\n· landlock sandboxed execution\n· idle timeout + Pdeathsig lifecycle"]
end
subgraph Backends["LLM Backends"]
OLLAMA["Ollama"]
@ -98,15 +88,20 @@ The core is a single Go module (`ollie`) with no binary. Binaries live in `cmd/`
### Package Layout
| Package | Purpose |
|---|---|
| `agent/` | Agent struct, loop, history, compaction, hooks, commands, prompt resolution, state |
| `backend/` | Backend interface + LLM providers (Anthropic, OpenAI, Ollama, Gemini, Copilot, CodeWhisperer) |
| `toolsrv/` | Tool server: dynamic tool dispatch, sandboxed execution, result tiering, remote execution (ollie-remote) |
| `session/` | Session lifecycle, config, persistence |
| `detach/` | Background process management (ring buffer, signal) |
| `bypass/` | Bypass broker (privilege escalation daemon, policy) |
| `sandbox/` | Landrun sandbox configuration and command wrapping |
| `cmd/olliesrv/internal/agent/` | Agent struct, loop, history, compaction, hooks, commands, prompt resolution, state |
| `cmd/olliesrv/internal/backend/` | Backend interface + LLM providers (Anthropic, OpenAI, Ollama, Gemini, Copilot, CodeWhisperer) |
| `cmd/olliesrv/internal/fs/` | 9P filesystem: EDSL spec + handlers, ctl dispatch, session nodes |
| `cmd/olliesrv/internal/session/` | Session lifecycle, persistence, tool server spawning |
| `cmd/olliesrv/internal/bypass/` | Bypass broker (privilege escalation daemon, policy) |
| `cmd/olliesrv/internal/toolclient/` | Spawning and managing toolsrv child processes |
| `cmd/toolsrv/internal/fs/` | toolsrv filesystem spec, state, process management |
| `cmd/toolsrv/internal/exec/` | Sandboxed tool execution (landrun, bypass) |
| `cmd/toolsrv/internal/registry/` | Session-scoped tool registry |
| `cmd/toolsrv/internal/sandbox/` | Landrun sandbox configuration and command wrapping |
| `toolsrv/` | 9P client library for connecting to toolsrv |
| `fsedsl/` | Generic filesystem declaration EDSL (used by both servers) |
| `env/` | Session environment helpers |
| `log/` | Structured logging |
| `log/` | Structured logging (olliesrv) |
| `paths/` | XDG path resolution |
### Key Types
**`agent.Agent`** — the concrete struct frontends drive:

View File

@ -39,6 +39,7 @@ gantt
9P Declarative EDSL :done, 2026-08-02, 1d
Zero built-in tools :done, 2026-08-02, 2d
EDSL extraction + cleanup :done, 2026-08-03, 2d
toolsrv 9P + process isolation :done, 2026-08-10, 2d
```
## Phase 1: Monorepo Bootstrap (Apr 11)
Started as independent git repos unified under a monorepo with submodules.
@ -621,34 +622,18 @@ The KDE frontend received extensive updates across both days:
## Updated Current Topology
```
ollie/ ← single Go module
├── agent/ ← agent loop, history, hooks, commands
├── backend/ ← LLM providers (6 backends)
├── toolsrv/ ← tool server, registry, remote execution
├── session/ ← session lifecycle, config
├── fs/ ← 9P filesystem (EDSL-declared, flat package)
│ ├── spec.go Namespace declaration (single source of truth)
│ ├── fsnode.go FsNodeDecl type + Dir/Leaf/TemplateDir
│ ├── builder.go BuildTree — spec → wired *Tree
│ ├── rootfiles.go Root-level handlers
│ ├── sessionfiles.go Session-level handlers
│ ├── agentfiles.go Agent-level handlers
│ ├── bypassfiles.go Bypass handlers
│ ├── procfiles.go Process handlers
│ ├── lifecycle.go Create/kill/rename + event ring
│ ├── newroot.go NewRoot constructor
│ ├── persist.go Session persistence
│ ├── types.go Session/AgentLog types
│ ├── tree.go 9P *Tree
│ ├── fs.go 9P File/FileConfig
│ └── format.go Event formatting
├── detach/ ← background process management
├── bypass/ ← bypass broker
├── sandbox/ ← landrun sandbox config
├── cmd/
│ ├── olliesrv/ ← 9P server (sessions, agents, backends)
│ │ └── internal/ agent/, backend/, bypass/, fs/, session/, prompts/, toolclient/
│ ├── toolsrv/ ← 9P tool execution server (separate process)
│ │ └── internal/ fs/, exec/, registry/, sandbox/
│ ├── ollie-9p/ ← 9P client CLI
│ └── ollie-remote/ ← remote execution binary
├── toolsrv/ ← 9P client library for toolsrv
├── fsedsl/ ← filesystem declaration EDSL (used by both servers)
├── env/ ← environment helpers
├── log/ ← structured logging
├── paths/ ← XDG path resolution
├── mount/ ← 9P FUSE mount client (network transparency)
├── cmd/ ← binaries (olliesrv, ollie-9p, ollie-remote)
├── kde/ ← KDE Plasma (submodule)
├── data/tools/ ← tool executables + .meta sidecar files
├── data/agents/ ← agent configs (JSON)
@ -657,7 +642,6 @@ ollie/ ← single Go module
├── data/scripts/ ← ollie-remount, o CLI
├── data/services/ ← systemd, xdg-autostart
├── prompts/ ← embedded prompt templates
├── sandbox/ ← landrun sandbox profiles (YAML)
└── doc/ ← documentation
```
@ -1085,3 +1069,94 @@ Systematic audit of the prompt construction system and codebase maintainability.
| **Total (core)** | **12,793** |
Excludes: backends (4,229), fsedsl (1,030), tests, KDE, tools, generated code.
## Phase 20: toolsrv 9P Migration & Process Isolation (Aug 10–11)
The tool server completed its evolution from an in-process library to a
**fully independent 9P server** running as a child process of olliesrv.
### Before
`toolsrv` was a Go package (`ollie/toolsrv`) with a `Server` struct that ran
in-process within olliesrv. Tool calls were Go function calls — no process
boundary, no separate namespace. The package mixed client code, server code,
registry, sandbox wrappers, and execution logic in a flat directory.
### After
Two distinct components:
1. **`cmd/toolsrv/`** — a standalone binary that serves its own 9P2000
filesystem over a Unix socket. Has its own `internal/` packages:
- `internal/fs/` — filesystem spec (fsedsl), server state, process management
- `internal/exec/` — sandboxed tool execution (landrun, bypass broker)
- `internal/registry/` — session-scoped tool registry
- `internal/sandbox/` — landrun configuration (unchanged)
2. **`toolsrv/`** (root package) — a 9P client library. `Conn` dials toolsrv
over a Unix socket, authenticates via Tauth, and exposes methods like
`CallTool`, `LoadTool`, `ListTools`, `Ping`.
### toolsrv 9P Namespace
```
/
├── ctl write: load <tool>, unload <tool>, env K=V, cwd <path>
├── tools read: list loaded tools (JSON), write: tool name to load
├── info read: platform, arch
└── proc/
├── new rdwr: write tool+args, blocks, read result
├── new.bg write: tool+args, returns pid immediately
└── {pid}/
├── out read: output
├── wait read: blocks until exit, returns exit code
├── stat read: running/exited, runtime, tool
└── ctl write: signal <N>, dismiss
```
Both `cmd/toolsrv` and `cmd/olliesrv` declare their namespaces using the
same `fsedsl` library. Both implement their own 9P protocol handlers (using
`9fans.net/go/plan9`) — they are intentionally separate servers that
communicate over a socket.
### Process Lifecycle
Three-layer defense against orphaned toolsrv processes:
1. **Pdeathsig** — `SysProcAttr{Pdeathsig: SIGTERM}` on local spawn. When
olliesrv dies, the kernel terminates toolsrv immediately.
2. **Idle timeout** — toolsrv tracks active 9P connections. After the first
client connects and then all connections drop, a 30s timer starts.
If no new connection arrives, toolsrv exits cleanly. 60s startup grace
period for the initial connection.
3. **Kill-before-respawn** — `ProcessKeeper.Dial()` kills the old process
before spawning a replacement. Prevents accumulation during reconnect cycles.
### Authentication
toolsrv uses 9P Tauth for authentication. The first client sets the shared
secret; subsequent clients must provide the same secret. This replaces
the previous token-in-environment approach and is compatible with socket
permission security.
### Structural Consistency
Both servers now follow the same physical layout:
```
cmd/{server}/
├── main.go entry point
├── server.go 9P protocol handler (top-level, like Plan 9 tradition)
└── internal/
├── fs/ filesystem spec + handlers + state
├── ... domain-specific packages
```
### Logging
toolsrv uses `log/slog` (Go stdlib) with a `"svc": "toolsrv"` attribute.
This is intentionally separate from olliesrv's `ollie/log` package — they
are different programs with different lifecycles.