Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sub-Agents

Sub-agents are child agent instances spawned by the harness to run a scoped task with a filtered toolset. The Zellij host opens a plugin pane; Montana starts another component instance on a thread in the same process. Both use files to communicate with the parent. Montana peers share its preopened directories and HTTP client; spawning a peer does not create another OS security boundary.

Source: crates/core/src/subagent.rs, crates/core/src/spawn.rs, crates/core/src/subagent_config.rs.

How spawning works

The parent calls the built-in spawn_subagent tool. The harness then:

  1. Writes the task to prompt.md and the filtered tool manifest to tools.json in a new directory under <cowboy_dir>/subagents/.
  2. Requests a peer through the host, passing a JSON-encoded SubagentConfig under the subagent_config key. Zellij loads a visible plugin pane; Montana’s supervisor creates a component instance with its own agent ID.
  3. The child, on its first heartbeat tick, writes a lock file and its pane_id, reads prompt.md + tools.json, and processes the prompt as a user message.
  4. When the child finishes, it writes response.md and removes lock.
  5. The parent detects completion on its heartbeat poll and injects the response back into its own conversation.

The shared spawn protocol uses host-executed shell commands for its spool files. WebAssembly does not itself forbid file I/O: Montana also grants direct WASI access to preopened directories. Pane visibility is a Zellij affordance; Montana has no terminal panes.

Directory layout

Each sub-agent gets a directory named <type>-<short_uuid>:

<cowboy_dir>/subagents/<type>-<id>/
  prompt.md     # task + metadata, written by parent
  tools.json    # filtered tool manifest, written by parent
  lock          # present while running, written by child
  pane_id       # host instance id (a pane id under Zellij)
  response.md   # final output, written by child on completion

<cowboy_dir> is $HOME on a ranch install and $XDG_DATA_HOME/cowboy (default ~/.local/share/cowboy) in lite mode.

Sub-agent types

Three types are defined in the SubAgentType enum. Each restricts the tools the child may call:

TypeAllowed tools
Researchread, search, find, web-search, ls
Coderead, write, search, find, bash, ls, __HASHLINE_READ__, __HASHLINE_EDIT__
Reviewread, search, find, bash, ls

Only Code gets write and the hashline edit tool. Review still has bash, so its tool list does not enforce read-only filesystem access. The configured host and tool policy determine what commands can do.

Models

SubagentConfig::cheap_defaults() selects the child’s provider and model. Its fallback model names are:

ProviderDefault sub-agent model
OpenAIgpt-4o-mini
OpenRouteropenai/gpt-4o-mini
Anthropicclaude-3-5-haiku-latest
Ollamamistral:7b
Codexgpt-5.6-luna
Vercelzai/glm-5.3-flash

An explicit subagent_model wins when that provider has a non-empty key. Otherwise the main harness provider/model is used when it is usable (a key, or a keyless Ollama/Codex provider), falling back to the first keyed provider in the order OpenRouter, Anthropic, OpenAI, Vercel — so the Ollama and Codex defaults above are only reached through the main model.

Status

The parent tracks each child with the SubAgentStatus enum:

  • Starting — directory created, peer starting
  • Running — lock present
  • Completed — response.md present, lock removed
  • Failed(String) — lock removed without a response.md

Polling happens on the heartbeat timer.