doc: add sub-agents to evolution.md and README

This commit is contained in:
Levi Neely 2026-08-14 15:09:02 +02:00
parent 06cdc05c44
commit c25e2e708c
2 changed files with 102 additions and 0 deletions

View File

@ -61,6 +61,7 @@ Everything lives under `$XDG_CONFIG_HOME/ollie/` (default: `~/.config/ollie/`)
| **Remote execution** | Set `remote=user@host` in session config |
| **Background processes** | Pass `"background": true` to any tool — runs with no timeout, output pushed to agent prompt on completion |
| **Parallel execution** | Non-conflicting tool calls within a turn run in parallel (scope-based conflict scheduling) |
| **Sub-agents** | `subagent_spawn` — delegate work to a transient agent, block until done, get the reply. Multiple spawns run in parallel. |
| **Agents** | Write a JSON config in `~/.config/ollie/agents/` with agent prompt in `~/.config/ollie/prompts/` |
| **Tools** | Drop an executable + `.meta` in `~/.config/ollie/tools/` — load at runtime via `tool_load` |
| **Tool definitions** | Define tools with `.meta` files only — no wrapper script needed ([doc](doc/tool-definitions.md)) |

View File

@ -54,6 +54,7 @@ gantt
fs/ flattening :done, 2026-08-11, 1d
Feed + observer agents :done, 2026-08-13, 1d
BlockOnce/Stream refactor :done, 2026-08-13, 1d
Sub-agents via 9P rdwr :done, 2026-08-14, 1d
```
## Phase 1: Monorepo Bootstrap (Apr 11)
Started as independent git repos unified under a monorepo with submodules.
@ -1449,3 +1450,103 @@ Three related changes in one session. Net **+278 SLOC** across 21 files.
| `BlockOnceRaw` as primary API | Forced handlers to implement blocking themselves |
| Concurrent-by-default tool execution | Rejected: turns one reasoning step into N generation cycles with partial info — sequential but slower and more expensive |
## Sub-Agents via 9P rdwr (Aug 14)
Sub-agents implemented in **42 net lines of code**. The entire feature is
wiring — no new infrastructure, no new concepts, no new processes.
### The mechanism
`session/{sname}/agent/new` is an `Rdwr` file. Without `prompt=`, it creates
a persistent agent and returns its ID (existing behavior). With `prompt=`, it
enters sub-agent mode: creates a transient agent, submits the prompt, blocks
until the agent finishes, returns the reply, and destroys the agent.
```
printf 'cwd=%s\nprompt=fix the tests\n' "$PWD" \
| ollie-9p rdwr session/$OLLIE_SESSION_ID/agent/new
```
### Why it works in 42 lines
The infrastructure was already there:
- **`Rdwr` file primitive** (née `Request`) — atomic write-then-read, per-open isolation
- **`Agent.Submit()`** — already blocks through the full turn
- **`Agent.Reply()`** — already captures the final response
- **`Session.RemoveAgent()`** — already handles cleanup
- **Event bus** — normal agent lifecycle events fire (GUI sees sub-agents appear/disappear)
The handler is just: parse params → create agent → submit → read reply → destroy → return.
### Path-based lock table
Prerequisite: moved conflict serialization from the agent loop into toolsrv
itself. All foreground tool calls now acquire a path-based lock before execution:
- `scope "read"` — no lock (never conflicts)
- `scope "write"` — exclusive lock on the file path
- `scope "global"` — exclusive global lock
This ensures sub-agents writing to the same file serialize correctly regardless
of which agent initiated the call. The agent loop's batching remains as a
performance optimization; correctness is enforced at the execution layer.
### Parallel dispatch
`subagent_spawn` is declared `scope: "read"` — multiple spawn calls in the same
turn run in parallel automatically. The calling agent blocks until all results
return. No shell backgrounding, no polling, no coordination code.
```mermaid
sequenceDiagram
participant P as Parent Agent
participant S1 as Sub-Agent 1
participant S2 as Sub-Agent 2
P->>S1: subagent_spawn(prompt="task A")
P->>S2: subagent_spawn(prompt="task B")
Note over P: blocked (parallel tool calls)
S1-->>P: reply A
S2-->>P: reply B
Note over P: resumes with both results
```
### What was killed
- **`subagent_generate`** — JIT agent profile generation tool. Obsolete: sub-agents
use existing profiles directly.
- **D-Bus spawn path** — `subagent_spawn` previously used `dbus-send` to create
sessions. Replaced by a 3-line `ollie-9p rdwr` call.
### Framework rename: `Request` → `Rdwr`
The three atomic 9P operations are now named for what they are:
- `Read` — non-blocking read
- `Write` — non-blocking write (fire-and-forget)
- `Rdwr` — atomic write-then-read (blocking, produces result)
`BlockOnce` and `Stream` are special cases of `Read`.
`Rdwr` is its own primitive — not a variant of either.
### Architecture validation
This feature is evidence that the "everything is a file" architecture works at
scale. A major capability (parallel sub-agents with automatic serialization)
landed as pure wiring because:
1. The 9P namespace already exposes `agent/new` as an rdwr file
2. The agent loop already blocks and produces results
3. The tool server already serializes conflicting writes by path
4. The batching algorithm already parallelizes non-conflicting calls
No new abstractions. No new processes. No new protocols. Just connecting
existing pieces with 42 lines of glue.
### Future work
- **Shared toolsrv per host** — currently each session spawns its own toolsrv.
Cross-session path serialization requires sessions to share a single toolsrv
instance per host.
- **Budget/depth controls** — token limits, step limits, and recursion depth
caps for sub-agents.