@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.
- package/README.md +267 -267
- package/dist/build-meta.json +3 -3
- package/dist/core/export-html/AGENT.md +11 -11
- package/dist/core/export-html/template.css +971 -971
- package/dist/core/export-html/template.html +54 -54
- package/dist/core/extensions-host/index.d.ts +1 -1
- package/dist/core/extensions-host/types.d.ts +5 -8
- package/dist/extensions/builtin/AGENT.md +115 -115
- package/dist/extensions/builtin/browser/AGENT.md +17 -17
- package/dist/extensions/builtin/browser/agent-workspace/agent_helpers.py +12 -12
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/amazon/product-search.md +198 -198
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/archive-org/scraping.md +341 -341
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv/scraping.md +311 -311
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv-bulk/scraping.md +333 -333
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/atlas/overview.md +70 -70
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/booking-com/scraping.md +578 -578
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/capterra/scraping.md +440 -440
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/centilebrain/generate-estimates.md +110 -110
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coingecko/scraping.md +325 -325
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coinmarketcap/scraping.md +463 -463
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coursera/scraping.md +360 -360
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/craigslist/scraping.md +390 -390
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/crossref/scraping.md +568 -568
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/dev-to/scraping.md +323 -323
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/duckduckgo/scraping.md +349 -349
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/ebay/scraping.md +435 -435
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/etsy/scraping.md +506 -506
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/eventbrite/scraping.md +363 -363
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/expedia/automation.md +168 -168
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/groups.md +236 -236
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/pages.md +295 -295
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/framer/editor.md +108 -108
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/fred/scraping.md +493 -493
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/g2/scraping.md +580 -580
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/genius/scraping.md +511 -511
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/repo-actions.md +65 -65
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/scraping.md +184 -184
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/glassdoor/scraping.md +543 -543
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gmail/compose.md +122 -122
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/goodreads/scraping.md +461 -461
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gutenberg/scraping.md +383 -383
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/hackernews/scraping.md +243 -243
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/howlongtobeat/scraping.md +473 -473
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/imdb/scraping.md +271 -271
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/itch-io/scraping.md +436 -436
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/job-boards/indeed-glassdoor.md +1021 -1021
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/letterboxd/scraping.md +349 -349
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/linkedin/invitation-manager.md +109 -109
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/loom/folder-enumeration.md +170 -170
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/macrotrends/scraping.md +537 -537
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/article-hydration.md +120 -120
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/scraping.md +414 -414
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/metacritic/scraping.md +477 -477
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/musicbrainz/scraping.md +478 -478
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/nasa/scraping.md +339 -339
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/news-aggregation/multi-source.md +205 -205
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/open-library/scraping.md +472 -472
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openalex/scraping.md +470 -470
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openstreetmap/scraping.md +490 -490
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/package-registries/npm-pypi.md +478 -478
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/polymarket/scraping.md +234 -234
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/producthunt/scraping.md +307 -307
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/pubmed/scraping.md +421 -421
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/quora/scraping.md +364 -364
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rawg/scraping.md +352 -352
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/reddit/scraping.md +124 -124
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rest-countries/scraping.md +233 -233
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/sec-edgar/scraping.md +361 -361
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/README.md +36 -36
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/embedded-apps.md +72 -72
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/knowledge-base.md +109 -109
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/polaris-inputs.md +137 -137
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/soundcloud/scraping.md +362 -362
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/spotify/scraping.md +339 -339
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/stackoverflow/scraping.md +435 -435
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/steam/scraping.md +575 -575
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/substack/scraping.md +338 -338
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/thetechgeeks/pricing.md +52 -52
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tiktok/upload.md +107 -107
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tradingview/scraping.md +309 -309
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trello/boards-and-lists.md +88 -88
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trustpilot/scraping.md +375 -375
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/walmart/scraping.md +444 -444
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wayback-machine/scraping.md +306 -306
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/weather/scraping.md +398 -398
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wellfound/scraping.md +596 -596
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/world-bank/scraping.md +356 -356
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/xiaohongshu/scraping.md +84 -84
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/youtube/scraping.md +418 -418
- package/dist/extensions/builtin/browser/agent-workspace/domain-skills/zillow/scraping.md +433 -433
- package/dist/extensions/builtin/browser/browser.md +73 -73
- package/dist/extensions/builtin/browser/install.md +142 -142
- package/dist/extensions/builtin/browser/interaction-skills/connection.md +48 -48
- package/dist/extensions/builtin/browser/interaction-skills/cookies.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/cross-origin-iframes.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/dialogs.md +64 -64
- package/dist/extensions/builtin/browser/interaction-skills/downloads.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/drag-and-drop.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/dropdowns.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/iframes.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/network-requests.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/print-as-pdf.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/profile-sync.md +90 -90
- package/dist/extensions/builtin/browser/interaction-skills/screenshots.md +17 -17
- package/dist/extensions/builtin/browser/interaction-skills/scrolling.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/shadow-dom.md +3 -3
- package/dist/extensions/builtin/browser/interaction-skills/tabs.md +69 -69
- package/dist/extensions/builtin/browser/interaction-skills/uploads.md +1 -1
- package/dist/extensions/builtin/browser/interaction-skills/viewport.md +3 -3
- package/dist/extensions/builtin/browser/src/browser_harness/AGENT.md +15 -15
- package/dist/extensions/builtin/browser/src/browser_harness/__init__.py +8 -8
- package/dist/extensions/builtin/browser/src/browser_harness/_ipc.py +90 -90
- package/dist/extensions/builtin/browser/src/browser_harness/admin.py +722 -722
- package/dist/extensions/builtin/browser/src/browser_harness/daemon.py +328 -328
- package/dist/extensions/builtin/browser/src/browser_harness/helpers.py +396 -396
- package/dist/extensions/builtin/browser/src/browser_harness/run.py +103 -103
- package/dist/extensions/builtin/discipline/skills/brainstorming/SKILL.md +33 -33
- package/dist/extensions/builtin/discipline/skills/executing-plans/SKILL.md +25 -25
- package/dist/extensions/builtin/discipline/skills/finishing-development-branch/SKILL.md +25 -25
- package/dist/extensions/builtin/discipline/skills/receiving-code-review/SKILL.md +22 -22
- package/dist/extensions/builtin/discipline/skills/requesting-code-review/SKILL.md +31 -31
- package/dist/extensions/builtin/discipline/skills/systematic-debugging/SKILL.md +28 -28
- package/dist/extensions/builtin/discipline/skills/test-driven-development/SKILL.md +32 -32
- package/dist/extensions/builtin/discipline/skills/using-git-worktrees/SKILL.md +25 -25
- package/dist/extensions/builtin/discipline/skills/verification-before-completion/SKILL.md +27 -27
- package/dist/extensions/builtin/discipline/skills/writing-plans/SKILL.md +26 -26
- package/dist/extensions/builtin/goal/README.md +67 -67
- package/dist/extensions/builtin/goal/goal-controller.js +1 -1
- package/dist/extensions/builtin/goal/goal-prompts.js +4 -4
- package/dist/extensions/builtin/grub/README.md +112 -112
- package/dist/extensions/builtin/link-world/agent-workspace/README.md +16 -16
- package/dist/extensions/builtin/link-world/internet-search/internet-search.md +65 -65
- package/dist/extensions/builtin/link-world/link-world-agent.md +82 -82
- package/dist/extensions/builtin/link-world/linkworld.md +313 -313
- package/dist/extensions/builtin/link-world/network-routing/network-routing.md +67 -67
- package/dist/extensions/builtin/loop/README.md +92 -92
- package/dist/extensions/builtin/mcp/figma-design.md +68 -68
- package/dist/extensions/builtin/mcp/mcp-management.md +85 -85
- package/dist/extensions/builtin/recap/AGENT.md +15 -15
- package/dist/extensions/builtin/sal/README.md +72 -72
- package/dist/extensions/builtin/security-audit/README.md +289 -289
- package/dist/extensions/builtin/team/AGENT.md +112 -112
- package/dist/extensions/builtin/team/TESTING.md +299 -299
- package/dist/extensions/builtin/token-save/README.md +56 -56
- package/dist/extensions/optional/AGENT.md +10 -10
- package/dist/index.d.ts +5 -30
- package/dist/index.js +1 -1
- package/dist/models.d.ts +7 -0
- package/dist/models.js +1 -0
- package/dist/modes/interactive/theme/dark.json +85 -85
- package/dist/modes/interactive/theme/light.json +84 -84
- package/dist/modes/interactive/theme/theme-schema.json +335 -335
- package/dist/modes/interactive/theme/warm.json +81 -81
- package/dist/node_modules/@pencil-agent/ai/dist/cli.js +0 -0
- package/dist/packages/protocol/src/flags.d.ts +20 -0
- package/dist/packages/protocol/src/flags.js +0 -0
- package/dist/packages/protocol/src/hooks.d.ts +17 -0
- package/dist/packages/protocol/src/hooks.js +0 -0
- package/dist/packages/protocol/src/index.d.ts +4 -2
- package/dist/packages/protocol/src/index.js +1 -1
- package/dist/packages/protocol/src/lifecycle.d.ts +11 -21
- package/dist/public-config.d.ts +12 -0
- package/dist/public-config.js +1 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.js +1 -0
- package/dist/session-compaction.d.ts +7 -0
- package/dist/session-compaction.js +1 -0
- package/dist/session.d.ts +7 -0
- package/dist/session.js +1 -0
- package/dist/skills.d.ts +7 -0
- package/dist/skills.js +1 -0
- package/dist/tools.d.ts +7 -0
- package/dist/tools.js +1 -0
- 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
- package/docs/SDK-TESTING.md +364 -0
- package/docs/codex-goal-command-impl.md +1055 -1055
- package/docs/codex-goal-vs-grub.md +500 -500
- package/docs/custom-provider.md +27 -27
- package/docs/extensions.md +27 -27
- package/docs/keybindings.md +27 -27
- package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +250 -250
- package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +122 -122
- package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +1222 -1222
- 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
- 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
- package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +320 -320
- package/docs/loop-usage-examples.md +214 -214
- package/docs/mem-core/346/212/200/346/234/257/346/226/207/346/241/243.md +593 -0
- package/docs/models.md +27 -27
- package/docs/packages.md +27 -27
- package/docs/pi-design-philosophy.md +457 -457
- package/docs/planmode.md +1987 -1987
- package/docs/prompt-templates.md +27 -27
- package/docs/providers.md +27 -27
- package/docs/sdk.md +27 -27
- package/docs/skills.md +27 -27
- package/docs/startup-performance-optimization.md +301 -0
- package/docs/themes.md +27 -27
- package/docs/tui.md +27 -27
- package/docs//350/256/244/347/237/245/345/234/260/345/233/276.md +47 -0
- package/package.json +190 -162
- package/docs/cc-agent-design.md +0 -1297
- package/docs/cc-tui-design.md +0 -1333
- package/docs/nanoPencil-/345/255/246/344/271/240/350/256/241/345/210/222.md +0 -170
- package/docs/scan-report.md +0 -3820
- package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
- 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
package/docs/cc-agent-design.md
DELETED
|
@@ -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 |
|