ollie/doc/resources/multi-agent.md

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:

  1. Create empty session: echo "name=my-session" | ollie-9p rdwr session/new
  2. 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.