Experimental Research fork — APIs and behaviour may change without notice. Read disclaimer →

This is a hands-on guide to using picobot sub-agents. For the technical reference (depth tracking, registry internals, config), see Sub-agents.

What Are Sub-agents?

Sub-agents are background workers. You give one a task, it runs independently using its own context window, and reports back when done. The parent agent continues working while the sub-agent runs.

Think of it like delegating to a colleague: you hand them a brief, they go away and do the work, you carry on with other things, and they come back with results.

When to Use Them

Good reasons to spawn a sub-agent:

  • Parallel research. "Research Rust, Go, and Zig advantages" — spawn three sub-agents, each researches one language, results come back in parallel.
  • Long-running tasks. "Audit all Python files in this project" — let a sub-agent grind through it while you answer other questions.
  • Context isolation. The parent is mid-conversation and doesn't want to pollute its context with a deep tangent.

Bad reasons:

  • Simple tool calls. Don't spawn a sub-agent to read one file. Just read it.
  • Tasks that need conversation context. Sub-agents can't see the parent's chat history.
  • Tasks that need coordination. Sub-agents can't talk to each other.

The Three Tools

spawn — Start a sub-agent

spawn(task="...", label="my-task")

The task string is everything the sub-agent receives. It has no other context. Write it like a brief for someone who just walked in the door.

The label is optional but makes status tracking readable.

wait_for_subagents — Block until done

wait_for_subagents(ids=["abc123"], timeout=120)

This is the key tool. It blocks until the specified sub-agents finish, then returns their results. It costs zero tokens while waiting — no API calls are made during the wait. The parent's ReAct loop simply pauses at this tool call.

If you omit ids, it waits for all running sub-agents. The timeout defaults to 120 seconds.

check_subagents — Status check and cancel

check_subagents(action="list")              # overview of all sub-agents
check_subagents(action="check", id="abc123") # detailed live state for one
check_subagents(action="cancel", id="abc123") # kill a stuck sub-agent

Use list for a quick glance at what's running, check for detailed inspection (iteration progress, tool call log, files written), and cancel to kill a stuck or off-track sub-agent. But don't use this in a polling loop — that burns tokens on every check. Use wait_for_subagents instead.

Writing Good Task Descriptions

The most common mistake is writing vague sub-agent tasks. The sub-agent has no context from your conversation — the task string is all it gets.

Bad:

spawn(task="Research Python web frameworks")

What should it research? Where should it put the results? What format? The sub-agent will make something up.

Good:

spawn(task="""Search the web for Flask, FastAPI, and Django.
For each framework, find: year created, async support (yes/no), ORM included (yes/no), latest stable version.
Write a markdown comparison table to ~/workspace/frameworks.md with columns: Framework, Year, Async, ORM, Version.
Include a 2-3 sentence summary of each framework's main use case below the table.""")

The checklist for a good sub-agent task:

  1. What to do — specific actions, not vague goals
  2. What to produce — exact file paths and formats
  3. What success looks like — how to verify it's done correctly

The Typical Pattern

Here's how a well-structured sub-agent workflow looks:

1. spawn(task="detailed instructions...", label="my-task")
   → Sub-agent starts in background

2. [Parent does its own work — writes files, runs commands, etc.]

3. wait_for_subagents(ids=["abc123"])
   → Parent blocks (zero cost) until sub-agent finishes
   → Returns the sub-agent's results

4. [Parent reads sub-agent output files, integrates results]

5. [Parent writes final deliverable combining its work + sub-agent results]

The parent should always have something useful to do between spawn and wait. If it has nothing to do, that's a sign the task shouldn't be delegated — just do it directly.

Parallel Sub-agents

Spawn multiple sub-agents for parallel work:

spawn(task="Research Rust advantages...", label="rust-research")
spawn(task="Research Go advantages...", label="go-research")
spawn(task="Research Zig advantages...", label="zig-research")

[Do your own work while they run]

wait_for_subagents()  # waits for all three

All three run concurrently. wait_for_subagents() with no ids waits for all of them.

What Sub-agents Can and Can't Do

Always available (core tools):

  • Read, write, edit files and list directories
  • Run shell commands

Available on request (pass tools=["web_search", "kb"] etc. to spawn):

  • Search the web and fetch URLs (web_search, web_fetch)
  • Read and write to the knowledge base (kb)
  • Discover and load additional tools (tool_search)
  • Use the browser (browser, requires playwright)
  • Spawn their own sub-agents (spawn, up to max depth)

They can't:

  • See the parent's conversation history
  • Talk to other sub-agents
  • Schedule cron jobs
  • Access the parent's tool registry (they get their own)

Keeping the tool set lean matters — each extra tool adds schema tokens to every LLM call in the sub-agent's loop. A monitoring task that only needs exec and write_file doesn't need web_search or kb.

What Happens When Things Go Wrong

Sub-agent fails: The error is captured in the registry. When the parent calls wait_for_subagents, it sees the failure message. The parent can then handle it — retry, do the work itself, or report the error.

Sub-agent times out: If a sub-agent exceeds the wait_for_subagents timeout, the parent gets a note saying it's still running. The sub-agent continues in the background — it doesn't get killed. The parent can wait again later or move on.

Sub-agent writes garbage: This is usually a task description problem. If the sub-agent produces poor output, improve the task instructions — be more specific about expected format and content.

Depth and Nesting

Sub-agents can spawn their own sub-agents, up to a configurable limit (default: 2 levels).

Parent (depth 0)
  └── Sub-agent A (depth 1) — can spawn
        └── Sub-agent B (depth 2) — cannot spawn (at max depth)

At max depth, the spawn tool isn't available. The sub-agent knows this — its system prompt says "do not spawn further sub-agents."

If you find yourself wanting depth 3+, rethink the task decomposition. Two levels is usually enough.

Monitoring

Sub-agents are tracked in ~/.picobot/data/subagents/registry.json. Each entry records:

  • Task ID and label
  • Status (running, completed, failed)
  • Spawn and completion timestamps
  • The full result or error message
  • Parent session key (for delivery routing)

The dashboard (picobot dashboard) shows running sub-agents in real time.

In interactive mode, you'll see a console notification when a sub-agent finishes:

[Sub-agent "rust-research" completed]

Quick Reference

Tool Purpose Token cost
spawn Start a background sub-agent N/A (returns immediately)
wait_for_subagents Block until sub-agents finish Zero while waiting
check_subagents(list) Overview of all sub-agents One API round-trip
check_subagents(check) Detailed live state for one One API round-trip
check_subagents(cancel) Kill a stuck sub-agent One API round-trip