7.0 KiB
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 /session/{name}/ where {name} is the session's
mutable human-readable name (not the immutable UUID — see id and name
files). Session directories are keyed by Name; renaming a session via writing
to its name file moves the entire directory subtree.
| Path | Purpose |
|---|---|
/session/new |
Create an empty session (write name=..., read back name) |
/session/{name}/agent/new |
Create an agent within a session (write cwd=... backend=...) |
/session/{name}/env |
Session environment variables |
/session/{name}/id |
Immutable session UUID |
/session/{name}/name |
Mutable session name (write to rename) |
/session/{name}/agent/{aname}/prompt |
Submit a prompt |
/session/{name}/agent/{aname}/chat |
Streaming chat (filtered text) |
/session/{name}/agent/{aname}/plan |
Agent-scoped markdown checklist |
/session/{name}/agent/{aname}/cfg |
Agent config KV: backend, model, agent, cwd, params |
/session/{name}/agent/{aname}/statewait |
Blocking read — returns when state changes |
/session/{name}/agent/{aname}/ctl |
Control commands: stop, kill, compact, rn <name> |
/session/{name}/agent/{aname}/id |
Immutable agent ID (uname) |
/session/{name}/agent/{aname}/name |
Mutable display name (write to rename) |
Global request-response files provide stateless operations without sessions:
| File | Purpose |
|---|---|
/generate |
Write prompt, read response (one-shot generation) |
Global namespaces are shared across all sessions:
| Path | Purpose |
|---|---|
/session/idx |
Session index (id, state, cwd, backend, model) |
/session/{name}/agent/{aname}/plan |
Agent checklist |
/session/{name}/agent/{aname}/chat |
Streaming filtered text |
Spawning Agents
From within an agent session, use subagent_spawn:
subagent_spawn [-name NAME] [-agent AGENT] [-backend BACKEND] [-model MODEL] [-cwd DIR] <prompt>
This creates a child session + agent atomically, writes the prompt, and returns
the session path (e.g. session/my-session). Child sessions carry the parent
session ID in their name, so the spawning tree is recoverable from session IDs alone.
For transient subagent_spawn calls, the child gets its own context window and
runtime state. It receives a one-time snapshot of the parent's conversation
history, not a live shared context. The child cannot mutate the parent's
history, and the parent receives only the child's final reply. The parent
explicitly decides how to incorporate that reply. Workspace changes and other
shared resources are coordinated separately through toolsrv locks.
Explicit Two-Step Creation
Session creation can be split into two phases for advanced orchestration scenarios:
- Create empty session:
echo "name=my-session" | ollie-9p rdwr session/new - Add agent:
echo "cwd=$HOME agent=default backend=openai model=gpt-4o" | ollie-9p rdwr session/my-session/agent/new
Empty sessions (Core=nil) are valid — they appear in session/idx, have
no running agent, and can be killed or renamed. This enables pre-provisioning
session directories before the agent environment is ready.
Coordination Patterns
Hub-and-Spoke (Conductor)
flowchart TB
ROOT["Root Agent\n(reasoning)"]
W1["Worker A\n(cheap model)"]
W2["Worker B\n(cheap model)"]
ROOT -- "subagent_spawn" --> W1
ROOT -- "subagent_spawn" --> W2
W1 -- "statewait → read chat" --> ROOT
W2 -- "statewait → read chat" --> ROOT
A root agent reasons about a task and delegates subtasks to workers. Workers
report back via their log; the conductor reads /session/{name}/agent/{aname}/log after
statewait unblocks.
# 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
flowchart LR
A["Step 1\nExtract TODOs"]
B["Step 2\nPrioritise"]
A -->|"/tmp/todos.txt"| B
Agent A produces output; agent B consumes it. Composition via /tmp/ or direct
path passing.
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)
flowchart TB
P["Parent"]
S1["Worker 1"]
S2["Worker 2"]
S3["Worker N"]
P --> S1
P --> S2
P --> S3
S1 --> P
S2 --> P
S3 --> P
Spawn N workers in parallel, collect when all reach idle. Because statewait
blocks until state changes, no polling loop is needed.
# 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 shell:
{
"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:
# Block until agent finishes, then trigger downstream work
cat /session/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.