Agent orchestration with one lead, a team of specialists and one bill

Your lead agent decides at runtime who reviews, who tests and who researches, and can run them in parallel. Every specialist’s spend lands in the lead’s total, and its verified-or-not verdict feeds the lead’s done-gate.

curl -fsSL https://octomind.run/install.sh | bash
octomind -c supervisor.learning.enabled=false -c max_session_spending_threshold=0.40 run lead --name orch-demo --format jsonl < ../task.txt
{"type":"tool_use","tool":"agent_reviewer","tool_id":"call_723d5cc6733c45e98ea34d77","server":"agent","params":{"task":"Review the file pricing.py in /private/tmp/claude-usecases/agent-orchestration/work/project for bugs, logic errors, and code quality issues. Return findings as file:line references with brief explanations."},"session_id":"orch-demo"}
{"type":"tool_use","tool":"agent_tester","tool_id":"call_c4904262eece407b983fa999","server":"agent","params":{"task":"Analyze pricing.py in /private/tmp/claude-usecases/agent-orchestration/work/project and list concrete inputs or edge cases that would break it (crashes, wrong results, unhandled exceptions). Return specific failing input examples."},"session_id":"orch-demo"}
…
octomind 0.58.8 · captured

How it works

  1. Define your specialists

    A specialist is a role plus an [[agents]] entry. The command starts that role as an ACP child, so it gets its own prompt, model and tool list.

    ~/.local/share/octomind/config/agents.toml
    # ~/.local/share/octomind/config/agents.toml
    # Every *.toml in the config dir is merged into config.toml.
    [[mcp.servers]]
    name = "filesystem"
    type = "stdio"
    command = "octofs"
    args = ["mcp"]
    timeout_seconds = 30
    tools = []
    
    [[roles]]
    name = "reviewer"
    welcome = ""
    system = "You review code for bugs. Report file:line, the bug, and the fix."
    
    [roles.mcp]
    server_refs = ["filesystem"]
    allowed_tools = ["filesystem:view"]
    
    [[roles]]
    name = "tester"
    welcome = ""
    system = "You design test cases. List the inputs that would break the code."
    
    [roles.mcp]
    server_refs = ["filesystem"]
    allowed_tools = ["filesystem:view"]
    
    [[agents]]
    name = "reviewer"
    description = "Reviews code for bugs and returns file:line findings."
    command = "octomind acp reviewer"
    
    [[agents]]
    name = "tester"
    description = "Lists inputs that would break the given code."
    command = "octomind acp tester"
  2. Give the orchestrator agent its tools

    Grant the lead the agent server and every [[agents]] entry shows up as an agent_<name> tool. Add orchestration:tap and it can also start background specialists from your tap registry.

    ~/.local/share/octomind/config/lead.toml
    [[roles]]
    name = "lead"
    welcome = ""
    system = """
    You lead a small team. Hand focused work to your specialists with the agent_* tools,
    read what they return, and give the user one answer.
    """
    
    [roles.mcp]
    server_refs = ["agent", "orchestration", "filesystem"]
    allowed_tools = ["agent:*", "orchestration:tap", "filesystem:view"]
  3. Run the lead and let it delegate

    Ask for the outcome, not the plan. The lead picks which specialists to call and how many, at runtime. Set a session spend threshold: child spend counts toward it, and a non-interactive run stops when it is crossed.

    octomind config            # writes config.toml on first run
    octomind config --validate  # checks your roles and agents
    octomind -c max_session_spending_threshold=0.40 run lead

What you get

  • Each specialist is a tool the lead calls, sync or async

    Every [[agents]] entry becomes agent_<name>. Sync (the default) blocks until the child answers. async=true returns at once and injects the result as a message when the child finishes.

    src/mcp/agent/functions.rs:90
  • Background specialists from your tap registry

    tap(action="run", role="developer:general", prompt="…") starts octomind acp <role> in the background and returns an id. Pass session=<id> to continue the same specialist; list and stop manage runs.

    src/mcp/orchestration/tap.rs:584
  • One bill: child spend rolls up recursively

    Each child reports its running session cost, which already includes anything it delegated. The parent banks only the increment, so every dollar lands in your total once, and spend thresholds see it.

    src/mcp/agent/functions.rs:912
  • One verdict: the child’s check feeds the lead’s done-gate

    A finished child reports whether its own trajectory verified. The lead’s gate reads that verdict. A child that failed, was cancelled or reported nothing counts as unverified.

    src/mcp/agent/functions.rs:727
  • Clean handoffs, small returns

    A child starts with zero context: it sees only the task text. Every task gets a return contract: a summary of at most 2,000 tokens plus the exact file paths, never raw dumps into the lead’s context.

    src/mcp/agent/functions.rs:128

A real run: one lead, two specialists in parallel

Captured on octomind 0.58.8 with the config above and a 10-line pricing.py. The lead called agent_reviewer and agent_tester in the same turn; each child read the file with its own tools, and a specialist’s findings stream back into the lead’s log. The run was stopped before the final cost frame, so this capture shows the fan-out, not the rollup total.

octomind -c supervisor.learning.enabled=false -c max_session_spending_threshold=0.40 run lead --name orch-demo --format jsonl < ../task.txt
{"type":"tool_use","tool":"agent_reviewer","tool_id":"call_723d5cc6733c45e98ea34d77","server":"agent","params":{"task":"Review the file pricing.py in /private/tmp/claude-usecases/agent-orchestration/work/project for bugs, logic errors, and code quality issues. Return findings as file:line references with brief explanations."},"session_id":"orch-demo"}
{"type":"tool_use","tool":"agent_tester","tool_id":"call_c4904262eece407b983fa999","server":"agent","params":{"task":"Analyze pricing.py in /private/tmp/claude-usecases/agent-orchestration/work/project and list concrete inputs or edge cases that would break it (crashes, wrong results, unhandled exceptions). Return specific failing input examples."},"session_id":"orch-demo"}
{"type":"tool_use","tool":"view","tool_id":"call_-7144385489364051723","server":"","params":{"path":"/private/tmp/claude-usecases/agent-orchestration/work/project/pricing.py"},"session_id":"orch-demo"}
{"type":"tool_result","tool":"","tool_id":"call_-7144385489364051723","server":"","content":"1:ce|def apply_discount(price, percent):\n2:70|    \"\"\"Return price after a percent discount, never below zero.\"\"\"\n3:86|    if percent > 100:\n4:50|        percent = 100\n5:61|    return price - price * percent / 100\n6:c5|\n7:c5|\n8:61|def split_bill(total, people):\n9:29|    \"\"\"Split a bill evenly, rounded to cents.\"\"\"\n10:6b|    return round(total / people, 2)","success":true,"session_id":"orch-demo"}
…
{"type":"assistant","content":"Findings for `/private/tmp/claude-usecases/agent-orchestration/work/project/pricing.py`:\n\n1. **pricing.py:3-5** — Docstring promises \"never below zero\" but a negative `price` input returns a more negative result, and a negative `percent` increases the price. Fix: clamp `price = max(price, 0)` and `percent = max(percent, 0)` (or validate and raise).\n\n2. **pricing.py:5** — Percent > 100 is clamped, but the clamp runs after use of unchecked input elsewhere isn't an issue here; however clamping silently hides caller errors — raising `ValueError` for out-of-range percents would be cleaner.\n\n3. **pricing.py:10** — `split_bill` divides by `people` with no guard: `people=0` raises `ZeroDivisionError`, and negative `people` silently returns a nonsensical negative split. Fix: validate `people >= 1`.\n\n4. **pricing.py:10** — `round()` uses banker's rounding (0.005 rounds down), so per-person splits can be off by a cent inconsistently; also no indication of how the remainder cent is handled. Consider `decimal.Decimal` with `ROUND_HALF_UP` and distributing the remainder.\n\n5. **Code quality** — No type hints, no input validation for non-numeric types; `percent` expected as 0–100 but the API doesn't reject 0.5 being mistaken for a fraction.","session_id":"orch-demo"}
…
octomind 0.58.8 · captured

What it doesn't do

  • No recursion depth limit

    Nothing stops a role from calling itself, directly or through another agent. Keep each role’s agent tool list acyclic: specialists should not get the agent server. Docs →

  • No cap on concurrent tap runs

    The lead can start as many tap run specialists as it decides to. Set max_session_spending_threshold: child spend counts toward it, and a non-interactive run stops when it is crossed.

  • No filesystem isolation between children

    Children share the working tree. Give parallel writers distinct paths, a separate workdir, or their own git worktree. Docs →

  • Async agent calls are capped at your CPU core count

    Past the cap an async=true call returns "Async job limit reached". Split the work into batches or call some specialists sync.

  • No fixed order of steps

    The lead decides at runtime, so two runs of the same request can delegate differently. When every run must take the same steps, write a TOML workflow instead. Docs →

Questions

What is agent orchestration?

Agent orchestration is a lead agent deciding at runtime which specialist handles which part of a job, then merging the results into one answer. In octomind the lead is any role, and the specialists are [[agents]] entries or tap roles it calls as tools.

What is an orchestrator agent?

It is the agent that plans and delegates instead of doing every step itself. In octomind it is a normal role that has been granted the agent and orchestration tool servers.

Do I need a multi agent orchestration framework?

Not for this. Roles and agents are TOML entries in your config directory, and each specialist runs as an octomind acp <role> child, so there is no SDK or Python code to write.

How do I see what the specialists cost?

Their spend is folded into the lead session’s total, including spend from anything they delegated in turn. /status agents shows per-run usage and cost where the runtime reports it.

How many agents can run in parallel?

Async agent_* calls are capped at your CPU core count, with 4 as the fallback. Background tap run specialists have no cap, so put a session spend threshold on the lead.

Should I use agent orchestration or a workflow?

Use a workflow when you know the steps in advance: a fixed TOML pipeline with parallel, loop and best-of-N steps. Use orchestration when the lead should decide at runtime who does what and how many specialists to start.

Start with one command

Apache-2.0 and any model. Install it, then run step one above.

curl -fsSL https://octomind.run/install.sh | bash
Multi-agent delegation →