Team & Swarm
除了简单的 sub-agent 派生和 coordinator pattern,Claude Code 还实现了完整的 team/swarm 系统——持久化的 multi-agent team,成员可以以不同的后端策略并发运行。这是代码库中最复杂的 multi-agent 模式。
Team 架构
Section titled “Team 架构”team 是存储为 JSON 文件的持久化配置,其成员可以跨不同执行后端进行派生:
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 文件结构
Section titled “Team 文件结构”team 以 JSON 文件形式持久化,由 src/utils/swarm/teamHelpers.ts 管理:
// TeamFile 结构(来自 teamHelpers.ts)type TeamFile = { name: string description: string members: TeamMember[]}
type TeamMember = { agentId: string // 格式:"name@team"(如 "researcher@project-team") name: string agentType: string model?: string prompt: string color?: string planModeRequired: boolean worktreePath?: string // 用于 isolation 的 git worktree sessionId?: string subscriptions?: string[] backendType: 'tmux' | 'iterm2' | 'in_process' isActive: boolean mode?: string}agentId 遵循 name@team 的确定性格式,由 formatAgentId() 生成:
export function formatAgentId(name: string, teamName: string): string { return `${name}@${teamName}`}三种后端策略
Section titled “三种后端策略”1. InProcess 后端
Section titled “1. InProcess 后端”in-process 后端使用 AsyncLocalStorage 进行 context isolation,在同一 Node.js 进程中运行队友:
export async function spawnInProcessTeammate( config: InProcessSpawnConfig, context: SpawnContext,): Promise<InProcessSpawnOutput> { const agentId = formatAgentId(name, teamName) const taskId = generateTaskId('in_process_teammate')
// 独立的 AbortController——不与 leader 的 query 绑定 const abortController = createAbortController()
// 为 AsyncLocalStorage 创建队友 context const teammateContext = createTeammateContext({ agentId, agentName: name, teamName, color, planModeRequired, parentSessionId, abortController, })
// 在 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 后端
Section titled “2. Tmux 后端”Tmux 后端在 tmux pane 中以独立终端 session 的形式派生队友。每个队友作为独立的 Claude Code 进程在自己的 pane 中运行。
3. iTerm2 后端
Section titled “3. iTerm2 后端”与 Tmux 类似,但使用 iTerm2 在 macOS 上的原生 tab/pane 管理,为 iTerm2 用户提供更集成的体验。
export type InProcessSpawnConfig = { name: string // 显示名称,如 "researcher" teamName: string // 该成员所属的 team prompt: string // 初始任务/指令 color?: string // 队友的 UI 颜色 planModeRequired: boolean // 实现前必须进入 plan 模式 model?: string // 可选的模型覆盖}每个队友都携带一个在其生命周期内持久存在的 TeammateIdentity:
// 以普通数据形式存储在 AppState 中const identity: TeammateIdentity = { agentId, // "researcher@project-team" agentName: name, // "researcher" teamName, // "project-team" color, planModeRequired, parentSessionId, // 链接回 leader 的 session}killInProcessTeammate() 函数处理优雅关闭:
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. 中止 controller 以停止执行 task.abortController?.abort()
// 2. 调用清理处理函数 task.unregisterCleanup?.()
// 3. 调用待处理的 idle 回调以解除等待者的阻塞 task.onIdleCallbacks?.forEach(cb => cb())
// 4. 从 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]!] // 仅保留最后一条消息 : undefined, abortController: undefined, // 释放引用 unregisterCleanup: undefined, currentWorkAbortController: undefined, }, }, } })
// 状态更新后的清理 if (teamName && agentId) { removeMemberByAgentId(teamName, agentId) // 从 team 文件中移除 } evictTaskOutput(taskId) // 清理磁盘输出 emitTaskTerminatedSdk(taskId, 'stopped') // SDK 通知 unregisterPerfettoAgent(agentId) // 释放追踪条目
return killed}Team 清理
Section titled “Team 清理”team 解散或 session 结束时,会执行全面的清理:
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 销毁(teamHelpers.ts 中的 destroyWorktree)尤为谨慎——它读取 .git 文件以找到主仓库,然后使用 git worktree remove --force。若该命令失败,则回退到 rm -rf。
Perfetto Trace 集成
Section titled “Perfetto Trace 集成”team 成员在 Perfetto trace 中注册,以实现层次结构可视化:
// 派生时if (isPerfettoTracingEnabled()) { registerPerfettoAgent(agentId, name, parentSessionId)}
// 终止时unregisterPerfettoAgent(agentId)这使得在性能追踪工具中查看整个 team 层次结构——leader 和所有队友——成为可能。
AppState 中的 Team 状态
Section titled “AppState 中的 Team 状态”team 系统与 Claude Code 的响应式状态深度集成:
- AppState 上的 teamContext 保存实时 team 配置和队友映射
- 每个队友的
InProcessTeammateTaskState存储在AppState.tasks中 InProcessTeammateTaskReact 组件(Task #14)驱动实际的 agent 执行循环- 状态变更触发 UI 更新,显示队友状态、spinner 动词和 tool 调用计数
队友的状态机:
spawned → running → idle ↔ running → completed/killed ↑ | └─ pending user messages队友在 running(正在执行 tool)和 idle(等待指令)之间交替,leader 可以通过 SendMessage 向处于 idle 状态的队友发送消息。