@pencil-agent/nano-pencil 2.0.0-beta.8 → 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/loader.js +1 -1
- package/dist/core/extensions-host/runner.d.ts +1 -0
- package/dist/core/extensions-host/runner.js +2 -2
- package/dist/core/extensions-host/types.d.ts +17 -22
- package/dist/core/lib/ai/src/types.d.ts +12 -2
- package/dist/core/persona/persona-manager.js +5 -2
- package/dist/core/runtime/agent-session.js +3 -3
- package/dist/core/runtime/extension-core-bindings.d.ts +1 -0
- package/dist/core/runtime/extension-core-bindings.js +2 -2
- 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.d.ts +39 -10
- package/dist/extensions/builtin/goal/goal-controller.js +1 -1
- package/dist/extensions/builtin/goal/goal-format.js +1 -1
- package/dist/extensions/builtin/goal/goal-prompts.d.ts +2 -0
- package/dist/extensions/builtin/goal/goal-prompts.js +5 -4
- package/dist/extensions/builtin/goal/goal-store.js +1 -1
- package/dist/extensions/builtin/goal/index.d.ts +1 -1
- package/dist/extensions/builtin/goal/index.js +10 -7
- 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/index.js +6 -6
- 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.md → 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/plan/index.js +1 -1
- 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/task/task-store.d.ts +4 -0
- package/dist/extensions/builtin/task/task-store.js +1 -1
- 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/components/footer.js +1 -1
- package/dist/modes/interactive/components/task-status-panel.d.ts +36 -0
- package/dist/modes/interactive/components/task-status-panel.js +1 -0
- package/dist/modes/interactive/controllers/stream-render-controller.d.ts +7 -0
- package/dist/modes/interactive/controllers/stream-render-controller.js +2 -2
- package/dist/modes/interactive/interactive-mode.js +40 -40
- package/dist/modes/interactive/state/interactive-state.d.ts +2 -0
- package/dist/modes/interactive/state/interactive-state.js +1 -1
- 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/node_modules/@pencil-agent/ai/dist/models.generated.js +1 -1
- package/dist/node_modules/@pencil-agent/ai/dist/providers/anthropic.js +2 -2
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-completions.js +5 -5
- package/dist/node_modules/@pencil-agent/ai/dist/providers/openai-responses.js +1 -1
- package/dist/node_modules/@pencil-agent/ai/dist/stream.js +1 -1
- package/dist/packages/protocol/src/commands.d.ts +33 -0
- package/dist/packages/protocol/src/flags.d.ts +20 -0
- package/dist/packages/protocol/src/hooks.d.ts +17 -0
- package/dist/packages/protocol/src/hooks.js +0 -0
- package/dist/packages/{extension-sdk → protocol}/src/index.d.ts +7 -4
- package/dist/packages/protocol/src/index.js +1 -0
- package/dist/packages/{extension-sdk → protocol}/src/lifecycle.d.ts +15 -27
- package/dist/packages/protocol/src/lifecycle.js +0 -0
- package/dist/packages/{extension-sdk → protocol}/src/tools.d.ts +1 -1
- package/dist/packages/protocol/src/tools.js +0 -0
- 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/dist/packages/extension-sdk/src/index.js +0 -1
- package/docs/cc-agent-design.md +0 -1297
- package/docs/cc-tui-design.md +0 -1333
- package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
- /package/dist/packages/{extension-sdk/src/lifecycle.js → protocol/src/commands.js} +0 -0
- /package/dist/packages/{extension-sdk/src/tools.js → protocol/src/flags.js} +0 -0
|
@@ -0,0 +1,851 @@
|
|
|
1
|
+
# nanoPencil ACP 协议集成开发文档
|
|
2
|
+
|
|
3
|
+
> **状态**:待开发
|
|
4
|
+
>
|
|
5
|
+
> **目标版本**:1.12.0
|
|
6
|
+
>
|
|
7
|
+
> **ACP 版本**:0.16.x(@agentclientprotocol/sdk)
|
|
8
|
+
>
|
|
9
|
+
> **相关文档**:
|
|
10
|
+
> - ACP 官方:https://agentclientprotocol.com
|
|
11
|
+
> - TypeScript SDK:https://agentclientprotocol.github.io/typescript-sdk
|
|
12
|
+
> - 参考实现:[Gemini CLI ACP Agent](https://github.com/google-gemini/gemini-cli)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 目录
|
|
17
|
+
|
|
18
|
+
- [一、背景与动机](#一背景与动机)
|
|
19
|
+
- [二、ACP 协议概述](#二acp-协议概述)
|
|
20
|
+
- [三、现有架构分析](#三现有架构分析)
|
|
21
|
+
- [四、改动方案](#四改动方案)
|
|
22
|
+
- [五、详细设计](#五详细设计)
|
|
23
|
+
- [六、事件映射表](#六事件映射表)
|
|
24
|
+
- [七、文件变更总览](#七文件变更总览)
|
|
25
|
+
- [八、实施步骤](#八实施步骤)
|
|
26
|
+
- [九、验收标准](#九验收标准)
|
|
27
|
+
- [十、附录](#十附录)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 一、背景与动机
|
|
32
|
+
|
|
33
|
+
### 1.1 什么是 ACP
|
|
34
|
+
|
|
35
|
+
ACP(Agent Client Protocol)是由 Zed 发起的开放标准,定义了代码编辑器(Client)与 AI 编码代理(Agent)之间的通信协议。基于 JSON-RPC 2.0,通过 stdin/stdout 进行双向通信。
|
|
36
|
+
|
|
37
|
+
### 1.2 为什么 nanoPencil 需要支持 ACP
|
|
38
|
+
|
|
39
|
+
| 维度 | 当前状态 | 支持 ACP 后 |
|
|
40
|
+
|------|---------|------------|
|
|
41
|
+
| 编辑器集成 | 仅自有 RPC 协议,Pencil Desktop 专用 | 兼容 Zed、JetBrains 等所有 ACP Client |
|
|
42
|
+
| 外部调用 | 需要了解自定义 RPC 协议 | 标准协议,零学习成本 |
|
|
43
|
+
| 生态兼容 | 独立生态 | 与 Claude Code、Gemini CLI、Copilot 等互通 |
|
|
44
|
+
| 维护成本 | 自维护私有协议 | 社区维护开放标准 |
|
|
45
|
+
|
|
46
|
+
### 1.3 设计原则
|
|
47
|
+
|
|
48
|
+
1. **增量集成** — ACP 作为新的运行模式,不影响现有 Interactive / Print / RPC 三种模式
|
|
49
|
+
2. **复用核心** — 复用 `AgentSession` 和 `createAgentSession` SDK,仅在 I/O 层适配
|
|
50
|
+
3. **最小改动** — 核心改动集中在新增 `modes/acp/` 目录,对现有代码改动控制在 30 行以内
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 二、ACP 协议概述
|
|
55
|
+
|
|
56
|
+
### 2.1 通信模型
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
┌─────────────────┐ JSON-RPC 2.0 ┌─────────────────┐
|
|
60
|
+
│ Client (编辑器) │ ◄──── stdin/stdout ────────► │ Agent (CLI) │
|
|
61
|
+
│ Zed / JetBrains │ │ nanoPencil │
|
|
62
|
+
└─────────────────┘ └─────────────────┘
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
- **Client → Agent**(方法调用):`initialize`、`session/new`、`session/prompt`、`session/cancel`
|
|
66
|
+
- **Agent → Client**(通知):`session/update`(流式文本、工具调用、思考过程等)
|
|
67
|
+
- **Agent → Client**(方法调用):`session/request_permission`(请求工具执行授权)
|
|
68
|
+
|
|
69
|
+
### 2.2 一次完整的 Prompt Turn
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
Client Agent (nanoPencil)
|
|
73
|
+
│ │
|
|
74
|
+
│──── session/prompt (用户消息) ──────────────►│
|
|
75
|
+
│ │── 调用 LLM
|
|
76
|
+
│◄─── session/update (thinking) ────────────── │
|
|
77
|
+
│◄─── session/update (agent_message_chunk) ──── │
|
|
78
|
+
│◄─── session/update (tool_call, pending) ───── │
|
|
79
|
+
│ │
|
|
80
|
+
│◄─── session/request_permission ────────────── │ (可选)
|
|
81
|
+
│──── permission response ──────────────────► │
|
|
82
|
+
│ │── 执行工具
|
|
83
|
+
│◄─── session/update (tool_call_update) ────── │
|
|
84
|
+
│ │── 将结果送回 LLM
|
|
85
|
+
│◄─── session/update (agent_message_chunk) ──── │
|
|
86
|
+
│ │
|
|
87
|
+
│◄─── session/prompt response (end_turn) ────── │
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 2.3 关键消息格式
|
|
91
|
+
|
|
92
|
+
**initialize 响应**:
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"protocolVersion": "2025-03-26",
|
|
96
|
+
"agentCapabilities": {
|
|
97
|
+
"loadSession": false
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**session/update 通知(文本块)**:
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"jsonrpc": "2.0",
|
|
106
|
+
"method": "session/update",
|
|
107
|
+
"params": {
|
|
108
|
+
"sessionId": "sess_abc",
|
|
109
|
+
"update": {
|
|
110
|
+
"sessionUpdate": "agent_message_chunk",
|
|
111
|
+
"content": { "type": "text", "text": "好的," }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**session/update 通知(工具调用)**:
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"jsonrpc": "2.0",
|
|
121
|
+
"method": "session/update",
|
|
122
|
+
"params": {
|
|
123
|
+
"sessionId": "sess_abc",
|
|
124
|
+
"update": {
|
|
125
|
+
"sessionUpdate": "tool_call",
|
|
126
|
+
"toolCallId": "call_001",
|
|
127
|
+
"title": "Reading file",
|
|
128
|
+
"kind": "read",
|
|
129
|
+
"status": "pending",
|
|
130
|
+
"locations": [{ "path": "/project/src/main.ts" }]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**session/prompt 响应**:
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"jsonrpc": "2.0",
|
|
140
|
+
"id": 2,
|
|
141
|
+
"result": { "stopReason": "end_turn" }
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 三、现有架构分析
|
|
148
|
+
|
|
149
|
+
### 3.1 nanoPencil 运行模式架构
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
cli.ts → main.ts → parseArgs()
|
|
153
|
+
→ createAgentSession()
|
|
154
|
+
→ 模式选择:
|
|
155
|
+
├── Interactive (TUI)
|
|
156
|
+
├── Print (stdout 输出)
|
|
157
|
+
├── RPC (自定义 JSON Lines)
|
|
158
|
+
└── ACP (新增 ✦)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
所有模式共享同一个 `AgentSession` 核心,区别仅在 I/O 层。
|
|
162
|
+
|
|
163
|
+
### 3.2 核心接口
|
|
164
|
+
|
|
165
|
+
**AgentSession** 提供:
|
|
166
|
+
- `session.prompt(message, options)` — 发送用户消息并触发 Agent 循环
|
|
167
|
+
- `session.subscribe(listener)` — 订阅 `AgentSessionEvent` 事件流
|
|
168
|
+
- `session.abort()` — 中止当前 turn
|
|
169
|
+
- `session.bindExtensions(options)` — 绑定扩展 UI 上下文
|
|
170
|
+
- `session.steer(message)` / `session.followUp(message)` — 流式中追加消息
|
|
171
|
+
|
|
172
|
+
**AgentSessionEvent** 事件类型:
|
|
173
|
+
```typescript
|
|
174
|
+
type AgentSessionEvent =
|
|
175
|
+
| AgentEvent // agent-core 事件
|
|
176
|
+
| { type: "auto_compaction_start"; ... }
|
|
177
|
+
| { type: "auto_compaction_end"; ... }
|
|
178
|
+
| { type: "auto_retry_start"; ... }
|
|
179
|
+
| { type: "auto_retry_end"; ... };
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**AgentEvent** 事件类型:
|
|
183
|
+
```typescript
|
|
184
|
+
type AgentEvent =
|
|
185
|
+
| { type: "agent_start" }
|
|
186
|
+
| { type: "agent_end"; messages: AgentMessage[] }
|
|
187
|
+
| { type: "turn_start" }
|
|
188
|
+
| { type: "turn_end"; message: AgentMessage; toolResults: ToolResultMessage[] }
|
|
189
|
+
| { type: "message_start"; message: AgentMessage }
|
|
190
|
+
| { type: "message_update"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }
|
|
191
|
+
| { type: "message_end"; message: AgentMessage }
|
|
192
|
+
| { type: "tool_execution_start"; toolCallId: string; toolName: string; args: any }
|
|
193
|
+
| { type: "tool_execution_update"; toolCallId: string; toolName: string; args: any; partialResult: any }
|
|
194
|
+
| { type: "tool_execution_end"; toolCallId: string; toolName: string; result: any; isError: boolean };
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**AssistantMessageEvent** 事件类型(在 message_update 中传递):
|
|
198
|
+
```typescript
|
|
199
|
+
type AssistantMessageEvent =
|
|
200
|
+
| { type: "text_start"; ... }
|
|
201
|
+
| { type: "text_delta"; delta: string; ... }
|
|
202
|
+
| { type: "text_end"; ... }
|
|
203
|
+
| { type: "thinking_start"; ... }
|
|
204
|
+
| { type: "thinking_delta"; delta: string; ... }
|
|
205
|
+
| { type: "thinking_end"; ... }
|
|
206
|
+
| { type: "toolcall_start"; ... }
|
|
207
|
+
| { type: "toolcall_delta"; delta: string; ... }
|
|
208
|
+
| { type: "toolcall_end"; toolCall: ToolCall; ... }
|
|
209
|
+
| { type: "done"; reason: "stop" | "length" | "toolUse"; ... }
|
|
210
|
+
| { type: "error"; ... };
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 3.3 RPC 模式参考
|
|
214
|
+
|
|
215
|
+
现有 RPC 模式 (`modes/rpc/rpc-mode.ts`) 是 ACP 模式的最佳参考:
|
|
216
|
+
|
|
217
|
+
- **相同点**:stdin/stdout 双向通信、无头模式、事件流转发
|
|
218
|
+
- **不同点**:RPC 用自定义 JSON Lines;ACP 用标准 JSON-RPC 2.0 + SDK
|
|
219
|
+
|
|
220
|
+
RPC 模式核心流程:
|
|
221
|
+
1. `session.bindExtensions()` 绑定扩展(非 TUI 环境)
|
|
222
|
+
2. `session.subscribe(event => output(event))` 转发事件
|
|
223
|
+
3. `readline` 逐行读取 stdin → `handleCommand()` 处理命令
|
|
224
|
+
4. 返回 `new Promise(() => {})` 保持进程存活
|
|
225
|
+
|
|
226
|
+
ACP 模式将遵循相同模式,但使用 `AgentSideConnection` + `ndJsonStream` 替代手动 readline。
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 四、改动方案
|
|
231
|
+
|
|
232
|
+
### 4.1 改动文件清单
|
|
233
|
+
|
|
234
|
+
| 文件 | 操作 | 改动量 | 说明 |
|
|
235
|
+
|------|------|--------|------|
|
|
236
|
+
| `package.json` | 修改 | +1 行 | 新增 `@agentclientprotocol/sdk` 依赖 |
|
|
237
|
+
| `cli/args.ts` | 修改 | +5 行 | 新增 `--acp` 参数 |
|
|
238
|
+
| `main.ts` | 修改 | +10 行 | ACP 模式分支 + stdin 跳过 |
|
|
239
|
+
| `modes/acp/acp-mode.ts` | **新增** | ~300 行 | ACP Agent 模式核心 |
|
|
240
|
+
| `modes/index.ts` | 修改 | +1 行 | 导出 `runAcpMode` |
|
|
241
|
+
|
|
242
|
+
**总计:~320 行新增,~15 行改动**
|
|
243
|
+
|
|
244
|
+
### 4.2 不变的文件
|
|
245
|
+
|
|
246
|
+
以下文件**完全不需要修改**:
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
core/runtime/agent-session.ts ✓ 核心逻辑不变
|
|
250
|
+
core/runtime/sdk.ts ✓ SDK 接口不变
|
|
251
|
+
packages/agent-core/ ✓ agent-core 不变
|
|
252
|
+
packages/ai/ ✓ AI 层不变
|
|
253
|
+
modes/interactive/ ✓ 交互模式不变
|
|
254
|
+
modes/print-mode.ts ✓ Print 模式不变
|
|
255
|
+
modes/rpc/ ✓ RPC 模式不变
|
|
256
|
+
core/extensions/ ✓ 扩展系统不变
|
|
257
|
+
core/tools/ ✓ 工具系统不变
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## 五、详细设计
|
|
263
|
+
|
|
264
|
+
### 5.1 依赖变更 — `package.json`
|
|
265
|
+
|
|
266
|
+
在 `dependencies` 中新增:
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"dependencies": {
|
|
271
|
+
"@agentclientprotocol/sdk": "^0.16.1"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 5.2 CLI 参数 — `cli/args.ts`
|
|
277
|
+
|
|
278
|
+
在 `Args` 接口中新增字段:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
export interface Args {
|
|
282
|
+
// ...existing fields...
|
|
283
|
+
/** Enable ACP (Agent Client Protocol) mode for editor integration */
|
|
284
|
+
acp?: boolean;
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
在 `parseArgs` 函数中添加解析:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
} else if (arg === "--acp") {
|
|
292
|
+
result.acp = true;
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
在 `printHelp` 中添加说明:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
--acp Run as ACP Agent (for editor integration)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### 5.3 模式选择 — `main.ts`
|
|
302
|
+
|
|
303
|
+
#### 5.3.1 跳过 stdin 管道读取
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// 现有代码 (约第 707 行):
|
|
307
|
+
if (parsed.mode !== "rpc") {
|
|
308
|
+
|
|
309
|
+
// 改为:
|
|
310
|
+
if (parsed.mode !== "rpc" && !parsed.acp) {
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### 5.3.2 添加 ACP 模式分支
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// 现有代码 (约第 825 行):
|
|
317
|
+
if (mode === "rpc") {
|
|
318
|
+
await runRpcMode(session);
|
|
319
|
+
} else if (isInteractive) {
|
|
320
|
+
|
|
321
|
+
// 改为:
|
|
322
|
+
if (parsed.acp) {
|
|
323
|
+
const { runAcpMode } = await import("./modes/acp/acp-mode.js");
|
|
324
|
+
await runAcpMode(session);
|
|
325
|
+
} else if (mode === "rpc") {
|
|
326
|
+
await runRpcMode(session);
|
|
327
|
+
} else if (isInteractive) {
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
使用动态 `import()` 确保不使用 ACP 模式时不加载 SDK,减少启动时间。
|
|
331
|
+
|
|
332
|
+
### 5.4 ACP Agent 模式 — `modes/acp/acp-mode.ts`
|
|
333
|
+
|
|
334
|
+
这是核心新增文件。实现 ACP `Agent` 接口,将 `AgentSession` 的事件流映射为 ACP 协议消息。
|
|
335
|
+
|
|
336
|
+
#### 5.4.1 整体结构
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
import * as acp from "@agentclientprotocol/sdk";
|
|
340
|
+
import { Readable, Writable } from "node:stream";
|
|
341
|
+
import type { AgentSession } from "../../core/runtime/agent-session.js";
|
|
342
|
+
import { VERSION } from "../../config.js";
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* NanoPencilAgent - 实现 ACP Agent 接口
|
|
346
|
+
*
|
|
347
|
+
* 将 nanoPencil 的 AgentSession 包装为 ACP Agent,
|
|
348
|
+
* 通过 stdin/stdout 与 ACP Client 通信。
|
|
349
|
+
*/
|
|
350
|
+
class NanoPencilAgent implements acp.Agent {
|
|
351
|
+
private connection: acp.AgentSideConnection;
|
|
352
|
+
private session: AgentSession;
|
|
353
|
+
private activeSessions: Map<string, { abortController: AbortController | null }>;
|
|
354
|
+
|
|
355
|
+
constructor(connection: acp.AgentSideConnection, session: AgentSession) { ... }
|
|
356
|
+
|
|
357
|
+
async initialize(params: acp.InitializeRequest): Promise<acp.InitializeResult> { ... }
|
|
358
|
+
async newSession(params: acp.NewSessionRequest): Promise<acp.NewSessionResult> { ... }
|
|
359
|
+
async authenticate(params: acp.AuthenticateRequest): Promise<acp.AuthenticateResult> { ... }
|
|
360
|
+
async prompt(params: acp.PromptRequest): Promise<acp.PromptResult> { ... }
|
|
361
|
+
async cancel(params: acp.CancelNotification): Promise<void> { ... }
|
|
362
|
+
async setSessionMode(params: acp.SetSessionModeRequest): Promise<acp.SetSessionModeResult> { ... }
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export async function runAcpMode(session: AgentSession): Promise<never> { ... }
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### 5.4.2 `initialize` — 协商版本和能力
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
async initialize(params: acp.InitializeRequest): Promise<acp.InitializeResult> {
|
|
372
|
+
return {
|
|
373
|
+
protocolVersion: acp.PROTOCOL_VERSION,
|
|
374
|
+
agentCapabilities: {
|
|
375
|
+
loadSession: false, // 暂不支持 loadSession
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### 5.4.3 `newSession` — 创建会话
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
async newSession(params: acp.NewSessionRequest): Promise<acp.NewSessionResult> {
|
|
385
|
+
const sessionId = `sess-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
386
|
+
this.activeSessions.set(sessionId, { abortController: null });
|
|
387
|
+
return { sessionId };
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
#### 5.4.4 `prompt` — 核心交互
|
|
392
|
+
|
|
393
|
+
这是最复杂的方法,负责:
|
|
394
|
+
1. 从 ACP prompt 内容中提取文本
|
|
395
|
+
2. 订阅 `AgentSessionEvent` 并转换为 ACP `session/update`
|
|
396
|
+
3. 调用 `session.prompt()` 阻塞直到 turn 结束
|
|
397
|
+
4. 返回 `stopReason`
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
async prompt(params: acp.PromptRequest): Promise<acp.PromptResult> {
|
|
401
|
+
const { sessionId, prompt } = params;
|
|
402
|
+
const sessionState = this.activeSessions.get(sessionId);
|
|
403
|
+
if (!sessionState) throw new Error(`Session ${sessionId} not found`);
|
|
404
|
+
|
|
405
|
+
// 中止之前的 prompt(如果有)
|
|
406
|
+
sessionState.abortController?.abort();
|
|
407
|
+
sessionState.abortController = new AbortController();
|
|
408
|
+
|
|
409
|
+
// 提取文本
|
|
410
|
+
const userText = prompt
|
|
411
|
+
.filter((block): block is acp.TextContent => block.type === "text")
|
|
412
|
+
.map((block) => block.text)
|
|
413
|
+
.join("\n");
|
|
414
|
+
|
|
415
|
+
// 订阅事件并转发为 ACP session/update
|
|
416
|
+
const unsubscribe = this.session.subscribe((event) => {
|
|
417
|
+
this.mapEventToAcp(sessionId, event);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
await this.session.prompt(userText, { source: "acp" });
|
|
422
|
+
return { stopReason: "end_turn" };
|
|
423
|
+
} catch (error) {
|
|
424
|
+
if (sessionState.abortController.signal.aborted) {
|
|
425
|
+
return { stopReason: "cancelled" };
|
|
426
|
+
}
|
|
427
|
+
return { stopReason: "error" };
|
|
428
|
+
} finally {
|
|
429
|
+
unsubscribe();
|
|
430
|
+
sessionState.abortController = null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### 5.4.5 `cancel` — 中止当前 turn
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
async cancel(params: acp.CancelNotification): Promise<void> {
|
|
439
|
+
const sessionState = this.activeSessions.get(params.sessionId);
|
|
440
|
+
if (sessionState) {
|
|
441
|
+
sessionState.abortController?.abort();
|
|
442
|
+
await this.session.abort();
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### 5.4.6 事件映射方法
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
private mapEventToAcp(sessionId: string, event: AgentSessionEvent): void {
|
|
451
|
+
switch (event.type) {
|
|
452
|
+
case "message_update": {
|
|
453
|
+
const sub = event.assistantMessageEvent;
|
|
454
|
+
switch (sub.type) {
|
|
455
|
+
case "text_delta":
|
|
456
|
+
this.connection.sessionUpdate({
|
|
457
|
+
sessionId,
|
|
458
|
+
update: {
|
|
459
|
+
sessionUpdate: "agent_message_chunk",
|
|
460
|
+
content: { type: "text", text: sub.delta },
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
break;
|
|
464
|
+
|
|
465
|
+
case "thinking_delta":
|
|
466
|
+
this.connection.sessionUpdate({
|
|
467
|
+
sessionId,
|
|
468
|
+
update: {
|
|
469
|
+
sessionUpdate: "agent_message_chunk",
|
|
470
|
+
content: { type: "thought", text: sub.delta },
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
break;
|
|
474
|
+
|
|
475
|
+
// toolcall_start, toolcall_end 等在 tool_execution_* 事件中处理
|
|
476
|
+
}
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
case "tool_execution_start":
|
|
481
|
+
this.connection.sessionUpdate({
|
|
482
|
+
sessionId,
|
|
483
|
+
update: {
|
|
484
|
+
sessionUpdate: "tool_call",
|
|
485
|
+
toolCallId: event.toolCallId,
|
|
486
|
+
title: `${event.toolName}`,
|
|
487
|
+
kind: this.mapToolKind(event.toolName),
|
|
488
|
+
status: "pending",
|
|
489
|
+
rawInput: event.args,
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
break;
|
|
493
|
+
|
|
494
|
+
case "tool_execution_end":
|
|
495
|
+
this.connection.sessionUpdate({
|
|
496
|
+
sessionId,
|
|
497
|
+
update: {
|
|
498
|
+
sessionUpdate: "tool_call_update",
|
|
499
|
+
toolCallId: event.toolCallId,
|
|
500
|
+
status: event.isError ? "errored" : "completed",
|
|
501
|
+
content: [{
|
|
502
|
+
type: "content",
|
|
503
|
+
content: {
|
|
504
|
+
type: "text",
|
|
505
|
+
text: typeof event.result === "string"
|
|
506
|
+
? event.result
|
|
507
|
+
: JSON.stringify(event.result, null, 2),
|
|
508
|
+
},
|
|
509
|
+
}],
|
|
510
|
+
rawOutput: event.result,
|
|
511
|
+
},
|
|
512
|
+
});
|
|
513
|
+
break;
|
|
514
|
+
|
|
515
|
+
// agent_start, agent_end, turn_start, turn_end 等不需要映射
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
#### 5.4.7 工具类型映射
|
|
521
|
+
|
|
522
|
+
ACP 定义了标准化的工具 `kind`:
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
private mapToolKind(toolName: string): string {
|
|
526
|
+
switch (toolName) {
|
|
527
|
+
case "read": return "read";
|
|
528
|
+
case "edit": return "edit";
|
|
529
|
+
case "write": return "write";
|
|
530
|
+
case "bash": return "command";
|
|
531
|
+
case "grep": return "read";
|
|
532
|
+
case "find": return "read";
|
|
533
|
+
case "ls": return "read";
|
|
534
|
+
default: return "other";
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
#### 5.4.8 扩展 UI Context
|
|
540
|
+
|
|
541
|
+
ACP 模式需要一个无头环境的扩展 UI Context,与 RPC 模式类似。可以从 `modes/rpc/rpc-mode.ts` 第 120-274 行的 `createExtensionUIContext()` 复制并简化:
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
function createAcpExtensionUIContext(): ExtensionUIContext {
|
|
545
|
+
return {
|
|
546
|
+
// 对话式交互 → 静默返回默认值
|
|
547
|
+
select: async () => undefined,
|
|
548
|
+
confirm: async () => false,
|
|
549
|
+
input: async () => undefined,
|
|
550
|
+
editor: async () => undefined,
|
|
551
|
+
|
|
552
|
+
// 通知/状态 → 输出到 stderr(不干扰 ACP 协议的 stdout)
|
|
553
|
+
notify(message, type) {
|
|
554
|
+
process.stderr.write(`[${type ?? "info"}] ${message}\n`);
|
|
555
|
+
},
|
|
556
|
+
setStatus() {},
|
|
557
|
+
setWorkingMessage() {},
|
|
558
|
+
setWidget() {},
|
|
559
|
+
setFooter() {},
|
|
560
|
+
setHeader() {},
|
|
561
|
+
setTitle() {},
|
|
562
|
+
setEditorComponent() {},
|
|
563
|
+
pasteToEditor() {},
|
|
564
|
+
setEditorText() {},
|
|
565
|
+
getEditorText: () => "",
|
|
566
|
+
|
|
567
|
+
async custom() { return undefined as never; },
|
|
568
|
+
onTerminalInput: () => () => {},
|
|
569
|
+
get theme() { return theme; },
|
|
570
|
+
getAllThemes: () => [],
|
|
571
|
+
getTheme: () => undefined,
|
|
572
|
+
setTheme: () => ({ success: false, error: "Theme not supported in ACP mode" }),
|
|
573
|
+
getToolsExpanded: () => false,
|
|
574
|
+
setToolsExpanded() {},
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
#### 5.4.9 `runAcpMode` 入口函数
|
|
580
|
+
|
|
581
|
+
```typescript
|
|
582
|
+
export async function runAcpMode(session: AgentSession): Promise<never> {
|
|
583
|
+
// 绑定扩展
|
|
584
|
+
await session.bindExtensions({
|
|
585
|
+
uiContext: createAcpExtensionUIContext(),
|
|
586
|
+
commandContextActions: {
|
|
587
|
+
waitForIdle: () => session.agent.waitForIdle(),
|
|
588
|
+
newSession: async (options) => {
|
|
589
|
+
const success = await session.newSession(options);
|
|
590
|
+
return { cancelled: !success };
|
|
591
|
+
},
|
|
592
|
+
fork: async (entryId) => {
|
|
593
|
+
const result = await session.fork(entryId);
|
|
594
|
+
return { cancelled: result.cancelled };
|
|
595
|
+
},
|
|
596
|
+
navigateTree: async (targetId, options) => {
|
|
597
|
+
const result = await session.navigateTree(targetId, options);
|
|
598
|
+
return { cancelled: result.cancelled };
|
|
599
|
+
},
|
|
600
|
+
switchSession: async (sessionPath) => {
|
|
601
|
+
const success = await session.switchSession(sessionPath);
|
|
602
|
+
return { cancelled: !success };
|
|
603
|
+
},
|
|
604
|
+
reload: async () => { await session.reload(); },
|
|
605
|
+
},
|
|
606
|
+
shutdownHandler: () => { process.exit(0); },
|
|
607
|
+
onError: (err) => {
|
|
608
|
+
process.stderr.write(
|
|
609
|
+
`[extension_error] ${err.extensionPath}: ${err.error}\n`
|
|
610
|
+
);
|
|
611
|
+
},
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// 建立 ACP 连接(stdin/stdout)
|
|
615
|
+
const input = Writable.toWeb(process.stdout);
|
|
616
|
+
const output = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;
|
|
617
|
+
const stream = acp.ndJsonStream(input, output);
|
|
618
|
+
|
|
619
|
+
new acp.AgentSideConnection(
|
|
620
|
+
(conn) => new NanoPencilAgent(conn, session),
|
|
621
|
+
stream,
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
// 保持进程存活
|
|
625
|
+
return new Promise(() => {});
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### 5.5 导出 — `modes/index.ts`
|
|
630
|
+
|
|
631
|
+
新增导出:
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
export { runAcpMode } from "./acp/acp-mode.js";
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## 六、事件映射表
|
|
640
|
+
|
|
641
|
+
### 6.1 AgentSessionEvent → ACP session/update 完整映射
|
|
642
|
+
|
|
643
|
+
| nanoPencil 事件 | 子事件 | ACP sessionUpdate 类型 | 说明 |
|
|
644
|
+
|-----------------|--------|----------------------|------|
|
|
645
|
+
| `message_update` | `text_delta` | `agent_message_chunk` (text) | 流式文本增量 |
|
|
646
|
+
| `message_update` | `text_start` | — | 不需要映射 |
|
|
647
|
+
| `message_update` | `text_end` | — | 不需要映射 |
|
|
648
|
+
| `message_update` | `thinking_delta` | `agent_message_chunk` (thought) | 思考过程增量 |
|
|
649
|
+
| `message_update` | `thinking_start` | — | 不需要映射 |
|
|
650
|
+
| `message_update` | `thinking_end` | — | 不需要映射 |
|
|
651
|
+
| `message_update` | `toolcall_start` | — | 由 `tool_execution_start` 处理 |
|
|
652
|
+
| `message_update` | `toolcall_delta` | — | 参数流式(可忽略) |
|
|
653
|
+
| `message_update` | `toolcall_end` | — | 由 `tool_execution_start` 处理 |
|
|
654
|
+
| `message_update` | `done` | — | 由 prompt 返回值处理 |
|
|
655
|
+
| `message_update` | `error` | — | 由 prompt 异常处理 |
|
|
656
|
+
| `tool_execution_start` | — | `tool_call` (pending) | 工具开始执行 |
|
|
657
|
+
| `tool_execution_update` | — | `tool_call_update` (partial) | 工具中间输出(可选) |
|
|
658
|
+
| `tool_execution_end` | — | `tool_call_update` (completed/errored) | 工具执行完成 |
|
|
659
|
+
| `agent_start` | — | — | 不需要映射 |
|
|
660
|
+
| `agent_end` | — | — | 由 prompt 返回值处理 |
|
|
661
|
+
| `turn_start` | — | — | 不需要映射 |
|
|
662
|
+
| `turn_end` | — | — | 由 prompt 返回值处理 |
|
|
663
|
+
| `message_start` | — | — | 不需要映射 |
|
|
664
|
+
| `message_end` | — | — | 不需要映射 |
|
|
665
|
+
| `auto_compaction_start` | — | — | 可通过 stderr 日志 |
|
|
666
|
+
| `auto_compaction_end` | — | — | 可通过 stderr 日志 |
|
|
667
|
+
| `auto_retry_start` | — | — | 可通过 stderr 日志 |
|
|
668
|
+
| `auto_retry_end` | — | — | 可通过 stderr 日志 |
|
|
669
|
+
|
|
670
|
+
### 6.2 工具名称 → ACP tool kind 映射
|
|
671
|
+
|
|
672
|
+
| nanoPencil 工具名 | ACP kind | 说明 |
|
|
673
|
+
|-------------------|----------|------|
|
|
674
|
+
| `read` | `read` | 文件读取 |
|
|
675
|
+
| `edit` | `edit` | 文件编辑 |
|
|
676
|
+
| `write` | `write` | 文件写入 |
|
|
677
|
+
| `bash` | `command` | 命令执行 |
|
|
678
|
+
| `grep` | `read` | 内容搜索 |
|
|
679
|
+
| `find` | `read` | 文件搜索 |
|
|
680
|
+
| `ls` | `read` | 目录列表 |
|
|
681
|
+
| `source` | `read` | 源码分析 |
|
|
682
|
+
| MCP 工具 | `other` | MCP 扩展工具 |
|
|
683
|
+
| 扩展注册工具 | `other` | 扩展自定义工具 |
|
|
684
|
+
|
|
685
|
+
### 6.3 ACP stopReason 映射
|
|
686
|
+
|
|
687
|
+
| nanoPencil 结果 | ACP stopReason | 条件 |
|
|
688
|
+
|-----------------|---------------|------|
|
|
689
|
+
| prompt 正常完成 | `end_turn` | `session.prompt()` resolve |
|
|
690
|
+
| 用户取消 | `cancelled` | `session.abort()` 或 AbortController |
|
|
691
|
+
| LLM 错误 | `error` | `session.prompt()` reject(非取消) |
|
|
692
|
+
|
|
693
|
+
---
|
|
694
|
+
|
|
695
|
+
## 七、文件变更总览
|
|
696
|
+
|
|
697
|
+
### 7.1 新增文件
|
|
698
|
+
|
|
699
|
+
```
|
|
700
|
+
modes/acp/acp-mode.ts # ACP Agent 模式核心(~300 行)
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### 7.2 修改文件
|
|
704
|
+
|
|
705
|
+
```
|
|
706
|
+
package.json # +1 依赖
|
|
707
|
+
cli/args.ts # +5 行(--acp 参数)
|
|
708
|
+
main.ts # +10 行(ACP 分支 + stdin 跳过)
|
|
709
|
+
modes/index.ts # +1 行(导出)
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### 7.3 不变文件
|
|
713
|
+
|
|
714
|
+
```
|
|
715
|
+
core/runtime/agent-session.ts ✓ 核心不变
|
|
716
|
+
core/runtime/sdk.ts ✓ SDK 不变
|
|
717
|
+
modes/interactive/ ✓ 交互模式不变
|
|
718
|
+
modes/print-mode.ts ✓ Print 模式不变
|
|
719
|
+
modes/rpc/ ✓ RPC 模式不变
|
|
720
|
+
packages/agent-core/ ✓ agent-core 不变
|
|
721
|
+
packages/ai/ ✓ AI 层不变
|
|
722
|
+
core/extensions/ ✓ 扩展系统不变
|
|
723
|
+
core/tools/ ✓ 工具系统不变
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
---
|
|
727
|
+
|
|
728
|
+
## 八、实施步骤
|
|
729
|
+
|
|
730
|
+
### Step 1:安装依赖
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
npm install @agentclientprotocol/sdk
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### Step 2:添加 --acp 参数
|
|
737
|
+
|
|
738
|
+
修改 `cli/args.ts`:
|
|
739
|
+
- `Args` 接口新增 `acp?: boolean`
|
|
740
|
+
- `parseArgs` 中处理 `--acp`
|
|
741
|
+
- `printHelp` 中添加说明
|
|
742
|
+
|
|
743
|
+
### Step 3:创建 ACP 模式
|
|
744
|
+
|
|
745
|
+
新增 `modes/acp/acp-mode.ts`:
|
|
746
|
+
1. 实现 `NanoPencilAgent` 类(implements `acp.Agent`)
|
|
747
|
+
2. 实现 `createAcpExtensionUIContext()`
|
|
748
|
+
3. 实现 `runAcpMode(session)` 入口函数
|
|
749
|
+
4. 实现事件映射 `mapEventToAcp()`
|
|
750
|
+
|
|
751
|
+
### Step 4:接入主流程
|
|
752
|
+
|
|
753
|
+
修改 `main.ts`:
|
|
754
|
+
- stdin 读取跳过 ACP 模式
|
|
755
|
+
- 模式选择添加 ACP 分支
|
|
756
|
+
|
|
757
|
+
修改 `modes/index.ts`:
|
|
758
|
+
- 导出 `runAcpMode`
|
|
759
|
+
|
|
760
|
+
### Step 5:构建与手动测试
|
|
761
|
+
|
|
762
|
+
```bash
|
|
763
|
+
# 构建
|
|
764
|
+
npm run build
|
|
765
|
+
|
|
766
|
+
# 测试 ACP 模式启动
|
|
767
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"test","version":"1.0"},"capabilities":{}}}' | node dist/cli.js --acp
|
|
768
|
+
|
|
769
|
+
# 预期输出(stdout):
|
|
770
|
+
# {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26","agentCapabilities":{"loadSession":false}}}
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Step 6:端到端测试
|
|
774
|
+
|
|
775
|
+
使用 ACP Client 进行端到端验证:
|
|
776
|
+
|
|
777
|
+
```bash
|
|
778
|
+
# 方式 1:使用 Zed 编辑器
|
|
779
|
+
# 在 Zed 的 Agent 设置中添加 nanoPencil:
|
|
780
|
+
# Command: nanopencil --acp
|
|
781
|
+
|
|
782
|
+
# 方式 2:使用 ACP SDK 自带的 ClientSideConnection 编写测试客户端
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
---
|
|
786
|
+
|
|
787
|
+
## 九、验收标准
|
|
788
|
+
|
|
789
|
+
| 编号 | 验收项 | 验证方法 |
|
|
790
|
+
|------|--------|----------|
|
|
791
|
+
| AC-1 | `nanopencil --acp` 启动后等待 stdin | 手动运行,进程不退出 |
|
|
792
|
+
| AC-2 | 发送 `initialize` 收到正确响应 | echo JSON-RPC → stdin,检查 stdout |
|
|
793
|
+
| AC-3 | 发送 `session/new` 返回 `sessionId` | echo JSON-RPC → stdin |
|
|
794
|
+
| AC-4 | 发送 `session/prompt` 后收到流式 `session/update` | 观察 stdout 逐条 JSON-RPC 通知 |
|
|
795
|
+
| AC-5 | 文本流式输出为 `agent_message_chunk` (text) | 检查 `sessionUpdate` 字段 |
|
|
796
|
+
| AC-6 | 思考过程输出为 `agent_message_chunk` (thought) | 使用 thinking 模型测试 |
|
|
797
|
+
| AC-7 | 工具调用输出 `tool_call` (pending) + `tool_call_update` (completed) | Agent 执行工具时观察 |
|
|
798
|
+
| AC-8 | Turn 结束时 `session/prompt` 响应包含 `stopReason: "end_turn"` | 检查 JSON-RPC 响应 |
|
|
799
|
+
| AC-9 | 发送 `session/cancel` 后 turn 中止 | 长 prompt 期间发送 cancel |
|
|
800
|
+
| AC-10 | 现有 Interactive / Print / RPC 模式不受影响 | 分别运行三种模式验证 |
|
|
801
|
+
| AC-11 | `npm run build` 编译通过 | CI 构建 |
|
|
802
|
+
| AC-12 | 扩展系统在 ACP 模式下正常加载 | 观察 stderr 日志 |
|
|
803
|
+
|
|
804
|
+
---
|
|
805
|
+
|
|
806
|
+
## 十、附录
|
|
807
|
+
|
|
808
|
+
### 附录 A:ACP 协议与 nanoPencil RPC 对比
|
|
809
|
+
|
|
810
|
+
| 维度 | nanoPencil RPC | ACP |
|
|
811
|
+
|------|---------------|-----|
|
|
812
|
+
| 协议标准 | 自定义 JSON Lines | JSON-RPC 2.0 开放标准 |
|
|
813
|
+
| SDK 支持 | 自实现 | `@agentclientprotocol/sdk` |
|
|
814
|
+
| 请求格式 | `{ type, id?, ... }` | `{ jsonrpc: "2.0", method, params, id }` |
|
|
815
|
+
| 响应格式 | `{ type: "response", command, success }` | `{ jsonrpc: "2.0", result/error, id }` |
|
|
816
|
+
| 事件格式 | 原始 `AgentSessionEvent` | `session/update` 通知 |
|
|
817
|
+
| Client 兼容 | Pencil Desktop 专用 | Zed、JetBrains 等通用 |
|
|
818
|
+
| 功能丰富度 | 完整(模型切换、compaction 等) | 基础 prompt/cancel |
|
|
819
|
+
| 扩展 UI | 通过 `extension_ui_request` | 不支持(需自定义扩展) |
|
|
820
|
+
|
|
821
|
+
> RPC 模式保留用于 Pencil Desktop 深度集成,ACP 模式用于通用编辑器集成。两者共存。
|
|
822
|
+
|
|
823
|
+
### 附录 B:已知 ACP 兼容编辑器
|
|
824
|
+
|
|
825
|
+
| 编辑器 | ACP Client 支持 | 状态 |
|
|
826
|
+
|--------|----------------|------|
|
|
827
|
+
| Zed | 原生支持 | 已发布 |
|
|
828
|
+
| JetBrains IDEs | 原生支持 | 已发布 |
|
|
829
|
+
| VS Code | 通过扩展 | 社区支持 |
|
|
830
|
+
| Cursor | 通过扩展 | 社区支持 |
|
|
831
|
+
|
|
832
|
+
### 附录 C:已知 ACP Agent
|
|
833
|
+
|
|
834
|
+
| Agent | 启动命令 | 说明 |
|
|
835
|
+
|-------|---------|------|
|
|
836
|
+
| Claude Code | `claude --acp` | Anthropic |
|
|
837
|
+
| Gemini CLI | `gemini --acp` | Google |
|
|
838
|
+
| GitHub Copilot | `github-copilot --acp` | GitHub |
|
|
839
|
+
| Codex CLI | `codex --acp` | OpenAI |
|
|
840
|
+
| Goose | `goose --acp` | Square |
|
|
841
|
+
| **nanoPencil** | **`nanopencil --acp`** | **本项目** |
|
|
842
|
+
|
|
843
|
+
### 附录 D:后续演进方向
|
|
844
|
+
|
|
845
|
+
1. **loadSession 支持** — 实现 ACP `session/load`,复用 nanoPencil 的 SessionManager
|
|
846
|
+
2. **权限请求** — 对 bash/edit/write 工具通过 `requestPermission` 请求用户授权
|
|
847
|
+
3. **Plan 支持** — 将 plan-mode 扩展的计划步骤映射为 ACP plan update
|
|
848
|
+
4. **Slash Commands** — 通过 ACP `available_commands_update` 广播可用命令
|
|
849
|
+
5. **Session Modes** — 映射 nanoPencil 的 thinking level 为 ACP session mode
|
|
850
|
+
6. **文件系统集成** — 实现 ACP 的 `fs/readTextFile` / `fs/writeTextFile`,使用 Client 的文件系统
|
|
851
|
+
7. **终端集成** — 实现 ACP 的 `terminal/*` 方法,使用 Client 的终端执行 bash
|