@pencil-agent/nano-pencil 2.0.0-beta.9 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +267 -267
  2. package/dist/build-meta.json +3 -3
  3. package/dist/core/export-html/AGENT.md +11 -11
  4. package/dist/core/export-html/template.css +971 -971
  5. package/dist/core/export-html/template.html +54 -54
  6. package/dist/core/extensions-host/index.d.ts +1 -1
  7. package/dist/core/extensions-host/types.d.ts +5 -8
  8. package/dist/extensions/builtin/AGENT.md +115 -115
  9. package/dist/extensions/builtin/browser/AGENT.md +17 -17
  10. package/dist/extensions/builtin/browser/agent-workspace/agent_helpers.py +12 -12
  11. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/amazon/product-search.md +198 -198
  12. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/archive-org/scraping.md +341 -341
  13. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv/scraping.md +311 -311
  14. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv-bulk/scraping.md +333 -333
  15. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/atlas/overview.md +70 -70
  16. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/booking-com/scraping.md +578 -578
  17. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/capterra/scraping.md +440 -440
  18. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/centilebrain/generate-estimates.md +110 -110
  19. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coingecko/scraping.md +325 -325
  20. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coinmarketcap/scraping.md +463 -463
  21. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coursera/scraping.md +360 -360
  22. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/craigslist/scraping.md +390 -390
  23. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/crossref/scraping.md +568 -568
  24. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/dev-to/scraping.md +323 -323
  25. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/duckduckgo/scraping.md +349 -349
  26. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/ebay/scraping.md +435 -435
  27. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/etsy/scraping.md +506 -506
  28. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/eventbrite/scraping.md +363 -363
  29. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/expedia/automation.md +168 -168
  30. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/groups.md +236 -236
  31. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/pages.md +295 -295
  32. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/framer/editor.md +108 -108
  33. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/fred/scraping.md +493 -493
  34. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/g2/scraping.md +580 -580
  35. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/genius/scraping.md +511 -511
  36. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/repo-actions.md +65 -65
  37. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/scraping.md +184 -184
  38. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/glassdoor/scraping.md +543 -543
  39. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gmail/compose.md +122 -122
  40. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/goodreads/scraping.md +461 -461
  41. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gutenberg/scraping.md +383 -383
  42. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/hackernews/scraping.md +243 -243
  43. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/howlongtobeat/scraping.md +473 -473
  44. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/imdb/scraping.md +271 -271
  45. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/itch-io/scraping.md +436 -436
  46. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/job-boards/indeed-glassdoor.md +1021 -1021
  47. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/letterboxd/scraping.md +349 -349
  48. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/linkedin/invitation-manager.md +109 -109
  49. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/loom/folder-enumeration.md +170 -170
  50. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/macrotrends/scraping.md +537 -537
  51. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/article-hydration.md +120 -120
  52. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/scraping.md +414 -414
  53. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/metacritic/scraping.md +477 -477
  54. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/musicbrainz/scraping.md +478 -478
  55. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/nasa/scraping.md +339 -339
  56. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/news-aggregation/multi-source.md +205 -205
  57. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/open-library/scraping.md +472 -472
  58. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openalex/scraping.md +470 -470
  59. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openstreetmap/scraping.md +490 -490
  60. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/package-registries/npm-pypi.md +478 -478
  61. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/polymarket/scraping.md +234 -234
  62. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/producthunt/scraping.md +307 -307
  63. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/pubmed/scraping.md +421 -421
  64. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/quora/scraping.md +364 -364
  65. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rawg/scraping.md +352 -352
  66. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/reddit/scraping.md +124 -124
  67. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rest-countries/scraping.md +233 -233
  68. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/sec-edgar/scraping.md +361 -361
  69. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/README.md +36 -36
  70. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/embedded-apps.md +72 -72
  71. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/knowledge-base.md +109 -109
  72. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/polaris-inputs.md +137 -137
  73. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/soundcloud/scraping.md +362 -362
  74. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/spotify/scraping.md +339 -339
  75. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/stackoverflow/scraping.md +435 -435
  76. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/steam/scraping.md +575 -575
  77. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/substack/scraping.md +338 -338
  78. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/thetechgeeks/pricing.md +52 -52
  79. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tiktok/upload.md +107 -107
  80. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tradingview/scraping.md +309 -309
  81. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trello/boards-and-lists.md +88 -88
  82. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trustpilot/scraping.md +375 -375
  83. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/walmart/scraping.md +444 -444
  84. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wayback-machine/scraping.md +306 -306
  85. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/weather/scraping.md +398 -398
  86. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wellfound/scraping.md +596 -596
  87. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/world-bank/scraping.md +356 -356
  88. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/xiaohongshu/scraping.md +84 -84
  89. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/youtube/scraping.md +418 -418
  90. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/zillow/scraping.md +433 -433
  91. package/dist/extensions/builtin/browser/browser.md +73 -73
  92. package/dist/extensions/builtin/browser/install.md +142 -142
  93. package/dist/extensions/builtin/browser/interaction-skills/connection.md +48 -48
  94. package/dist/extensions/builtin/browser/interaction-skills/cookies.md +3 -3
  95. package/dist/extensions/builtin/browser/interaction-skills/cross-origin-iframes.md +3 -3
  96. package/dist/extensions/builtin/browser/interaction-skills/dialogs.md +64 -64
  97. package/dist/extensions/builtin/browser/interaction-skills/downloads.md +3 -3
  98. package/dist/extensions/builtin/browser/interaction-skills/drag-and-drop.md +3 -3
  99. package/dist/extensions/builtin/browser/interaction-skills/dropdowns.md +3 -3
  100. package/dist/extensions/builtin/browser/interaction-skills/iframes.md +3 -3
  101. package/dist/extensions/builtin/browser/interaction-skills/network-requests.md +3 -3
  102. package/dist/extensions/builtin/browser/interaction-skills/print-as-pdf.md +3 -3
  103. package/dist/extensions/builtin/browser/interaction-skills/profile-sync.md +90 -90
  104. package/dist/extensions/builtin/browser/interaction-skills/screenshots.md +17 -17
  105. package/dist/extensions/builtin/browser/interaction-skills/scrolling.md +3 -3
  106. package/dist/extensions/builtin/browser/interaction-skills/shadow-dom.md +3 -3
  107. package/dist/extensions/builtin/browser/interaction-skills/tabs.md +69 -69
  108. package/dist/extensions/builtin/browser/interaction-skills/uploads.md +1 -1
  109. package/dist/extensions/builtin/browser/interaction-skills/viewport.md +3 -3
  110. package/dist/extensions/builtin/browser/src/browser_harness/AGENT.md +15 -15
  111. package/dist/extensions/builtin/browser/src/browser_harness/__init__.py +8 -8
  112. package/dist/extensions/builtin/browser/src/browser_harness/_ipc.py +90 -90
  113. package/dist/extensions/builtin/browser/src/browser_harness/admin.py +722 -722
  114. package/dist/extensions/builtin/browser/src/browser_harness/daemon.py +328 -328
  115. package/dist/extensions/builtin/browser/src/browser_harness/helpers.py +396 -396
  116. package/dist/extensions/builtin/browser/src/browser_harness/run.py +103 -103
  117. package/dist/extensions/builtin/discipline/skills/brainstorming/SKILL.md +33 -33
  118. package/dist/extensions/builtin/discipline/skills/executing-plans/SKILL.md +25 -25
  119. package/dist/extensions/builtin/discipline/skills/finishing-development-branch/SKILL.md +25 -25
  120. package/dist/extensions/builtin/discipline/skills/receiving-code-review/SKILL.md +22 -22
  121. package/dist/extensions/builtin/discipline/skills/requesting-code-review/SKILL.md +31 -31
  122. package/dist/extensions/builtin/discipline/skills/systematic-debugging/SKILL.md +28 -28
  123. package/dist/extensions/builtin/discipline/skills/test-driven-development/SKILL.md +32 -32
  124. package/dist/extensions/builtin/discipline/skills/using-git-worktrees/SKILL.md +25 -25
  125. package/dist/extensions/builtin/discipline/skills/verification-before-completion/SKILL.md +27 -27
  126. package/dist/extensions/builtin/discipline/skills/writing-plans/SKILL.md +26 -26
  127. package/dist/extensions/builtin/goal/README.md +67 -67
  128. package/dist/extensions/builtin/goal/goal-controller.js +1 -1
  129. package/dist/extensions/builtin/goal/goal-prompts.js +4 -4
  130. package/dist/extensions/builtin/grub/README.md +112 -112
  131. package/dist/extensions/builtin/link-world/agent-workspace/README.md +16 -16
  132. package/dist/extensions/builtin/link-world/internet-search/internet-search.md +65 -65
  133. package/dist/extensions/builtin/link-world/link-world-agent.md +82 -82
  134. package/dist/extensions/builtin/link-world/linkworld.md +313 -313
  135. package/dist/extensions/builtin/link-world/network-routing/network-routing.md +67 -67
  136. package/dist/extensions/builtin/loop/README.md +92 -92
  137. package/dist/extensions/builtin/mcp/figma-design.md +68 -68
  138. package/dist/extensions/builtin/mcp/mcp-management.md +85 -85
  139. package/dist/extensions/builtin/recap/AGENT.md +15 -15
  140. package/dist/extensions/builtin/sal/README.md +72 -72
  141. package/dist/extensions/builtin/security-audit/README.md +289 -289
  142. package/dist/extensions/builtin/team/AGENT.md +112 -112
  143. package/dist/extensions/builtin/team/TESTING.md +299 -299
  144. package/dist/extensions/builtin/token-save/README.md +56 -56
  145. package/dist/extensions/optional/AGENT.md +10 -10
  146. package/dist/index.d.ts +5 -30
  147. package/dist/index.js +1 -1
  148. package/dist/models.d.ts +7 -0
  149. package/dist/models.js +1 -0
  150. package/dist/modes/interactive/theme/dark.json +85 -85
  151. package/dist/modes/interactive/theme/light.json +84 -84
  152. package/dist/modes/interactive/theme/theme-schema.json +335 -335
  153. package/dist/modes/interactive/theme/warm.json +81 -81
  154. package/dist/node_modules/@pencil-agent/ai/dist/cli.js +0 -0
  155. package/dist/packages/protocol/src/flags.d.ts +20 -0
  156. package/dist/packages/protocol/src/flags.js +0 -0
  157. package/dist/packages/protocol/src/hooks.d.ts +17 -0
  158. package/dist/packages/protocol/src/hooks.js +0 -0
  159. package/dist/packages/protocol/src/index.d.ts +4 -2
  160. package/dist/packages/protocol/src/index.js +1 -1
  161. package/dist/packages/protocol/src/lifecycle.d.ts +11 -21
  162. package/dist/public-config.d.ts +12 -0
  163. package/dist/public-config.js +1 -0
  164. package/dist/runtime.d.ts +9 -0
  165. package/dist/runtime.js +1 -0
  166. package/dist/session-compaction.d.ts +7 -0
  167. package/dist/session-compaction.js +1 -0
  168. package/dist/session.d.ts +7 -0
  169. package/dist/session.js +1 -0
  170. package/dist/skills.d.ts +7 -0
  171. package/dist/skills.js +1 -0
  172. package/dist/tools.d.ts +7 -0
  173. package/dist/tools.js +1 -0
  174. package/docs/ACP/345/215/217/350/256/256/351/233/206/346/210/220/345/274/200/345/217/221/346/226/207/346/241/243.md +851 -0
  175. package/docs/SDK-TESTING.md +364 -0
  176. package/docs/codex-goal-command-impl.md +1055 -1055
  177. package/docs/codex-goal-vs-grub.md +500 -500
  178. package/docs/custom-provider.md +27 -27
  179. package/docs/extensions.md +27 -27
  180. package/docs/keybindings.md +27 -27
  181. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +250 -250
  182. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +122 -122
  183. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +1222 -1222
  184. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/256/236/347/216/260/346/212/245/345/221/212.md" +158 -158
  185. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/257/271/346/257/224/345/210/206/346/236/220.md" +128 -128
  186. package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +320 -320
  187. package/docs/loop-usage-examples.md +214 -214
  188. package/docs/mem-core/346/212/200/346/234/257/346/226/207/346/241/243.md +593 -0
  189. package/docs/models.md +27 -27
  190. package/docs/packages.md +27 -27
  191. package/docs/pi-design-philosophy.md +457 -457
  192. package/docs/planmode.md +1987 -1987
  193. package/docs/prompt-templates.md +27 -27
  194. package/docs/providers.md +27 -27
  195. package/docs/sdk.md +27 -27
  196. package/docs/skills.md +27 -27
  197. package/docs/startup-performance-optimization.md +301 -0
  198. package/docs/themes.md +27 -27
  199. package/docs/tui.md +27 -27
  200. package/docs//350/256/244/347/237/245/345/234/260/345/233/276.md +47 -0
  201. package/package.json +190 -162
  202. package/docs/cc-agent-design.md +0 -1297
  203. package/docs/cc-tui-design.md +0 -1333
  204. package/docs/nanoPencil-/345/255/246/344/271/240/350/256/241/345/210/222.md +0 -170
  205. package/docs/scan-report.md +0 -3820
  206. package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
  207. package/docs//351/230/277/351/207/214/345/267/264/345/267/264/350/264/242/346/212/245/345/210/206/346/236/220/344/271/246.md +0 -261
@@ -1,1297 +0,0 @@
1
- # Claude Code 主子 Agent 架构拆解
2
-
3
- > 源码版本:@anthropic-ai/claude-code@2.1.92
4
- > 目标:让一个模型可以在 nanoPencil 中一比一复刻 CC 的 Agent(子代理)系统
5
-
6
- ---
7
-
8
- ## 一、一句话概括
9
-
10
- CC 的 Agent 工具是一个**进程内子代理生成器**:父 agent 通过 `Agent` tool call 创建一个全新的 `AgentSession`(独立 LLM 循环),子 agent 拥有独立的系统提示、工具集和消息历史,完成后将最后一条 assistant 消息作为结果返回给父 agent。
11
-
12
- **不是**子进程、不是 HTTP 调用、不是 IPC——是同一个进程内的异步函数调用,共享同一套 runtime 基础设施。
13
-
14
- ---
15
-
16
- ## 二、核心概念映射
17
-
18
- | CC 概念 | nanoPencil 对应 | 说明 |
19
- |---------|----------------|------|
20
- | `Agent` tool | `SubAgentSpec` + `SubAgentRuntime` | CC 是 LLM 可调用的 tool;nanoPencil 是内部 API |
21
- | `Task` (alias) | 无 | CC 中 Agent 的别名 |
22
- | `subagent_type` | `runRole` / agent definition | 决定工具集和系统提示 |
23
- | `run_in_background` | `run_in_background` | 异步执行,输出写文件 |
24
- | `isolation: "worktree"` | `WorktreeManager` | git worktree 隔离 |
25
- | `agentNameRegistry` | `activeAgents` Map | 按名称寻址子 agent |
26
- | Handoff classifier | 无 | CC 独有的安全审查 |
27
-
28
- ---
29
-
30
- ## 三、Agent 工具的 JSON Schema
31
-
32
- ### 3.1 Input Schema(LLM 看到的)
33
-
34
- ```typescript
35
- // CC 源码中的定义(cli.js 第 3947 行附近)
36
- const AgentInputSchema = z.object({
37
- description: z.string()
38
- .describe("A short (3-5 word) description of the task"),
39
-
40
- prompt: z.string()
41
- .describe("The task for the agent to perform"),
42
-
43
- subagent_type: z.string().optional()
44
- .describe("The type of specialized agent to use for this task"),
45
-
46
- model: z.enum(["sonnet", "opus", "haiku"]).optional()
47
- .describe("Optional model override for this agent. Takes precedence over the agent definition's model frontmatter. If omitted, uses the agent definition's model, or inherits from the parent."),
48
-
49
- run_in_background: z.boolean().optional()
50
- .describe("Set to true to run this agent in the background. You will be notified when it completes."),
51
-
52
- name: z.string().optional()
53
- .describe("Name for the spawned agent. Makes it addressable via SendMessage({to: name}) while running."),
54
-
55
- team_name: z.string().optional()
56
- .describe("Team name for spawning. Uses current team context if omitted."),
57
-
58
- mode: z.enum(["acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"]).optional()
59
- .describe('Permission mode for spawned teammate (e.g., "plan" to require plan approval).'),
60
-
61
- isolation: z.enum(["worktree"]).optional()
62
- .describe('Isolation mode. "worktree" creates a temporary git worktree so the agent works on an isolated copy of the repo.'),
63
-
64
- cwd: z.string().optional()
65
- .describe('Absolute path to run the agent in. Overrides the working directory for all filesystem and shell operations within this agent. Mutually exclusive with isolation: "worktree".'),
66
- });
67
- ```
68
-
69
- **注意**:
70
- - `cwd` 在最终发送给 LLM 的 schema 中被 omit 掉了(`nzY().omit({cwd:!0})`),但内部处理时仍然接受。
71
- - `run_in_background` 在 `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS` 环境变量启用时也会被 omit(`xs1` 函数:`DS6||hx()?q.omit({run_in_background:!0}):q`)。
72
-
73
- ### 3.2 Output Schema
74
-
75
- ```typescript
76
- // 同步完成时
77
- type AgentOutputCompleted = {
78
- agentId: string;
79
- agentType?: string;
80
- content: { type: "text"; text: string }[];
81
- totalToolUseCount: number;
82
- totalDurationMs: number;
83
- totalTokens: number;
84
- usage: {
85
- input_tokens: number;
86
- output_tokens: number;
87
- cache_creation_input_tokens: number | null;
88
- cache_read_input_tokens: number | null;
89
- server_tool_use: {
90
- web_search_requests: number;
91
- web_fetch_requests: number;
92
- } | null;
93
- service_tier: ("standard" | "priority" | "batch") | null;
94
- cache_creation: {
95
- ephemeral_1h_input_tokens: number;
96
- ephemeral_5m_input_tokens: number;
97
- } | null;
98
- };
99
- status: "completed";
100
- prompt: string; // 原始 prompt 文本
101
- };
102
-
103
- // 异步启动时
104
- type AgentOutputAsync = {
105
- status: "async_launched";
106
- agentId: string; // 异步 agent 的 ID
107
- description: string; // 任务描述
108
- prompt: string; // 原始 prompt
109
- outputFile: string; // 输出文件路径,可用于检查进度
110
- canReadOutputFile?: boolean; // 父 agent 是否有 Read/Bash 工具
111
- };
112
-
113
- type AgentOutput = AgentOutputCompleted | AgentOutputAsync;
114
- ```
115
-
116
- ---
117
-
118
- ## 四、AgentDefinition 完整接口
119
-
120
- 从源码中提取的 `AgentDefinition` 所有字段:
121
-
122
- ```typescript
123
- interface AgentDefinition {
124
- // === 必填 ===
125
- agentType: string; // 唯一标识,如 "general-purpose"
126
- description: string; // 一行描述
127
- whenToUse: string | (() => string); // ⚠️ 可以是函数!Explore 就是函数引用
128
- getSystemPrompt: (ctx: { toolUseContext: any }) => string;
129
-
130
- // === 工具控制(二选一) ===
131
- tools?: string[]; // 白名单,["*"] = 全部
132
- disallowedTools?: string[]; // 黑名单
133
-
134
- // === 模型 ===
135
- model?: string; // "sonnet" | "opus" | "haiku" | "inherit"
136
- effort?: "low" | "medium" | "high" | number; // 推理努力程度
137
-
138
- // === 权限 ===
139
- permissionMode?: "acceptEdits" | "auto" | "bypassPermissions" | "default" | "dontAsk" | "plan";
140
-
141
- // === 隔离 ===
142
- isolation?: "worktree"; // worktree 隔离模式
143
-
144
- // === 后台 ===
145
- background?: boolean; // ⚠️ agent 定义级的后台标志,不同于 run_in_background 参数
146
-
147
- // === Fork 行为 ===
148
- forksParentContext?: boolean | "turn"; // ⚠️ 文档未提及!控制 fork 时继承哪些父消息
149
- // true = 继承全部父消息
150
- // "turn" = 只继承当前 turn 的消息
151
- // undefined = 不继承
152
-
153
- // === MCP ===
154
- requiredMcpServers?: string[]; // ⚠️ 文档未提及!需要的 MCP 服务器
155
- mcpServers?: string[]; // 关联的 MCP 服务器
156
-
157
- // === 其他 ===
158
- source: "built-in" | "plugin" | "flagSettings" | "userSettings" | "projectSettings";
159
- baseDir: string; // 基础目录
160
- color?: string; // ⚠️ 文档未提及!UI 颜色标识
161
- maxTurns?: number; // 最大轮次
162
- skills?: string[]; // 关联的 skills
163
- initialPrompt?: string; // 初始提示
164
- memory?: "user" | "project" | "local"; // 记忆范围
165
- omitClaudeMd?: boolean; // 是否跳过 CLAUDE.md
166
- appendSystemPrompt?: boolean; // 是否追加系统提示
167
- hooks?: any; // 钩子配置
168
- filename?: string; // 文件名(自定义 agent)
169
- }
170
- ```
171
-
172
- ---
173
-
174
- ## 五、内置 Agent 类型定义
175
-
176
- ### 5.1 general-purpose
177
-
178
- ```typescript
179
- {
180
- agentType: "general-purpose",
181
- whenToUse: "General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you.",
182
- tools: ["*"], // 所有工具
183
- source: "built-in",
184
- baseDir: "built-in",
185
- getSystemPrompt: hr_, // 继承主 agent 的系统提示
186
- }
187
- ```
188
-
189
- **关键**:`tools: ["*"]` 表示通配符——继承父 agent 的全部工具集。
190
-
191
- ### 5.2 Explore
192
-
193
- ```typescript
194
- {
195
- agentType: "Explore",
196
- whenToUse: Lr_, // ⚠️ 是函数引用,不是字符串字面量!运行时动态生成
197
- disallowedTools: ["Agent", "ExitPlanMode", "Edit", "Write", "NotebookEdit"],
198
- source: "built-in",
199
- baseDir: "built-in",
200
- model: "haiku", // 强制使用 haiku 模型
201
- omitClaudeMd: true, // 不加载 CLAUDE.md
202
- getSystemPrompt: () => Er_(), // 专用系统提示
203
- }
204
- ```
205
-
206
- **关键**:
207
- - `disallowedTools` 而非 `tools`——用黑名单而非白名单
208
- - 禁止 `Agent`(不能递归 spawn)
209
- - 禁止 `Edit`/`Write`/`NotebookEdit`(只读)
210
- - 强制 `haiku` 模型(快速、便宜)
211
-
212
- ### 5.3 Plan
213
-
214
- ```typescript
215
- {
216
- agentType: "Plan",
217
- whenToUse: "Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs.",
218
- disallowedTools: ["Agent", "ExitPlanMode", "Edit", "Write", "NotebookEdit"],
219
- source: "built-in",
220
- baseDir: "built-in",
221
- model: "inherit", // ⚠️ 继承父 agent 的模型,不强制指定
222
- omitClaudeMd: true, // ⚠️ 不加载 CLAUDE.md
223
- getSystemPrompt: () => Rr_(), // 专用系统提示
224
- }
225
- ```
226
-
227
- ### 5.4 statusline-setup
228
-
229
- ```typescript
230
- {
231
- agentType: "statusline-setup",
232
- whenToUse: "Use this agent to configure the user's Claude Code status line setting.",
233
- tools: ["Read", "Edit"], // ⚠️ 只有读和编辑,没有 Bash/Write
234
- source: "built-in",
235
- baseDir: "built-in",
236
- model: "sonnet", // ⚠️ 指定 sonnet 模型
237
- color: "orange", // ⚠️ UI 颜色标识
238
- getSystemPrompt: () => `You are a status line setup agent for Claude Code...`,
239
- }
240
- ```
241
-
242
- ### 5.5 claude-code-guide
243
-
244
- ```typescript
245
- {
246
- agentType: "claude-code-guide",
247
- whenToUse: `Use this agent when the user asks questions ("Can Claude...", "Does Claude...", "How do I...") about: (1) Claude Code (the CLI tool) - features, hooks, slash commands, MCP servers, settings, IDE integrations, keyboard shortcuts; (2) Claude Agent SDK - building custom agents; (3) Claude API (formerly Anthropic API) - API usage, tool use, Anthropic SDK usage. IMPORTANT: Before spawning a new agent, check if there is already a running or recently completed claude-code-guide agent that you can continue via SendMessage.`,
248
- tools: bj() ? [e7, pq, Bj, Kh] : [Z_, H9, pq, Bj, Kh], // ⚠️ 条件工具集,取决于某个运行时标志
249
- source: "built-in",
250
- baseDir: "built-in",
251
- model: "haiku", // ⚠️ 使用 haiku 模型
252
- permissionMode: "dontAsk", // ⚠️ 不询问权限
253
- getSystemPrompt: ({ toolUseContext }) => /* 专用系统提示 */,
254
- }
255
- ```
256
-
257
- ---
258
-
259
- ## 六、Agent Spawn 完整流程
260
-
261
- ### 6.1 同步执行路径
262
-
263
- ```
264
- [1] LLM 输出 tool_use: Agent({ prompt, subagent_type, description, ... })
265
-
266
- [2] Agent tool handler 被调用
267
- async call({prompt, subagent_type, description, model, run_in_background, name, team_name, mode, isolation, cwd}, ...)
268
-
269
- [3] 参数预处理
270
- - model: 如果 Ay6() 为 true(某些限制模式),忽略 model 参数
271
- - team_name: 检查是否在 team context 中
272
-
273
- [4] Team 路径判断
274
- if (team_name && name) → 走 team teammate spawn 路径(KZK 函数)
275
-
276
- [5] Agent 类型解析
277
- if (subagent_type 未指定) {
278
- if (在 fork worker 中) → 抛错 "Fork is not available inside a forked worker"
279
- agentDef = PS6(默认 fork agent 定义)
280
- isFork = true
281
- } else {
282
- 从 agentDefinitions.activeAgents 中查找匹配的 agentType
283
- if (找不到) → 抛错 "Agent type 'X' not found"
284
- if (被 permission rule 拒绝) → 抛错 "Agent type 'X' has been denied"
285
- agentDef = 找到的定义
286
- isFork = false
287
- }
288
-
289
- [6] MCP 服务器检查
290
- if (agentDef.requiredMcpServers 有值) {
291
- 等待最多 30 秒让 pending MCP 连接完成
292
- 检查所需 MCP 服务器是否可用
293
- if (不可用) → 抛错
294
- }
295
-
296
- [7] 模型选择
297
- resolvedModel = JE6(agentDef.model, mainLoopModel, userOverride, permissionMode)
298
- // 优先级:agent 定义的 model > 用户指定的 model > 主循环的 model
299
-
300
- [8] 系统提示构建
301
- if (isFork) {
302
- // Fork 模式:继承父 agent 的系统提示
303
- systemPrompt = parentSession.renderedSystemPrompt
304
- // ⚠️ 如果 renderedSystemPrompt 不存在,走 Lx() 构建
305
- messages = buildForkMessages(prompt, queryMetadata)
306
- } else {
307
- // 普通模式:使用 agent 定义的系统提示
308
- // ⚠️ 注意:Z18() 会把 worktree path 和 cwd 作为 Notes 注入系统提示
309
- systemPrompt = Z18([additionalWorkingDirs], model, additionalWorkingDirs)
310
- // Z18 开头是 "Notes:" 然后列出所有工作目录
311
- if (agentDef.memory) { /* 加载记忆 */ }
312
- messages = [userMessage(prompt)]
313
- }
314
-
315
- [9] 权限模式确定
316
- permissionContext = { ...parentPermissionContext, mode: agentDef.permissionMode ?? "acceptEdits" }
317
-
318
- [10] 工具集确定
319
- if (isFork) {
320
- availableTools = parentTools // 完全继承
321
- useExactTools = true // ⚠️ 精确使用父工具,不重新过滤
322
- } else {
323
- availableTools = td(permissionContext, mcpTools) // 根据权限过滤
324
- }
325
-
326
- [11] Worktree 创建(如果 isolation === "worktree")
327
- worktreeResult = await Xq8(`agent-${agentId.slice(0,8)}`)
328
- // 详见第七节
329
-
330
- [12] 异步判断
331
- // ⚠️ 三个独立的异步触发源:
332
- // 1. run_in_background 参数(用户显式指定)
333
- // 2. agentDef.background(agent 定义中的标志)
334
- // 3. 自动后台化(运行时超时)
335
- //
336
- // ⚠️ 限制:In-process teammate 不能用 run_in_background=true 或 background=true
337
- if (TD() && teamContext && agentDef.background === true) {
338
- throw Error("In-process teammates cannot spawn background agents.")
339
- }
340
- //
341
- isAsync = (run_in_background === true || agentDef.background === true)
342
- autoBackgroundMs = czY() // 默认 120000ms = 2 分钟(如果启用)
343
- //
344
- // ⚠️ DS6 = CLAUDE_CODE_DISABLE_BACKGROUND_TASKS 环境变量
345
- // 如果 DS6 为 true,所有异步能力被禁用
346
-
347
- [13] 同步执行
348
- 创建 AgentSession(详见第八节)
349
- 迭代 LLM 流:
350
- while (true) {
351
- result = await stream.next()
352
- if (result.done) break
353
- // 收集消息
354
- messages.push(result.value)
355
- // 检查是否应该转为后台
356
- if (shouldBackground) {
357
- // 转为异步路径
358
- break
359
- }
360
- }
361
-
362
- [14] 结果提取
363
- VS8(messages, agentId, metadata) → AgentOutputCompleted
364
- - 从最后一条 assistant 消息提取文本
365
- - 计算 totalTokens, totalToolUseCount, totalDurationMs
366
- - 提取 usage 统计
367
- - ⚠️ maxResultSizeChars = 100,000(1e5)字符,超出会被截断
368
-
369
- [15] 安全审查(auto mode 下)
370
- // ⚠️ 同步路径直接调用 ES8
371
- // ⚠️ 异步路径:ES8 在父 agent 读取 outputFile 时触发(延迟审查)
372
- ES8({ agentMessages, tools, toolPermissionContext, abortSignal, subagentType })
373
- - 调用 handoff classifier(TS8 函数)检查子 agent 输出
374
- - classifier 使用一个小模型审查子 agent 的操作
375
- - 如果 flagged → 返回 SECURITY WARNING 前缀
376
- - ⚠️ 如果 classifier 不可用(unavailable),返回警告但不阻断
377
-
378
- [16] Worktree 清理
379
- if (worktree 存在) {
380
- if (无变更 && 无新提交) → git worktree remove
381
- else → 保留(可手动检查)
382
- }
383
-
384
- [17] 返回 AgentOutputCompleted 给 LLM
385
- ```
386
-
387
- ### 6.2 异步执行路径
388
-
389
- ```
390
- [1-11] 同上
391
-
392
- [12] isAsync = true
393
-
394
- [13] 创建后台任务
395
- taskId = dg8({ agentId, description, prompt, selectedAgent, setAppState, toolUseId })
396
- // 在 AppState 中注册任务
397
-
398
- if (name 参数存在) {
399
- agentNameRegistry.set(name, agentId) // 注册名称映射
400
- }
401
-
402
- [14] 启动异步执行
403
- OU(sessionMetadata, () =>
404
- q6(() => LS8({
405
- taskId, abortController, makeStream, metadata, description,
406
- toolUseContext, rootSetAppState, agentIdForCleanup,
407
- enableSummarization, getWorktreeResult
408
- }))
409
- )
410
- // LS8 = 异步 agent runner(详见第八节 8.2)
411
-
412
- [15] 检查父 agent 是否有 Read/Bash 工具
413
- canReadOutputFile = tools.some(t => isReadTool(t) || isBashTool(t))
414
-
415
- [16] 立即返回 AgentOutputAsync
416
- {
417
- status: "async_launched",
418
- agentId,
419
- description,
420
- prompt,
421
- outputFile: lY(agentId), // <project>/.claude/tasks/<agentId>.output
422
- canReadOutputFile
423
- }
424
- ```
425
-
426
- ---
427
-
428
- ## 七、Worktree 隔离机制
429
-
430
- ### 7.1 创建(Xq8 函数)
431
-
432
- ```typescript
433
- async function Xq8(agentId: string, options?: WorktreeOptions) {
434
- // 1. 注册 worktree 元数据
435
- zS6(agentId);
436
-
437
- // 2. 检查是否有 hook-based worktree
438
- if (hM6()) {
439
- const hookResult = await P58(agentId);
440
- // hook 可以自定义 worktree 创建逻辑
441
- return { worktreePath: hookResult.worktreePath, hookBased: true };
442
- }
443
-
444
- // 3. 获取 worktree 配置
445
- const config = getWorktreeConfig();
446
- // symlinkDirectories: 需要 symlink 的目录(如 node_modules)
447
- // sparsePaths: git sparse-checkout 路径
448
-
449
- // 4. 创建 git worktree
450
- const worktreePath = path.join(getTempDir(), `agent-${agentId}`);
451
- const branchName = `agent-${agentId}`;
452
-
453
- await exec(`git worktree add --detach ${worktreePath}`);
454
-
455
- // 5. 处理 sparse checkout(如果配置了)
456
- if (config.sparsePaths?.length) {
457
- await exec(`git sparse-checkout set --cone ${config.sparsePaths.join(' ')}`, { cwd: worktreePath });
458
- }
459
-
460
- // 6. 创建 symlink(如果配置了)
461
- if (config.symlinkDirectories?.length) {
462
- for (const dir of config.symlinkDirectories) {
463
- const src = path.join(projectRoot, dir);
464
- const dst = path.join(worktreePath, dir);
465
- if (fs.existsSync(src) && !fs.existsSync(dst)) {
466
- await fs.symlink(src, dst);
467
- }
468
- }
469
- }
470
-
471
- // 7. 记录 head commit(用于后续变更检测)
472
- const headCommit = await exec('git rev-parse HEAD', { cwd: worktreePath });
473
-
474
- return {
475
- worktreePath,
476
- worktreeBranch: branchName,
477
- headCommit: headCommit.stdout.trim(),
478
- gitRoot: projectRoot,
479
- };
480
- }
481
- ```
482
-
483
- ### 7.2 变更检测(E77 函数)
484
-
485
- ```typescript
486
- async function E77(worktreePath: string, baselineCommit: string): Promise<boolean> {
487
- const { dirty, commitsAhead } = await il8(worktreePath, baselineCommit);
488
- // il8 内部:
489
- // dirty = git status --porcelain 输出非空
490
- // commitsAhead = git rev-list ${baselineCommit}..HEAD --count
491
- return dirty || commitsAhead > 0;
492
- }
493
- ```
494
-
495
- ### 7.3 清理(jJ6 函数)
496
-
497
- ```typescript
498
- async function jJ6(worktreePath: string, branchName: string, gitRoot: string, hookBased?: boolean) {
499
- if (hookBased) {
500
- // hook-based worktree 由 hook 负责清理
501
- const cleanup = await dl8(worktreePath);
502
- return;
503
- }
504
-
505
- // 1. 删除 worktree
506
- await exec(`git worktree remove ${worktreePath} --force`, { cwd: gitRoot });
507
-
508
- // 2. 删除分支(如果存在)
509
- try {
510
- await exec(`git branch -D ${branchName}`, { cwd: gitRoot });
511
- } catch {
512
- // 分支可能不存在,忽略
513
- }
514
- }
515
- ```
516
-
517
- ### 7.4 生命周期决策
518
-
519
- ```
520
- Agent 执行完成
521
-
522
- worktree 存在?
523
- ├─ NO → 无操作
524
- └─ YES → E77(worktreePath, headCommit)
525
- ├─ 无变更 → jJ6() 清理 worktree
526
- └─ 有变更 → 保留 worktree,返回 worktreePath 给父 agent
527
- // 父 agent 可以检查变更,手动决定是否应用
528
- ```
529
-
530
- ---
531
-
532
- ## 八、AgentSession 创建与执行
533
-
534
- ### 8.1 同步执行(call 函数内部)
535
-
536
- ```typescript
537
- // CC 源码中的同步执行路径(简化)
538
- const agentId = generateUUID();
539
-
540
- const sessionConfig = {
541
- agentDefinition: agentDef,
542
- promptMessages: messages, // 用户消息
543
- toolUseContext: parentContext, // 继承父 agent 的 context
544
- canUseTool: permissionChecker, // 权限检查函数
545
- isAsync: false,
546
- querySource: `agent:${agentDef.agentType}`,
547
- model: resolvedModel,
548
- // ⚠️ override 逻辑比文档描述的更复杂:
549
- override: isFork
550
- ? { systemPrompt: parentSystemPrompt } // Fork: 继承父系统提示
551
- : agentSystemPrompt && !worktree && !cwd
552
- ? { systemPrompt: I5(agentSystemPrompt) } // 普通(无 worktree/cwd): 格式化 agent 系统提示
553
- : undefined, // 有 worktree/cwd: 不 override,让 Z18 处理
554
- availableTools: isFork ? parentTools : filteredTools,
555
- // ⚠️ forkContextMessages 取决于 agentDef.forksParentContext:
556
- forkContextMessages: isFork
557
- ? agentDef.forksParentContext === "turn"
558
- ? parentMessages.slice(turnStartIndex) // "turn": 只继承当前 turn
559
- : agentDef.forksParentContext === true
560
- ? parentMessages // true: 继承全部
561
- : undefined // undefined: 不继承
562
- : undefined,
563
- worktreePath: worktree?.worktreePath,
564
- description: description,
565
- };
566
-
567
- // 创建 LLM 流
568
- const stream = Yx(sessionConfig);
569
-
570
- // 迭代执行
571
- const messages = [];
572
- let totalTokens = 0;
573
- let totalToolUseCount = 0;
574
-
575
- for await (const event of stream) {
576
- messages.push(event);
577
-
578
- // 更新进度 UI
579
- if (elapsed > 2000 && !showedSpinner) {
580
- setToolJSX(<AgentProgressUI />);
581
- showedSpinner = true;
582
- }
583
-
584
- // 检查是否应该转为后台
585
- if (shouldBackground && taskId) {
586
- // 转为异步路径
587
- break;
588
- }
589
- }
590
-
591
- // 提取结果
592
- const result = VS8(messages, agentId, {
593
- prompt, resolvedAgentModel, isBuiltInAgent, startTime, agentType, isAsync: false
594
- });
595
- ```
596
-
597
- ### 8.2 异步执行(LS8 函数)
598
-
599
- ```typescript
600
- async function LS8({
601
- taskId, abortController, makeStream, metadata, description,
602
- toolUseContext, rootSetAppState, agentIdForCleanup,
603
- enableSummarization, getWorktreeResult
604
- }) {
605
- const messages = [];
606
-
607
- try {
608
- // 1. 创建 token 计数器
609
- const tokenCounter = c26();
610
- const toolCounter = l26(toolUseContext.options.tools);
611
-
612
- // 2. 如果需要摘要,注册停止回调
613
- let stopFn;
614
- if (enableSummarization) {
615
- const { stop } = qe6(taskId, agentId, tokenCounter, rootSetAppState);
616
- stopFn = stop;
617
- }
618
-
619
- // 3. 创建流并迭代
620
- const stream = makeStream(/* cacheSafeParams */);
621
-
622
- for await (const event of stream) {
623
- // 检查 abort
624
- if (abortController.signal.aborted) break;
625
-
626
- messages.push(event);
627
- // 更新 token 和 tool 计数
628
- KK6(tokenCounter, event, toolCounter, toolUseContext.options.tools);
629
-
630
- // 更新任务状态
631
- _e6(taskId, Pa(tokenCounter), rootSetAppState);
632
-
633
- // 记录工具使用
634
- const toolUse = NS8(event);
635
- if (toolUse) {
636
- yS8(tokenCounter, taskId, toolUseId, description, metadata, toolUse);
637
- }
638
- }
639
-
640
- // 4. 提取结果
641
- const result = VS8(messages, taskId, metadata);
642
-
643
- // 5. 更新任务状态为完成
644
- hS8(result, rootSetAppState);
645
-
646
- // 6. 清理 worktree
647
- const worktreeResult = await getWorktreeResult();
648
-
649
- // 7. 清理 agent 注册
650
- v18(agentId, { agentType: metadata.agentType, description })
651
- .catch(err => log(`Failed to clear worktree metadata: ${err}`));
652
-
653
- return result;
654
-
655
- } catch (error) {
656
- // 错误处理
657
- throw error;
658
- } finally {
659
- // 清理
660
- if (agentIdForCleanup) {
661
- // 从 activeAgents 中移除
662
- }
663
- }
664
- }
665
- ```
666
-
667
- ---
668
-
669
- ## 九、工具过滤机制
670
-
671
- ### 9.1 过滤逻辑
672
-
673
- ```typescript
674
- // 工具过滤的核心函数
675
- function filterToolsForAgent(agentDef, parentTools, permissionContext, mcpTools) {
676
- // 1. 获取基础工具集
677
- let tools = td(permissionContext, mcpTools);
678
- // td = 根据权限模式过滤工具
679
-
680
- // 2. 应用 agent 定义的工具限制
681
- if (agentDef.tools) {
682
- // 白名单模式:只保留指定的工具
683
- if (agentDef.tools.includes("*")) {
684
- // 通配符:保留所有工具
685
- } else {
686
- tools = tools.filter(t => agentDef.tools.includes(t.name));
687
- }
688
- }
689
-
690
- if (agentDef.disallowedTools) {
691
- // 黑名单模式:移除指定的工具
692
- tools = tools.filter(t => !agentDef.disallowedTools.includes(t.name));
693
- }
694
-
695
- return tools;
696
- }
697
- ```
698
-
699
- ### 9.2 各类型的工具集
700
-
701
- | Agent 类型 | 工具策略 | 具体工具 | 模型 | 其他 |
702
- |-----------|---------|---------|------|------|
703
- | general-purpose | `tools: ["*"]` | 继承父 agent 全部工具 | 继承 | - |
704
- | Explore | `disallowedTools` | 全部 - Agent - ExitPlanMode - Edit - Write - NotebookEdit | haiku | omitClaudeMd |
705
- | Plan | `disallowedTools` | 同 Explore | inherit | omitClaudeMd |
706
- | statusline-setup | `tools` 白名单 | Read, Edit | sonnet | color: orange |
707
- | claude-code-guide | `tools` 条件 | 取决于 bj() 标志 | haiku | permissionMode: dontAsk |
708
-
709
- ### 9.3 权限模式对工具的影响
710
-
711
- ```typescript
712
- function td(permissionContext, mcpTools) {
713
- // permissionContext.mode 可能的值:
714
- // - "acceptEdits": 默认模式,编辑需要确认
715
- // - "auto": 自动模式,大部分操作自动批准
716
- // - "bypassPermissions": 跳过所有权限检查
717
- // - "plan": 只读 + plan 模式
718
- // - "dontAsk": 不询问,直接拒绝未授权操作
719
-
720
- let tools = [...builtinTools];
721
-
722
- // 根据模式过滤
723
- if (permissionContext.mode === "plan") {
724
- tools = tools.filter(t => isReadOnly(t));
725
- }
726
-
727
- // 添加 MCP 工具
728
- tools.push(...mcpTools);
729
-
730
- return tools;
731
- }
732
- ```
733
-
734
- ---
735
-
736
- ## 十、系统提示构建
737
-
738
- ### 10.1 普通子 Agent
739
-
740
- ```typescript
741
- // 子 agent 的系统提示构建
742
- function buildSubAgentSystemPrompt(agentDef, toolUseContext) {
743
- // 1. 获取 agent 定义的系统提示
744
- const agentPrompt = agentDef.getSystemPrompt({ toolUseContext });
745
-
746
- // 2. 如果 agent 有 memory 配置,加载记忆
747
- if (agentDef.memory) {
748
- const memory = loadMemory(agentDef.memory);
749
- agentPrompt += `\n${memory}`;
750
- }
751
-
752
- // 3. 格式化
753
- return I5(agentPrompt);
754
- // I5 = 将系统提示格式化为 Anthropic API 消息格式
755
- }
756
- ```
757
-
758
- ### 10.2 Fork 模式
759
-
760
- ```typescript
761
- // Fork 模式:继承父 agent 的系统提示
762
- function buildForkSystemPrompt(parentSession) {
763
- // 直接使用父 agent 的 renderedSystemPrompt
764
- return parentSession.renderedSystemPrompt;
765
- }
766
- ```
767
-
768
- ### 10.3 系统提示的差异
769
-
770
- | 场景 | 系统提示来源 | CLAUDE.md | 记忆 | 工作目录注入 |
771
- |------|------------|-----------|------|------------|
772
- | 普通子 agent(无 worktree/cwd) | agentDef.getSystemPrompt() → I5() 格式化 | 取决于 agent 定义 | 取决于 memory 配置 | 无 |
773
- | 普通子 agent(有 worktree/cwd) | Z18() 构建(含 "Notes:" + 工作目录列表) | 取决于 agent 定义 | 取决于 memory 配置 | ✅ 自动注入 |
774
- | Fork 模式 | 父 agent 的 renderedSystemPrompt | 继承父 agent | 继承父 agent | 无 |
775
- | Explore agent | Er_() 专用系统提示 | omitClaudeMd: true | 无 | 无 |
776
-
777
- ---
778
-
779
- ## 十一、消息传递协议
780
-
781
- ### 11.1 父 → 子
782
-
783
- ```typescript
784
- // 消息构建
785
- const messages = [];
786
-
787
- if (isFork) {
788
- // Fork 模式:包含父 agent 的上下文消息
789
- messages.push(...buildForkContextMessages(parentMessages, prompt, queryMetadata));
790
- // buildForkContextMessages 会:
791
- // 1. 取父 agent 的消息历史
792
- // 2. 添加用户的新 prompt
793
- // 3. 添加 query metadata(requestId 等)
794
- } else {
795
- // 普通模式:只有用户消息
796
- messages.push({ type: "user", content: [{ type: "text", text: prompt }] });
797
- }
798
- ```
799
-
800
- ### 11.2 子 → 父(同步)
801
-
802
- ```typescript
803
- // 结果提取(VS8 函数)
804
- function VS8(messages, agentId, metadata) {
805
- // 1. 获取最后一条 assistant 消息
806
- const lastAssistant = findLastAssistantMessage(messages);
807
- if (!lastAssistant) throw new Error("No assistant messages found");
808
-
809
- // 2. 提取文本内容
810
- let textContent = lastAssistant.message.content.filter(c => c.type === "text");
811
-
812
- // 3. 如果最后一条没有文本,向前搜索
813
- if (textContent.length === 0) {
814
- for (let i = messages.length - 1; i >= 0; i--) {
815
- const msg = messages[i];
816
- if (msg.type !== "assistant") continue;
817
- const texts = msg.message.content.filter(c => c.type === "text");
818
- if (texts.length > 0) {
819
- textContent = texts;
820
- break;
821
- }
822
- }
823
- }
824
-
825
- // 4. 统计
826
- const totalTokens = $W(messages)?.usage?.total_tokens ?? 0;
827
- const totalToolUseCount = wZz(messages);
828
-
829
- // 5. 构建结果
830
- return {
831
- agentId,
832
- agentType: metadata.agentType,
833
- content: textContent,
834
- totalDurationMs: Date.now() - metadata.startTime,
835
- totalTokens,
836
- totalToolUseCount,
837
- usage: $W(messages)?.message?.usage,
838
- status: "completed",
839
- prompt: metadata.prompt,
840
- };
841
- }
842
- ```
843
-
844
- ### 11.3 子 → 父(异步)
845
-
846
- 异步 agent 的结果通过**文件系统**传递:
847
-
848
- ```typescript
849
- // 输出文件路径
850
- function lY(agentId) {
851
- return path.join(qR6(), `${agentId}.output`);
852
- // qR6() = <project>/.claude/tasks/
853
- }
854
-
855
- // 输出文件内容 = 最后一条 assistant 消息的文本
856
- ```
857
-
858
- 父 agent 可以通过 Read 工具读取 `outputFile` 来检查子 agent 的进度。
859
-
860
- ---
861
-
862
- ## 十二、安全机制
863
-
864
- ### 12.1 Handoff Classifier(auto mode)
865
-
866
- ```typescript
867
- async function ES8({
868
- agentMessages, tools, toolPermissionContext, abortSignal, subagentType, totalToolUseCount
869
- }) {
870
- // 只在 auto mode 下运行
871
- if (toolPermissionContext.mode !== "auto") return null;
872
-
873
- // 构建审查 prompt
874
- const reviewPrompt = {
875
- role: "user",
876
- content: [{
877
- type: "text",
878
- text: "Sub-agent has finished and is handing back control to the main agent. Review the sub-agent's work based on the block rules and let the main agent know if any file is dangerous (the main agent will see the reason)."
879
- }]
880
- };
881
-
882
- // 调用 classifier
883
- const result = await TS8(
884
- [...agentMessages, reviewPrompt],
885
- tools, toolPermissionContext, abortSignal
886
- );
887
-
888
- // 记录决策
889
- d("tengu_auto_mode_decision", {
890
- decision: result.shouldBlock ? "blocked" : "allowed",
891
- toolName: "Agent",
892
- subagentType,
893
- toolUseCount: totalToolUseCount,
894
- isHandoff: true,
895
- });
896
-
897
- if (result.shouldBlock) {
898
- if (result.unavailable) {
899
- return "Note: The safety classifier was unavailable when reviewing this sub-agent's work. Please carefully verify the sub-agent's actions and output before acting on them.";
900
- }
901
- return `SECURITY WARNING: Sub-agent performed actions that may violate security policy. Reason: ${result.reason}. Review the sub-agent's actions carefully before acting on its output.`;
902
- }
903
-
904
- return null; // 安全,无警告
905
- }
906
- ```
907
-
908
- ### 12.2 权限继承
909
-
910
- ```typescript
911
- // 子 agent 的权限模式
912
- const childPermissionContext = {
913
- ...parentPermissionContext,
914
- mode: agentDef.permissionMode ?? "acceptEdits",
915
- // agent 定义可以覆盖权限模式
916
- // 但不能比父 agent 更宽松
917
- };
918
- ```
919
-
920
- ### 12.3 递归限制
921
-
922
- ```typescript
923
- // 在 Fork worker 中不能再次 fork
924
- if (querySource === `agent:builtin:${forkAgentType}` || isForkWorker(messages)) {
925
- throw new Error("Fork is not available inside a forked worker. Complete your task directly using your tools.");
926
- }
927
-
928
- // Team 中 teammate 不能 spawn teammate
929
- if (isTeamContext && name) {
930
- throw new Error("Teammates cannot spawn other teammates — the team roster is flat.");
931
- }
932
-
933
- // In-process teammate 不能 spawn background agent
934
- if (isInProcessTeam && run_in_background === true) {
935
- throw new Error("In-process teammates cannot spawn background agents.");
936
- }
937
- ```
938
-
939
- ---
940
-
941
- ## 十三、自动后台化机制
942
-
943
- ### 13.1 触发条件
944
-
945
- ```typescript
946
- function czY() {
947
- // 如果启用了自动后台任务,返回阈值(2 分钟)
948
- if (U6(process.env.CLAUDE_AUTO_BACKGROUND_TASKS) ||
949
- S8("tengu_auto_background_agents", false)) {
950
- return 120000; // 2 分钟
951
- }
952
- return 0; // 禁用
953
- }
954
- ```
955
-
956
- ### 13.2 转后台流程
957
-
958
- ```
959
- 同步执行中,elapsed > autoBackgroundMs
960
-
961
- 检查条件:
962
- - 子 agent 还在运行
963
- - 父 agent 的 UI 可以设置 JSX
964
-
965
- 创建后台任务对象
966
- taskId = dg8({ agentId, description, prompt, ... })
967
-
968
- 设置 agentNameRegistry(如果 name 存在)
969
-
970
- 中断当前同步迭代
971
- stream.return(undefined)
972
-
973
- 启动异步继续
974
- OU(metadata, () => LS8({ ... }))
975
-
976
- 返回 AgentOutputAsync 给父 agent
977
- ```
978
-
979
- ---
980
-
981
- ## 十四、Agent 注册表与命名
982
-
983
- ### 14.1 agentNameRegistry
984
-
985
- ```typescript
986
- // 在 AppState 中维护
987
- agentNameRegistry: Map<string, string> // name → agentId
988
-
989
- // 注册
990
- if (name) {
991
- setAppState(state => {
992
- const registry = new Map(state.agentNameRegistry);
993
- registry.set(name, agentId);
994
- return { ...state, agentNameRegistry: registry };
995
- });
996
- }
997
-
998
- // 查找(SendMessage 使用)
999
- function findAgentByName(name) {
1000
- return agentNameRegistry.get(name);
1001
- }
1002
- ```
1003
-
1004
- ### 14.2 Agent 定义缓存
1005
-
1006
- ```typescript
1007
- // agentDefinitions 在 AppState 中维护
1008
- agentDefinitions: {
1009
- activeAgents: AgentDefinition[], // 当前可用的 agent 列表
1010
- allAgents: AgentDefinition[], // 所有 agent(包括不可用的)
1011
- failedFiles?: { path: string, error: string }[],
1012
- }
1013
-
1014
- // 加载时机:
1015
- // 1. 启动时
1016
- // 2. 配置变更时
1017
- // 3. 插件加载时
1018
-
1019
- // 来源:
1020
- // 1. 内置 agent(fy8())
1021
- // 2. 插件定义的 agent(ao6())
1022
- // 3. 用户自定义 agent(.claude/agents/*.md)
1023
- ```
1024
-
1025
- ---
1026
-
1027
- ## 十五、自定义 Agent 定义格式
1028
-
1029
- ### 15.1 Markdown 格式(.claude/agents/*.md)
1030
-
1031
- ```markdown
1032
- ---
1033
- name: my-agent
1034
- description: "A specialized agent for X"
1035
- tools: ["Read", "Glob", "Grep"] # 可选,白名单
1036
- disallowedTools: ["Write"] # 可选,黑名单
1037
- model: sonnet # 可选
1038
- effort: high # 可选
1039
- permissionMode: plan # 可选
1040
- maxTurns: 10 # 可选
1041
- background: false # 可选
1042
- memory: project # 可选
1043
- isolation: worktree # 可选
1044
- skills: ["skill-name"] # 可选
1045
- initialPrompt: "..." # 可选
1046
- appendSystemPrompt: true # 可选
1047
- mcpServers: ["server-name"] # 可选
1048
- ---
1049
-
1050
- You are a specialized agent for X.
1051
-
1052
- Your responsibilities:
1053
- 1. ...
1054
- 2. ...
1055
- ```
1056
-
1057
- ### 15.2 JSON 格式(插件定义)
1058
-
1059
- ```json
1060
- {
1061
- "agents": {
1062
- "my-agent": {
1063
- "description": "A specialized agent for X",
1064
- "tools": ["Read", "Glob", "Grep"],
1065
- "prompt": "You are a specialized agent for X...",
1066
- "model": "sonnet",
1067
- "permissionMode": "plan",
1068
- "maxTurns": 10,
1069
- "background": false,
1070
- "memory": "project",
1071
- "isolation": "worktree"
1072
- }
1073
- }
1074
- }
1075
- ```
1076
-
1077
- ---
1078
-
1079
- ## 十六、Telemetry 事件
1080
-
1081
- ```typescript
1082
- // Agent 选择
1083
- d("tengu_agent_tool_selected", {
1084
- agent_type: agentDef.agentType,
1085
- model: resolvedModel,
1086
- source: agentDef.source, // "built-in" | "plugin" | "flagSettings"
1087
- color: agentDef.color,
1088
- is_built_in_agent: isBuiltIn(agentDef),
1089
- is_resume: false,
1090
- is_async: isAsync,
1091
- is_fork: isFork,
1092
- });
1093
-
1094
- // Agent 完成
1095
- d("tengu_agent_tool_completed", {
1096
- agent_type: metadata.agentType,
1097
- model: metadata.resolvedAgentModel,
1098
- prompt_char_count: metadata.prompt.length,
1099
- response_char_count: responseText.length,
1100
- assistant_message_count: messages.length,
1101
- total_tool_use_count: totalToolUseCount,
1102
- duration_ms: Date.now() - metadata.startTime,
1103
- total_tokens: totalTokens,
1104
- is_built_in_agent: metadata.isBuiltInAgent,
1105
- is_async: metadata.isAsync,
1106
- });
1107
-
1108
- // Auto mode 决策
1109
- d("tengu_auto_mode_decision", {
1110
- decision: "blocked" | "allowed",
1111
- toolName: "Agent",
1112
- subagentType,
1113
- toolUseCount,
1114
- isHandoff: true,
1115
- });
1116
-
1117
- // Agent 记忆加载
1118
- d("tengu_agent_memory_loaded", {
1119
- scope: agentDef.memory,
1120
- source: "subagent",
1121
- });
1122
- ```
1123
-
1124
- ---
1125
-
1126
- ## 十七、完整状态机
1127
-
1128
- ```
1129
- ┌─────────────────────────────────────┐
1130
- │ Agent Tool Called │
1131
- └──────────────┬──────────────────────┘
1132
-
1133
- ┌──────────────▼──────────────────────┐
1134
- │ Resolve Agent Definition │
1135
- │ (subagent_type → AgentDefinition) │
1136
- └──────────────┬──────────────────────┘
1137
-
1138
- ┌──────────────▼──────────────────────┐
1139
- │ Check Permissions & MCP │
1140
- └──────────────┬──────────────────────┘
1141
-
1142
- ┌──────────────▼──────────────────────┐
1143
- │ Build System Prompt │
1144
- │ (fork: inherit, normal: agentDef) │
1145
- └──────────────┬──────────────────────┘
1146
-
1147
- ┌──────────────▼──────────────────────┐
1148
- │ Create Worktree? │
1149
- │ (isolation === "worktree") │
1150
- └──────────────┬──────────────────────┘
1151
-
1152
- ┌────────────────────┼────────────────────┐
1153
- │ │ │
1154
- ┌─────────▼─────────┐ ┌───────▼───────┐ ┌──────────▼──────────┐
1155
- │ Sync Execution │ │ Background │ │ Team Teammate │
1156
- │ (default) │ │ (async) │ │ (team_name+name) │
1157
- └─────────┬─────────┘ └───────┬───────┘ └──────────┬──────────┘
1158
- │ │ │
1159
- ┌─────────▼─────────┐ ┌───────▼───────┐ ┌──────────▼──────────┐
1160
- │ Create Session │ │ Create Task │ │ KZK() │
1161
- │ Yx(config) │ │ dg8() │ │ Team Spawn │
1162
- └─────────┬─────────┘ └───────┬───────┘ └─────────────────────┘
1163
- │ │
1164
- ┌─────────▼─────────┐ ┌───────▼───────┐
1165
- │ Iterate Stream │ │ LS8() │
1166
- │ for await │ │ Async Runner │
1167
- └─────────┬─────────┘ └───────┬───────┘
1168
- │ │
1169
- ┌─────────▼─────────┐ ┌───────▼───────┐
1170
- │ Auto-background? │ │ Write Output │
1171
- │ elapsed > 2min │ │ File │
1172
- └─────────┬─────────┘ └───────┬───────┘
1173
- │ │
1174
- ┌─────────▼─────────┐ ┌───────▼───────┐
1175
- │ VS8() Extract │ │ Return │
1176
- │ Result │ │ async_launched│
1177
- └─────────┬─────────┘ └───────────────┘
1178
-
1179
- ┌─────────▼─────────┐
1180
- │ Handoff Check │
1181
- │ (auto mode) │
1182
- └─────────┬─────────┘
1183
-
1184
- ┌─────────▼─────────┐
1185
- │ Cleanup Worktree │
1186
- └─────────┬─────────┘
1187
-
1188
- ┌─────────▼─────────┐
1189
- │ Return Completed │
1190
- └───────────────────┘
1191
- ```
1192
-
1193
- ---
1194
-
1195
- ## 十八、复刻清单
1196
-
1197
- ### 18.1 数据结构
1198
-
1199
- - [ ] `AgentDefinition` 接口(详见第四节,25+ 字段)
1200
- - [ ] `AgentInput` / `AgentOutput` 类型
1201
- - [ ] `AgentSessionConfig` 接口
1202
- - [ ] `agentNameRegistry` Map
1203
- - [ ] `maxResultSizeChars` 常量(100,000)
1204
- - [ ] `forksParentContext` 三态逻辑(true/"turn"/undefined)
1205
-
1206
- ### 18.2 核心函数
1207
-
1208
- - [ ] `resolveAgentType(subagent_type)` → 查找 agent 定义
1209
- - [ ] `filterToolsForAgent(agentDef, parentTools, permissionContext)` → 工具过滤
1210
- - [ ] `Z18(additionalWorkingDirs, model)` → 系统提示构建(含工作目录 Notes)
1211
- - [ ] `createWorktree(agentId)` → worktree 创建
1212
- - [ ] `checkWorktreeDirty(worktreePath, baseline)` → 变更检测
1213
- - [ ] `cleanupWorktree(worktreePath, branch, gitRoot)` → worktree 清理
1214
- - [ ] `createAgentSession(config)` → 创建 LLM session
1215
- - [ ] `extractAgentResult(messages, agentId, metadata)` → 结果提取
1216
- - [ ] `checkHandoffSafety(messages, tools, permissionContext)` → 安全审查
1217
- - [ ] `Lx()` → 主 session 系统提示构建(fork 模式回退)
1218
-
1219
- ### 18.3 工具注册
1220
-
1221
- - [ ] Agent tool 的 JSON Schema 定义
1222
- - [ ] Agent tool handler(call 函数)
1223
- - [ ] 同步/异步执行路径
1224
- - [ ] 自动后台化逻辑
1225
- - [ ] `maxResultSizeChars: 1e5` 截断
1226
-
1227
- ### 18.4 内置 Agent
1228
-
1229
- - [ ] general-purpose(tools: ["*"],`whenToUse` 是字符串)
1230
- - [ ] Explore(disallowedTools, model: haiku, omitClaudeMd,`whenToUse` 是函数引用)
1231
- - [ ] Plan(disallowedTools)
1232
- - [ ] statusline-setup
1233
- - [ ] claude-code-guide
1234
-
1235
- ### 18.5 安全
1236
-
1237
- - [ ] 递归限制(fork 中不能再 fork)
1238
- - [ ] Team 限制(teammate 不能 spawn teammate)
1239
- - [ ] In-process teammate 不能 spawn background agent
1240
- - [ ] 权限继承(不能比父更宽松)
1241
- - [ ] Handoff classifier(auto mode 安全审查)
1242
- - [ ] Classifier 不可用时的降级处理
1243
-
1244
- ### 18.6 持久化
1245
-
1246
- - [ ] 输出文件(`.claude/tasks/<agentId>.output`)
1247
- - [ ] Agent 定义缓存(启动时/配置变更时/插件加载时)
1248
- - [ ] agentNameRegistry 持久化
1249
- - [ ] Worktree 元数据(head commit 用于变更检测)
1250
-
1251
- ---
1252
-
1253
- ## 十九、与 nanoPencil 现有实现的差异
1254
-
1255
- | 维度 | CC | nanoPencil |
1256
- |------|-----|-----------|
1257
- | **工具定义** | LLM 可调用的 tool | 内部 API(SubAgentSpec) |
1258
- | **Agent 类型** | 5 个内置 + 自定义 | 2 种模式(research/implement)+ team modes |
1259
- | **Worktree** | git worktree add --detach | 同 |
1260
- | **异步执行** | 内置 run_in_background | 内置 |
1261
- | **自动后台** | 2 分钟阈值 | 无 |
1262
- | **安全审查** | Handoff classifier | 无 |
1263
- | **Fork 模式** | 继承父 agent 系统提示和消息 | 无 |
1264
- | **forksParentContext** | true/"turn"/undefined 三种模式 | 无 |
1265
- | **命名注册** | agentNameRegistry | activeAgents Map |
1266
- | **输出文件** | .claude/tasks/*.output | .nanopencil/subagent-runs/*.md |
1267
- | **系统提示** | 按 agent type 构建 + Z18 注入工作目录 | 按 runRole 构建 |
1268
- | **工具过滤** | tools/disallowedTools | createReadOnlyTools/createCodingTools |
1269
- | **自定义 Agent** | .claude/agents/*.md | 无 |
1270
- | **结果大小限制** | maxResultSizeChars = 100,000 | 无 |
1271
- | **background 字段** | agent 定义级 + 参数级,两个独立控制 | 仅参数级 |
1272
-
1273
- ---
1274
-
1275
- ## 二十、关键源码位置
1276
-
1277
- | 组件 | CC 源码位置(cli.js 行号/函数名) |
1278
- |------|-------------------------------|
1279
- | 工具名常量 | `H4="Agent"`, `eI="Task"` |
1280
- | Input Schema | `lzY`, `nzY`, `xs1`(第 3947 行附近) |
1281
- | Output Schema | `izY` |
1282
- | Agent handler | `ng8.call()`(第 3947 行附近) |
1283
- | 内置 agent 定义 | `fy8()` |
1284
- | Agent 类型过滤 | `Mq8()` |
1285
- | 工具过滤 | `td()` |
1286
- | Worktree 创建 | `Xq8()` |
1287
- | 变更检测 | `E77()` → `il8()` |
1288
- | Worktree 清理 | `jJ6()` |
1289
- | 系统提示构建 | `Z18()`, `Lx()` |
1290
- | 结果提取 | `VS8()` |
1291
- | 异步 runner | `LS8()` |
1292
- | 后台任务创建 | `dg8()` |
1293
- | 输出文件路径 | `lY()` → `qR6()` |
1294
- | 自动后台阈值 | `czY()` |
1295
- | Handoff 审查 | `ES8()` |
1296
- | Agent 定义解析 | `mM4()`(markdown), `uM4()`(JSON) |
1297
- | Agent 注册表 | `agentNameRegistry` in AppState |