Agent Swarms(蜂群模式)详解

1. 概述
Agent Swarms(蜂群模式)是 Claude Code 的多 Agent 协作系统,允许一个 Team Lead (主会话)与多个 Teammate(独立 Claude Code 实例)协同工作,共享一个任务列表和文件级消息系统。
核心文件位置:
| 模块 | 路径 |
|---|---|
| 团队创建工具 | packages/builtin-tools/src/tools/TeamCreateTool/ |
| 团队删除工具 | packages/builtin-tools/src/tools/TeamDeleteTool/ |
| 消息发送工具 | packages/builtin-tools/src/tools/SendMessageTool/ |
| 蜂群核心工具 | src/utils/swarm/ |
| 邮箱系统 | src/utils/teammateMailbox.ts |
| 团队文件管理 | src/utils/swarm/teamHelpers.ts |
| 权限同步 | src/utils/swarm/permissionSync.ts |
| 进程内执行器 | src/utils/swarm/inProcessRunner.ts |
| 终端后端 | src/utils/swarm/backends/ |
| 功能开关 | src/utils/agentSwarmsEnabled.ts |
| React Hooks | src/hooks/useSwarm*.ts |
2. 架构总览
2.1 系统拓扑
┌─────────────────────────────────────────────────────────┐
│ Team Lead (主会话) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ TeamCreate │ │ TaskCreate │ │ AgentTool │ │
│ │ 创建团队 │ │ 创建任务 │ │ 创建队友 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ TeamContext │
│ (teamName, leadAgentId, ...) │
└─────────────────────────┬───────────────────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ config.json │ │ ~/.claude/ │ │ ~/.claude/ │
│ Team File │ │ tasks/{id}/ │ │ teams/{id}/ │
│ │ │ inboxes/ │ │ permissions/│
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
┌───────┴──────────────┴──────────────┴───────┐
│ Shared Filesystem │
└──────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Teammate A │ │ Teammate B │ │ Teammate C │
│ 独立上下文 │ │ 独立上下文 │ │ 独立上下文 │
│ pane-based / │ │ pane-based / │ │ pane-based / │
│ in-process │ │ in-process │ │ in-process │
└──────────────┘ └──────────────┘ └──────────────┘
2.2 Team File 数据结构
团队配置存储在 ~/.claude/teams/{team-name}/config.json:
// src/utils/swarm/teamHelpers.ts:64-90
type TeamFile = {
name: string // 团队名称
description?: string // 团队描述
createdAt: number // 创建时间戳
leadAgentId: string // Team Lead 的 Agent ID (格式: team-lead@teamName)
leadSessionId?: string // Leader 的实际会话 UUID(用于发现)
hiddenPaneIds?: string[] // 当前隐藏的 Pane ID 列表
teamAllowedPaths?: TeamAllowedPath[] // 全团队可编辑路径
members: Array<{
agentId: string // Agent ID (格式: name@teamName)
name: string // 人类可读名称,用于消息和任务分配
agentType?: string // Agent 类型/角色
model?: string // 使用的模型
prompt?: string // 初始 Prompt
color?: string // UI 颜色
planModeRequired?: boolean // 是否需要 Plan Mode
joinedAt: number // 加入时间戳
tmuxPaneId: string // 终端 Pane ID
cwd: string // 工作目录
worktreePath?: string // Git Worktree 路径
sessionId?: string // 会话 UUID
subscriptions: string[] // 消息订阅
backendType?: BackendType // 执行后端类型
isActive?: boolean // 是否活跃
mode?: PermissionMode // 当前权限模式
}>
}
3. 核心组件
3.1 TeamCreate --- 团队创建
文件: packages/builtin-tools/src/tools/TeamCreateTool/TeamCreateTool.ts
TeamCreate 是 Agent Swarms 的入口工具。调用后完成以下初始化:
-
生成唯一团队名称 --- 若名称已存在,自动生成 word-slug
-
创建 Team File --- 写入
~/.claude/teams/{name}/config.json -
创建任务目录 --- 初始化
~/.claude/tasks/{name}/ -
设置 Leader 上下文 --- 更新 AppState.teamContext
-
注册会话清理 --- 确保会话退出时清理资源
// TeamCreateTool.ts:129-244
async call(input, context) {
// 检查是否已在一个团队中(一个 Leader 只能管理一个团队)
const appState = getAppState()
const existingTeam = appState.teamContext?.teamName
if (existingTeam) {
throw new Error(Already leading team "${existingTeam}"...)
}// 生成唯一 Team Name
const finalTeamName = generateUniqueTeamName(team_name)// 创建确定性 Agent ID:team-lead@teamName
const leadAgentId = formatAgentId(TEAM_LEAD_NAME, finalTeamName)// 创建 TeamFile 并写入磁盘
await writeTeamFileAsync(finalTeamName, teamFile)// 创建对应任务列表目录(Team = TaskList)
await resetTaskList(taskListId)// 更新 AppState
setAppState(prev => ({
...prev,
teamContext: {
teamName: finalTeamName,
teamFilePath,
leadAgentId,
teammates: { /* ... */ },
},
}))
}
关键设计: Leader 不设置 CLAUDE_CODE_AGENT_ID,因此 isTeammate() 对 Leader 返回 false,避免干扰 Leader 的 Inbox 轮询逻辑。
3.2 TeamDelete --- 团队清理
负责优雅关闭所有 Teammate、清理资源和目录:
- 向所有 Teammate 发送 Shutdown Request
- 等待
shutdown_approved响应 - 杀死 Pane / 清理进程内上下文
- 清理团队目录、任务目录、Git Worktrees
3.3 SendMessage --- 消息发送
文件: packages/builtin-tools/src/tools/SendMessageTool/SendMessageTool.ts
支持三种消息类型:
-
普通消息 --- 发送文本到指定 Teammate 或广播 (
*) -
结构化消息 --- 支持
shutdown_request、shutdown_response、plan_approval_response -
协议消息 --- 权限请求/响应、模式变更等由邮箱系统自动路由
// inputSchema (SendMessageTool.ts:67)
const inputSchema = z.object({
to: z.string(), // 接收者: name, "*", 或 peer address
summary: z.string(), // UI 预览摘要
message: z.union([ // 纯文本或结构化消息
z.string(),
StructuredMessage, // {type: "shutdown_request"|"shutdown_response"|...}
]),
})
SendMessage 的消息投递最终依赖 TeammateMailbox 邮箱系统,详见第5章。
4. 执行模式
Agent Swarms 支持两种 Teammate 执行模式,由 isInProcessEnabled() 决定:
4.1 模式判定逻辑
文件: src/utils/swarm/backends/registry.ts:426-476
export function isInProcessEnabled(): boolean {
// 非交互模式强制使用 in-process
if (getIsNonInteractiveSession()) return true
const mode = getTeammateMode() // 'auto' | 'in-process' | 'tmux'
if (mode === 'in-process') return true
if (mode === 'tmux' || mode === 'windows-terminal') return false
// 'auto' 模式:检测环境
const insideTmux = isInsideTmuxSync()
const inITerm2 = isInITerm2()
const inWindowsTerminal = isInWindowsTerminal()
// 在 tmux/iTerm2/Windows Terminal 中使用 pane 模式
// 在普通终端中回退到 in-process
return !insideTmux && !inITerm2 && !inWindowsTerminal
}
决策表:
| 运行环境 | 默认模式 | 说明 |
|---|---|---|
| 非交互 (-p) | in-process | 强制,无终端 UI |
| tmux 内部 | pane (tmux) | 使用 tmux split-pane |
| iTerm2 (有 it2) | pane (iterm2) | 使用 iTerm2 原生分屏 |
| iTerm2 (无 it2) | pane (tmux) | 回退到 tmux |
| Windows Terminal | pane (wt) | 使用 Windows Terminal 分屏 |
| 普通终端 | in-process | 同一进程内运行 |
4.2 Pane-Based 模式(tmux / iTerm2 / Windows Terminal)
每个 Teammate 是一个独立进程,运行在独立的终端 Pane 中。
文件: src/utils/swarm/backends/TmuxBackend.ts
Pane 布局策略 (Leader 在 tmux 内):
┌──────────┬─────────────────────────────┐
│ │ Teammate A │
│ Leader ├──────────────┬──────────────┤
│ (30%) │ Teammate B │ Teammate C │
│ │ │ │
└──────────┴──────────────┴──────────────┘
←── 右侧区 (70%) ──→
// TmuxBackend.ts:587-666 - Pane 创建流程
private async createTeammatePaneWithLeader(teammateName, teammateColor) {
const isFirstTeammate = paneCount === 1
if (isFirstTeammate) {
// 第一个 Teammate:从 Leader pane 水平分割 (70%)
splitResult = await execFileNoThrow(TMUX_COMMAND, [
'split-window', '-t', currentPaneId, '-h', '-l', '70%', ...
])
} else {
// 后续 Teammate:交替垂直/水平分割(网格布局)
const splitVertically = teammateCount % 2 === 1
splitResult = await execFileNoThrow(TMUX_COMMAND, [
'split-window', '-t', targetPane, splitVertically ? '-v' : '-h', ...
])
}
// 设置 Pane 颜色、标题、Shell 初始化等待
await this.setPaneBorderColor(paneId, teammateColor)
await this.setPaneTitle(paneId, teammateName, teammateColor)
}
关键特性:
- Pane 颜色编码 --- 每个 Teammate 分配不同颜色(红/蓝/绿/黄/紫/橙/粉/青)
- Pane 锁机制 --- 并发 Spawn 通过异步锁
acquirePaneCreationLock()串行化 - Shell 初始化等待 --- 创建 Pane 后等待 200ms 让 Shell 加载 rc 文件
- Pane Show/Hide --- 支持将 Pane 移入隐藏 Session,稍后恢复
4.3 In-Process 模式
文件: src/utils/swarm/inProcessRunner.ts
Teammate 在同一 Node.js 进程内 运行,通过 AsyncLocalStorage 实现上下文隔离。
进程内架构:
┌─────────────────────────────────────────────────┐
│ Main Process │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Team Lead │ │ Teammate A │ │
│ │ (主事件循环) │ │ runAgent() │ │
│ │ │ │ AsyncLocalStorage │ │
│ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Teammate B │ │ Teammate C │ │
│ │ runAgent() │ │ runAgent() │ │
│ │ AsyncLocalStorage │ │ AsyncLocalStorage │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────┘
核心运行循环: runInProcessTeammate() (inProcessRunner.ts:902-1632)
Loop:
1. 构建 System Prompt (default + addendum + agent definition)
2. 检查是否需要 Auto-Compact
3. 调用 runAgent() 执行当前 Prompt
4. 发送 Idle 通知给 Leader
5. 进入 waitForNextPromptOrShutdown() 等待循环
├── 检查 pendingUserMessages (内存消息)
├── 检查 Mailbox (文件消息)
│ ├── Shutdown Request → 返回给 Model 决策
│ ├── Team Lead 消息 → 优先处理
│ └── Peer 消息 → FIFO 处理
├── 检查 Task List (自动认领)
└── 每 500ms 轮询一次
6. 收到新 Prompt 或 Shutdown → 回到 1
消息优先级 (waitForNextPromptOrShutdown):
-
pendingUserMessages(最高优先级,立即检查)
-
Shutdown Request(从 Mailbox 中断处理)
-
Team Lead 消息(优先于 Peer 消息)
-
Peer 消息(FIFO)
-
未认领 Task(从 Task List 自动认领)
// inProcessRunner.ts:700-887
async function waitForNextPromptOrShutdown(identity, abortController, ...) {
while (!abortController.signal.aborted) {
// 1. 检查内存队列
const task = appState.tasks[taskId]
if (task.pendingUserMessages.length > 0) {
return { type: 'new_message', message: pending.message, from: 'user' }
}// 2. 轮询 Mailbox (优先 shutdown) const allMessages = await readMailbox(identity.agentName, identity.teamName) // 扫描 Shutdown Request const shutdownRequest = allMessages.find(m => isShutdownRequest(m.text)) if (shutdownRequest) return { type: 'shutdown_request', request: ... } // 3. Team Lead 消息优先 const leaderMsg = allMessages.find(m => m.from === 'team-lead') if (leaderMsg) return { type: 'new_message', message: leaderMsg.text, ... } // 4. FIFO Peer 消息 const firstUnread = allMessages.find(m => !m.read) if (firstUnread) return { type: 'new_message', message: firstUnread.text, ... } // 5. 尝试认领 Task const taskPrompt = await tryClaimNextTask(taskListId, identity.agentName) if (taskPrompt) return { type: 'new_message', message: taskPrompt, from: 'task-list' } await sleep(500) // 每 500ms 轮询}
}
Auto-Compact 支持: In-Process Teammate 在每次循环前检查 Token 用量,超出阈值时自动压缩历史,防止上下文窗口溢出。
5. 消息与通信系统
5.1 Mailbox 邮箱系统
文件: src/utils/teammateMailbox.ts(~1460 行)
邮箱系统是 Agent Swarms 中 Agent 间通信的底层基础设施,基于文件系统 + 锁机制实现,无需任何中间件。
5.1.1 架构概述
.teams/{team_name}/inboxes/
├── team-lead.json # Leader 的收件箱
├── alice.json # Teammate alice 的收件箱
├── bob.json # Teammate bob 的收件箱
├── alice.json.lock # 写锁文件
└── bob.json.lock
每个 Agent 拥有一个 JSON 收件箱文件,发送方通过 writeToMailbox() 写入消息,接收方通过 readUnreadMessages() 轮询新消息。所有写操作使用文件锁(proper-lockfile)保证并发安全。
5.1.2 核心数据结构
type TeammateMessage = {
from: string // 发送者 agent name
text: string // 消息正文(纯文本或 JSON 结构化消息)
timestamp: string // ISO 8601 时间戳
read: boolean // 是否已读
color?: string // 发送者颜色(UI 展示用)
summary?: string // 5-10 词的预览摘要
}
5.1.3 核心 API
| API | 功能 | 并发控制 |
|---|---|---|
writeToMailbox(recipient, message, teamName?) |
向指定 Agent 收件箱写入消息 | 文件锁 + 重试(最多 10 次,5-100ms 退避) |
readMailbox(agentName, teamName?) |
读取完整收件箱(含已读/未读) | 无锁 |
readUnreadMessages(agentName, teamName?) |
仅读取未读消息 | 无锁 |
markMessageAsReadByIndex(...) |
按索引标记单条已读 | 文件锁 |
markMessagesAsRead(agentName, teamName?) |
标记全部消息已读 | 文件锁 |
markMessagesAsReadByPredicate(...) |
按条件标记已读 | 文件锁 |
clearMailbox(agentName, teamName?) |
清空收件箱 | 直接写入 [] |
写操作流程 (以 writeToMailbox 为例):
1. ensureInboxDir() → 确保 ~/.claude/teams/{team}/inboxes/ 目录存在
2. 以 'wx' 模式创建收件箱文件(不存在时初始化为 [])
3. lockfile.lock() → 获取写锁(带退避重试)
4. readMailboxForMutation() → 重新读取最新消息列表
5. messages.push(newMessage) → 追加新消息
6. writeCompactedMailbox() → 压缩后原子写入
7. lockfile.release() → 释放锁
其中 原子写入 通过先写临时文件再 rename 实现:
async function writeMailboxAtomic(inboxPath, content) {
const tempPath = `${inboxPath}.${pid}.${randomBytes(8).hex}.tmp`
await writeFile(tempPath, content) // 写入临时文件
await rename(tempPath, inboxPath) // 原子重命名
}
5.1.4 消息压缩策略
compactMailboxMessages() 防止收件箱无限增长,采用三级保留策略(倒序遍历消息):
| 限制参数 | 默认值 | 说明 |
|---|---|---|
MAX_MAILBOX_MESSAGES |
1,000 | 最大保留消息总数 |
MAX_READ_MAILBOX_MESSAGES |
200 | 已读普通消息上限 |
MAX_UNREAD_PROTOCOL_MAILBOX_MESSAGES |
2,000 | 未读协议消息上限 |
MAX_MAILBOX_RETAINED_BYTES |
2MB | 总字节数上限 |
MAX_MAILBOX_FILE_BYTES |
4MB | 收件箱文件硬上限 |
压缩优先级:未读协议消息 > 未读普通消息 > 已读消息。压缩会记录被淘汰的未读消息数量到日志。
5.1.5 结构化协议消息
邮箱不仅传输普通文本,还承载多种结构化协议消息 ,它们通过 type 字段区分,由 useInboxPoller Hook 自动路由到对应处理器:
| 消息类型 | 方向 | 用途 |
|---|---|---|
permission_request |
Worker → Leader | Worker 请求工具执行权限 |
permission_response |
Leader → Worker | Leader 返回权限决策 |
sandbox_permission_request |
Worker → Leader | Sandbox 网络访问权限请求 |
sandbox_permission_response |
Leader → Worker | Sandbox 网络权限回复 |
shutdown_request |
Leader → Worker | 请求 Teammate 关停 |
shutdown_approved / shutdown_rejected |
Worker → Leader | 关停确认/拒绝 |
plan_approval_request |
Worker → Leader | 请求计划审批 |
plan_approval_response |
Leader → Worker | 计划审批结果 |
mode_set_request |
Leader → Worker | 权限模式同步 |
team_permission_update |
Leader → Worker | 团队级权限变更广播 |
task_assignment |
Leader → Worker | 任务分配通知 |
idle_notification |
Worker → Leader | Worker 空闲/完成/中断通知 |
其中 isStructuredProtocolMessage() 函数用于判断消息是否为协议消息,协议消息不会作为原始 LLM 上下文消费,而是被 useInboxPoller 拦截并路由到专用队列处理。
// teammateMailbox.ts:1338-1360
export function isStructuredProtocolMessage(messageText: string): boolean {
const type = (parsed as { type: unknown }).type
return (
type === 'permission_request' ||
type === 'permission_response' ||
type === 'sandbox_permission_request' ||
type === 'sandbox_permission_response' ||
type === 'shutdown_request' ||
type === 'shutdown_approved' ||
type === 'team_permission_update' ||
type === 'mode_set_request' ||
type === 'plan_approval_request' ||
type === 'plan_approval_response'
)
}
5.1.6 Idle 通知机制
当 Teammate 完成工作或中断时,通过 Stop hook 自动发送 idle 通知:
type IdleNotificationMessage = {
type: 'idle_notification'
from: string
idleReason?: 'available' | 'interrupted' | 'failed'
summary?: string // 本轮最后一条 DM 的摘要
completedTaskId?: string
completedStatus?: 'resolved' | 'blocked' | 'failed'
failureReason?: string
}
Leader 收到 idle_notification 后可据此决定是否分配新任务或重新唤醒 Teammate。
5.2 消息格式化
Teammate 消息在注入 Model 上下文时包装为 XML:
// 格式: <teammate-message teammate_id="发送者" color="颜色" summary="摘要">
// 消息内容
// </teammate-message>
function formatAsTeammateMessage(from, content, color?, summary?) {
return `<${TEAMMATE_MESSAGE_TAG} teammate_id="${from}" color="${color}" summary="${summary}">
${content}
</${TEAMMATE_MESSAGE_TAG}>`
}
5.3 Peer DM 摘要
getLastPeerDmSummary() (teammateMailbox.ts:1418-1461) 提取上一个 Turn 中 Teammate 之间通信的摘要。例如,当 Teammate A 向 Teammate B 发送消息后进入 Idle,Leader 收到的 Idle 通知会包含 [to tester] fixing unit tests 这样的摘要。
6. 权限同步机制
文件: src/utils/swarm/permissionSync.ts
6.1 两条权限通道
Agent Swarms 有两套并行的权限请求机制:
通道 1:ToolUseConfirm Queue(主通道,Leader 进程内 Teammate)
文件: src/utils/swarm/inProcessRunner.ts:137-459
function createInProcessCanUseTool(identity, abortController, onPermissionWaitMs) {
return async (tool, input, toolUseContext, ...) => {
// 1. 先检查权限规则
const result = await hasPermissionsToUseTool(...)
// allow/deny → 直接返回
if (result.behavior !== 'ask') return result
// 2. Bash 分类器自动批准
if (tool.name === BASH_TOOL_NAME && result.pendingClassifierCheck) {
const classifierDecision = await awaitClassifierAutoApproval(...)
if (classifierDecision) return { behavior: 'allow', ... }
}
// 3. 提交到 Leader 的 ToolUseConfirm 队列
// 带 WorkerBadge 标记显示这是哪个 Teammate 的请求
setToolUseConfirmQueue(queue => [...queue, {
tool, description, input, toolUseContext, toolUseID,
workerBadge: { name: identity.agentName, color: identity.color },
onAllow(updatedInput, permissionUpdates, feedback, contentBlocks) {
// 权限变更写回 Leader 的共享上下文
const setToolPermissionContext = getLeaderSetToolPermissionContext()
if (setToolPermissionContext) {
setToolPermissionContext(updatedContext, { preserveMode: true })
}
resolve({ behavior: 'allow', updatedInput, ... })
},
onReject(feedback, contentBlocks) {
resolve({ behavior: 'ask', message: rejectionMessage, ... })
},
}])
}
}
通道 2:Mailbox 系统(回退通道,Pane-Based Teammate)
当 In-Process Teammate 无法直接访问 Leader 的 UI 时,通过文件和 Mailbox 通信:
Worker 流程:
1. writePermissionRequest() → 写入 pending/ 目录
2. sendPermissionRequestViaMailbox() → 向 Leader Mailbox 发送请求
3. 注册 Permission Callback (registerPermissionCallback)
4. 轮询 Mailbox 等待响应 (500ms 间隔)
Leader 流程:
1. useInboxPoller 检测到 permission_request 消息
2. 显示权限审批 UI (Leader 的 ToolUseConfirm 对话框)
3. 用户决策 → resolvePermission() → 写入 resolved/ 目录
4. sendPermissionResponseViaMailbox() → 向 Worker Mailbox 发送响应
文件存储结构:
~/.claude/teams/{team-name}/permissions/
├── pending/ # 待审批请求
│ └── perm-{ts}-{random}.json
└── resolved/ # 已处理请求(1小时后清理)
└── perm-{ts}-{random}.json
6.2 权限请求数据模型
// permissionSync.ts:49-86
type SwarmPermissionRequest = {
id: string // perm-{timestamp}-{random}
workerId: string // Worker 的 Agent ID
workerName: string // Worker 的人类可读名称
workerColor?: string // Worker 的 UI 颜色
teamName: string // 团队名称
toolName: string // 需要权限的工具名
toolUseId: string // 原始 tool_use ID
description: string // 人类可读描述
input: Record<string, unknown> // 原始工具输入
permissionSuggestions: unknown[] // 权限建议
status: 'pending' | 'approved' | 'rejected'
resolvedBy?: 'worker' | 'leader'
resolvedAt?: number
feedback?: string // 拒绝理由
updatedInput?: Record<string, unknown> // 修改后的输入
permissionUpdates?: unknown[] // "始终允许" 规则
createdAt: number
}
6.3 Team-Wide 权限
Team Lead 可以为整个团队批量授权特定目录的文件编辑权限:
// teamHelpers.ts:57-62
type TeamAllowedPath = {
path: string // 目录绝对路径
toolName: string // 适用的工具名 (Edit/Write)
addedBy: string // 谁添加的规则
addedAt: number // 添加时间
}
Teammate 启动时自动应用这些权限:
// teammateInit.ts:28-129
export function initializeTeammateHooks(setAppState, sessionId, teamInfo) {
// 读取 Team File 获取 teamAllowedPaths
const teamFile = readTeamFile(teamName)
// 为每个路径创建规则并应用
for (const allowedPath of teamFile.teamAllowedPaths) {
setAppState(prev => ({
...prev,
toolPermissionContext: applyPermissionUpdate(prev.toolPermissionContext, {
type: 'addRules',
rules: [{ toolName: allowedPath.toolName, ruleContent: `/${path}/**` }],
behavior: 'allow',
destination: 'session',
}),
}))
}
// 注册 Stop Hook:当 Teammate 停止时通知 Leader
addFunctionHook(setAppState, sessionId, 'Stop', '', async (messages) => {
await setMemberActive(teamName, agentName, false)
const notification = createIdleNotification(agentName, { ... })
await writeToMailbox(leadAgentName, { from: agentName, text: jsonStringify(notification) })
})
}
7. Teammate 生命周期
7.1 Teammate 创建流程
AgentTool.call({ team_name: "...", name: "researcher", prompt: "..." })
│
├── 1. 验证 Team 存在性 (readTeamFile)
├── 2. 检查名称冲突
├── 3. 分配 Color (round-robin)
├── 4. 选择 Backend (detectAndGetBackend / isInProcessEnabled)
│
├── [Pane Mode]
│ ├── createTeammatePaneInSwarmView() → 创建终端 Pane
│ ├── 设置 Pane 颜色、标题
│ ├── sendCommandToPane() → 启动 Claude Code 实例
│ │ 设置环境变量: CLAUDE_CODE_TEAM_NAME, CLAUDE_CODE_AGENT_ID, ...
│ └── 写入 teamFile.members
│
└── [In-Process Mode]
├── 创建 InProcessTeammateTask
├── 构建 TeammateContext (AsyncLocalStorage)
├── startInProcessTeammate() → 后台启动 runInProcessTeammate()
└── 写入 teamFile.members
7.2 Teammate 空闲机制
Turn 完成
│
├── 标记 task.isIdle = true
├── 调用 onIdleCallbacks
├── setMemberActive(false) → 更新 teamFile
├── createIdleNotification({ idleReason: 'available' | 'interrupted' | 'failed' })
└── writeToMailbox(leadAgentName, notification)
│
▼
Leader 收到 Idle 通知
│
├── 可选:分配新任务
├── 可选:发送新 Prompt
└── 可选:发送 Shutdown Request
Teammate 等待循环
│
├── 收到新 Prompt → 继续工作
├── 收到 Shutdown Request → Model 决定接受/拒绝
├── 发现未认领 Task → 自动认领
└── 被 Abort → 退出
关键行为:
- Idle ≠ 完成 --- "Teammate going idle after every turn is completely normal."
- Idle Teammate 可接收消息 --- 发送消息会唤醒 Idle Teammate
- 自动任务认领 --- 等待循环中发现未认领任务时自动认领
7.3 优雅关闭流程
Leader 发起关闭:
SendMessage({ to: "researcher", message: { type: "shutdown_request" } })
│
├── waitForNextPromptOrShutdown 检测到 shutdown_request
├── 将 shutdown_request 包装为 XML 注入 Model 上下文
├── Model 使用 SendMessage + shutdown_response 响应
│ ├── approve: true → 发送 shutdown_approved,清理资源
│ └── approve: false → 发送 shutdown_rejected,继续工作
│
└── Leader 收到 shutdown_approved
├── killPane() / killInProcessTeammate()
├── removeMemberFromTeam()
├── cleanupTeamDirectories()
└── 通知 TeamDelete 完成
7.4 异常与中断
- SIGINT/SIGTERM →
cleanupSessionTeams()杀死所有孤立 Pane,清理目录 - Escape 键 →
currentWorkAbortController中断当前 Turn(不影响 Teammate 继续存在) - 生命周期 Abort →
abortController终止整个 Teammate
8. 任务分配与认领
8.1 任务系统集成
Agent Swarms 与 Task 系统深度集成(src/utils/tasks.ts):
- Team = TaskList --- 创建 Team 时自动创建对应 Task List
- 共享任务目录 ---
~/.claude/tasks/{team-name}/ - 文件锁保护 --- 任务状态更新使用文件锁保证原子性
8.2 任务认领算法
// inProcessRunner.ts:604-614
function findAvailableTask(tasks: Task[]): Task | undefined {
// 收集所有未完成任务的 ID
const unresolvedTaskIds = new Set(
tasks.filter(t => t.status !== 'completed').map(t => t.id)
)
return tasks.find(task => {
if (task.status !== 'pending') return false // 不是待处理
if (task.owner) return false // 已有所有者
// 所有依赖已完成(blockedBy 中的任务都不在未完成列表中)
return task.blockedBy.every(id => !unresolvedTaskIds.has(id))
})
}
认领流程:
tryClaimNextTask(taskListId, agentName)
│
├── listTasks() → 读取所有任务
├── findAvailableTask() → 找首个 pending + 无 owner + 无阻塞的任务
├── claimTask() → 使用文件锁原子写入 owner
└── 返回 formatTaskAsPrompt() → "Complete all open tasks. Start with task #N: ..."
8.3 Task Prompt 指导原则
文件: packages/builtin-tools/src/tools/TeamCreateTool/prompt.ts:94-112
TeamCreate 的 Prompt 中明确指导了 Agent 在 Swarm 中的任务行为:
- 按 ID 顺序认领 --- 优先认领 ID 最小的任务(前置任务设置上下文)
- 完成后续查 --- 每完成一个任务后检查 TaskList,寻找新工作
- 阻塞任务处理 --- 如所有可用任务都被阻塞,通知 Team Lead
- 主动创建 --- 发现额外工作时通过 TaskCreate 添加新任务
- 按名称引用 --- 始终使用 name(非 agentId)进行消息和任务分配
9. 关键代码路径
9.1 启动流程
命令行: claude
│
main.tsx → CLI 启动
│
├── [有 --team-name + --agent-name]
│ → computeInitialTeamContext() --- reconnection.ts
│ → 设置 teamContext 为初始 AppState
│ → initializeTeammateHooks() --- teammateInit.ts
│ → 注册 Stop Hook
│
├── [从 checkpoint 恢复]
│ → initializeTeammateContextFromSession() --- reconnection.ts
│ → 从 transcript 中恢复 teamName/agentName
│
└── [普通启动]
→ 等待用户输入 / Agent 调用 TeamCreate
9.2 Teammate 系统提示
In-Process Teammate 的 System Prompt 由三部分构成:
1. Default System Prompt (getSystemPrompt)
- 工具列表、CWD、日期、CLAUDE.md 内容等
2. TEAMMATE_SYSTEM_PROMPT_ADDENDUM
- "You are a teammate in an agent swarm."
- 消息格式、任务流程说明
3. Custom Agent Definition Prompt (可选)
- Agent 特有指令
9.3 Teammate 上下文隔离
In-Process Teammate 使用 AsyncLocalStorage (src/utils/teammateContext.ts) 隔离上下文:
// inProcessRunner.ts:1182
await runWithTeammateContext(teammateContext, async () => {
return runWithAgentContext(agentContext, async () => {
// runAgent() 内部通过 getCurrentTeammateContext() 获取身份
// 影响: Mailbox 路径、权限检查、日志标记
})
})
9.4 功能开关
文件: src/utils/agentSwarmsEnabled.ts
export function isAgentSwarmsEnabled(): boolean {
if (isEnvTruthy(process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS_DISABLED)) {
return false
}
return true // 默认启用
}
通过环境变量 CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS_DISABLED=1 禁用。
9.5 关键文件索引
src/utils/swarm/
├── constants.ts # 常量定义 (team-lead, session names)
├── teamHelpers.ts # Team File 读写、成员管理、清理
├── teammateInit.ts # Teammate 启动初始化
├── teammateLayoutManager.ts # Pane 颜色分配、Pane 创建
├── permissionSync.ts # 权限同步 (文件 + Mailbox)
├── reconnection.ts # 断线重连 / 会话恢复
├── inProcessRunner.ts # In-Process Teammate 运行器
└── backends/
├── types.ts # PaneBackend 接口定义
├── registry.ts # Backend 发现、选择、缓存
├── detection.ts # 环境检测 (tmux, iTerm2, Windows Terminal)
├── TmuxBackend.ts # tmux Pane 管理
├── ITermBackend.ts # iTerm2 Pane 管理
├── WindowsTerminalBackend.ts # Windows Terminal 管理
├── PaneBackendExecutor.ts # PaneBackend → TeammateExecutor 适配器
└── InProcessBackend.ts # In-Process TeammateExecutor
src/utils/
├── teammateMailbox.ts # Mailbox 系统 (消息队列、结构化消息)
├── agentSwarmsEnabled.ts # 功能开关
├── teammate.ts # Teammate 身份工具
├── teammateContext.ts # AsyncLocalStorage 上下文
└── teamDiscovery.ts # 团队发现
packages/builtin-tools/src/tools/
├── TeamCreateTool/ # TeamCreate 工具
├── TeamDeleteTool/ # TeamDelete 工具
├── SendMessageTool/ # SendMessage 工具
├── AgentTool/ # Agent 工具 (spawn teammate)
│ ├── prompt.ts # Agent spawning prompt
│ └── runAgent.ts # runAgent 核心循环
└── TaskCreateTool/ # 任务创建
src/hooks/
├── useSwarmInitialization.ts # Swarm 初始化 Hook
├── useSwarmPermissionPoller.ts # 权限轮询 Hook
├── useInboxPoller.ts # Mailbox 轮询 Hook
└── toolPermission/handlers/
└── swarmWorkerHandler.ts # Swarm Worker 权限处理器
src/components/
├── teams/TeamsDialog.tsx # 团队管理 UI
├── PromptInput/
│ ├── useSwarmBanner.ts # Swarm Banner 显示
│ ├── PromptInputModeIndicator.tsx # 模式指示器
│ └── PromptInput.tsx
├── permissions/WorkerBadge.tsx # Worker 权限 Badge
└── TaskListV2.tsx # 任务列表 UI
src/tasks/
└── InProcessTeammateTask/ # In-Process Teammate 任务类型
附录:Backend 检测优先级
// registry.ts:160-322
detectAndGetBackend() 检测优先级:
1. 显式设置 windows-terminal 模式 → WindowsTerminalBackend
2. 在 tmux 内部 → TmuxBackend (native)
3. 在 iTerm2 (有 it2 CLI) → ITermBackend (native)
4. 在 iTerm2 (无 it2) → 回退 TmuxBackend (external, 推荐安装 it2)
5. 在 Windows Terminal → WindowsTerminalBackend (native)
6. tmux 可用 (不在 tmux 内) → TmuxBackend (external session)
7. 无可用 Backend → 抛出错误,提示安装 tmux
附录:环境变量速查
| 变量 | 作用 | 默认值 |
|---|---|---|
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS_DISABLED |
完全禁用 Agent Swarms | 未设置(启用) |
CLAUDE_CODE_TEAMMATE_COMMAND |
覆盖 Spawn Teammate 的命令 | process.execPath |
CLAUDE_CODE_AGENT_COLOR |
Teammate 的 Pane 颜色 | 自动分配 |
CLAUDE_CODE_PLAN_MODE_REQUIRED |
Teammate 需要 Plan Mode | 未设置 |
CLAUDE_CODE_TEAM_NAME |
当前 Team Name | 由 TeamCreate 设置 |
CLAUDE_CODE_AGENT_ID |
Teammate 的 Agent ID | 格式: name@teamName |