Module Structure
子代理是拥有独立 SessionRuntime、transcript 和 sidecar 的 child run;Task 工具负责身份与调用契约,执行器负责运行,后台管理器负责持久状态、通知和崩溃恢复。
Directory Layout
packages/cli/src/agent/subagents/— 注册表、执行器、sidecar、恢复、结果采纳和内置代理SubagentRegistry.ts— Markdown/frontmatter 配置加载和优先级SubagentExecutor.ts— 独立 child SessionRuntime 执行BackgroundAgentManager.ts— 后台运行、等待、取消、resume 和 orphan reconciliationAgentSessionStore.ts— durable sidecar 与 lineage/config snapshotSubagentWorktreeLifecycle.ts— 隔离 worktree 准备与收尾SubagentResultAdoption.ts— 父 Task 缺失结果时的重启采纳BackgroundSubagentCompletion.ts— 后台终态通知构造
packages/cli/src/tools/builtin/task/task.ts— Task 工具前台、后台和 resume 协议packages/cli/src/cli/agents.ts—--agents单次运行定义严格解析packages/cli/src/slash-commands/agents.ts— TUI agent 管理入口.claude/agents/— Git 可见的 Claude Code 兼容 agent 定义docs/guides/subagents.md— 用户可见配置与恢复契约
Key Entry Points
createTaskTool()inpackages/cli/src/tools/builtin/task/task.ts— 模型侧委派入口SubagentRegistry.loadFromStandardLocations()inpackages/cli/src/agent/subagents/SubagentRegistry.ts— 分层资源发现SubagentExecutor.execute()inpackages/cli/src/agent/subagents/SubagentExecutor.ts— 直接前台执行器BackgroundAgentManager.startBackgroundAgent()inpackages/cli/src/agent/subagents/BackgroundAgentManager.ts— durable 后台 child 启动BackgroundAgentManager.resumeAgent()inpackages/cli/src/agent/subagents/BackgroundAgentManager.ts— immutable child resume
Gotchas
resume_from不会复用原 agent ID,而会创建新的 child run;源运行必须已终止、类型必须一致,并记录rootAgentId → resumedFrom → resumeDepth,否则恢复被拒绝 (packages/cli/src/tools/builtin/task/task.ts,packages/cli/src/agent/subagents/BackgroundAgentManager.ts)- resume 使用源 sidecar 中冻结的模型、权限、工具、系统提示、最大回合和隔离配置,不重新读取同名 agent 的最新定义;修改配置不会改变既有 lineage (
packages/cli/src/agent/subagents/AgentSessionStore.ts,packages/cli/src/agent/subagents/BackgroundAgentManager.ts,git:888d608f) - 子代理访问权按
parent sessionId + canonical projectPath复合校验;只有相同 session ID 不足以跨 workspace 查询、取消、采纳或恢复 child (packages/cli/src/agent/subagents/AgentSessionStore.ts,packages/cli/src/tools/builtin/task/task.ts) - 子 Agent 的 blacklist 总会加入
EnterWorktree、ExitWorktree和TeamCreate;隔离 worktree 由宿主生命周期创建,teammate 也不能嵌套创建团队 (packages/cli/src/agent/subagents/SubagentExecutor.ts,packages/cli/src/agent/subagents/BackgroundAgentManager.ts) - 隔离运行只有“成功且无改动、无提交”才自动删除 worktree;失败或任何变更都会保留路径与分支,调用方必须按交付语义处理 (
packages/cli/src/agent/subagents/SubagentWorktreeLifecycle.ts) - 后台完成通知只内联最多 32,000 字符结果和 8,000 字符错误,并明确标为不可信数据;需要完整 durable 结果时才调用
TaskOutput,不能通过高频轮询代替 completion push (packages/cli/src/agent/subagents/BackgroundSubagentCompletion.ts,packages/cli/src/tools/builtin/task/task.ts) - orphan child 只有在 durable turn 已
turn_completed且恢复上下文中存在最终 assistant 文本时才恢复为 completed;其他情况记为 interrupted failed 并允许用新 ID resume (packages/cli/src/agent/subagents/BackgroundAgentManager.ts) - running sidecar 同时保存 PID 和进程启动身份,防止 PID 复用被误判为原 owner 仍存活;旧记录缺身份时采用更保守的兼容窗口 (
packages/cli/src/agent/subagents/AgentSessionStore.ts,packages/cli/src/agent/subagents/BackgroundAgentManager.ts) - 父 transcript 中缺失前台 Task 结果时,只有 child ID、owner、description、显式 type、resume lineage 和终态结果全部匹配才会采纳;后台 child 或 cancelled child 不走该路径 (
packages/cli/src/agent/subagents/SubagentResultAdoption.ts,git:103e57b7) verification与goal-verification是保留名称,用户、项目和命令行 override 都不能覆盖;普通内置 agent 则允许被后续高优先级来源替换 (packages/cli/src/agent/subagents/SubagentRegistry.ts)
Architecture
- 每个 child 创建独立
SessionRuntime和Agent,继承父 Session 冻结的 agent/model/LSP 资源及 Provider admission owner,但使用自己的 session ID、transcript 和取消边界 (packages/cli/src/agent/subagents/SubagentExecutor.ts,packages/cli/src/agent/subagents/BackgroundAgentManager.ts) - Task 前台和后台路径共享相同 canonical 终态结果构造器;结果 metadata 承载 lineage、验证证据、修改文件与 worktree 信息,避免各表面自行解释 child sidecar (
packages/cli/src/agent/subagents/SubagentResultAdoption.ts,packages/cli/src/tools/builtin/task/task.ts) - 后台 child 先提交 terminal sidecar,再由父 Runtime 校验并写入 client-hidden inbox receipt;父 Agent 在安全边界消费该消息并继续,而不是把后台回调直接拼进当前模型输出 (
packages/cli/src/agent/subagents/BackgroundSubagentCompletion.ts,packages/cli/src/agent/Agent.ts,git:aa5060a7) Task工具声明parallelism: shared以支持同一模型响应批量委派,但不在流式预启动 allowlist;child 只会在父 tool-call durable identity 建立后启动 (packages/cli/src/tools/builtin/task/task.ts,packages/cli/src/agent/loop/StreamingToolExecutor.ts)- 子代理 LoopEvent 经 bridge 同时更新本地 progress store 和父 Session Bus;终态事件只发送一次,后台路径随后再触发 durable completion notification (
packages/cli/src/tools/builtin/task/task.ts)
Decisions
- 配置覆盖顺序为内置、Claude 用户级、受信 Claude 项目级、Blade 用户级、受信 Blade 项目级,最后是 invocation
--agents;项目来源受 Workspace Trust 限制 (packages/cli/src/agent/subagents/SubagentRegistry.ts,packages/cli/src/cli/agents.ts) - Agent sidecar 使用 schema v2、
0600原子 fsync 写入和0700目录;公开投影刻意排除 prompt、消息、配置快照、workspace 与进程身份 (packages/cli/src/agent/subagents/AgentSessionStore.ts) - 终态 sidecar 缓存上限为 256,淘汰时跳过 running session;这是为了限制长驻 server 内存,同时不丢失活跃 child 的协调状态 (
packages/cli/src/agent/subagents/AgentSessionStore.ts,git:8887f958) - 内置独立验证代理优先使用宿主提供的 JSON Schema verdict,兼容路径也只接受恰好一个固定 verdict 标题;修改文件与成功验证命令由工具 metadata 采集 (
packages/cli/src/agent/subagents/builtinVerificationAgent.ts,packages/cli/src/agent/subagents/SubagentExecutor.ts)
Patterns
- fresh Task 必须显式给
subagent_type,resume 可省略并从源运行继承;两者都先生成 canonicalsubagent_session_id,用于流式、持久化和重启恢复关联 (packages/cli/src/tools/builtin/task/task.ts,packages/cli/src/agent/loop/durableToolIdentity.ts) - 后台活动最多每秒刷新一次 sidecar 的
lastActiveAt,避免每个 LoopEvent 都写盘;最终消息、统计和 worktree outcome 在终态一次性保存 (packages/cli/src/agent/subagents/BackgroundAgentManager.ts) - agent 配置的
tools为空或缺失表示继承全部工具,disallowedTools始终优先;Claude Code 权限名会先映射到 Blade permission mode (packages/cli/src/agent/subagents/types.ts,packages/cli/src/agent/subagents/SubagentRegistry.ts)
Recovery Semantics
- orphan reconciliation 先取得 child Session lease,再回收该 child 的前后台进程、修复 interrupted turn,并把 child JSONL 与 sidecar 已继承历史按最大首尾重叠合并 (
packages/cli/src/agent/subagents/BackgroundAgentManager.ts) - 后台 completion inbox ID 由 child ID 确定,父 ACK 因而可 exactly-once;同一终态重复通知不会形成不同的父输入身份 (
packages/cli/src/agent/subagents/BackgroundSubagentCompletion.ts) - restart recovery 无法验证 durable history 时写
restartRecovery: failed并永久禁止该 run 再 resume,但不会把损坏 child 当作成功回接父会话 (packages/cli/src/agent/subagents/BackgroundAgentManager.ts)