08 / 20 Subagents & Swarm src/tools/AgentTool/ · src/utils/swarm/

3 levels. Forks share prompt cache.

Subagents are in-process — no forking, no subprocess. The parent's conversation context is cloned, tools are resolved per-agent, and results return inline. Fork subagents go further: byte-identical message history ensures prompt cache hits across all children.

3
Agent levels — subagent, fork, swarm team
200
Max turns for general-purpose subagent
8
Colors in agent palette

Subagent Lifecycle — Step by Step

What happens when AgentTool.call({ prompt, subagent_type }) is invoked.

1
Resolution — which agent type?
Checks isForkSubagentEnabled(). Routes to FORK_AGENT or resolves named agent from registry (Explore, Plan, general-purpose, etc.). Validates agent exists and passes permission rules.
2
Tool pool assembled
resolveAgentTools() applies 3 filtering layers: (a) remove ALL_AGENT_DISALLOWED_TOOLS, (b) remove CUSTOM_AGENT_DISALLOWED_TOOLS for custom agents, (c) filter non-async tools if async agent. Wildcard ['*'] means all filtered tools. Fork path sets useExactTools: true for byte-identical API prefix.
3
System prompt built
Fork path: inherits parent's renderedSystemPrompt (cached bytes) to prevent GrowthBook divergence that would bust prompt cache. Normal path: calls selectedAgent.getSystemPrompt() for agent-specific instructions.
4
MCP servers initialized (if agent defines them)
Agents can define MCP servers in frontmatter. Inline definitions: agent-owned, cleaned up on exit. Referenced definitions: shared via memoization. Plugin-only mode respects admin-trusted sources only.
5
File state cache cloned
cloneFileStateCache() gives the agent its own read cache (LRU). Parent's cached file reads are inherited — prevents redundant disk I/O.
6
Agent runs — sync or async
Sync: wraps in runWithAgentContext() for analytics attribution. Executes runAgent() inline, blocks until completion. Async: registers via registerAsyncAgent(), spawns in detached closure, returns async_launched immediately.
7
Cleanup & return
Transcript recorded to .claude/sessions/<id>/sidechain/<agentId>/. Invoked skills and dump state cleared. Analytics event logged (tengu_agent_tool_completed). Result text extracted and returned to parent.

Fork Subagents — Cache-Optimized Parallelism

buildForkedMessages() creates messages where every fork shares an identical byte prefix — only the final directive differs. This maximizes prompt cache hits.

How fork messages are built

  1. Clone parent's assistant message — all tool_use blocks, thinking, text
  2. Generate synthetic tool_result blocks for every tool_use — all identical: "Fork started — processing in background"
  3. Append per-child directive in final text block with fork rules
  4. Result: all forks have byte-identical prefix; only the directive text differs
Parent conversation — system prompt + user messages + assistant tool_use blocks
↓ buildForkedMessages()
Fork 1
identical prefix + task A
Fork 2
identical prefix + task B
Fork 3
identical prefix + task C
Prompt cache hit — shared prefix is byte-identical across all forks
Fork rules injected: (1) You ARE the fork, not the main agent. (2) Do NOT spawn sub-agents. (3) Use tools directly, silently. (4) Commit changes before reporting. (5) Report structured facts in <500 words. Guard: FORK_BOILERPLATE_TAG prevents recursive forking.

Built-in Agent Types

AgentToolsMax TurnsModelUse Case
General PurposeAll (*)200InheritedComplex multi-step tasks
ExploreRead-only (no Edit/Write/Agent)50ConfigurableCodebase research
PlanRead-only (no Edit/Write/Agent)50ConfigurableArchitecture design
Claude Code GuideGlob, Grep, Read, WebFetch, WebSearch30InheritedDocumentation Q&A
Statusline SetupRead, Edit10InheritedStatus line config
VerificationConfigurable20InheritedResult verification

Swarm Teams (Level 3)

Leader + multiple teammates. File-based mailbox communication. Permissions routed through leader's UI.

Team Topology

Leader creates team via TeamCreateTool, spawns teammates, approves permissions, receives idle notifications. Deterministic agent ID: formatAgentId("lead", teamName).

Teammates get assigned names, colors, prompts. Run in-process (AsyncLocalStorage), tmux panes, or iTerm tabs. File at ~/.claude/teams/<name>/team.json.

Permission Sync

Teammate writes SwarmPermissionRequest to permissions/pending/. Leader polls every 500ms, shows UI dialog. Response moved to resolved/. "Always allow" rules propagate to teammate's context.

Idle notification: On Stop hook, teammate marks itself as idle in team config, sends idle notification to leader's mailbox with last peer DM summary.

Backend Comparison

BackendAvailabilityIsolationTerminationOverhead
In-ProcessAlways (default)AsyncLocalStorageAbortControllerZero fork
TmuxIf installedSeparate pane (30/70 split)Kill paneShell init 200ms
iTerm2If in iTerm + it2 CLISeparate tab (vertical split)Kill paneProcess fork

Agent Memory

Three scopes for agent-specific persistent knowledge.

ScopePathVCS
User~/.claude/agent-memory/<type>/Not committed
Project.claude/agent-memory/<type>/Committable
Local.claude/agent-memory-local/<type>/Not committed
Remote$CLAUDE_CODE_REMOTE_MEMORY_DIR/<project>/<type>/Remote mount
Memory snapshots: Before spawning, checkAgentMemorySnapshot() checks if a newer snapshot exists. If so, pulls fresh context before the agent starts — ensures agents don't work with stale knowledge.