692 lines
16 KiB
Markdown
692 lines
16 KiB
Markdown
# Swarm 系统
|
|
|
|
Claude Code 的 Swarm 系统是一个多智能体协作框架,支持团队模式、进程内队友和多种终端后端实现。
|
|
|
|
## 1. Swarm 系统概述
|
|
|
|
### 1.1 核心目录结构
|
|
|
|
**位置:** `/src/utils/swarm/`
|
|
|
|
```
|
|
swarm/
|
|
├── backends/
|
|
│ ├── types.ts # 后端类型定义
|
|
│ ├── registry.ts # 后端注册表
|
|
│ ├── detection.ts # 环境检测
|
|
│ ├── TmuxBackend.ts # Tmux 后端
|
|
│ ├── ITermBackend.ts # iTerm2 后端
|
|
│ ├── InProcessBackend.ts # 进程内后端
|
|
│ ├── PaneBackendExecutor.ts # 面板执行器
|
|
│ └── teammateModeSnapshot.ts # 团队模式快照
|
|
├── teamHelpers.ts # 团队辅助函数
|
|
├── teammateInit.ts # 队友初始化
|
|
├── teammateLayoutManager.ts # 布局管理
|
|
├── teammateModel.ts # 队友模型
|
|
├── teammatePromptAddendum.ts # 提示补充
|
|
├── permissionSync.ts # 权限同步
|
|
├── reconnection.ts # 重连机制
|
|
├── spawnUtils.ts # 生成工具
|
|
├── spawnInProcess.ts # 进程内生成
|
|
├── inProcessRunner.ts # 进程内运行器
|
|
├── constants.ts # 常量定义
|
|
└── leaderPermissionBridge.ts # 权限桥接
|
|
```
|
|
|
|
## 2. 团队模式快照 (teammateModeSnapshot.ts)
|
|
|
|
### 2.1 快照机制
|
|
|
|
```typescript
|
|
// 位置:/src/utils/swarm/backends/teammateModeSnapshot.ts
|
|
|
|
export type TeammateMode = 'auto' | 'tmux' | 'in-process'
|
|
|
|
// CLI 覆盖
|
|
let cliTeammateModeOverride: TeammateMode | null = null
|
|
|
|
// 捕获启动时的团队模式
|
|
export function captureTeammateModeSnapshot(): void {
|
|
if (cliTeammateModeOverride) {
|
|
initialTeammateMode = cliTeammateModeOverride
|
|
} else {
|
|
const config = getGlobalConfig()
|
|
initialTeammateMode = config.teammateMode ?? 'auto'
|
|
}
|
|
}
|
|
|
|
// 获取快照模式
|
|
export function getTeammateModeFromSnapshot(): TeammateMode
|
|
```
|
|
|
|
### 2.2 模式类型
|
|
|
|
| 模式 | 描述 |
|
|
|------|------|
|
|
| `auto` | 自动检测可用后端 |
|
|
| `tmux` | 强制使用 Tmux 后端 |
|
|
| `in-process` | 使用进程内后端 |
|
|
|
|
## 3. 团队辅助函数 (teamHelpers.ts)
|
|
|
|
### 3.1 团队文件操作
|
|
|
|
```typescript
|
|
// 获取团队目录
|
|
export function getTeamDir(teamName: string): string
|
|
|
|
// 获取团队配置文件路径
|
|
export function getTeamFilePath(teamName: string): string
|
|
|
|
// 读取团队文件(同步)
|
|
export function readTeamFile(teamName: string): TeamFile | null
|
|
|
|
// 读取团队文件(异步)
|
|
export async function readTeamFileAsync(teamName: string): Promise<TeamFile | null>
|
|
|
|
// 写入团队文件(同步)
|
|
function writeTeamFile(teamName: string, teamFile: TeamFile): void
|
|
|
|
// 写入团队文件(异步)
|
|
export async function writeTeamFileAsync(teamName: string, teamFile: TeamFile): Promise<void>
|
|
```
|
|
|
|
### 3.2 团队成员管理
|
|
|
|
```typescript
|
|
// 从团队文件移除队友
|
|
export function removeTeammateFromTeamFile(
|
|
teamName: string,
|
|
identifier: { agentId?: string; name?: string }
|
|
): boolean
|
|
|
|
// 移除成员
|
|
export function removeMemberFromTeam(
|
|
teamName: string,
|
|
tmuxPaneId: string
|
|
): boolean
|
|
|
|
// 按 Agent ID 移除成员
|
|
export function removeMemberByAgentId(
|
|
teamName: string,
|
|
agentId: string
|
|
): boolean
|
|
|
|
// 设置成员权限模式
|
|
export function setMemberMode(
|
|
teamName: string,
|
|
memberName: string,
|
|
mode: PermissionMode
|
|
): boolean
|
|
|
|
// 同步队友模式
|
|
export function syncTeammateMode(mode: PermissionMode, teamNameOverride?: string): void
|
|
|
|
// 设置成员活跃状态
|
|
export async function setMemberActive(
|
|
teamName: string,
|
|
memberName: string,
|
|
isActive: boolean
|
|
): Promise<void>
|
|
```
|
|
|
|
### 3.3 团队清理
|
|
|
|
```typescript
|
|
// 清理会话团队
|
|
export async function cleanupSessionTeams(): Promise<void>
|
|
|
|
// 清理团队目录
|
|
export async function cleanupTeamDirectories(teamName: string): Promise<void>
|
|
|
|
// 注册团队会话清理
|
|
export function registerTeamForSessionCleanup(teamName: string): void
|
|
|
|
// 注销团队会话清理
|
|
export function unregisterTeamForSessionCleanup(teamName: string): void
|
|
```
|
|
|
|
## 4. 队友初始化 (teammateInit.ts)
|
|
|
|
### 4.1 初始化流程
|
|
|
|
```typescript
|
|
// 位置:/src/utils/swarm/teammateInit.ts
|
|
|
|
export function initializeTeammateHooks(
|
|
setAppState: (updater: (prev: AppState) => AppState) => void,
|
|
sessionId: string,
|
|
teamInfo: { teamName: string; agentId: string; agentName: string }
|
|
): void
|
|
```
|
|
|
|
### 4.2 团队权限应用
|
|
|
|
```typescript
|
|
// 应用团队范围的允许路径
|
|
if (teamFile.teamAllowedPaths && teamFile.teamAllowedPaths.length > 0) {
|
|
for (const allowedPath of teamFile.teamAllowedPaths) {
|
|
setAppState(prev => ({
|
|
...prev,
|
|
toolPermissionContext: applyPermissionUpdate(
|
|
prev.toolPermissionContext,
|
|
{
|
|
type: 'addRules',
|
|
rules: [{ toolName: allowedPath.toolName, ruleContent }],
|
|
behavior: 'allow',
|
|
destination: 'session'
|
|
}
|
|
)
|
|
}))
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4.3 停止钩子注册
|
|
|
|
```typescript
|
|
// 注册停止钩子以通知团队领导
|
|
addFunctionHook(
|
|
setAppState,
|
|
sessionId,
|
|
'Stop',
|
|
'',
|
|
async (messages, _signal) => {
|
|
// 标记为空闲
|
|
void setMemberActive(teamName, agentName, false)
|
|
|
|
// 发送空闲通知
|
|
const notification = createIdleNotification(agentName, {...})
|
|
await writeToMailbox(leadAgentName, {...})
|
|
|
|
return true
|
|
}
|
|
)
|
|
```
|
|
|
|
## 5. 布局管理 (teammateLayoutManager.ts)
|
|
|
|
### 5.1 队友颜色分配
|
|
|
|
```typescript
|
|
// 分配队友颜色
|
|
export function assignTeammateColor(teammateId: string): AgentColorName
|
|
|
|
// 获取队友颜色
|
|
export function getTeammateColor(teammateId: string): AgentColorName | undefined
|
|
|
|
// 清除所有颜色分配
|
|
export function clearTeammateColors(): void
|
|
```
|
|
|
|
### 5.2 后端选择
|
|
|
|
```typescript
|
|
async function getBackend(): Promise<PaneBackend> {
|
|
return (await detectAndGetBackend()).backend
|
|
}
|
|
```
|
|
|
|
### 5.3 面板操作
|
|
|
|
```typescript
|
|
// 在 Swarm 视图中创建队友面板
|
|
export async function createTeammatePaneInSwarmView(
|
|
teammateName: string,
|
|
teammateColor: AgentColorName
|
|
): Promise<{ paneId: string; isFirstTeammate: boolean }>
|
|
|
|
// 启用面板边框状态
|
|
export async function enablePaneBorderStatus(
|
|
windowTarget?: string,
|
|
useSwarmSocket?: boolean
|
|
): Promise<void>
|
|
|
|
// 发送命令到面板
|
|
export async function sendCommandToPane(
|
|
paneId: string,
|
|
command: string,
|
|
useSwarmSocket?: boolean
|
|
): Promise<void>
|
|
```
|
|
|
|
## 6. 后端系统 (backends/)
|
|
|
|
### 6.1 后端类型
|
|
|
|
**位置:** `/src/utils/swarm/backends/types.ts`
|
|
|
|
```typescript
|
|
export type BackendType = 'tmux' | 'iterm2' | 'in-process'
|
|
|
|
export type PaneBackendType = 'tmux' | 'iterm2'
|
|
|
|
export type PaneId = string
|
|
|
|
export type CreatePaneResult = {
|
|
paneId: PaneId
|
|
isFirstTeammate: boolean
|
|
}
|
|
```
|
|
|
|
### 6.2 PaneBackend 接口
|
|
|
|
```typescript
|
|
export type PaneBackend = {
|
|
readonly type: BackendType
|
|
readonly displayName: string
|
|
readonly supportsHideShow: boolean
|
|
|
|
isAvailable(): Promise<boolean>
|
|
isRunningInside(): Promise<boolean>
|
|
|
|
createTeammatePaneInSwarmView(
|
|
name: string,
|
|
color: AgentColorName
|
|
): Promise<CreatePaneResult>
|
|
|
|
sendCommandToPane(
|
|
paneId: PaneId,
|
|
command: string,
|
|
useExternalSession?: boolean
|
|
): Promise<void>
|
|
|
|
setPaneBorderColor(
|
|
paneId: PaneId,
|
|
color: AgentColorName,
|
|
useExternalSession?: boolean
|
|
): Promise<void>
|
|
|
|
setPaneTitle(
|
|
paneId: PaneId,
|
|
name: string,
|
|
color: AgentColorName,
|
|
useExternalSession?: boolean
|
|
): Promise<void>
|
|
|
|
enablePaneBorderStatus(
|
|
windowTarget?: string,
|
|
useExternalSession?: boolean
|
|
): Promise<void>
|
|
|
|
rebalancePanes(windowTarget: string, hasLeader: boolean): Promise<void>
|
|
|
|
killPane(paneId: PaneId, useExternalSession?: boolean): Promise<boolean>
|
|
|
|
hidePane(paneId: PaneId, useExternalSession?: boolean): Promise<boolean>
|
|
|
|
showPane(
|
|
paneId: PaneId,
|
|
targetWindowOrPane: string,
|
|
useExternalSession?: boolean
|
|
): Promise<boolean>
|
|
}
|
|
```
|
|
|
|
### 6.3 TmuxBackend
|
|
|
|
**位置:** `/src/utils/swarm/backends/TmuxBackend.ts`
|
|
|
|
使用 tmux 进行面板管理的后端实现。
|
|
|
|
**功能:**
|
|
- 创建 tmux 窗口和面板
|
|
- 发送命令到指定面板
|
|
- 设置面板颜色和标题
|
|
- 管理面板布局
|
|
|
|
### 6.4 ITermBackend
|
|
|
|
**位置:** `/src/utils/swarm/backends/ITermBackend.ts`
|
|
|
|
使用 iTerm2 原生分割面板的后端实现。
|
|
|
|
**功能:**
|
|
- 通过 it2 CLI 与 iTerm2 通信
|
|
- 创建垂直/水平分割
|
|
- 设置 profile 和颜色
|
|
|
|
### 6.5 InProcessBackend
|
|
|
|
**位置:** `/src/utils/swarm/backends/InProcessBackend.ts`
|
|
|
|
在当前 Node.js 进程中运行队友的后端。
|
|
|
|
**特性:**
|
|
- 共享 API 客户端和 MCP 连接
|
|
- 通过文件邮箱通信
|
|
- 使用 AbortController 终止
|
|
- 始终可用(无外部依赖)
|
|
|
|
```typescript
|
|
export class InProcessBackend implements TeammateExecutor {
|
|
readonly type = 'in-process' as const
|
|
|
|
async spawn(config: TeammateSpawnConfig): Promise<TeammateSpawnResult>
|
|
async sendMessage(agentId: string, message: TeammateMessage): Promise<void>
|
|
async terminate(agentId: string, reason?: string): Promise<boolean>
|
|
async kill(agentId: string): Promise<boolean>
|
|
async isActive(agentId: string): Promise<boolean>
|
|
}
|
|
```
|
|
|
|
### 6.6 后端注册表
|
|
|
|
**位置:** `/src/utils/swarm/backends/registry.ts`
|
|
|
|
```typescript
|
|
// 检测并获取合适的后端
|
|
export async function detectAndGetBackend(): Promise<BackendDetectionResult>
|
|
|
|
// 注册所有后端
|
|
export async function ensureBackendsRegistered(): Promise<void>
|
|
|
|
// 按类型获取后端
|
|
export async function getBackendByType(type: BackendType): Promise<PaneBackend>
|
|
```
|
|
|
|
## 7. 权限同步 (permissionSync.ts)
|
|
|
|
### 7.1 权限请求流程
|
|
|
|
```
|
|
Worker Agent → 权限请求 → Leader Mailbox
|
|
Leader Agent ← 轮询 ← 权限队列
|
|
↓
|
|
用户批准/拒绝
|
|
↓
|
|
Leader → 权限响应 → Worker Mailbox
|
|
Worker ← 处理响应 ←
|
|
```
|
|
|
|
### 7.2 权限请求结构
|
|
|
|
```typescript
|
|
export type SwarmPermissionRequest = {
|
|
id: string
|
|
workerId: string
|
|
workerName: string
|
|
workerColor?: string
|
|
teamName: string
|
|
toolName: string
|
|
toolUseId: string
|
|
description: string
|
|
input: Record<string, unknown>
|
|
permissionSuggestions: unknown[]
|
|
status: 'pending' | 'approved' | 'rejected'
|
|
resolvedBy?: 'worker' | 'leader'
|
|
resolvedAt?: number
|
|
feedback?: string
|
|
updatedInput?: Record<string, unknown>
|
|
permissionUpdates?: PermissionUpdate[]
|
|
createdAt: number
|
|
}
|
|
```
|
|
|
|
### 7.3 权限操作
|
|
|
|
```typescript
|
|
// 创建权限请求
|
|
export function createPermissionRequest(params: {...}): SwarmPermissionRequest
|
|
|
|
// 写入待处理权限请求
|
|
export async function writePermissionRequest(
|
|
request: SwarmPermissionRequest
|
|
): Promise<SwarmPermissionRequest>
|
|
|
|
// 读取待处理权限
|
|
export async function readPendingPermissions(
|
|
teamName?: string
|
|
): Promise<SwarmPermissionRequest[]>
|
|
|
|
// 解决权限请求
|
|
export async function resolvePermission(
|
|
requestId: string,
|
|
resolution: PermissionResolution,
|
|
teamName?: string
|
|
): Promise<boolean>
|
|
|
|
// 通过邮箱发送权限请求
|
|
export async function sendPermissionRequestViaMailbox(
|
|
request: SwarmPermissionRequest
|
|
): Promise<boolean>
|
|
|
|
// 通过邮箱发送权限响应
|
|
export async function sendPermissionResponseViaMailbox(
|
|
workerName: string,
|
|
resolution: PermissionResolution,
|
|
requestId: string,
|
|
teamName?: string
|
|
): Promise<boolean>
|
|
```
|
|
|
|
### 7.4 沙箱权限
|
|
|
|
```typescript
|
|
// 发送沙箱权限请求
|
|
export async function sendSandboxPermissionRequestViaMailbox(
|
|
host: string,
|
|
requestId: string,
|
|
teamName?: string
|
|
): Promise<boolean>
|
|
|
|
// 发送沙箱权限响应
|
|
export async function sendSandboxPermissionResponseViaMailbox(
|
|
workerName: string,
|
|
requestId: string,
|
|
host: string,
|
|
allow: boolean,
|
|
teamName?: string
|
|
): Promise<boolean>
|
|
```
|
|
|
|
### 7.5 团队领导检查
|
|
|
|
```typescript
|
|
// 检查是否为团队领导
|
|
export function isTeamLeader(teamName?: string): boolean
|
|
|
|
// 检查是否为 Swarm Worker
|
|
export function isSwarmWorker(): boolean
|
|
```
|
|
|
|
## 8. 重连机制 (reconnection.ts)
|
|
|
|
### 8.1 初始团队上下文计算
|
|
|
|
```typescript
|
|
export function computeInitialTeamContext():
|
|
| AppState['teamContext']
|
|
| undefined {
|
|
// 从 CLI 参数获取动态团队上下文
|
|
const context = getDynamicTeamContext()
|
|
|
|
if (!context?.teamName || !context?.agentName) {
|
|
return undefined
|
|
}
|
|
|
|
// 读取团队文件获取领导 Agent ID
|
|
const teamFile = readTeamFile(teamName)
|
|
|
|
return {
|
|
teamName,
|
|
teamFilePath,
|
|
leadAgentId: teamFile.leadAgentId,
|
|
selfAgentId: agentId,
|
|
selfAgentName: agentName,
|
|
isLeader,
|
|
teammates: {}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 8.2 从会话恢复团队上下文
|
|
|
|
```typescript
|
|
export function initializeTeammateContextFromSession(
|
|
setAppState: (updater: (prev: AppState) => AppState) => void,
|
|
teamName: string,
|
|
agentName: string
|
|
): void {
|
|
// 从团队文件读取成员信息
|
|
const teamFile = readTeamFile(teamName)
|
|
const member = teamFile.members.find(m => m.name === agentName)
|
|
const agentId = member?.agentId
|
|
|
|
// 设置 AppState 中的团队上下文
|
|
setAppState(prev => ({
|
|
...prev,
|
|
teamContext: {
|
|
teamName,
|
|
teamFilePath,
|
|
leadAgentId: teamFile.leadAgentId,
|
|
selfAgentId: agentId,
|
|
selfAgentName: agentName,
|
|
isLeader: false,
|
|
teammates: {}
|
|
}
|
|
}))
|
|
}
|
|
```
|
|
|
|
## 9. 队友生成 (spawnUtils.ts, spawnInProcess.ts)
|
|
|
|
### 9.1 进程内队友生成
|
|
|
|
```typescript
|
|
// 位置:/src/utils/swarm/spawnInProcess.ts
|
|
|
|
export async function spawnInProcessTeammate(
|
|
identity: TeammateIdentity,
|
|
context: ToolUseContext
|
|
): Promise<{
|
|
success: boolean
|
|
agentId: string
|
|
taskId?: string
|
|
teammateContext?: TeammateContext
|
|
abortController?: AbortController
|
|
error?: string
|
|
}>
|
|
|
|
export function killInProcessTeammate(
|
|
taskId: string,
|
|
setAppState: (updater: (prev: AppState) => AppState) => void
|
|
): boolean
|
|
```
|
|
|
|
### 9.2 进程内运行器
|
|
|
|
```typescript
|
|
// 位置:/src/utils/swarm/inProcessRunner.ts
|
|
|
|
export async function startInProcessTeammate(params: {
|
|
identity: TeammateIdentity
|
|
taskId: string
|
|
prompt: string
|
|
teammateContext: TeammateContext
|
|
toolUseContext: ToolUseContext
|
|
abortController: AbortController
|
|
model?: string
|
|
systemPrompt?: string
|
|
systemPromptMode?: 'default' | 'replace' | 'append'
|
|
allowedTools?: string[]
|
|
allowPermissionPrompts?: boolean
|
|
}): Promise<void>
|
|
```
|
|
|
|
## 10. TeammateExecutor 接口
|
|
|
|
### 10.1 接口定义
|
|
|
|
```typescript
|
|
export type TeammateExecutor = {
|
|
readonly type: BackendType
|
|
|
|
isAvailable(): Promise<boolean>
|
|
spawn(config: TeammateSpawnConfig): Promise<TeammateSpawnResult>
|
|
sendMessage(agentId: string, message: TeammateMessage): Promise<void>
|
|
terminate(agentId: string, reason?: string): Promise<boolean>
|
|
kill(agentId: string): Promise<boolean>
|
|
isActive(agentId: string): Promise<boolean>
|
|
}
|
|
```
|
|
|
|
### 10.2 TeammateSpawnConfig
|
|
|
|
```typescript
|
|
export type TeammateSpawnConfig = TeammateIdentity & {
|
|
prompt: string
|
|
cwd: string
|
|
model?: string
|
|
systemPrompt?: string
|
|
systemPromptMode?: 'default' | 'replace' | 'append'
|
|
worktreePath?: string
|
|
parentSessionId: string
|
|
permissions?: string[]
|
|
allowPermissionPrompts?: boolean
|
|
}
|
|
```
|
|
|
|
### 10.3 TeammateSpawnResult
|
|
|
|
```typescript
|
|
export type TeammateSpawnResult = {
|
|
success: boolean
|
|
agentId: string // 格式: agentName@teamName
|
|
error?: string
|
|
abortController?: AbortController // 进程内队友使用
|
|
taskId?: string // AppState.tasks 中的任务 ID
|
|
paneId?: PaneId // 基于面板的后端使用
|
|
}
|
|
```
|
|
|
|
## 11. 消息类型
|
|
|
|
### 11.1 TeammateMessage
|
|
|
|
```typescript
|
|
export type TeammateMessage = {
|
|
text: string
|
|
from: string
|
|
color?: string
|
|
timestamp?: string
|
|
summary?: string // 5-10 字预览
|
|
}
|
|
```
|
|
|
|
## 12. 类型守卫
|
|
|
|
### 12.1 面板后端检查
|
|
|
|
```typescript
|
|
export function isPaneBackend(type: BackendType): type is 'tmux' | 'iterm2' {
|
|
return type === 'tmux' || type === 'iterm2'
|
|
}
|
|
```
|
|
|
|
## 13. 环境检测
|
|
|
|
### 13.1 tmux 检测
|
|
|
|
```typescript
|
|
// 检测是否在 tmux 会话内运行
|
|
export async function isInsideTmux(): Promise<boolean>
|
|
```
|
|
|
|
### 13.2 iTerm2 检测
|
|
|
|
```typescript
|
|
// 检测 iTerm2 是否可用
|
|
export async function isITerm2Available(): Promise<boolean>
|
|
```
|
|
|
|
## 14. 团队常量
|
|
|
|
**位置:** `/src/utils/swarm/constants.ts`
|
|
|
|
```typescript
|
|
export const TEAM_LEAD_NAME = 'team-lead'
|
|
export const DEFAULT_TEAM_PANE_WIDTH = '30%'
|
|
export const DEFAULT_TEAM_PANE_HEIGHT = '50%'
|
|
```
|