Team & Swarm
此内容尚不支持你的语言。
Beyond simple sub-agent spawning and the coordinator pattern, Claude Code implements a full team/swarm system — persistent multi-agent teams where members run concurrently with different backend strategies. This is the most complex multi-agent pattern in the codebase.
Team Architecture
Section titled “Team Architecture”A team is a persistent configuration stored as a JSON file, with members that can be spawned across different execution backends:
graph TD L[Leader Agent] --> TC[TeamContext in AppState] TC --> M1[Member: researcher<br/>InProcess Backend] TC --> M2[Member: implementer<br/>Tmux Backend] TC --> M3[Member: reviewer<br/>iTerm2 Backend]
M1 ---|AsyncLocalStorage| P1[Same Node.js process] M2 ---|tmux session| P2[Separate terminal pane] M3 ---|iTerm2 tab| P3[Separate iTerm2 tab]Team File Structure
Section titled “Team File Structure”Teams are persisted as JSON files managed by src/utils/swarm/teamHelpers.ts:
// TeamFile structure (from teamHelpers.ts)type TeamFile = { name: string description: string members: TeamMember[]}
type TeamMember = { agentId: string // Format: "name@team" (e.g., "researcher@project-team") name: string agentType: string model?: string prompt: string color?: string planModeRequired: boolean worktreePath?: string // Git worktree for isolation sessionId?: string subscriptions?: string[] backendType: 'tmux' | 'iterm2' | 'in_process' isActive: boolean mode?: string}The agentId follows a deterministic format name@team, generated by formatAgentId():
export function formatAgentId(name: string, teamName: string): string { return `${name}@${teamName}`}Three Backend Strategies
Section titled “Three Backend Strategies”1. InProcess Backend
Section titled “1. InProcess Backend”The in-process backend runs teammates within the same Node.js process using AsyncLocalStorage for context isolation:
export async function spawnInProcessTeammate( config: InProcessSpawnConfig, context: SpawnContext,): Promise<InProcessSpawnOutput> { const agentId = formatAgentId(name, teamName) const taskId = generateTaskId('in_process_teammate')
// Independent AbortController — not linked to leader's query const abortController = createAbortController()
// Create teammate context for AsyncLocalStorage const teammateContext = createTeammateContext({ agentId, agentName: name, teamName, color, planModeRequired, parentSessionId, abortController, })
// Create and register task state in AppState const taskState: InProcessTeammateTaskState = { ...createTaskStateBase(taskId, 'in_process_teammate', description), type: 'in_process_teammate', status: 'running', identity, prompt, model, abortController, awaitingPlanApproval: false, permissionMode: planModeRequired ? 'plan' : 'default', isIdle: false, shutdownRequested: false, messages: [], // ... }
registerTask(taskState, setAppState) return { success: true, agentId, taskId, abortController, teammateContext }}2. Tmux Backend
Section titled “2. Tmux Backend”The Tmux backend spawns teammates as separate terminal sessions in tmux panes. Each teammate runs as an independent Claude Code process in its own pane.
3. iTerm2 Backend
Section titled “3. iTerm2 Backend”Similar to Tmux but uses iTerm2’s native tab/pane management on macOS, providing a more integrated experience for iTerm2 users.
Spawning Configuration
Section titled “Spawning Configuration”export type InProcessSpawnConfig = { name: string // Display name, e.g., "researcher" teamName: string // Team this member belongs to prompt: string // Initial task/instructions color?: string // UI color for the teammate planModeRequired: boolean // Must enter plan mode before implementing model?: string // Optional model override}Teammate Identity
Section titled “Teammate Identity”Each teammate carries a TeammateIdentity that persists across its lifecycle:
// Stored as plain data in AppStateconst identity: TeammateIdentity = { agentId, // "researcher@project-team" agentName: name, // "researcher" teamName, // "project-team" color, planModeRequired, parentSessionId, // Links back to the leader's session}Killing a Teammate
Section titled “Killing a Teammate”The killInProcessTeammate() function handles graceful shutdown:
export function killInProcessTeammate( taskId: string, setAppState: SetAppStateFn,): boolean { setAppState((prev: AppState) => { const task = prev.tasks[taskId] as InProcessTeammateTaskState if (!task || task.status !== 'running') return prev
// 1. Abort the controller to stop execution task.abortController?.abort()
// 2. Call cleanup handler task.unregisterCleanup?.()
// 3. Call pending idle callbacks to unblock waiters task.onIdleCallbacks?.forEach(cb => cb())
// 4. Remove from teamContext.teammates const { [agentId]: _, ...remainingTeammates } = prev.teamContext.teammates
return { ...prev, teamContext: { ...prev.teamContext, teammates: remainingTeammates }, tasks: { ...prev.tasks, [taskId]: { ...task, status: 'killed', endTime: Date.now(), messages: task.messages?.length ? [task.messages[task.messages.length - 1]!] // Keep only last message : undefined, abortController: undefined, // Release references unregisterCleanup: undefined, currentWorkAbortController: undefined, }, }, } })
// Post-state cleanup if (teamName && agentId) { removeMemberByAgentId(teamName, agentId) // Remove from team file } evictTaskOutput(taskId) // Clean up disk output emitTaskTerminatedSdk(taskId, 'stopped') // SDK notification unregisterPerfettoAgent(agentId) // Release trace entry
return killed}Team Cleanup
Section titled “Team Cleanup”When teams are disbanded or sessions end, comprehensive cleanup runs:
flowchart TD A[cleanupTeamDirectories] --> B[Read team config.json] B --> C{For each member} C --> D[destroyWorktree] C --> E[Remove task directories] D --> F{Has .git file?} F -->|yes| G[git worktree remove --force] F -->|no| H[rm -rf worktree path] C --> I[Delete team config file]Worktree destruction (destroyWorktree in teamHelpers.ts) is particularly careful — it reads the .git file to find the main repository, then uses git worktree remove --force. If that fails, it falls back to rm -rf.
Perfetto Trace Integration
Section titled “Perfetto Trace Integration”Team members are registered in Perfetto traces for hierarchy visualization:
// During spawnif (isPerfettoTracingEnabled()) { registerPerfettoAgent(agentId, name, parentSessionId)}
// During killunregisterPerfettoAgent(agentId)This enables viewing the entire team hierarchy — leader and all teammates — in performance trace tooling.
Team State in AppState
Section titled “Team State in AppState”The team system integrates deeply with Claude Code’s reactive state:
- teamContext on AppState holds the live team configuration and teammate map
- Each teammate’s
InProcessTeammateTaskStateis stored inAppState.tasks - The
InProcessTeammateTaskReact component (Task #14) drives the actual agent execution loop - State changes trigger UI updates showing teammate status, spinner verbs, and tool counts
The state machine for a teammate:
spawned → running → idle ↔ running → completed/killed ↑ | └─ pending user messagesTeammates alternate between running (actively executing tools) and idle (waiting for instructions), with the leader able to send messages to idle teammates via SendMessage.