Skip to content

Latest commit

 

History

History
287 lines (213 loc) · 15.7 KB

File metadata and controls

287 lines (213 loc) · 15.7 KB

Agent Orchestration

Overview

Claude Code supports spawning multiple sub-agents that work in parallel or sequentially on complex tasks. The agent system supports local execution, git worktree isolation, remote execution, fork-based context sharing, inter-agent messaging, and team-based coordination. The implementation lives primarily in src/tools/AgentTool/ with supporting infrastructure in src/tasks/, src/coordinator/, and src/utils/swarm/.

Agent Spawning (AgentTool)

AgentTool (src/tools/AgentTool/AgentTool.tsx) is the primary mechanism for creating sub-agents. The tool accepts a prompt, optional agent type, and execution parameters to determine how the agent runs.

Input Schema

The AgentTool input schema includes:

Parameter Type Description
prompt string (required) The task for the agent to perform
description string (required) Short 3-5 word task summary
subagent_type string (optional) Specialized agent type to use
model enum (optional) Model override: sonnet, opus, or haiku
run_in_background boolean (optional) Run asynchronously, return task ID
name string (optional) Addressable name for SendMessage routing
team_name string (optional) Team context for spawning
mode string (optional) Permission mode for the agent
isolation enum (optional) "worktree" for git isolation, "remote" for cloud
cwd string (optional) Working directory override

Routing Decision

The tool determines execution mode based on the combination of parameters:

AgentTool.call(input)
     |
     +-- subagent_type omitted + fork enabled --> Fork Agent
     |                                           (inherits parent context)
     |
     +-- isolation: "worktree" -----------------> Worktree Agent
     |                                           (isolated git copy)
     |
     +-- isolation: "remote" -------------------> Remote Agent (CCR)
     |                                           (cloud environment)
     |
     +-- run_in_background: true ---------------> Background Agent
     |                                           (async, returns task ID)
     |
     +-- Default -------------------------------> Foreground Agent
                                                 (blocks until complete)

Local Foreground Agent

When no special isolation or background mode is requested:

  • Calls runAgent() (src/tools/AgentTool/runAgent.ts) which invokes the query() function with isolated state
  • Gets its own token budget and file state cache (cloned from parent via cloneFileStateCache)
  • Inherits parent's tool permissions but with agent-specific tool restrictions applied via resolveAgentTools()
  • Parent's messages are normalized for isolation
  • Runs the full query loop within the same process, blocking the parent until completion
  • Result is returned directly as the tool result

Fork Agent

When the fork subagent feature is enabled (src/tools/AgentTool/forkSubagent.ts) and subagent_type is omitted:

  • Creates a child that inherits the parent's full conversation context and system prompt
  • Uses buildForkedMessages() to construct the child's message history: all parent tool_use blocks get identical placeholder results for prompt cache sharing, with a per-child directive appended
  • Always runs in the background (async) for a unified <task-notification> model
  • Fork children are explicitly instructed not to spawn sub-agents themselves (recursive forking is blocked by detecting the fork_boilerplate tag in history)
  • The FORK_AGENT definition uses tools: ['*'] with useExactTools to receive the parent's exact tool pool for cache-identical API prefixes
  • permissionMode: 'bubble' surfaces permission prompts to the parent terminal
  • model: 'inherit' preserves the parent's model for context length parity and cache sharing
  • Mutually exclusive with coordinator mode and non-interactive sessions

Worktree Agent

When isolation: "worktree" is specified:

  • createAgentWorktree() (src/utils/worktree.ts) creates a temporary git worktree with its own branch
  • Agent operates on a fully isolated copy of the repository
  • On completion, hasWorktreeChanges() checks if the agent made modifications
  • If no changes: worktree is auto-cleaned via removeAgentWorktree()
  • If changes exist: worktree path and branch name are returned in the result, allowing the parent to merge
  • Fork agents can also use worktrees; a notice is injected via buildWorktreeNotice() telling the child to translate paths from the parent context

Background Agent

When run_in_background: true or the agent definition has background: true:

  • Registered as an async task via registerAsyncAgent() with a unique agent ID
  • Control returns to the parent immediately with the task ID and output file path
  • The agent runs via runAsyncAgentLifecycle() which manages progress tracking, summarization, and notification
  • On completion, enqueueAgentNotification() delivers a <task-notification> to the parent's message queue
  • Parent can also check progress via TaskGetTool / TaskOutputTool
  • Parent can send follow-up messages via SendMessageTool (which auto-resumes stopped agents)

Remote Agent (Internal Only)

When isolation: "remote" is specified (restricted to internal Anthropic users):

  • checkRemoteAgentEligibility() validates preconditions (MCP servers, resource limits)
  • registerRemoteAgentTask() creates a RemoteAgentTaskState entry
  • The agent is teleported to a separate cloud environment via teleportToRemote()
  • Always runs as a background task; parent is notified when complete
  • Remote session events are polled via pollRemoteSessionEvents()
  • Used for heavy or long-running workloads in trusted sandbox environments

Built-in Agent Types

Built-in agents are defined in src/tools/AgentTool/built-in/ and assembled by getBuiltInAgents() in src/tools/AgentTool/builtInAgents.ts.

Agent Type Purpose Model Key Restrictions
general-purpose Default agent for multi-step tasks, code search, research Default subagent model All tools (['*'])
Explore Fast read-only codebase exploration (glob, grep, read) haiku (external), inherit (internal) No Agent, Edit, Write, NotebookEdit, ExitPlanMode
Plan Architecture and implementation planning (read-only) inherit No Agent, Edit, Write, NotebookEdit, ExitPlanMode
verification Adversarial testing of implementation work inherit No Agent, Edit, Write, NotebookEdit, ExitPlanMode; always background
claude-code-guide Help users understand Claude Code, Agent SDK, Claude API (default) Non-SDK entrypoints only
statusline-setup Create/update statusLine settings from shell PS1 config (default) Specialized for settings

Agent Definition Structure

Each agent definition (AgentDefinition type in src/tools/AgentTool/loadAgentsDir.ts) includes:

type BaseAgentDefinition = {
  agentType: string           // Unique identifier
  whenToUse: string           // Description shown to the model
  tools?: string[]            // Allowlisted tools (undefined = all)
  disallowedTools?: string[]  // Denylisted tools
  skills?: string[]           // Skill names to preload
  mcpServers?: AgentMcpServerSpec[]  // Agent-specific MCP servers
  hooks?: HooksSettings       // Session-scoped hooks
  color?: AgentColorName      // Visual color in UI
  model?: string              // Model override or 'inherit'
  effort?: EffortValue        // Reasoning effort level
  permissionMode?: PermissionMode
  maxTurns?: number           // Max agentic turns before stopping
  background?: boolean        // Always run as background task
  initialPrompt?: string      // Prepended to first user turn
  memory?: AgentMemoryScope   // Persistent memory: 'user' | 'project' | 'local'
  isolation?: 'worktree' | 'remote'
  omitClaudeMd?: boolean      // Skip CLAUDE.md for read-only agents
}

Agent Sources

Agents can come from multiple sources, with later sources overriding earlier ones for the same agentType:

  1. Built-in (source: 'built-in'): Hardcoded in src/tools/AgentTool/built-in/
  2. Plugin (source: 'plugin'): Loaded via loadPluginAgents()
  3. User settings (source: 'userSettings'): From user's ~/.claude/agents/ directory
  4. Project settings (source: 'projectSettings'): From project's .claude/agents/ directory
  5. Flag settings (source: 'flagSettings'): From GrowthBook feature flags (JSON format)
  6. Policy/managed (source: 'policySettings'): From enterprise/managed policy

Custom agents are defined as markdown files with YAML frontmatter containing the agent metadata, and the markdown body serving as the system prompt. They can also be defined in JSON format via parseAgentFromJson().

Inter-Agent Communication

SendMessageTool

SendMessageTool (src/tools/SendMessageTool/SendMessageTool.ts) routes messages between agents using a mailbox-based system.

Message Types (discriminated union):

Type Description
Plain text string Regular message content (requires summary)
shutdown_request Ask a teammate to shut down (with optional reason)
shutdown_response Approve or reject a shutdown request
plan_approval_response Approve or reject a plan (team lead only)

Routing targets:

  • By name: Routes to a specific teammate by name or agent ID
  • Broadcast (to: "*"): Sends to all teammates in the current team (except sender)
  • UDS socket (to: "uds:<path>"): Cross-session local peer messaging
  • Bridge (to: "bridge:<session-id>"): Cross-machine Remote Control peer messaging

Agent resumption: When a message is sent to a stopped agent (via name or agent ID), SendMessageTool automatically resumes it via resumeAgentBackground(). The agent is reconstituted from its disk transcript with the new message appended, preserving full context. Running agents receive messages via queuePendingMessage() for delivery at their next tool round.

Mailbox system: Messages are written to per-agent mailbox files via writeToMailbox() (src/utils/teammateMailbox.ts). Each message includes sender name, text content, timestamp, summary, and optional color for UI display.

Team Agents

Teams are managed through two tools:

TeamCreateTool (src/tools/TeamCreateTool/TeamCreateTool.ts):

  • Creates a named team with a team file on disk
  • Registers the team lead with a unique agent ID
  • Each team member gets an assigned color via assignTeammateColor()
  • Team file tracks all members, their pane IDs, backend types, and colors

TeamDeleteTool (src/tools/TeamDeleteTool/TeamDeleteTool.ts):

  • Cleans up a team and its resources

Teams support multiple backend types for spawning teammates:

  • In-process: Runs within the same process (used for in-process teammates)
  • Tmux: Spawns in a tmux pane
  • iTerm: Spawns in an iTerm tab

Coordinator Mode

Coordinator mode (src/coordinator/coordinatorMode.ts) is a distinct operating mode enabled via CLAUDE_CODE_COORDINATOR_MODE=1:

  • The coordinator gets a special user context listing available worker tools via getCoordinatorUserContext()
  • Worker tools are filtered to exclude internal team management tools (TeamCreate, TeamDelete, SendMessage, SyntheticOutput)
  • The coordinator uses getCoordinatorAgents() to get coordinator-specific agent definitions
  • Session mode is persisted; resuming a coordinator session re-enters coordinator mode via matchSessionMode()
  • Mutually exclusive with fork subagent mode

Task Lifecycle

Tasks are tracked in the application state with the following lifecycle:

registered --> running --> completed
                 |              |
                 +--> failed    +--> Results available via
                 |                   TaskOutputTool at output_file
                 +--> stopped
                      (by TaskStopTool or abort)

Task infrastructure (in src/tasks/):

  • LocalAgentTask (src/tasks/LocalAgentTask/LocalAgentTask.tsx): Tracks in-process agent execution with progress metrics (tool use count, token count, recent activities)
  • RemoteAgentTask (src/tasks/RemoteAgentTask/RemoteAgentTask.tsx): Tracks remote cloud agent sessions with polling-based progress
  • LocalShellTask (src/tasks/LocalShellTask/): Tracks shell command execution within agents

Progress tracking:

Each agent maintains a ProgressTracker with:

  • toolUseCount: Number of tool invocations
  • latestInputTokens: Latest cumulative input token count
  • cumulativeOutputTokens: Sum of all output tokens
  • recentActivities: Last 5 tool activities with descriptions

Task management tools:

Tool Purpose
TaskListTool List all registered tasks with status
TaskGetTool Get detailed info for a specific task
TaskOutputTool Read the output file of a completed task
TaskStopTool Stop/abort a running task

Output persistence: Agent output is written to disk via initTaskOutputAsSymlink() / appendTaskOutput(). The output file path follows the pattern tied to the agent ID, accessible via getTaskOutputPath().

Agent Memory

Agents can have persistent memory scoped to different levels (src/tools/AgentTool/agentMemory.ts):

  • user: Per-user memory, synced via project snapshots
  • project: Per-project memory
  • local: Local-only memory

Memory is loaded via loadAgentMemoryPrompt() and appended to the agent's system prompt. When memory is enabled, file read/write/edit tools are automatically injected into the agent's tool set. Snapshot initialization (agentMemorySnapshot.ts) handles copying project snapshots to local storage on first use.

Agent-Specific MCP Servers

Agents can declare their own MCP servers (mcpServers in the agent definition) that are additive to the parent's MCP clients:

  • Servers can be referenced by name (matching an existing configured server) or defined inline with full config
  • initializeAgentMcpServers() connects agent-specific servers when the agent starts
  • Cleanup function disconnects them when the agent finishes
  • Agent availability can be gated on required MCP servers via requiredMcpServers (pattern matching against available server names)

Prompt Cache Optimization

The agent system includes several optimizations for API prompt cache sharing:

  1. Agent list attachment: The dynamic agent list can be injected as an attachment message rather than embedded in the tool description, preventing cache busts when MCP servers or permissions change (shouldInjectAgentListInMessages())
  2. Fork prefix sharing: All fork children produce byte-identical API request prefixes using placeholder tool results, with only the final directive text block differing per child
  3. Model inheritance: Fork agents use model: 'inherit' to stay on the parent's model, enabling cache reuse
  4. Exact tool pools: Fork agents receive the parent's exact tool definitions (useExactTools: true) for cache-identical tool schemas

Design Patterns

  • Factory: AgentTool creates the appropriate task type (local, remote, worktree, fork) based on input parameters
  • Observer/Event Queue: LocalAgentTask uses progress tracking with notification enqueueing for async completion
  • Mediator: SendMessageTool routes messages between agents via mailbox files, handling running, stopped, cross-session, and cross-machine targets
  • Worker Pool: Team agents execute in parallel via tmux/iTerm/in-process backends with shared coordination through team files
  • Decorator: Agent MCP servers are additive wrappers around the parent's MCP client pool
  • Resume from Transcript: Stopped agents can be reconstituted from their disk-persisted transcript, enabling long-lived agent identities across sessions