diff --git a/doc/MULTI_AGENT.md b/doc/MULTI_AGENT.md new file mode 100644 index 0000000..6e423a6 --- /dev/null +++ b/doc/MULTI_AGENT.md @@ -0,0 +1,148 @@ +# Multi-Agent Coordination + +ollie exposes agents as filesystem objects via the 9P protocol. This means +multi-agent coordination requires no framework, SDK, or message bus — it is +ordinary shell scripting against a mounted filesystem. + +## The Namespace + +Every session lives under `/s/{id}/`: + +| Path | Purpose | +|------|---------| +| `/s/new` | Create a session (write `name`, `backend`, `model`, `agent`, `cwd`) | +| `/s/{id}/prompt` | Submit a prompt (dispatched async on close) | +| `/s/{id}/chat` | Cumulative conversation history (append-only read) | +| `/s/{id}/state` | Current state: `idle`, `thinking`, `calling:` | +| `/s/{id}/statewait` | **Blocking read** — returns when state changes | +| `/s/{id}/ctl` | Control commands: `stop`, `kill`, `compact`, model/backend switches | +| `/s/{id}/cfg` | Mutable session config (KV pairs) | + +Utility files at the session root (`/s/`) provide higher-level operations: + +| File | Purpose | +|------|---------| +| `/s/b` | One-shot: create, submit, wait, print result, kill | +| `/s/bfg` | Batch foreground: submit to existing session, wait, print | +| `/s/bbg` | Batch background: submit, return session path immediately | +| `/s/sh` | Interactive shell session | + +Global namespaces are shared across all sessions: + +| Path | Purpose | +|------|---------| +| `/t/{name}` | Tool scripts (read/write) | +| `/t/idx` | Synthesized tool index (descriptions + args) | +| `/a/{name}.json` | Agent configs (supports `mv`, `cp`, `rm`) | +| `/sk/{name}` | Skill files | +| `/p/{name}` | Prompt templates | + +## Spawning Agents + +From within an agent session, use `subagent_spawn`: + +```sh +subagent_spawn [-name NAME] [-agent AGENT] [-backend BACKEND] [-model MODEL] [-cwd DIR] +``` + +This creates a child session, writes the prompt, and returns the session path +(e.g. `s/my-session`). Child sessions carry the parent session ID in their name, +so the spawning tree is recoverable from session IDs alone. + +## Coordination Patterns + +### Hub-and-Spoke (Conductor) + +A root agent reasons about a task and delegates subtasks to workers. Workers +report back via their chat history; the conductor reads `/s/{id}/chat` after +`statewait` unblocks. + +```sh +# Conductor spawns two workers +worker_a=$(subagent_spawn -model haiku "analyse /src/a.go") +worker_b=$(subagent_spawn -model haiku "analyse /src/b.go") + +# Wait for both +until [ "$(cat $worker_a/state)" = "idle" ]; do cat $worker_a/statewait; done +until [ "$(cat $worker_b/state)" = "idle" ]; do cat $worker_b/statewait; done + +# Collect results +cat $worker_a/chat $worker_b/chat +``` + +The conductor and workers can run on different models. Mechanical analysis goes +to cheaper models; reasoning stays on the expensive one. + +### Pipeline + +Agent A produces output; agent B consumes it. Composition via `/tmp/` or direct +path passing. + +```sh +step1=$(subagent_spawn "extract all TODOs from /src/ and write to /tmp/todos.txt") +until [ "$(cat $step1/state)" = "idle" ]; do cat $step1/statewait; done +subagent_spawn "prioritise the TODOs in /tmp/todos.txt and produce a plan" +``` + +### Scatter-Gather (Swarm) + +Spawn N workers in parallel, collect when all reach `idle`. Because `statewait` +blocks until state changes, no polling loop is needed. + +```sh +# Scatter +for f in /src/*.go; do + subagent_spawn "review $f for security issues" & +done +wait # wait for all subagent_spawn calls to return session paths +``` + +Or from within `execute_code` using parallel steps: + +```json +{ + "steps": [ + { "parallel": [ + { "code": "subagent_spawn 'task A'" }, + { "code": "subagent_spawn 'task B'" }, + { "code": "subagent_spawn 'task C'" } + ]} + ] +} +``` + +### Peer-to-Peer + +Any agent can read any other agent's session directory. There is no privileged +orchestrator role — agents coordinate by reading each other's `chat` and `state` +files directly. A peer can wait on another peer's `statewait`, then act on what +it reads in `chat`. + +### Reactive / Event-Driven + +`statewait` is a proper synchronization primitive. A shell script (or another +agent) blocks on it and wakes exactly when the target agent transitions state. +This enables event-driven pipelines without polling: + +```sh +# Block until agent finishes, then trigger downstream work +cat /s/worker-123/statewait +# agent is now idle — proceed +``` + +### Self-Directed Spawning + +Agents can spawn sub-agents mid-task without human direction. If an agent +determines that parallelism or specialization would help, it calls +`subagent_spawn` on its own. No human prompt is required to initiate a +multi-agent workflow — the model decides. + +## Design Principle + +ollie imposes no coordination model. There is no built-in conductor, swarm +layer, or workflow engine. Orchestration is handled by the surrounding +environment — shell scripts, cron, systemd, containers, or another agent. + +This is intentional. The filesystem namespace is the API. Any tool that can +read and write files can participate in a multi-agent workflow, which means the +full Unix toolchain is available as an orchestration layer.