@pencil-agent/nano-pencil 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/README.md +267 -267
  2. package/dist/build-meta.json +3 -3
  3. package/dist/core/export-html/AGENT.md +11 -11
  4. package/dist/core/export-html/template.css +971 -971
  5. package/dist/core/export-html/template.html +54 -54
  6. package/dist/core/model/custom-providers.js +1 -1
  7. package/dist/core/model-registry.js +5 -5
  8. package/dist/extensions/builtin/AGENT.md +115 -115
  9. package/dist/extensions/builtin/browser/AGENT.md +17 -17
  10. package/dist/extensions/builtin/browser/agent-workspace/agent_helpers.py +12 -12
  11. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/amazon/product-search.md +198 -198
  12. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/archive-org/scraping.md +341 -341
  13. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv/scraping.md +311 -311
  14. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/arxiv-bulk/scraping.md +333 -333
  15. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/atlas/overview.md +70 -70
  16. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/booking-com/scraping.md +578 -578
  17. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/capterra/scraping.md +440 -440
  18. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/centilebrain/generate-estimates.md +110 -110
  19. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coingecko/scraping.md +325 -325
  20. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coinmarketcap/scraping.md +463 -463
  21. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/coursera/scraping.md +360 -360
  22. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/craigslist/scraping.md +390 -390
  23. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/crossref/scraping.md +568 -568
  24. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/dev-to/scraping.md +323 -323
  25. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/duckduckgo/scraping.md +349 -349
  26. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/ebay/scraping.md +435 -435
  27. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/etsy/scraping.md +506 -506
  28. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/eventbrite/scraping.md +363 -363
  29. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/expedia/automation.md +168 -168
  30. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/groups.md +236 -236
  31. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/facebook/pages.md +295 -295
  32. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/framer/editor.md +108 -108
  33. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/fred/scraping.md +493 -493
  34. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/g2/scraping.md +580 -580
  35. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/genius/scraping.md +511 -511
  36. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/repo-actions.md +65 -65
  37. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/github/scraping.md +184 -184
  38. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/glassdoor/scraping.md +543 -543
  39. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gmail/compose.md +122 -122
  40. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/goodreads/scraping.md +461 -461
  41. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/gutenberg/scraping.md +383 -383
  42. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/hackernews/scraping.md +243 -243
  43. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/howlongtobeat/scraping.md +473 -473
  44. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/imdb/scraping.md +271 -271
  45. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/itch-io/scraping.md +436 -436
  46. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/job-boards/indeed-glassdoor.md +1021 -1021
  47. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/letterboxd/scraping.md +349 -349
  48. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/linkedin/invitation-manager.md +109 -109
  49. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/loom/folder-enumeration.md +170 -170
  50. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/macrotrends/scraping.md +537 -537
  51. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/article-hydration.md +120 -120
  52. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/medium/scraping.md +414 -414
  53. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/metacritic/scraping.md +477 -477
  54. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/musicbrainz/scraping.md +478 -478
  55. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/nasa/scraping.md +339 -339
  56. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/news-aggregation/multi-source.md +205 -205
  57. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/open-library/scraping.md +472 -472
  58. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openalex/scraping.md +470 -470
  59. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/openstreetmap/scraping.md +490 -490
  60. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/package-registries/npm-pypi.md +478 -478
  61. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/polymarket/scraping.md +234 -234
  62. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/producthunt/scraping.md +307 -307
  63. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/pubmed/scraping.md +421 -421
  64. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/quora/scraping.md +364 -364
  65. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rawg/scraping.md +352 -352
  66. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/reddit/scraping.md +124 -124
  67. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/rest-countries/scraping.md +233 -233
  68. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/sec-edgar/scraping.md +361 -361
  69. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/README.md +36 -36
  70. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/embedded-apps.md +72 -72
  71. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/knowledge-base.md +109 -109
  72. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/shopify-admin/polaris-inputs.md +137 -137
  73. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/soundcloud/scraping.md +362 -362
  74. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/spotify/scraping.md +339 -339
  75. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/stackoverflow/scraping.md +435 -435
  76. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/steam/scraping.md +575 -575
  77. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/substack/scraping.md +338 -338
  78. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/thetechgeeks/pricing.md +52 -52
  79. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tiktok/upload.md +107 -107
  80. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/tradingview/scraping.md +309 -309
  81. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trello/boards-and-lists.md +88 -88
  82. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/trustpilot/scraping.md +375 -375
  83. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/walmart/scraping.md +444 -444
  84. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wayback-machine/scraping.md +306 -306
  85. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/weather/scraping.md +398 -398
  86. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/wellfound/scraping.md +596 -596
  87. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/world-bank/scraping.md +356 -356
  88. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/xiaohongshu/scraping.md +84 -84
  89. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/youtube/scraping.md +418 -418
  90. package/dist/extensions/builtin/browser/agent-workspace/domain-skills/zillow/scraping.md +433 -433
  91. package/dist/extensions/builtin/browser/browser.md +73 -73
  92. package/dist/extensions/builtin/browser/install.md +142 -142
  93. package/dist/extensions/builtin/browser/interaction-skills/connection.md +48 -48
  94. package/dist/extensions/builtin/browser/interaction-skills/cookies.md +3 -3
  95. package/dist/extensions/builtin/browser/interaction-skills/cross-origin-iframes.md +3 -3
  96. package/dist/extensions/builtin/browser/interaction-skills/dialogs.md +64 -64
  97. package/dist/extensions/builtin/browser/interaction-skills/downloads.md +3 -3
  98. package/dist/extensions/builtin/browser/interaction-skills/drag-and-drop.md +3 -3
  99. package/dist/extensions/builtin/browser/interaction-skills/dropdowns.md +3 -3
  100. package/dist/extensions/builtin/browser/interaction-skills/iframes.md +3 -3
  101. package/dist/extensions/builtin/browser/interaction-skills/network-requests.md +3 -3
  102. package/dist/extensions/builtin/browser/interaction-skills/print-as-pdf.md +3 -3
  103. package/dist/extensions/builtin/browser/interaction-skills/profile-sync.md +90 -90
  104. package/dist/extensions/builtin/browser/interaction-skills/screenshots.md +17 -17
  105. package/dist/extensions/builtin/browser/interaction-skills/scrolling.md +3 -3
  106. package/dist/extensions/builtin/browser/interaction-skills/shadow-dom.md +3 -3
  107. package/dist/extensions/builtin/browser/interaction-skills/tabs.md +69 -69
  108. package/dist/extensions/builtin/browser/interaction-skills/uploads.md +1 -1
  109. package/dist/extensions/builtin/browser/interaction-skills/viewport.md +3 -3
  110. package/dist/extensions/builtin/browser/src/browser_harness/AGENT.md +15 -15
  111. package/dist/extensions/builtin/browser/src/browser_harness/__init__.py +8 -8
  112. package/dist/extensions/builtin/browser/src/browser_harness/_ipc.py +90 -90
  113. package/dist/extensions/builtin/browser/src/browser_harness/admin.py +722 -722
  114. package/dist/extensions/builtin/browser/src/browser_harness/daemon.py +328 -328
  115. package/dist/extensions/builtin/browser/src/browser_harness/helpers.py +396 -396
  116. package/dist/extensions/builtin/browser/src/browser_harness/run.py +103 -103
  117. package/dist/extensions/builtin/discipline/skills/brainstorming/SKILL.md +33 -33
  118. package/dist/extensions/builtin/discipline/skills/executing-plans/SKILL.md +25 -25
  119. package/dist/extensions/builtin/discipline/skills/finishing-development-branch/SKILL.md +25 -25
  120. package/dist/extensions/builtin/discipline/skills/receiving-code-review/SKILL.md +22 -22
  121. package/dist/extensions/builtin/discipline/skills/requesting-code-review/SKILL.md +31 -31
  122. package/dist/extensions/builtin/discipline/skills/systematic-debugging/SKILL.md +28 -28
  123. package/dist/extensions/builtin/discipline/skills/test-driven-development/SKILL.md +32 -32
  124. package/dist/extensions/builtin/discipline/skills/using-git-worktrees/SKILL.md +25 -25
  125. package/dist/extensions/builtin/discipline/skills/verification-before-completion/SKILL.md +27 -27
  126. package/dist/extensions/builtin/discipline/skills/writing-plans/SKILL.md +26 -26
  127. package/dist/extensions/builtin/goal/README.md +67 -67
  128. package/dist/extensions/builtin/grub/README.md +112 -112
  129. package/dist/extensions/builtin/link-world/agent-workspace/README.md +16 -16
  130. package/dist/extensions/builtin/link-world/internet-search/internet-search.md +65 -65
  131. package/dist/extensions/builtin/link-world/link-world-agent.md +82 -82
  132. package/dist/extensions/builtin/link-world/linkworld.md +313 -313
  133. package/dist/extensions/builtin/link-world/network-routing/network-routing.md +67 -67
  134. package/dist/extensions/builtin/loop/README.md +92 -92
  135. package/dist/extensions/builtin/mcp/figma-design.md +68 -68
  136. package/dist/extensions/builtin/mcp/mcp-management.md +85 -85
  137. package/dist/extensions/builtin/recap/AGENT.md +15 -15
  138. package/dist/extensions/builtin/sal/README.md +72 -72
  139. package/dist/extensions/builtin/security-audit/README.md +289 -289
  140. package/dist/extensions/builtin/team/AGENT.md +112 -112
  141. package/dist/extensions/builtin/team/TESTING.md +299 -299
  142. package/dist/extensions/builtin/token-save/README.md +56 -56
  143. package/dist/extensions/optional/AGENT.md +10 -10
  144. package/dist/modes/interactive/controllers/input-submit-controller.js +2 -2
  145. package/dist/modes/interactive/controllers/stream-render-controller.js +2 -2
  146. package/dist/modes/interactive/interactive-mode.js +19 -19
  147. package/dist/modes/interactive/theme/dark.json +85 -85
  148. package/dist/modes/interactive/theme/light.json +84 -84
  149. package/dist/modes/interactive/theme/theme-schema.json +335 -335
  150. package/dist/modes/interactive/theme/warm.json +81 -81
  151. package/dist/node_modules/@pencil-agent/ai/dist/cli.js +0 -0
  152. package/dist/node_modules/@pencil-agent/ai/dist/models.generated.js +1 -1
  153. 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
  154. package/docs/SDK-TESTING.md +364 -0
  155. package/docs/codex-goal-command-impl.md +1055 -1055
  156. package/docs/codex-goal-vs-grub.md +500 -500
  157. package/docs/custom-provider.md +27 -27
  158. package/docs/extensions.md +27 -27
  159. package/docs/keybindings.md +27 -27
  160. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +250 -250
  161. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +122 -122
  162. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +1222 -1222
  163. 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
  164. 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
  165. package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +320 -320
  166. package/docs/loop-usage-examples.md +214 -214
  167. package/docs/mem-core/346/212/200/346/234/257/346/226/207/346/241/243.md +593 -0
  168. package/docs/models.md +27 -27
  169. package/docs/packages.md +27 -27
  170. package/docs/pi-design-philosophy.md +457 -457
  171. package/docs/planmode.md +1987 -1987
  172. package/docs/prompt-templates.md +27 -27
  173. package/docs/providers.md +27 -27
  174. package/docs/sdk.md +27 -27
  175. package/docs/skills.md +27 -27
  176. package/docs/startup-performance-optimization.md +301 -0
  177. package/docs/themes.md +27 -27
  178. package/docs/tui.md +27 -27
  179. package/docs//350/256/244/347/237/245/345/234/260/345/233/276.md +47 -0
  180. package/package.json +190 -190
  181. package/docs/cc-agent-design.md +0 -1297
  182. package/docs/cc-tui-design.md +0 -1333
  183. package/docs/nanoPencil-/345/255/246/344/271/240/350/256/241/345/210/222.md +0 -170
  184. package/docs/scan-report.md +0 -3820
  185. package/docs//345/257/271/346/240/207Claude-Code.md +0 -1775
  186. 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
@@ -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