Claude code源码精读之蜂群模式

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 的入口工具。调用后完成以下初始化:

  1. 生成唯一团队名称 --- 若名称已存在,自动生成 word-slug

  2. 创建 Team File --- 写入 ~/.claude/teams/{name}/config.json

  3. 创建任务目录 --- 初始化 ~/.claude/tasks/{name}/

  4. 设置 Leader 上下文 --- 更新 AppState.teamContext

  5. 注册会话清理 --- 确保会话退出时清理资源

    // 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、清理资源和目录:

  1. 向所有 Teammate 发送 Shutdown Request
  2. 等待 shutdown_approved 响应
  3. 杀死 Pane / 清理进程内上下文
  4. 清理团队目录、任务目录、Git Worktrees

3.3 SendMessage --- 消息发送

文件: packages/builtin-tools/src/tools/SendMessageTool/SendMessageTool.ts

支持三种消息类型:

  • 普通消息 --- 发送文本到指定 Teammate 或广播 (*)

  • 结构化消息 --- 支持 shutdown_requestshutdown_responseplan_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):

  1. pendingUserMessages(最高优先级,立即检查)

  2. Shutdown Request(从 Mailbox 中断处理)

  3. Team Lead 消息(优先于 Peer 消息)

  4. Peer 消息(FIFO)

  5. 未认领 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/SIGTERMcleanupSessionTeams() 杀死所有孤立 Pane,清理目录
  • Escape 键currentWorkAbortController 中断当前 Turn(不影响 Teammate 继续存在)
  • 生命周期 AbortabortController 终止整个 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 中的任务行为:

  1. 按 ID 顺序认领 --- 优先认领 ID 最小的任务(前置任务设置上下文)
  2. 完成后续查 --- 每完成一个任务后检查 TaskList,寻找新工作
  3. 阻塞任务处理 --- 如所有可用任务都被阻塞,通知 Team Lead
  4. 主动创建 --- 发现额外工作时通过 TaskCreate 添加新任务
  5. 按名称引用 --- 始终使用 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
相关推荐
黄啊码1 小时前
【黄啊码】加个 AI 接口就是 AI 公司?这跟 Loading 改 Thinking 有啥区别?你需要了解什么是 AI Native 了
人工智能
薛先生_0991 小时前
vue-编程式跳转-基本跳转
前端·javascript·vue.js
Marst Code1 小时前
[特殊字符] 利用 AI 大模型进行长任务项目开发
人工智能
陈如水1 小时前
Agent Skill
agent
西安老张(AIGC&ComfyUI)2 小时前
第006章:ComfyUI图片绘制常用大模型介绍
人工智能·aigc·comfyui
yongyoudayee2 小时前
AI原生与AI附加:CRM选型的架构分水岭与六维评估框架
人工智能·架构·ai-native
哈哈,柳暗花明2 小时前
人工智能专业术语详解(G)
人工智能·专业术语
码农小白AI2 小时前
钢筋力学检测报告校验升级:IACheck通审Agent版如何实现原始试验记录全链路溯源
人工智能
招标采购导航网2 小时前
招标采购导航网的召回通道设计:为什么同时用协同过滤、向量召回、规则召回三种策略
大数据·人工智能