@kr78/pi-coding-agent 17.2.10

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 (1593) hide show
  1. package/CHANGELOG.md +14861 -0
  2. package/README.md +35 -0
  3. package/dist/CHANGELOG-chtdxh1z.md +14861 -0
  4. package/dist/cli.js +20483 -0
  5. package/dist/template-c2hyaytt.js +1653 -0
  6. package/dist/template-f8wx9vfn.css +1355 -0
  7. package/dist/template-qat058wr.html +55 -0
  8. package/dist/tool-views.generated-jdfmzwmn.js +35 -0
  9. package/examples/README.md +21 -0
  10. package/examples/custom-tools/README.md +104 -0
  11. package/examples/custom-tools/hello/index.ts +20 -0
  12. package/examples/extensions/README.md +142 -0
  13. package/examples/extensions/api-demo.ts +78 -0
  14. package/examples/extensions/chalk-logger.ts +25 -0
  15. package/examples/extensions/hello.ts +31 -0
  16. package/examples/extensions/pirate.ts +43 -0
  17. package/examples/extensions/plan-mode.ts +549 -0
  18. package/examples/extensions/reload-runtime.ts +38 -0
  19. package/examples/extensions/thinking-note.ts +13 -0
  20. package/examples/extensions/tools.ts +145 -0
  21. package/examples/extensions/with-deps/index.ts +36 -0
  22. package/examples/extensions/with-deps/package-lock.json +31 -0
  23. package/examples/extensions/with-deps/package.json +17 -0
  24. package/examples/hooks/README.md +56 -0
  25. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  26. package/examples/hooks/confirm-destructive.ts +58 -0
  27. package/examples/hooks/custom-compaction.ts +115 -0
  28. package/examples/hooks/dirty-repo-guard.ts +51 -0
  29. package/examples/hooks/file-trigger.ts +40 -0
  30. package/examples/hooks/git-checkpoint.ts +52 -0
  31. package/examples/hooks/handoff.ts +149 -0
  32. package/examples/hooks/permission-gate.ts +33 -0
  33. package/examples/hooks/protected-paths.ts +29 -0
  34. package/examples/hooks/qna.ts +118 -0
  35. package/examples/hooks/status-line.ts +39 -0
  36. package/examples/sdk/01-minimal.ts +21 -0
  37. package/examples/sdk/02-custom-model.ts +49 -0
  38. package/examples/sdk/03-custom-prompt.ts +46 -0
  39. package/examples/sdk/04-skills.ts +43 -0
  40. package/examples/sdk/06-extensions.ts +82 -0
  41. package/examples/sdk/06-hooks.ts +61 -0
  42. package/examples/sdk/07-context-files.ts +35 -0
  43. package/examples/sdk/08-prompt-templates.ts +41 -0
  44. package/examples/sdk/08-slash-commands.ts +46 -0
  45. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  46. package/examples/sdk/11-sessions.ts +47 -0
  47. package/examples/sdk/12-redis-sessions.ts +54 -0
  48. package/examples/sdk/13-sql-sessions.ts +61 -0
  49. package/examples/sdk/README.md +169 -0
  50. package/package.json +587 -0
  51. package/scripts/bench-guard.ts +71 -0
  52. package/scripts/bench-title-models.ts +332 -0
  53. package/scripts/build-binary.ts +117 -0
  54. package/scripts/bundle-dist.ts +123 -0
  55. package/scripts/compile-binary.ts +69 -0
  56. package/scripts/embed-mupdf-wasm.ts +67 -0
  57. package/scripts/format-prompts.ts +68 -0
  58. package/scripts/generate-aria-snapshot.ts +134 -0
  59. package/scripts/generate-docs-index.ts +58 -0
  60. package/scripts/generate-share-viewer.ts +34 -0
  61. package/scripts/legacy-pi-virtual-module.ts +205 -0
  62. package/scripts/measure-prompt-tokens.ts +63 -0
  63. package/scripts/omp +51 -0
  64. package/scripts/omp.ts +19 -0
  65. package/scripts/security-compare.ts +40 -0
  66. package/src/advisor/advise-tool.ts +234 -0
  67. package/src/advisor/config.ts +341 -0
  68. package/src/advisor/emission-guard.ts +172 -0
  69. package/src/advisor/index.ts +6 -0
  70. package/src/advisor/runtime.ts +1412 -0
  71. package/src/advisor/transcript-recorder.ts +215 -0
  72. package/src/advisor/watchdog.ts +135 -0
  73. package/src/async/index.ts +1 -0
  74. package/src/async/job-manager.ts +847 -0
  75. package/src/auto-thinking/classifier.ts +220 -0
  76. package/src/autolearn/controller.ts +152 -0
  77. package/src/autolearn/managed-skills.ts +255 -0
  78. package/src/autoresearch/command-resume.md +14 -0
  79. package/src/autoresearch/dashboard.ts +436 -0
  80. package/src/autoresearch/git.ts +331 -0
  81. package/src/autoresearch/helpers.ts +218 -0
  82. package/src/autoresearch/index.ts +541 -0
  83. package/src/autoresearch/prompt-setup.md +43 -0
  84. package/src/autoresearch/prompt.md +103 -0
  85. package/src/autoresearch/resume-message.md +10 -0
  86. package/src/autoresearch/state.ts +273 -0
  87. package/src/autoresearch/storage.ts +700 -0
  88. package/src/autoresearch/tools/init-experiment.ts +268 -0
  89. package/src/autoresearch/tools/log-experiment.ts +520 -0
  90. package/src/autoresearch/tools/run-experiment.ts +407 -0
  91. package/src/autoresearch/tools/update-notes.ts +109 -0
  92. package/src/autoresearch/types.ts +168 -0
  93. package/src/capability/context-file.ts +44 -0
  94. package/src/capability/extension-module.ts +34 -0
  95. package/src/capability/extension.ts +47 -0
  96. package/src/capability/fs.ts +117 -0
  97. package/src/capability/hook.ts +40 -0
  98. package/src/capability/index.ts +467 -0
  99. package/src/capability/instruction.ts +37 -0
  100. package/src/capability/mcp.ts +115 -0
  101. package/src/capability/prompt.ts +35 -0
  102. package/src/capability/rule-buckets.ts +66 -0
  103. package/src/capability/rule.ts +298 -0
  104. package/src/capability/settings.ts +34 -0
  105. package/src/capability/skill.ts +69 -0
  106. package/src/capability/slash-command.ts +40 -0
  107. package/src/capability/ssh.ts +41 -0
  108. package/src/capability/system-prompt.ts +34 -0
  109. package/src/capability/tool.ts +38 -0
  110. package/src/capability/types.ts +187 -0
  111. package/src/cleanse/agent.ts +226 -0
  112. package/src/cleanse/balance.ts +79 -0
  113. package/src/cleanse/checkers.ts +996 -0
  114. package/src/cleanse/index.ts +190 -0
  115. package/src/cleanse/loop.ts +51 -0
  116. package/src/cleanse/parsers.ts +726 -0
  117. package/src/cleanse/progress.ts +50 -0
  118. package/src/cleanse/prompts/assignment.md +47 -0
  119. package/src/cleanse/types.ts +72 -0
  120. package/src/cli/agents-cli.ts +138 -0
  121. package/src/cli/args.ts +368 -0
  122. package/src/cli/auth-broker-cli.ts +940 -0
  123. package/src/cli/auth-gateway-cli.ts +674 -0
  124. package/src/cli/bench-cli.ts +990 -0
  125. package/src/cli/browser-relay-cli.ts +119 -0
  126. package/src/cli/classify-install-target.ts +76 -0
  127. package/src/cli/claude-trace-cli.ts +795 -0
  128. package/src/cli/command-help.ts +107 -0
  129. package/src/cli/commands/init-xdg.ts +27 -0
  130. package/src/cli/completion-gen.ts +550 -0
  131. package/src/cli/config-cli.ts +459 -0
  132. package/src/cli/dry-balance-cli.ts +864 -0
  133. package/src/cli/extension-flags.ts +48 -0
  134. package/src/cli/file-processor.ts +132 -0
  135. package/src/cli/flag-tables.ts +367 -0
  136. package/src/cli/gallery-cli.ts +272 -0
  137. package/src/cli/gallery-fixtures/agentic.ts +420 -0
  138. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  139. package/src/cli/gallery-fixtures/edit.ts +254 -0
  140. package/src/cli/gallery-fixtures/fs.ts +245 -0
  141. package/src/cli/gallery-fixtures/index.ts +40 -0
  142. package/src/cli/gallery-fixtures/interaction.ts +46 -0
  143. package/src/cli/gallery-fixtures/memory.ts +81 -0
  144. package/src/cli/gallery-fixtures/misc.ts +177 -0
  145. package/src/cli/gallery-fixtures/search.ts +135 -0
  146. package/src/cli/gallery-fixtures/shell.ts +241 -0
  147. package/src/cli/gallery-fixtures/types.ts +57 -0
  148. package/src/cli/gallery-fixtures/web.ts +158 -0
  149. package/src/cli/gallery-screenshot.ts +279 -0
  150. package/src/cli/gc-cli.ts +1566 -0
  151. package/src/cli/grep-cli.ts +161 -0
  152. package/src/cli/grievances-cli.ts +256 -0
  153. package/src/cli/help-extra.ts +89 -0
  154. package/src/cli/initial-message.ts +58 -0
  155. package/src/cli/models-cli.ts +385 -0
  156. package/src/cli/plugin-cli.ts +996 -0
  157. package/src/cli/profile-alias.ts +369 -0
  158. package/src/cli/profile-bootstrap.ts +233 -0
  159. package/src/cli/read-cli.ts +99 -0
  160. package/src/cli/session-picker.ts +110 -0
  161. package/src/cli/setup-cli.ts +312 -0
  162. package/src/cli/setup-model-picker.ts +43 -0
  163. package/src/cli/shell-cli.ts +176 -0
  164. package/src/cli/ssh-cli.ts +179 -0
  165. package/src/cli/startup-cwd.ts +58 -0
  166. package/src/cli/stats-cli.ts +229 -0
  167. package/src/cli/thinking-levels.ts +7 -0
  168. package/src/cli/tiny-models-cli.ts +153 -0
  169. package/src/cli/ttsr-cli.ts +1013 -0
  170. package/src/cli/update-cli.ts +1184 -0
  171. package/src/cli/usage-cli.ts +1084 -0
  172. package/src/cli/usage-error.ts +7 -0
  173. package/src/cli/web-search-cli.ts +144 -0
  174. package/src/cli/worktree-cli.ts +311 -0
  175. package/src/cli-commands.ts +308 -0
  176. package/src/cli.ts +428 -0
  177. package/src/collab/crypto.ts +63 -0
  178. package/src/collab/display-name.ts +13 -0
  179. package/src/collab/guest.ts +764 -0
  180. package/src/collab/host.ts +692 -0
  181. package/src/collab/protocol.ts +296 -0
  182. package/src/collab/relay-client.ts +282 -0
  183. package/src/collab/replication-shrink.ts +111 -0
  184. package/src/commands/acp.ts +35 -0
  185. package/src/commands/agents.ts +58 -0
  186. package/src/commands/auth-broker.ts +100 -0
  187. package/src/commands/auth-gateway.ts +70 -0
  188. package/src/commands/bench.ts +64 -0
  189. package/src/commands/browser-relay.ts +53 -0
  190. package/src/commands/cleanse.ts +45 -0
  191. package/src/commands/commit.ts +47 -0
  192. package/src/commands/complete.ts +67 -0
  193. package/src/commands/completions.ts +61 -0
  194. package/src/commands/config.ts +52 -0
  195. package/src/commands/dry-balance.ts +43 -0
  196. package/src/commands/gallery.ts +61 -0
  197. package/src/commands/gc.ts +47 -0
  198. package/src/commands/grep.ts +49 -0
  199. package/src/commands/grievances.ts +52 -0
  200. package/src/commands/install.ts +107 -0
  201. package/src/commands/join.ts +40 -0
  202. package/src/commands/launch-help.ts +116 -0
  203. package/src/commands/launch.ts +34 -0
  204. package/src/commands/models.ts +61 -0
  205. package/src/commands/plugin.ts +79 -0
  206. package/src/commands/read.ts +39 -0
  207. package/src/commands/say.ts +146 -0
  208. package/src/commands/setup.ts +68 -0
  209. package/src/commands/share.ts +71 -0
  210. package/src/commands/shell.ts +30 -0
  211. package/src/commands/ssh.ts +61 -0
  212. package/src/commands/stats.ts +30 -0
  213. package/src/commands/tiny-models.ts +36 -0
  214. package/src/commands/token.ts +164 -0
  215. package/src/commands/ttsr.ts +125 -0
  216. package/src/commands/update.ts +34 -0
  217. package/src/commands/usage.ts +55 -0
  218. package/src/commands/web-search.ts +43 -0
  219. package/src/commands/worktree.ts +63 -0
  220. package/src/commit/agentic/agent.ts +322 -0
  221. package/src/commit/agentic/fallback.ts +96 -0
  222. package/src/commit/agentic/index.ts +383 -0
  223. package/src/commit/agentic/lock-files.ts +107 -0
  224. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  225. package/src/commit/agentic/prompts/session-user.md +25 -0
  226. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  227. package/src/commit/agentic/prompts/system.md +38 -0
  228. package/src/commit/agentic/state.ts +60 -0
  229. package/src/commit/agentic/tools/analyze-file.ts +148 -0
  230. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  231. package/src/commit/agentic/tools/git-hunk.ts +52 -0
  232. package/src/commit/agentic/tools/git-overview.ts +62 -0
  233. package/src/commit/agentic/tools/index.ts +54 -0
  234. package/src/commit/agentic/tools/propose-changelog.ts +147 -0
  235. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  236. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  237. package/src/commit/agentic/tools/schemas.ts +11 -0
  238. package/src/commit/agentic/tools/split-commit.ts +241 -0
  239. package/src/commit/agentic/topo-sort.ts +44 -0
  240. package/src/commit/agentic/trivial.ts +51 -0
  241. package/src/commit/agentic/validation.ts +183 -0
  242. package/src/commit/analysis/conventional.ts +64 -0
  243. package/src/commit/analysis/index.ts +4 -0
  244. package/src/commit/analysis/scope.ts +242 -0
  245. package/src/commit/analysis/summary.ts +107 -0
  246. package/src/commit/analysis/validation.ts +66 -0
  247. package/src/commit/changelog/detect.ts +40 -0
  248. package/src/commit/changelog/generate.ts +101 -0
  249. package/src/commit/changelog/index.ts +234 -0
  250. package/src/commit/changelog/parse.ts +44 -0
  251. package/src/commit/cli.ts +85 -0
  252. package/src/commit/git/diff.ts +148 -0
  253. package/src/commit/index.ts +5 -0
  254. package/src/commit/map-reduce/index.ts +69 -0
  255. package/src/commit/map-reduce/map-phase.ts +193 -0
  256. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  257. package/src/commit/map-reduce/utils.ts +9 -0
  258. package/src/commit/message.ts +11 -0
  259. package/src/commit/model-selection.ts +95 -0
  260. package/src/commit/pipeline.ts +244 -0
  261. package/src/commit/prompts/analysis-system.md +148 -0
  262. package/src/commit/prompts/analysis-user.md +38 -0
  263. package/src/commit/prompts/changelog-system.md +50 -0
  264. package/src/commit/prompts/changelog-user.md +18 -0
  265. package/src/commit/prompts/file-observer-system.md +24 -0
  266. package/src/commit/prompts/file-observer-user.md +8 -0
  267. package/src/commit/prompts/reduce-system.md +50 -0
  268. package/src/commit/prompts/reduce-user.md +17 -0
  269. package/src/commit/prompts/summary-retry.md +3 -0
  270. package/src/commit/prompts/summary-system.md +38 -0
  271. package/src/commit/prompts/summary-user.md +13 -0
  272. package/src/commit/prompts/types-description.md +2 -0
  273. package/src/commit/shared-llm.ts +70 -0
  274. package/src/commit/types.ts +118 -0
  275. package/src/commit/utils/exclusions.ts +42 -0
  276. package/src/commit/utils.ts +58 -0
  277. package/src/config/api-key-resolver.ts +81 -0
  278. package/src/config/append-only-context-mode.ts +76 -0
  279. package/src/config/config-file.ts +347 -0
  280. package/src/config/inline-tool-descriptors-mode.ts +26 -0
  281. package/src/config/keybindings.ts +703 -0
  282. package/src/config/mcp-schema.json +252 -0
  283. package/src/config/model-discovery.ts +1077 -0
  284. package/src/config/model-registry.ts +2822 -0
  285. package/src/config/model-resolver.ts +2115 -0
  286. package/src/config/model-roles.ts +113 -0
  287. package/src/config/models-config-schema-bundle.ts +315 -0
  288. package/src/config/models-config-schema.ts +14 -0
  289. package/src/config/models-config.ts +130 -0
  290. package/src/config/prompt-templates.ts +205 -0
  291. package/src/config/provider-globals.ts +25 -0
  292. package/src/config/resolve-config-value.ts +95 -0
  293. package/src/config/service-tier.ts +146 -0
  294. package/src/config/settings-schema.ts +5887 -0
  295. package/src/config/settings.ts +2394 -0
  296. package/src/config.ts +242 -0
  297. package/src/cursor-bridge-tools.ts +81 -0
  298. package/src/cursor.ts +960 -0
  299. package/src/dap/client.ts +1043 -0
  300. package/src/dap/config.ts +480 -0
  301. package/src/dap/defaults.json +212 -0
  302. package/src/dap/index.ts +4 -0
  303. package/src/dap/session.ts +1841 -0
  304. package/src/dap/types.ts +611 -0
  305. package/src/debug/index.ts +584 -0
  306. package/src/debug/log-formatting.ts +58 -0
  307. package/src/debug/log-viewer.ts +966 -0
  308. package/src/debug/profiler.ts +168 -0
  309. package/src/debug/protocol-probe.ts +267 -0
  310. package/src/debug/raw-sse-buffer.ts +421 -0
  311. package/src/debug/raw-sse.ts +312 -0
  312. package/src/debug/remote-debugger.ts +151 -0
  313. package/src/debug/report-bundle.ts +411 -0
  314. package/src/debug/system-info.ts +111 -0
  315. package/src/debug/terminal-info.ts +124 -0
  316. package/src/discovery/agent-plugin-format.ts +551 -0
  317. package/src/discovery/agent-plugins.ts +341 -0
  318. package/src/discovery/agents-md.ts +67 -0
  319. package/src/discovery/agents.ts +300 -0
  320. package/src/discovery/at-imports.ts +273 -0
  321. package/src/discovery/builtin-defaults.ts +39 -0
  322. package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
  323. package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
  324. package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
  325. package/src/discovery/builtin-rules/go-ioutil.md +37 -0
  326. package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
  327. package/src/discovery/builtin-rules/go-new-expr.md +44 -0
  328. package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
  329. package/src/discovery/builtin-rules/go-range-int.md +45 -0
  330. package/src/discovery/builtin-rules/index.ts +74 -0
  331. package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
  332. package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
  333. package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
  334. package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
  335. package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
  336. package/src/discovery/builtin-rules/rs-result-type.md +20 -0
  337. package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
  338. package/src/discovery/builtin-rules/ts-import-type.md +43 -0
  339. package/src/discovery/builtin-rules/ts-no-any.md +66 -0
  340. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
  341. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
  342. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +56 -0
  343. package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
  344. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  345. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  346. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  347. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
  348. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  349. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  350. package/src/discovery/builtin.ts +945 -0
  351. package/src/discovery/claude-plugins.ts +640 -0
  352. package/src/discovery/claude.ts +591 -0
  353. package/src/discovery/cline.ts +83 -0
  354. package/src/discovery/codex.ts +553 -0
  355. package/src/discovery/contained-path.ts +78 -0
  356. package/src/discovery/cursor.ts +223 -0
  357. package/src/discovery/gemini.ts +386 -0
  358. package/src/discovery/github.ts +337 -0
  359. package/src/discovery/helpers.ts +1183 -0
  360. package/src/discovery/index.ts +82 -0
  361. package/src/discovery/mcp-json.ts +182 -0
  362. package/src/discovery/omp-extension-roots.ts +272 -0
  363. package/src/discovery/omp-plugins.ts +409 -0
  364. package/src/discovery/opencode.ts +441 -0
  365. package/src/discovery/plugin-dir-roots.ts +28 -0
  366. package/src/discovery/ssh.ts +153 -0
  367. package/src/discovery/substitute-plugin-root.ts +77 -0
  368. package/src/discovery/vscode.ts +106 -0
  369. package/src/discovery/windsurf.ts +149 -0
  370. package/src/edit/apply-patch/index.ts +87 -0
  371. package/src/edit/apply-patch/parser.ts +174 -0
  372. package/src/edit/diff.ts +1008 -0
  373. package/src/edit/edit-clipboard.ts +23 -0
  374. package/src/edit/file-snapshot-store.ts +148 -0
  375. package/src/edit/hashline/block-resolver.ts +33 -0
  376. package/src/edit/hashline/diff.ts +402 -0
  377. package/src/edit/hashline/execute.ts +313 -0
  378. package/src/edit/hashline/filesystem.ts +247 -0
  379. package/src/edit/hashline/index.ts +5 -0
  380. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  381. package/src/edit/hashline/params.ts +12 -0
  382. package/src/edit/index.ts +699 -0
  383. package/src/edit/modes/apply-patch.lark +19 -0
  384. package/src/edit/modes/apply-patch.ts +53 -0
  385. package/src/edit/modes/patch.ts +1958 -0
  386. package/src/edit/modes/replace.ts +1191 -0
  387. package/src/edit/normalize.ts +345 -0
  388. package/src/edit/notebook.ts +242 -0
  389. package/src/edit/read-file.ts +25 -0
  390. package/src/edit/renderer.ts +998 -0
  391. package/src/edit/snapshot-details.ts +77 -0
  392. package/src/edit/streaming.ts +710 -0
  393. package/src/eval/agent-bridge.ts +224 -0
  394. package/src/eval/backend-helpers.ts +48 -0
  395. package/src/eval/backend.ts +71 -0
  396. package/src/eval/bridge-timeout.ts +65 -0
  397. package/src/eval/budget-bridge.ts +48 -0
  398. package/src/eval/completion-bridge.ts +212 -0
  399. package/src/eval/concurrency-bridge.ts +34 -0
  400. package/src/eval/executor-base.ts +562 -0
  401. package/src/eval/idle-timeout.ts +91 -0
  402. package/src/eval/index.ts +6 -0
  403. package/src/eval/jl/executor.ts +551 -0
  404. package/src/eval/jl/index.ts +54 -0
  405. package/src/eval/jl/kernel.ts +236 -0
  406. package/src/eval/jl/prelude.jl +736 -0
  407. package/src/eval/jl/prelude.ts +3 -0
  408. package/src/eval/jl/runner.jl +666 -0
  409. package/src/eval/jl/runtime.ts +118 -0
  410. package/src/eval/js/context-manager.ts +872 -0
  411. package/src/eval/js/executor.ts +178 -0
  412. package/src/eval/js/index.ts +41 -0
  413. package/src/eval/js/process-entry.ts +31 -0
  414. package/src/eval/js/shared/helpers.ts +170 -0
  415. package/src/eval/js/shared/indirect-eval.ts +30 -0
  416. package/src/eval/js/shared/local-module-loader.ts +364 -0
  417. package/src/eval/js/shared/prelude.ts +2 -0
  418. package/src/eval/js/shared/prelude.txt +294 -0
  419. package/src/eval/js/shared/rewrite-imports.ts +550 -0
  420. package/src/eval/js/shared/runtime.ts +590 -0
  421. package/src/eval/js/shared/types.ts +18 -0
  422. package/src/eval/js/tool-bridge.ts +163 -0
  423. package/src/eval/js/worker-core.ts +380 -0
  424. package/src/eval/js/worker-entry.ts +37 -0
  425. package/src/eval/js/worker-protocol.ts +47 -0
  426. package/src/eval/kernel-base.ts +569 -0
  427. package/src/eval/py/display.ts +71 -0
  428. package/src/eval/py/executor.ts +643 -0
  429. package/src/eval/py/index.ts +57 -0
  430. package/src/eval/py/kernel.ts +235 -0
  431. package/src/eval/py/prelude.py +673 -0
  432. package/src/eval/py/prelude.ts +3 -0
  433. package/src/eval/py/runner.py +1425 -0
  434. package/src/eval/py/runtime.ts +276 -0
  435. package/src/eval/py/spawn-options.ts +134 -0
  436. package/src/eval/py/tool-bridge.ts +201 -0
  437. package/src/eval/rb/executor.ts +511 -0
  438. package/src/eval/rb/index.ts +54 -0
  439. package/src/eval/rb/kernel.ts +231 -0
  440. package/src/eval/rb/prelude.rb +551 -0
  441. package/src/eval/rb/prelude.ts +3 -0
  442. package/src/eval/rb/runner.rb +581 -0
  443. package/src/eval/rb/runtime.ts +132 -0
  444. package/src/eval/runtime-env.ts +104 -0
  445. package/src/eval/session-id.ts +8 -0
  446. package/src/eval/types.ts +48 -0
  447. package/src/exa/index.ts +2 -0
  448. package/src/exa/mcp-client.ts +370 -0
  449. package/src/exa/types.ts +69 -0
  450. package/src/exec/bash-executor.ts +627 -0
  451. package/src/exec/direnv.ts +145 -0
  452. package/src/exec/exec.ts +53 -0
  453. package/src/exec/non-interactive-env.ts +118 -0
  454. package/src/export/custom-share.ts +65 -0
  455. package/src/export/html/args.ts +20 -0
  456. package/src/export/html/index.ts +320 -0
  457. package/src/export/html/share-loader.js +102 -0
  458. package/src/export/html/template.css +1355 -0
  459. package/src/export/html/template.html +55 -0
  460. package/src/export/html/template.js +1653 -0
  461. package/src/export/html/tool-views.generated.js +35 -0
  462. package/src/export/html/vendor/highlight.min.js +1213 -0
  463. package/src/export/html/vendor/marked.min.js +6 -0
  464. package/src/export/html/web-palette.ts +126 -0
  465. package/src/export/share.ts +686 -0
  466. package/src/export/ttsr.ts +590 -0
  467. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  468. package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
  469. package/src/extensibility/custom-commands/index.ts +2 -0
  470. package/src/extensibility/custom-commands/loader.ts +242 -0
  471. package/src/extensibility/custom-commands/types.ts +119 -0
  472. package/src/extensibility/custom-tools/index.ts +7 -0
  473. package/src/extensibility/custom-tools/loader.ts +301 -0
  474. package/src/extensibility/custom-tools/types.ts +287 -0
  475. package/src/extensibility/custom-tools/wrapper.ts +50 -0
  476. package/src/extensibility/extensions/compact-handler.ts +40 -0
  477. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  478. package/src/extensibility/extensions/index.ts +16 -0
  479. package/src/extensibility/extensions/load-errors.ts +13 -0
  480. package/src/extensibility/extensions/loader.ts +663 -0
  481. package/src/extensibility/extensions/managed-timers.ts +83 -0
  482. package/src/extensibility/extensions/model-api.ts +39 -0
  483. package/src/extensibility/extensions/runner.ts +1396 -0
  484. package/src/extensibility/extensions/types.ts +1603 -0
  485. package/src/extensibility/extensions/wrapper.ts +395 -0
  486. package/src/extensibility/hooks/index.ts +6 -0
  487. package/src/extensibility/hooks/loader.ts +243 -0
  488. package/src/extensibility/hooks/runner.ts +425 -0
  489. package/src/extensibility/hooks/tool-wrapper.ts +124 -0
  490. package/src/extensibility/hooks/types.ts +612 -0
  491. package/src/extensibility/legacy-pi-ai-shim.ts +161 -0
  492. package/src/extensibility/legacy-pi-coding-agent-shim.ts +1460 -0
  493. package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
  494. package/src/extensibility/legacy-typebox.ts +101 -0
  495. package/src/extensibility/plugins/bun-git-cache.ts +91 -0
  496. package/src/extensibility/plugins/doctor.ts +65 -0
  497. package/src/extensibility/plugins/git-url.ts +367 -0
  498. package/src/extensibility/plugins/index.ts +9 -0
  499. package/src/extensibility/plugins/installer.ts +201 -0
  500. package/src/extensibility/plugins/legacy-pi-compat.ts +2643 -0
  501. package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
  502. package/src/extensibility/plugins/loader.ts +535 -0
  503. package/src/extensibility/plugins/manager.ts +1142 -0
  504. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  505. package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
  506. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  507. package/src/extensibility/plugins/marketplace/manager.ts +927 -0
  508. package/src/extensibility/plugins/marketplace/registry.ts +192 -0
  509. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  510. package/src/extensibility/plugins/marketplace/types.ts +192 -0
  511. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  512. package/src/extensibility/plugins/parser.ts +107 -0
  513. package/src/extensibility/plugins/runtime-config.ts +9 -0
  514. package/src/extensibility/plugins/types.ts +194 -0
  515. package/src/extensibility/session-handler-types.ts +21 -0
  516. package/src/extensibility/shared-events.ts +401 -0
  517. package/src/extensibility/skills.ts +531 -0
  518. package/src/extensibility/slash-commands.ts +131 -0
  519. package/src/extensibility/tool-event-input.ts +80 -0
  520. package/src/extensibility/tool-proxy.ts +28 -0
  521. package/src/extensibility/utils.ts +192 -0
  522. package/src/goals/index.ts +3 -0
  523. package/src/goals/runtime.ts +521 -0
  524. package/src/goals/state.ts +37 -0
  525. package/src/goals/tools/goal-tool.ts +251 -0
  526. package/src/hindsight/backend.ts +354 -0
  527. package/src/hindsight/bank.ts +156 -0
  528. package/src/hindsight/client.ts +680 -0
  529. package/src/hindsight/config.ts +193 -0
  530. package/src/hindsight/content.ts +266 -0
  531. package/src/hindsight/index.ts +8 -0
  532. package/src/hindsight/mental-models.ts +429 -0
  533. package/src/hindsight/seeds.json +32 -0
  534. package/src/hindsight/state.ts +530 -0
  535. package/src/hindsight/transcript.ts +71 -0
  536. package/src/index.ts +66 -0
  537. package/src/internal-urls/agent-protocol.ts +180 -0
  538. package/src/internal-urls/artifact-protocol.ts +151 -0
  539. package/src/internal-urls/docs-index.ts +102 -0
  540. package/src/internal-urls/filesystem-resource.ts +34 -0
  541. package/src/internal-urls/history-protocol.ts +198 -0
  542. package/src/internal-urls/index.ts +28 -0
  543. package/src/internal-urls/issue-pr-protocol.ts +594 -0
  544. package/src/internal-urls/json-query.ts +126 -0
  545. package/src/internal-urls/local-protocol.ts +471 -0
  546. package/src/internal-urls/mcp-protocol.ts +168 -0
  547. package/src/internal-urls/memory-protocol.ts +380 -0
  548. package/src/internal-urls/omp-protocol.ts +94 -0
  549. package/src/internal-urls/parse.ts +103 -0
  550. package/src/internal-urls/registry-helpers.ts +132 -0
  551. package/src/internal-urls/router.ts +153 -0
  552. package/src/internal-urls/rule-protocol.ts +45 -0
  553. package/src/internal-urls/security-protocol.ts +261 -0
  554. package/src/internal-urls/skill-protocol.ts +131 -0
  555. package/src/internal-urls/ssh-protocol.ts +368 -0
  556. package/src/internal-urls/types.ts +196 -0
  557. package/src/internal-urls/vault-protocol.ts +940 -0
  558. package/src/internal-urls/xd-protocol.ts +46 -0
  559. package/src/irc/bus.ts +380 -0
  560. package/src/jsonrpc/message-framing.ts +142 -0
  561. package/src/launch/broker.ts +1368 -0
  562. package/src/launch/client.ts +521 -0
  563. package/src/launch/ensure.ts +73 -0
  564. package/src/launch/paths.ts +14 -0
  565. package/src/launch/presence.ts +82 -0
  566. package/src/launch/protocol.ts +448 -0
  567. package/src/launch/spawn-options.ts +17 -0
  568. package/src/launch/terminal-output-worker-client.ts +53 -0
  569. package/src/launch/terminal-output-worker-protocol.ts +11 -0
  570. package/src/launch/terminal-output-worker.ts +23 -0
  571. package/src/launch/terminal-output.ts +46 -0
  572. package/src/lib/xai-http.ts +150 -0
  573. package/src/live/attestation.ts +91 -0
  574. package/src/live/controller.ts +517 -0
  575. package/src/live/prompts/agent-final-message.md +3 -0
  576. package/src/live/prompts/live-instructions.md +23 -0
  577. package/src/live/protocol.ts +233 -0
  578. package/src/live/transport.ts +422 -0
  579. package/src/live/visualizer.ts +221 -0
  580. package/src/live/voices.ts +18 -0
  581. package/src/lsp/client.ts +1465 -0
  582. package/src/lsp/clients/biome-client.ts +263 -0
  583. package/src/lsp/clients/index.ts +50 -0
  584. package/src/lsp/clients/lsp-linter-client.ts +85 -0
  585. package/src/lsp/clients/swiftlint-client.ts +120 -0
  586. package/src/lsp/config.ts +549 -0
  587. package/src/lsp/defaults.json +499 -0
  588. package/src/lsp/deferred-diagnostics.ts +66 -0
  589. package/src/lsp/diagnostics-ledger.ts +51 -0
  590. package/src/lsp/edits.ts +288 -0
  591. package/src/lsp/format-options.ts +119 -0
  592. package/src/lsp/index.ts +2821 -0
  593. package/src/lsp/lspmux.ts +233 -0
  594. package/src/lsp/mux/daemon.ts +348 -0
  595. package/src/lsp/mux/protocol.ts +96 -0
  596. package/src/lsp/mux/server.ts +797 -0
  597. package/src/lsp/render.ts +668 -0
  598. package/src/lsp/startup-events.ts +13 -0
  599. package/src/lsp/types.ts +479 -0
  600. package/src/lsp/utils.ts +747 -0
  601. package/src/main.ts +1799 -0
  602. package/src/markit/NOTICE +32 -0
  603. package/src/markit/converters/docx.ts +56 -0
  604. package/src/markit/converters/epub.ts +136 -0
  605. package/src/markit/converters/pdf/columns.ts +103 -0
  606. package/src/markit/converters/pdf/extract.ts +598 -0
  607. package/src/markit/converters/pdf/grid.ts +780 -0
  608. package/src/markit/converters/pdf/headers.ts +106 -0
  609. package/src/markit/converters/pdf/index.ts +146 -0
  610. package/src/markit/converters/pdf/render.ts +501 -0
  611. package/src/markit/converters/pdf/types.ts +84 -0
  612. package/src/markit/converters/pptx.ts +325 -0
  613. package/src/markit/converters/xlsx.ts +173 -0
  614. package/src/markit/index.ts +2 -0
  615. package/src/markit/registry.ts +59 -0
  616. package/src/markit/types.ts +35 -0
  617. package/src/mcp/client.ts +511 -0
  618. package/src/mcp/config-writer.ts +377 -0
  619. package/src/mcp/config.ts +385 -0
  620. package/src/mcp/index.ts +29 -0
  621. package/src/mcp/json-rpc.ts +122 -0
  622. package/src/mcp/loader.ts +125 -0
  623. package/src/mcp/manager.ts +1524 -0
  624. package/src/mcp/oauth-credentials.ts +104 -0
  625. package/src/mcp/oauth-discovery.ts +587 -0
  626. package/src/mcp/oauth-flow.ts +830 -0
  627. package/src/mcp/render.ts +214 -0
  628. package/src/mcp/request-id.ts +24 -0
  629. package/src/mcp/smithery-auth.ts +108 -0
  630. package/src/mcp/smithery-connect.ts +154 -0
  631. package/src/mcp/smithery-registry.ts +500 -0
  632. package/src/mcp/startup-events.ts +116 -0
  633. package/src/mcp/timeout.ts +59 -0
  634. package/src/mcp/tool-bridge.ts +691 -0
  635. package/src/mcp/tool-cache.ts +117 -0
  636. package/src/mcp/transports/header-policy.ts +95 -0
  637. package/src/mcp/transports/http.ts +508 -0
  638. package/src/mcp/transports/index.ts +7 -0
  639. package/src/mcp/transports/sse.ts +372 -0
  640. package/src/mcp/transports/stdio.ts +905 -0
  641. package/src/mcp/types.ts +467 -0
  642. package/src/memories/index.ts +1434 -0
  643. package/src/memories/storage.ts +578 -0
  644. package/src/memory-backend/index.ts +19 -0
  645. package/src/memory-backend/local-backend.ts +47 -0
  646. package/src/memory-backend/messages.ts +19 -0
  647. package/src/memory-backend/off-backend.ts +25 -0
  648. package/src/memory-backend/resolve.ts +25 -0
  649. package/src/memory-backend/runtime.ts +66 -0
  650. package/src/memory-backend/tool-names.ts +2 -0
  651. package/src/memory-backend/types.ts +166 -0
  652. package/src/mnemopi/backend.ts +629 -0
  653. package/src/mnemopi/config.ts +267 -0
  654. package/src/mnemopi/embed-client.ts +293 -0
  655. package/src/mnemopi/embed-protocol.ts +35 -0
  656. package/src/mnemopi/embed-worker.ts +114 -0
  657. package/src/mnemopi/index.ts +3 -0
  658. package/src/mnemopi/state.ts +940 -0
  659. package/src/modes/acp/acp-agent.ts +2595 -0
  660. package/src/modes/acp/acp-client-bridge.ts +154 -0
  661. package/src/modes/acp/acp-event-mapper.ts +1084 -0
  662. package/src/modes/acp/acp-mode.ts +48 -0
  663. package/src/modes/acp/index.ts +2 -0
  664. package/src/modes/acp/terminal-auth.ts +37 -0
  665. package/src/modes/components/advisor-config.ts +635 -0
  666. package/src/modes/components/advisor-message.ts +109 -0
  667. package/src/modes/components/agent-dashboard.ts +1252 -0
  668. package/src/modes/components/agent-hub-projection.ts +248 -0
  669. package/src/modes/components/agent-hub-renderer.ts +194 -0
  670. package/src/modes/components/agent-hub.ts +1126 -0
  671. package/src/modes/components/agent-transcript-viewer.ts +647 -0
  672. package/src/modes/components/ask-dialog.ts +1017 -0
  673. package/src/modes/components/assistant-message.ts +978 -0
  674. package/src/modes/components/background-tan-message.ts +36 -0
  675. package/src/modes/components/bash-execution.ts +233 -0
  676. package/src/modes/components/bordered-loader.ts +41 -0
  677. package/src/modes/components/btw-panel.ts +161 -0
  678. package/src/modes/components/cache-invalidation-marker.ts +110 -0
  679. package/src/modes/components/chat-block.ts +111 -0
  680. package/src/modes/components/chat-transcript-builder.ts +518 -0
  681. package/src/modes/components/codex-reset-fireworks.ts +369 -0
  682. package/src/modes/components/collab-prompt-message.ts +32 -0
  683. package/src/modes/components/compaction-summary-message.ts +221 -0
  684. package/src/modes/components/copy-selector.ts +218 -0
  685. package/src/modes/components/countdown-timer.ts +75 -0
  686. package/src/modes/components/custom-editor.ts +1036 -0
  687. package/src/modes/components/custom-message.ts +70 -0
  688. package/src/modes/components/diff.ts +254 -0
  689. package/src/modes/components/dynamic-border.ts +37 -0
  690. package/src/modes/components/error-banner.ts +33 -0
  691. package/src/modes/components/eval-execution.ts +169 -0
  692. package/src/modes/components/execution-shared.ts +101 -0
  693. package/src/modes/components/extensions/extension-dashboard.ts +492 -0
  694. package/src/modes/components/extensions/extension-list.ts +507 -0
  695. package/src/modes/components/extensions/index.ts +9 -0
  696. package/src/modes/components/extensions/inspector-panel.ts +326 -0
  697. package/src/modes/components/extensions/state-manager.ts +648 -0
  698. package/src/modes/components/extensions/types.ts +186 -0
  699. package/src/modes/components/footer.ts +274 -0
  700. package/src/modes/components/history-search.ts +268 -0
  701. package/src/modes/components/hook-editor.ts +213 -0
  702. package/src/modes/components/hook-input.ts +87 -0
  703. package/src/modes/components/hook-message.ts +67 -0
  704. package/src/modes/components/hook-selector.ts +691 -0
  705. package/src/modes/components/index.ts +42 -0
  706. package/src/modes/components/keybinding-hints.ts +56 -0
  707. package/src/modes/components/late-diagnostics-message.ts +60 -0
  708. package/src/modes/components/login-dialog.ts +197 -0
  709. package/src/modes/components/logout-account-selector.ts +130 -0
  710. package/src/modes/components/mcp-add-wizard.ts +1413 -0
  711. package/src/modes/components/message-frame.ts +98 -0
  712. package/src/modes/components/model-browser.ts +888 -0
  713. package/src/modes/components/model-hub.ts +2014 -0
  714. package/src/modes/components/model-picker.ts +237 -0
  715. package/src/modes/components/move-overlay.ts +293 -0
  716. package/src/modes/components/oauth-selector.ts +474 -0
  717. package/src/modes/components/omfg-panel.ts +141 -0
  718. package/src/modes/components/overlay-box.ts +109 -0
  719. package/src/modes/components/pause-screen.ts +208 -0
  720. package/src/modes/components/plan-review-overlay.ts +1226 -0
  721. package/src/modes/components/plan-toc.ts +138 -0
  722. package/src/modes/components/plugin-selector.ts +100 -0
  723. package/src/modes/components/plugin-settings.ts +745 -0
  724. package/src/modes/components/queue-mode-selector.ts +61 -0
  725. package/src/modes/components/read-tool-group.ts +856 -0
  726. package/src/modes/components/reset-usage-selector.ts +161 -0
  727. package/src/modes/components/segment-track.ts +89 -0
  728. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  729. package/src/modes/components/selector-helpers.ts +129 -0
  730. package/src/modes/components/session-account-selector.ts +62 -0
  731. package/src/modes/components/session-selector.ts +1035 -0
  732. package/src/modes/components/settings-defs.ts +267 -0
  733. package/src/modes/components/settings-selector.ts +1445 -0
  734. package/src/modes/components/show-images-selector.ts +50 -0
  735. package/src/modes/components/skill-message.ts +110 -0
  736. package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
  737. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  738. package/src/modes/components/status-line/component.ts +1843 -0
  739. package/src/modes/components/status-line/context-thresholds.ts +86 -0
  740. package/src/modes/components/status-line/git-utils.ts +42 -0
  741. package/src/modes/components/status-line/index.ts +5 -0
  742. package/src/modes/components/status-line/presets.ts +106 -0
  743. package/src/modes/components/status-line/segments.ts +710 -0
  744. package/src/modes/components/status-line/separators.ts +55 -0
  745. package/src/modes/components/status-line/types.ts +159 -0
  746. package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
  747. package/src/modes/components/theme-selector.ts +68 -0
  748. package/src/modes/components/thinking-selector.ts +57 -0
  749. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  750. package/src/modes/components/tips.txt +26 -0
  751. package/src/modes/components/todo-reminder.ts +43 -0
  752. package/src/modes/components/tool-execution.ts +1467 -0
  753. package/src/modes/components/transcript-container.ts +527 -0
  754. package/src/modes/components/tree-selector.ts +1006 -0
  755. package/src/modes/components/ttsr-notification.ts +123 -0
  756. package/src/modes/components/usage-row.ts +52 -0
  757. package/src/modes/components/user-message-selector.ts +227 -0
  758. package/src/modes/components/user-message.ts +167 -0
  759. package/src/modes/components/visual-truncate.ts +63 -0
  760. package/src/modes/components/welcome.ts +578 -0
  761. package/src/modes/controllers/btw-controller.ts +246 -0
  762. package/src/modes/controllers/command-controller-shared.ts +109 -0
  763. package/src/modes/controllers/command-controller.ts +2024 -0
  764. package/src/modes/controllers/event-controller.ts +2173 -0
  765. package/src/modes/controllers/extension-ui-controller.ts +1243 -0
  766. package/src/modes/controllers/input-controller.ts +2037 -0
  767. package/src/modes/controllers/live-command-controller.ts +261 -0
  768. package/src/modes/controllers/mcp-command-controller.ts +2552 -0
  769. package/src/modes/controllers/omfg-controller.ts +287 -0
  770. package/src/modes/controllers/omfg-rule.ts +647 -0
  771. package/src/modes/controllers/selector-controller.ts +2058 -0
  772. package/src/modes/controllers/session-focus-controller.ts +117 -0
  773. package/src/modes/controllers/ssh-command-controller.ts +385 -0
  774. package/src/modes/controllers/streaming-reveal.ts +399 -0
  775. package/src/modes/controllers/tan-command-controller.ts +243 -0
  776. package/src/modes/controllers/todo-command-controller.ts +487 -0
  777. package/src/modes/controllers/tool-args-reveal.ts +591 -0
  778. package/src/modes/data/emojis.json +1 -0
  779. package/src/modes/emoji-autocomplete.ts +285 -0
  780. package/src/modes/github-ref-autocomplete.ts +75 -0
  781. package/src/modes/gradient-highlight.ts +99 -0
  782. package/src/modes/image-references.ts +137 -0
  783. package/src/modes/index.ts +10 -0
  784. package/src/modes/interactive-mode.ts +5055 -0
  785. package/src/modes/internal-url-autocomplete.ts +158 -0
  786. package/src/modes/loop-limit.ts +192 -0
  787. package/src/modes/magic-keyword-boundary.ts +23 -0
  788. package/src/modes/magic-keywords.ts +42 -0
  789. package/src/modes/markdown-prose.ts +247 -0
  790. package/src/modes/oauth-manual-input.ts +69 -0
  791. package/src/modes/orchestrate.ts +43 -0
  792. package/src/modes/print-mode.ts +282 -0
  793. package/src/modes/prompt-action-autocomplete.ts +322 -0
  794. package/src/modes/queue-input.ts +132 -0
  795. package/src/modes/rpc/host-tools.ts +204 -0
  796. package/src/modes/rpc/host-uris.ts +241 -0
  797. package/src/modes/rpc/rpc-client.ts +1213 -0
  798. package/src/modes/rpc/rpc-frame.ts +316 -0
  799. package/src/modes/rpc/rpc-input.ts +38 -0
  800. package/src/modes/rpc/rpc-messages.ts +127 -0
  801. package/src/modes/rpc/rpc-mode.ts +1519 -0
  802. package/src/modes/rpc/rpc-subagents.ts +265 -0
  803. package/src/modes/rpc/rpc-types.ts +544 -0
  804. package/src/modes/running-subagent-badge.ts +13 -0
  805. package/src/modes/runtime-init.ts +144 -0
  806. package/src/modes/session-observer-registry.ts +223 -0
  807. package/src/modes/session-teardown.ts +82 -0
  808. package/src/modes/setup-version.ts +11 -0
  809. package/src/modes/setup-wizard/index.ts +103 -0
  810. package/src/modes/setup-wizard/lazy.ts +16 -0
  811. package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
  812. package/src/modes/setup-wizard/scenes/model.ts +132 -0
  813. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  814. package/src/modes/setup-wizard/scenes/providers.ts +105 -0
  815. package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
  816. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  817. package/src/modes/setup-wizard/scenes/theme.ts +330 -0
  818. package/src/modes/setup-wizard/scenes/types.ts +65 -0
  819. package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
  820. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  821. package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
  822. package/src/modes/shared.ts +49 -0
  823. package/src/modes/skill-command.ts +91 -0
  824. package/src/modes/theme/dark.json +95 -0
  825. package/src/modes/theme/defaults/alabaster.json +93 -0
  826. package/src/modes/theme/defaults/amethyst.json +96 -0
  827. package/src/modes/theme/defaults/anthracite.json +93 -0
  828. package/src/modes/theme/defaults/basalt.json +91 -0
  829. package/src/modes/theme/defaults/birch.json +95 -0
  830. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  831. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  832. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  833. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  834. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  835. package/src/modes/theme/defaults/dark-copper.json +95 -0
  836. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  837. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  838. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  839. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  840. package/src/modes/theme/defaults/dark-ember.json +95 -0
  841. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  842. package/src/modes/theme/defaults/dark-forest.json +96 -0
  843. package/src/modes/theme/defaults/dark-github.json +105 -0
  844. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  845. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  846. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  847. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  848. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  849. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  850. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  851. package/src/modes/theme/defaults/dark-nord.json +97 -0
  852. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  853. package/src/modes/theme/defaults/dark-one.json +100 -0
  854. package/src/modes/theme/defaults/dark-poimandres.json +143 -0
  855. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  856. package/src/modes/theme/defaults/dark-reef.json +91 -0
  857. package/src/modes/theme/defaults/dark-retro.json +92 -0
  858. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  859. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  860. package/src/modes/theme/defaults/dark-slate.json +95 -0
  861. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  862. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  863. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  864. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  865. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  866. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  867. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  868. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  869. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  870. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  871. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  872. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  873. package/src/modes/theme/defaults/graphite.json +92 -0
  874. package/src/modes/theme/defaults/index.ts +199 -0
  875. package/src/modes/theme/defaults/light-arctic.json +107 -0
  876. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  877. package/src/modes/theme/defaults/light-canyon.json +91 -0
  878. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  879. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  880. package/src/modes/theme/defaults/light-coral.json +95 -0
  881. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  882. package/src/modes/theme/defaults/light-dawn.json +90 -0
  883. package/src/modes/theme/defaults/light-dunes.json +91 -0
  884. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  885. package/src/modes/theme/defaults/light-forest.json +100 -0
  886. package/src/modes/theme/defaults/light-frost.json +95 -0
  887. package/src/modes/theme/defaults/light-github.json +115 -0
  888. package/src/modes/theme/defaults/light-glacier.json +91 -0
  889. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  890. package/src/modes/theme/defaults/light-haze.json +90 -0
  891. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  892. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  893. package/src/modes/theme/defaults/light-lavender.json +95 -0
  894. package/src/modes/theme/defaults/light-meadow.json +91 -0
  895. package/src/modes/theme/defaults/light-mint.json +95 -0
  896. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  897. package/src/modes/theme/defaults/light-ocean.json +99 -0
  898. package/src/modes/theme/defaults/light-one.json +99 -0
  899. package/src/modes/theme/defaults/light-opal.json +91 -0
  900. package/src/modes/theme/defaults/light-orchard.json +91 -0
  901. package/src/modes/theme/defaults/light-paper.json +95 -0
  902. package/src/modes/theme/defaults/light-poimandres.json +143 -0
  903. package/src/modes/theme/defaults/light-prism.json +90 -0
  904. package/src/modes/theme/defaults/light-retro.json +98 -0
  905. package/src/modes/theme/defaults/light-sand.json +95 -0
  906. package/src/modes/theme/defaults/light-savanna.json +91 -0
  907. package/src/modes/theme/defaults/light-solarized.json +102 -0
  908. package/src/modes/theme/defaults/light-soleil.json +90 -0
  909. package/src/modes/theme/defaults/light-sunset.json +99 -0
  910. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  911. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  912. package/src/modes/theme/defaults/light-wetland.json +91 -0
  913. package/src/modes/theme/defaults/light-zenith.json +89 -0
  914. package/src/modes/theme/defaults/limestone.json +94 -0
  915. package/src/modes/theme/defaults/mahogany.json +97 -0
  916. package/src/modes/theme/defaults/marble.json +93 -0
  917. package/src/modes/theme/defaults/obsidian.json +91 -0
  918. package/src/modes/theme/defaults/onyx.json +91 -0
  919. package/src/modes/theme/defaults/pearl.json +93 -0
  920. package/src/modes/theme/defaults/porcelain.json +91 -0
  921. package/src/modes/theme/defaults/quartz.json +96 -0
  922. package/src/modes/theme/defaults/sandstone.json +95 -0
  923. package/src/modes/theme/defaults/titanium.json +90 -0
  924. package/src/modes/theme/light.json +93 -0
  925. package/src/modes/theme/mermaid-cache.ts +92 -0
  926. package/src/modes/theme/shimmer.ts +305 -0
  927. package/src/modes/theme/theme-schema.json +463 -0
  928. package/src/modes/theme/theme.ts +3171 -0
  929. package/src/modes/turn-budget.ts +31 -0
  930. package/src/modes/types.ts +494 -0
  931. package/src/modes/ultrathink.ts +42 -0
  932. package/src/modes/utils/context-usage.ts +518 -0
  933. package/src/modes/utils/copy-targets.ts +378 -0
  934. package/src/modes/utils/hotkeys-markdown.ts +65 -0
  935. package/src/modes/utils/interactive-context-helpers.ts +31 -0
  936. package/src/modes/utils/keybinding-matchers.ts +86 -0
  937. package/src/modes/utils/tools-markdown.ts +31 -0
  938. package/src/modes/utils/transcript-render-helpers.ts +253 -0
  939. package/src/modes/utils/ui-helpers.ts +976 -0
  940. package/src/modes/warp-events.ts +232 -0
  941. package/src/modes/workflow.ts +55 -0
  942. package/src/plan-mode/approved-plan.ts +194 -0
  943. package/src/plan-mode/model-transition.ts +51 -0
  944. package/src/plan-mode/plan-files.ts +40 -0
  945. package/src/plan-mode/plan-handoff.ts +37 -0
  946. package/src/plan-mode/plan-protection.ts +31 -0
  947. package/src/plan-mode/state.ts +6 -0
  948. package/src/priority.json +60 -0
  949. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  950. package/src/prompts/advisor/advise-tool.md +3 -0
  951. package/src/prompts/advisor/context-files.md +8 -0
  952. package/src/prompts/advisor/system.md +98 -0
  953. package/src/prompts/agents/designer.md +74 -0
  954. package/src/prompts/agents/frontmatter.md +12 -0
  955. package/src/prompts/agents/init.md +33 -0
  956. package/src/prompts/agents/librarian.md +119 -0
  957. package/src/prompts/agents/reviewer.md +139 -0
  958. package/src/prompts/agents/scout.md +58 -0
  959. package/src/prompts/agents/security-reviewer.md +75 -0
  960. package/src/prompts/agents/task.md +17 -0
  961. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  962. package/src/prompts/bench/cache-prefix.md +3 -0
  963. package/src/prompts/bench/cache-suffix.md +1 -0
  964. package/src/prompts/bench.md +6 -0
  965. package/src/prompts/ci-green-request.md +36 -0
  966. package/src/prompts/dry-balance-bench.md +8 -0
  967. package/src/prompts/goals/goal-budget-limit.md +16 -0
  968. package/src/prompts/goals/goal-continuation.md +28 -0
  969. package/src/prompts/goals/goal-mode-active.md +23 -0
  970. package/src/prompts/goals/goal-mode-context.md +4 -0
  971. package/src/prompts/goals/goal-todo-context.md +12 -0
  972. package/src/prompts/goals/guided-goal-interview.md +43 -0
  973. package/src/prompts/memories/consolidation.md +30 -0
  974. package/src/prompts/memories/consolidation_system.md +4 -0
  975. package/src/prompts/memories/read-path.md +17 -0
  976. package/src/prompts/memories/stage_one_input.md +6 -0
  977. package/src/prompts/memories/stage_one_system.md +21 -0
  978. package/src/prompts/review-custom-request.md +21 -0
  979. package/src/prompts/review-headless-request.md +16 -0
  980. package/src/prompts/review-request.md +68 -0
  981. package/src/prompts/security/scan-coordinator.md +7 -0
  982. package/src/prompts/security/scan-request.md +21 -0
  983. package/src/prompts/security/validate-request.md +8 -0
  984. package/src/prompts/session/launch-completion.md +1 -0
  985. package/src/prompts/skills/autoload.md +8 -0
  986. package/src/prompts/skills/user-invocation.md +11 -0
  987. package/src/prompts/steering/parent-irc.md +5 -0
  988. package/src/prompts/steering/user-interjection.md +6 -0
  989. package/src/prompts/system/active-repo-context.md +4 -0
  990. package/src/prompts/system/agent-creation-architect.md +50 -0
  991. package/src/prompts/system/agent-creation-user.md +6 -0
  992. package/src/prompts/system/auto-continue.md +1 -0
  993. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  994. package/src/prompts/system/auto-thinking-difficulty.md +14 -0
  995. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  996. package/src/prompts/system/autolearn-guidance.md +7 -0
  997. package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
  998. package/src/prompts/system/background-tan-dispatch.md +8 -0
  999. package/src/prompts/system/btw-user.md +8 -0
  1000. package/src/prompts/system/commit-message-system.md +14 -0
  1001. package/src/prompts/system/computer-safety.md +14 -0
  1002. package/src/prompts/system/custom-system-prompt.md +64 -0
  1003. package/src/prompts/system/eager-task.md +7 -0
  1004. package/src/prompts/system/eager-todo.md +18 -0
  1005. package/src/prompts/system/empty-stop-retry.md +4 -0
  1006. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  1007. package/src/prompts/system/interrupted-thinking.md +7 -0
  1008. package/src/prompts/system/irc-autoreply.md +6 -0
  1009. package/src/prompts/system/irc-incoming.md +9 -0
  1010. package/src/prompts/system/manual-continue.md +7 -0
  1011. package/src/prompts/system/mcp-xdev-guidance.md +11 -0
  1012. package/src/prompts/system/memory-consolidation-system.md +8 -0
  1013. package/src/prompts/system/memory-extraction-system.md +26 -0
  1014. package/src/prompts/system/mid-run-todo-nudge.md +3 -0
  1015. package/src/prompts/system/omfg-user.md +50 -0
  1016. package/src/prompts/system/orchestrate-notice.md +40 -0
  1017. package/src/prompts/system/personalities/default.md +18 -0
  1018. package/src/prompts/system/personalities/friendly.md +17 -0
  1019. package/src/prompts/system/personalities/pragmatic.md +15 -0
  1020. package/src/prompts/system/plan-mode-active.md +125 -0
  1021. package/src/prompts/system/plan-mode-approved.md +22 -0
  1022. package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
  1023. package/src/prompts/system/plan-mode-reference.md +10 -0
  1024. package/src/prompts/system/plan-mode-subagent.md +33 -0
  1025. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  1026. package/src/prompts/system/plan-yolo-handoff.md +5 -0
  1027. package/src/prompts/system/prewalk-checklist.md +7 -0
  1028. package/src/prompts/system/prewalk-continue.md +1 -0
  1029. package/src/prompts/system/prewalk-plan.md +13 -0
  1030. package/src/prompts/system/project-prompt.md +61 -0
  1031. package/src/prompts/system/recap-user.md +9 -0
  1032. package/src/prompts/system/resolve-device-reminder.md +3 -0
  1033. package/src/prompts/system/rewind-report.md +6 -0
  1034. package/src/prompts/system/side-channel-no-tools.md +3 -0
  1035. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  1036. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  1037. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  1038. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  1039. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  1040. package/src/prompts/system/speech-rewrite.md +15 -0
  1041. package/src/prompts/system/subagent-async-pending.md +6 -0
  1042. package/src/prompts/system/subagent-system-prompt.md +73 -0
  1043. package/src/prompts/system/subagent-user-prompt.md +3 -0
  1044. package/src/prompts/system/subagent-yield-reminder.md +23 -0
  1045. package/src/prompts/system/system-prompt.md +263 -0
  1046. package/src/prompts/system/tan-context-switch.md +17 -0
  1047. package/src/prompts/system/task-label.md +23 -0
  1048. package/src/prompts/system/thinking-loop-redirect.md +10 -0
  1049. package/src/prompts/system/title-marker-instruction.md +1 -0
  1050. package/src/prompts/system/title-system.md +16 -0
  1051. package/src/prompts/system/tool-call-loop-redirect.md +8 -0
  1052. package/src/prompts/system/ttsr-interrupt.md +7 -0
  1053. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  1054. package/src/prompts/system/ultrathink-notice.md +3 -0
  1055. package/src/prompts/system/unexpected-stop-classifier.md +17 -0
  1056. package/src/prompts/system/unexpected-stop-retry.md +4 -0
  1057. package/src/prompts/system/vibe-mode-active.md +26 -0
  1058. package/src/prompts/system/web-search.md +25 -0
  1059. package/src/prompts/system/workflow-notice.md +112 -0
  1060. package/src/prompts/system/xdev-mount-notice.md +20 -0
  1061. package/src/prompts/tools/apply-patch.md +65 -0
  1062. package/src/prompts/tools/ask.md +22 -0
  1063. package/src/prompts/tools/ast-edit.md +11 -0
  1064. package/src/prompts/tools/ast-grep.md +19 -0
  1065. package/src/prompts/tools/async-result.md +8 -0
  1066. package/src/prompts/tools/bash.md +23 -0
  1067. package/src/prompts/tools/browser.md +29 -0
  1068. package/src/prompts/tools/checkpoint.md +15 -0
  1069. package/src/prompts/tools/computer.md +26 -0
  1070. package/src/prompts/tools/debug.md +3 -0
  1071. package/src/prompts/tools/eval.md +45 -0
  1072. package/src/prompts/tools/github.md +22 -0
  1073. package/src/prompts/tools/glob.md +16 -0
  1074. package/src/prompts/tools/goal.md +11 -0
  1075. package/src/prompts/tools/grep.md +13 -0
  1076. package/src/prompts/tools/hub.md +34 -0
  1077. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  1078. package/src/prompts/tools/image-attachment-describe.md +10 -0
  1079. package/src/prompts/tools/image-gen.md +7 -0
  1080. package/src/prompts/tools/inspect-image-system.md +20 -0
  1081. package/src/prompts/tools/inspect-image.md +22 -0
  1082. package/src/prompts/tools/learn.md +7 -0
  1083. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  1084. package/src/prompts/tools/lsp.md +19 -0
  1085. package/src/prompts/tools/manage-skill.md +9 -0
  1086. package/src/prompts/tools/memory-edit.md +12 -0
  1087. package/src/prompts/tools/patch.md +57 -0
  1088. package/src/prompts/tools/read.md +27 -0
  1089. package/src/prompts/tools/recall.md +7 -0
  1090. package/src/prompts/tools/reflect.md +5 -0
  1091. package/src/prompts/tools/replace.md +30 -0
  1092. package/src/prompts/tools/retain.md +6 -0
  1093. package/src/prompts/tools/rewind.md +14 -0
  1094. package/src/prompts/tools/security-publish.md +1 -0
  1095. package/src/prompts/tools/security-scan.md +1 -0
  1096. package/src/prompts/tools/task-async-contract.md +1 -0
  1097. package/src/prompts/tools/task-summary.md +20 -0
  1098. package/src/prompts/tools/task.md +83 -0
  1099. package/src/prompts/tools/todo.md +42 -0
  1100. package/src/prompts/tools/vibe-kill.md +3 -0
  1101. package/src/prompts/tools/vibe-list.md +3 -0
  1102. package/src/prompts/tools/vibe-send.md +9 -0
  1103. package/src/prompts/tools/vibe-spawn.md +10 -0
  1104. package/src/prompts/tools/vibe-turn-result.md +19 -0
  1105. package/src/prompts/tools/vibe-wait.md +8 -0
  1106. package/src/prompts/tools/web-search.md +8 -0
  1107. package/src/prompts/tools/write.md +14 -0
  1108. package/src/registry/agent-lifecycle.ts +505 -0
  1109. package/src/registry/agent-registry.ts +287 -0
  1110. package/src/registry/persisted-agents.ts +423 -0
  1111. package/src/sdk.ts +3820 -0
  1112. package/src/secrets/index.ts +375 -0
  1113. package/src/secrets/obfuscator.ts +2629 -0
  1114. package/src/secrets/regex.ts +21 -0
  1115. package/src/security/auth.ts +98 -0
  1116. package/src/security/cloud.ts +686 -0
  1117. package/src/security/comparison.ts +255 -0
  1118. package/src/security/contracts/ids.ts +111 -0
  1119. package/src/security/contracts/index.ts +4 -0
  1120. package/src/security/contracts/schemas.ts +219 -0
  1121. package/src/security/contracts/types.ts +254 -0
  1122. package/src/security/contracts/validation.ts +69 -0
  1123. package/src/security/coordinator.ts +708 -0
  1124. package/src/security/importers/codex-security.ts +387 -0
  1125. package/src/security/importers/index.ts +2 -0
  1126. package/src/security/importers/sarif.ts +357 -0
  1127. package/src/security/index.ts +13 -0
  1128. package/src/security/preflight.ts +405 -0
  1129. package/src/security/provenance.ts +106 -0
  1130. package/src/security/publication.ts +326 -0
  1131. package/src/security/remediation.ts +93 -0
  1132. package/src/security/resource-output.ts +50 -0
  1133. package/src/security/sarif.ts +78 -0
  1134. package/src/security/store.ts +430 -0
  1135. package/src/session/acp-permission-gate.ts +165 -0
  1136. package/src/session/agent-session-events.ts +67 -0
  1137. package/src/session/agent-session-types.ts +407 -0
  1138. package/src/session/agent-session.ts +9000 -0
  1139. package/src/session/agent-storage.ts +807 -0
  1140. package/src/session/artifacts.ts +154 -0
  1141. package/src/session/async-job-delivery.ts +82 -0
  1142. package/src/session/auth-broker-config.ts +92 -0
  1143. package/src/session/auth-storage.ts +25 -0
  1144. package/src/session/bash-runner.ts +326 -0
  1145. package/src/session/blob-store.ts +295 -0
  1146. package/src/session/checkpoint-entries.ts +81 -0
  1147. package/src/session/claude-session-store.ts +426 -0
  1148. package/src/session/client-bridge.ts +85 -0
  1149. package/src/session/codex-auto-reset.ts +673 -0
  1150. package/src/session/codex-session-store.ts +673 -0
  1151. package/src/session/compact-modes.ts +105 -0
  1152. package/src/session/credential-pin.ts +93 -0
  1153. package/src/session/eval-runner.ts +219 -0
  1154. package/src/session/exit-diagnostics.ts +310 -0
  1155. package/src/session/foreign-session-import.ts +52 -0
  1156. package/src/session/foreign-session-jsonl.ts +29 -0
  1157. package/src/session/foreign-session-store.ts +26 -0
  1158. package/src/session/history-storage.ts +329 -0
  1159. package/src/session/indexed-session-storage.ts +553 -0
  1160. package/src/session/irc-bridge.ts +203 -0
  1161. package/src/session/launch-completion.ts +37 -0
  1162. package/src/session/messages.ts +1304 -0
  1163. package/src/session/model-controls.ts +757 -0
  1164. package/src/session/prewalk.ts +279 -0
  1165. package/src/session/provider-image-budget.ts +86 -0
  1166. package/src/session/queued-messages.ts +99 -0
  1167. package/src/session/redis-session-storage.ts +257 -0
  1168. package/src/session/retry-fallback-chains.ts +455 -0
  1169. package/src/session/role-models.ts +85 -0
  1170. package/src/session/session-advisors.ts +1896 -0
  1171. package/src/session/session-context.ts +584 -0
  1172. package/src/session/session-dump-format.ts +216 -0
  1173. package/src/session/session-entries.ts +306 -0
  1174. package/src/session/session-handoff.ts +325 -0
  1175. package/src/session/session-history-format.ts +459 -0
  1176. package/src/session/session-listing.ts +715 -0
  1177. package/src/session/session-loader.ts +362 -0
  1178. package/src/session/session-maintenance.ts +3115 -0
  1179. package/src/session/session-manager.ts +2747 -0
  1180. package/src/session/session-memory.ts +222 -0
  1181. package/src/session/session-metadata.ts +53 -0
  1182. package/src/session/session-migrations.ts +78 -0
  1183. package/src/session/session-paths.ts +280 -0
  1184. package/src/session/session-persistence.ts +293 -0
  1185. package/src/session/session-provider-boundary.ts +306 -0
  1186. package/src/session/session-stats.ts +349 -0
  1187. package/src/session/session-storage.ts +774 -0
  1188. package/src/session/session-title-slot.ts +141 -0
  1189. package/src/session/session-tools.ts +1385 -0
  1190. package/src/session/session-workspace.ts +53 -0
  1191. package/src/session/settings-stream-fn.ts +79 -0
  1192. package/src/session/shake-types.ts +43 -0
  1193. package/src/session/snapcompact-inline.ts +545 -0
  1194. package/src/session/snapcompact-savings-journal.ts +113 -0
  1195. package/src/session/sql-session-storage.ts +374 -0
  1196. package/src/session/stream-guards.ts +417 -0
  1197. package/src/session/streaming-output.ts +1459 -0
  1198. package/src/session/todo-tracker.ts +380 -0
  1199. package/src/session/tool-choice-queue.ts +305 -0
  1200. package/src/session/ttsr-coordinator.ts +496 -0
  1201. package/src/session/turn-persistence.ts +142 -0
  1202. package/src/session/turn-recovery.ts +2005 -0
  1203. package/src/session/unexpected-stop-classifier.ts +141 -0
  1204. package/src/session/yield-queue.ts +288 -0
  1205. package/src/slash-commands/acp-builtins.ts +70 -0
  1206. package/src/slash-commands/available-commands.ts +105 -0
  1207. package/src/slash-commands/builtin-registry.ts +3149 -0
  1208. package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
  1209. package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
  1210. package/src/slash-commands/helpers/context-report.ts +66 -0
  1211. package/src/slash-commands/helpers/format.ts +46 -0
  1212. package/src/slash-commands/helpers/logout.ts +108 -0
  1213. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  1214. package/src/slash-commands/helpers/mcp.ts +533 -0
  1215. package/src/slash-commands/helpers/parse.ts +85 -0
  1216. package/src/slash-commands/helpers/reset-usage.ts +68 -0
  1217. package/src/slash-commands/helpers/security.ts +451 -0
  1218. package/src/slash-commands/helpers/session-pin.ts +44 -0
  1219. package/src/slash-commands/helpers/ssh.ts +196 -0
  1220. package/src/slash-commands/helpers/stats-dashboard.ts +86 -0
  1221. package/src/slash-commands/helpers/todo.ts +285 -0
  1222. package/src/slash-commands/helpers/usage-report.ts +198 -0
  1223. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  1224. package/src/slash-commands/types.ts +139 -0
  1225. package/src/ssh/config-writer.ts +183 -0
  1226. package/src/ssh/connection-manager.ts +667 -0
  1227. package/src/ssh/file-transfer.ts +214 -0
  1228. package/src/ssh/sshfs-mount.ts +163 -0
  1229. package/src/ssh/utils.ts +51 -0
  1230. package/src/startup-splash.ts +19 -0
  1231. package/src/stt/asr-client.ts +401 -0
  1232. package/src/stt/asr-protocol.ts +65 -0
  1233. package/src/stt/asr-worker.ts +603 -0
  1234. package/src/stt/downloader.ts +142 -0
  1235. package/src/stt/endpointer.ts +259 -0
  1236. package/src/stt/index.ts +6 -0
  1237. package/src/stt/models.ts +150 -0
  1238. package/src/stt/sherpa-runtime.ts +71 -0
  1239. package/src/stt/stt-controller.ts +320 -0
  1240. package/src/stt/submit-trigger.ts +74 -0
  1241. package/src/subprocess/worker-client.ts +463 -0
  1242. package/src/subprocess/worker-runtime.ts +494 -0
  1243. package/src/system-prompt.ts +921 -0
  1244. package/src/task/agents.ts +170 -0
  1245. package/src/task/commands.ts +132 -0
  1246. package/src/task/discovery.ts +145 -0
  1247. package/src/task/executor.ts +3431 -0
  1248. package/src/task/index.ts +1515 -0
  1249. package/src/task/isolation-ownership.ts +106 -0
  1250. package/src/task/isolation-runner.ts +444 -0
  1251. package/src/task/label.ts +40 -0
  1252. package/src/task/name-generator.ts +1577 -0
  1253. package/src/task/omp-command.ts +26 -0
  1254. package/src/task/output-manager.ts +115 -0
  1255. package/src/task/parallel.ts +221 -0
  1256. package/src/task/persisted-revive.ts +173 -0
  1257. package/src/task/prewalk.ts +6 -0
  1258. package/src/task/prompt-policy.ts +8 -0
  1259. package/src/task/provider-concurrency.ts +100 -0
  1260. package/src/task/read-only-policy.ts +27 -0
  1261. package/src/task/render.ts +1830 -0
  1262. package/src/task/renderer.ts +14 -0
  1263. package/src/task/repair-args.ts +118 -0
  1264. package/src/task/spawn-policy.ts +72 -0
  1265. package/src/task/structured-subagent.ts +676 -0
  1266. package/src/task/subprocess-tool-registry.ts +88 -0
  1267. package/src/task/types.ts +555 -0
  1268. package/src/task/worktree.ts +967 -0
  1269. package/src/task/yield-assembly.ts +198 -0
  1270. package/src/telemetry-export.ts +504 -0
  1271. package/src/thinking.ts +377 -0
  1272. package/src/tiny/device.ts +111 -0
  1273. package/src/tiny/dtype.ts +101 -0
  1274. package/src/tiny/message-preproc.ts +155 -0
  1275. package/src/tiny/models.ts +268 -0
  1276. package/src/tiny/text.ts +290 -0
  1277. package/src/tiny/title-client.ts +460 -0
  1278. package/src/tiny/title-protocol.ts +56 -0
  1279. package/src/tiny/worker.ts +354 -0
  1280. package/src/tools/acp-bridge.ts +125 -0
  1281. package/src/tools/approval.ts +245 -0
  1282. package/src/tools/ask.ts +1459 -0
  1283. package/src/tools/ast-edit.ts +718 -0
  1284. package/src/tools/ast-grep.ts +526 -0
  1285. package/src/tools/auto-generated-guard.ts +335 -0
  1286. package/src/tools/bash-interactive.ts +435 -0
  1287. package/src/tools/bash-interceptor.ts +148 -0
  1288. package/src/tools/bash-pty-selection.ts +14 -0
  1289. package/src/tools/bash-skill-urls.ts +350 -0
  1290. package/src/tools/bash.ts +1826 -0
  1291. package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
  1292. package/src/tools/browser/aria/aria-snapshot.ts +131 -0
  1293. package/src/tools/browser/attach.ts +219 -0
  1294. package/src/tools/browser/cmux/cmux-tab.ts +1531 -0
  1295. package/src/tools/browser/cmux/rpc.ts +200 -0
  1296. package/src/tools/browser/cmux/socket-client.ts +445 -0
  1297. package/src/tools/browser/launch.ts +939 -0
  1298. package/src/tools/browser/readable.ts +112 -0
  1299. package/src/tools/browser/registry.ts +447 -0
  1300. package/src/tools/browser/relay/bridge.ts +945 -0
  1301. package/src/tools/browser/relay/daemon.ts +117 -0
  1302. package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
  1303. package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
  1304. package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
  1305. package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
  1306. package/src/tools/browser/relay/kind.ts +41 -0
  1307. package/src/tools/browser/relay/protocol.ts +54 -0
  1308. package/src/tools/browser/relay/server.ts +141 -0
  1309. package/src/tools/browser/render.ts +229 -0
  1310. package/src/tools/browser/run-output.ts +76 -0
  1311. package/src/tools/browser/shared-daemon.ts +139 -0
  1312. package/src/tools/browser/tab-protocol.ts +123 -0
  1313. package/src/tools/browser/tab-supervisor.ts +1077 -0
  1314. package/src/tools/browser/tab-worker-entry.ts +29 -0
  1315. package/src/tools/browser/tab-worker.ts +1982 -0
  1316. package/src/tools/browser.ts +487 -0
  1317. package/src/tools/builtin-names.ts +67 -0
  1318. package/src/tools/checkpoint.ts +137 -0
  1319. package/src/tools/computer/exposure.ts +14 -0
  1320. package/src/tools/computer/protocol.ts +69 -0
  1321. package/src/tools/computer/supervisor.ts +409 -0
  1322. package/src/tools/computer/worker-entry.ts +39 -0
  1323. package/src/tools/computer/worker.ts +745 -0
  1324. package/src/tools/computer-renderer.ts +147 -0
  1325. package/src/tools/computer.ts +222 -0
  1326. package/src/tools/conflict-detect.ts +815 -0
  1327. package/src/tools/context.ts +49 -0
  1328. package/src/tools/debug.ts +1121 -0
  1329. package/src/tools/default-renderer.ts +154 -0
  1330. package/src/tools/essential-tools.ts +46 -0
  1331. package/src/tools/eval-backends.ts +34 -0
  1332. package/src/tools/eval-format/index.ts +24 -0
  1333. package/src/tools/eval-format/javascript.ts +952 -0
  1334. package/src/tools/eval-format/julia.ts +446 -0
  1335. package/src/tools/eval-format/python.ts +544 -0
  1336. package/src/tools/eval-format/ruby.ts +380 -0
  1337. package/src/tools/eval-render.ts +784 -0
  1338. package/src/tools/eval.ts +774 -0
  1339. package/src/tools/fetch.ts +1889 -0
  1340. package/src/tools/file-recorder.ts +35 -0
  1341. package/src/tools/fs-cache-invalidation.ts +28 -0
  1342. package/src/tools/gh-cache-invalidation.ts +175 -0
  1343. package/src/tools/gh-format.ts +12 -0
  1344. package/src/tools/gh-renderer.ts +484 -0
  1345. package/src/tools/gh.ts +3958 -0
  1346. package/src/tools/github-cache.ts +663 -0
  1347. package/src/tools/glob.ts +691 -0
  1348. package/src/tools/grep.ts +1920 -0
  1349. package/src/tools/grouped-file-output.ts +210 -0
  1350. package/src/tools/hub/index.ts +579 -0
  1351. package/src/tools/hub/jobs.ts +714 -0
  1352. package/src/tools/hub/launch.ts +690 -0
  1353. package/src/tools/hub/messaging.ts +735 -0
  1354. package/src/tools/hub/types.ts +117 -0
  1355. package/src/tools/image-gen.ts +1689 -0
  1356. package/src/tools/image-providers.ts +50 -0
  1357. package/src/tools/index.ts +736 -0
  1358. package/src/tools/inspect-image-renderer.ts +133 -0
  1359. package/src/tools/inspect-image.ts +316 -0
  1360. package/src/tools/json-tree.ts +260 -0
  1361. package/src/tools/jtd-to-json-schema.ts +219 -0
  1362. package/src/tools/jtd-to-typescript.ts +136 -0
  1363. package/src/tools/jtd-utils.ts +102 -0
  1364. package/src/tools/learn.ts +141 -0
  1365. package/src/tools/list-limit.ts +40 -0
  1366. package/src/tools/manage-skill.ts +102 -0
  1367. package/src/tools/match-line-format.ts +20 -0
  1368. package/src/tools/memory-edit.ts +61 -0
  1369. package/src/tools/memory-recall.ts +102 -0
  1370. package/src/tools/memory-reflect.ts +88 -0
  1371. package/src/tools/memory-render.ts +211 -0
  1372. package/src/tools/memory-retain.ts +89 -0
  1373. package/src/tools/output-meta.ts +860 -0
  1374. package/src/tools/output-schema-validator.ts +307 -0
  1375. package/src/tools/path-utils.ts +1489 -0
  1376. package/src/tools/plan-mode-guard.ts +155 -0
  1377. package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
  1378. package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
  1379. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
  1380. package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
  1381. package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
  1382. package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
  1383. package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
  1384. package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
  1385. package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
  1386. package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
  1387. package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
  1388. package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
  1389. package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
  1390. package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
  1391. package/src/tools/read.ts +3762 -0
  1392. package/src/tools/render-utils.ts +926 -0
  1393. package/src/tools/renderers.ts +133 -0
  1394. package/src/tools/report-tool-issue.ts +568 -0
  1395. package/src/tools/resolve.ts +423 -0
  1396. package/src/tools/review.ts +103 -0
  1397. package/src/tools/run-scope.ts +417 -0
  1398. package/src/tools/security-scan.ts +287 -0
  1399. package/src/tools/shell-tokenize.ts +213 -0
  1400. package/src/tools/sqlite-reader.ts +884 -0
  1401. package/src/tools/terminal-output.ts +141 -0
  1402. package/src/tools/todo.ts +1226 -0
  1403. package/src/tools/tool-errors.ts +62 -0
  1404. package/src/tools/tool-result.ts +102 -0
  1405. package/src/tools/tool-timeouts.ts +38 -0
  1406. package/src/tools/tts.ts +266 -0
  1407. package/src/tools/vibe.ts +608 -0
  1408. package/src/tools/write.ts +1646 -0
  1409. package/src/tools/xdev.ts +560 -0
  1410. package/src/tools/yield.ts +486 -0
  1411. package/src/tts/downloader.ts +64 -0
  1412. package/src/tts/index.ts +10 -0
  1413. package/src/tts/models.ts +137 -0
  1414. package/src/tts/runtime.ts +21 -0
  1415. package/src/tts/speakable.ts +392 -0
  1416. package/src/tts/speech-enhancer.ts +206 -0
  1417. package/src/tts/streaming-player.ts +120 -0
  1418. package/src/tts/tts-client.ts +475 -0
  1419. package/src/tts/tts-protocol.ts +69 -0
  1420. package/src/tts/tts-worker.ts +434 -0
  1421. package/src/tts/vocalizer.ts +419 -0
  1422. package/src/tts/wav.ts +58 -0
  1423. package/src/tui/code-cell.ts +268 -0
  1424. package/src/tui/file-list.ts +55 -0
  1425. package/src/tui/hyperlink.ts +178 -0
  1426. package/src/tui/index.ts +13 -0
  1427. package/src/tui/output-block.ts +268 -0
  1428. package/src/tui/status-line.ts +54 -0
  1429. package/src/tui/tree-list.ts +172 -0
  1430. package/src/tui/types.ts +15 -0
  1431. package/src/tui/utils.ts +103 -0
  1432. package/src/tui/width-aware-text.ts +58 -0
  1433. package/src/utils/active-repo-context.ts +143 -0
  1434. package/src/utils/block-context.ts +312 -0
  1435. package/src/utils/changelog.ts +373 -0
  1436. package/src/utils/clipboard.ts +361 -0
  1437. package/src/utils/command-args.ts +74 -0
  1438. package/src/utils/commit-message-generator.ts +148 -0
  1439. package/src/utils/cpuprofile.ts +235 -0
  1440. package/src/utils/edit-mode.ts +61 -0
  1441. package/src/utils/enhanced-paste.ts +230 -0
  1442. package/src/utils/event-bus.ts +33 -0
  1443. package/src/utils/external-editor.ts +80 -0
  1444. package/src/utils/fetch-timeout.ts +10 -0
  1445. package/src/utils/file-display-mode.ts +44 -0
  1446. package/src/utils/file-mentions.ts +293 -0
  1447. package/src/utils/git.ts +2472 -0
  1448. package/src/utils/image-loading.ts +236 -0
  1449. package/src/utils/image-resize.ts +420 -0
  1450. package/src/utils/image-vision-fallback.ts +196 -0
  1451. package/src/utils/inspect-image-mode.ts +39 -0
  1452. package/src/utils/ipc.ts +38 -0
  1453. package/src/utils/jj.ts +422 -0
  1454. package/src/utils/lang-from-path.ts +251 -0
  1455. package/src/utils/late-cleanup.ts +17 -0
  1456. package/src/utils/local-date.ts +7 -0
  1457. package/src/utils/mac-file-urls.applescript +37 -0
  1458. package/src/utils/markit-cache.ts +166 -0
  1459. package/src/utils/markit.ts +223 -0
  1460. package/src/utils/mupdf-wasm-embed.ts +12 -0
  1461. package/src/utils/open.ts +126 -0
  1462. package/src/utils/profile-tree.ts +111 -0
  1463. package/src/utils/prompt-path.ts +3 -0
  1464. package/src/utils/qrcode.ts +535 -0
  1465. package/src/utils/sample-profile.ts +437 -0
  1466. package/src/utils/session-color.ts +142 -0
  1467. package/src/utils/shell-snapshot-fn-env.sh +63 -0
  1468. package/src/utils/shell-snapshot.ts +326 -0
  1469. package/src/utils/sixel.ts +69 -0
  1470. package/src/utils/thinking-display.ts +163 -0
  1471. package/src/utils/title-generator.ts +597 -0
  1472. package/src/utils/token-rate.ts +72 -0
  1473. package/src/utils/tool-choice.ts +50 -0
  1474. package/src/utils/tools-manager.ts +411 -0
  1475. package/src/utils/turndown.ts +82 -0
  1476. package/src/utils/zip.ts +1106 -0
  1477. package/src/vibe/runtime.ts +1684 -0
  1478. package/src/vibe/state.ts +4 -0
  1479. package/src/web/kagi.ts +305 -0
  1480. package/src/web/parallel.ts +354 -0
  1481. package/src/web/scrapers/artifacthub.ts +207 -0
  1482. package/src/web/scrapers/arxiv.ts +83 -0
  1483. package/src/web/scrapers/aur.ts +162 -0
  1484. package/src/web/scrapers/biorxiv.ts +133 -0
  1485. package/src/web/scrapers/bluesky.ts +262 -0
  1486. package/src/web/scrapers/brew.ts +172 -0
  1487. package/src/web/scrapers/cheatsh.ts +68 -0
  1488. package/src/web/scrapers/chocolatey.ts +196 -0
  1489. package/src/web/scrapers/choosealicense.ts +95 -0
  1490. package/src/web/scrapers/cisa-kev.ts +87 -0
  1491. package/src/web/scrapers/clojars.ts +154 -0
  1492. package/src/web/scrapers/coingecko.ts +177 -0
  1493. package/src/web/scrapers/crates-io.ts +97 -0
  1494. package/src/web/scrapers/crossref.ts +136 -0
  1495. package/src/web/scrapers/devto.ts +147 -0
  1496. package/src/web/scrapers/discogs.ts +306 -0
  1497. package/src/web/scrapers/discourse.ts +197 -0
  1498. package/src/web/scrapers/dockerhub.ts +138 -0
  1499. package/src/web/scrapers/docs-rs.ts +663 -0
  1500. package/src/web/scrapers/fdroid.ts +134 -0
  1501. package/src/web/scrapers/firefox-addons.ts +191 -0
  1502. package/src/web/scrapers/flathub.ts +223 -0
  1503. package/src/web/scrapers/github-gist.ts +58 -0
  1504. package/src/web/scrapers/github.ts +800 -0
  1505. package/src/web/scrapers/gitlab.ts +401 -0
  1506. package/src/web/scrapers/go-pkg.ts +266 -0
  1507. package/src/web/scrapers/hackage.ts +129 -0
  1508. package/src/web/scrapers/hackernews.ts +189 -0
  1509. package/src/web/scrapers/hex.ts +105 -0
  1510. package/src/web/scrapers/huggingface.ts +321 -0
  1511. package/src/web/scrapers/iacr.ts +89 -0
  1512. package/src/web/scrapers/index.ts +252 -0
  1513. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1514. package/src/web/scrapers/lemmy.ts +203 -0
  1515. package/src/web/scrapers/lobsters.ts +175 -0
  1516. package/src/web/scrapers/mastodon.ts +292 -0
  1517. package/src/web/scrapers/maven.ts +138 -0
  1518. package/src/web/scrapers/mdn.ts +173 -0
  1519. package/src/web/scrapers/metacpan.ts +222 -0
  1520. package/src/web/scrapers/musicbrainz.ts +250 -0
  1521. package/src/web/scrapers/npm.ts +98 -0
  1522. package/src/web/scrapers/nuget.ts +183 -0
  1523. package/src/web/scrapers/nvd.ts +222 -0
  1524. package/src/web/scrapers/ollama.ts +239 -0
  1525. package/src/web/scrapers/open-vsx.ts +106 -0
  1526. package/src/web/scrapers/opencorporates.ts +292 -0
  1527. package/src/web/scrapers/openlibrary.ts +336 -0
  1528. package/src/web/scrapers/orcid.ts +286 -0
  1529. package/src/web/scrapers/osv.ts +176 -0
  1530. package/src/web/scrapers/packagist.ts +160 -0
  1531. package/src/web/scrapers/pub-dev.ts +143 -0
  1532. package/src/web/scrapers/pubmed.ts +211 -0
  1533. package/src/web/scrapers/pypi.ts +112 -0
  1534. package/src/web/scrapers/rawg.ts +110 -0
  1535. package/src/web/scrapers/readthedocs.ts +121 -0
  1536. package/src/web/scrapers/reddit.ts +95 -0
  1537. package/src/web/scrapers/repology.ts +251 -0
  1538. package/src/web/scrapers/rfc.ts +201 -0
  1539. package/src/web/scrapers/rubygems.ts +103 -0
  1540. package/src/web/scrapers/searchcode.ts +189 -0
  1541. package/src/web/scrapers/sec-edgar.ts +261 -0
  1542. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1543. package/src/web/scrapers/snapcraft.ts +187 -0
  1544. package/src/web/scrapers/sourcegraph.ts +336 -0
  1545. package/src/web/scrapers/spdx.ts +108 -0
  1546. package/src/web/scrapers/spotify.ts +198 -0
  1547. package/src/web/scrapers/stackoverflow.ts +120 -0
  1548. package/src/web/scrapers/terraform.ts +277 -0
  1549. package/src/web/scrapers/tldr.ts +47 -0
  1550. package/src/web/scrapers/twitter.ts +94 -0
  1551. package/src/web/scrapers/types.ts +354 -0
  1552. package/src/web/scrapers/utils.ts +109 -0
  1553. package/src/web/scrapers/vimeo.ts +133 -0
  1554. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1555. package/src/web/scrapers/w3c.ts +156 -0
  1556. package/src/web/scrapers/wikidata.ts +344 -0
  1557. package/src/web/scrapers/wikipedia.ts +84 -0
  1558. package/src/web/scrapers/youtube.ts +325 -0
  1559. package/src/web/search/index.ts +393 -0
  1560. package/src/web/search/provider.ts +272 -0
  1561. package/src/web/search/providers/anthropic.ts +406 -0
  1562. package/src/web/search/providers/base.ts +112 -0
  1563. package/src/web/search/providers/brave.ts +181 -0
  1564. package/src/web/search/providers/browser-headers.ts +82 -0
  1565. package/src/web/search/providers/browser-page.ts +125 -0
  1566. package/src/web/search/providers/codex.ts +788 -0
  1567. package/src/web/search/providers/duckduckgo.ts +382 -0
  1568. package/src/web/search/providers/ecosia.ts +183 -0
  1569. package/src/web/search/providers/exa.ts +513 -0
  1570. package/src/web/search/providers/firecrawl.ts +213 -0
  1571. package/src/web/search/providers/gemini.ts +634 -0
  1572. package/src/web/search/providers/google.ts +195 -0
  1573. package/src/web/search/providers/jina.ts +144 -0
  1574. package/src/web/search/providers/kagi.ts +98 -0
  1575. package/src/web/search/providers/kimi.ts +221 -0
  1576. package/src/web/search/providers/mojeek.ts +220 -0
  1577. package/src/web/search/providers/parallel.ts +186 -0
  1578. package/src/web/search/providers/perplexity-auth.ts +142 -0
  1579. package/src/web/search/providers/perplexity.ts +997 -0
  1580. package/src/web/search/providers/public.ts +199 -0
  1581. package/src/web/search/providers/searxng.ts +467 -0
  1582. package/src/web/search/providers/startpage.ts +225 -0
  1583. package/src/web/search/providers/synthetic.ts +126 -0
  1584. package/src/web/search/providers/tavily.ts +244 -0
  1585. package/src/web/search/providers/tinyfish.ts +166 -0
  1586. package/src/web/search/providers/utils.ts +130 -0
  1587. package/src/web/search/providers/xai.ts +372 -0
  1588. package/src/web/search/providers/zai.ts +452 -0
  1589. package/src/web/search/query.ts +850 -0
  1590. package/src/web/search/render.ts +262 -0
  1591. package/src/web/search/types.ts +518 -0
  1592. package/src/web/search/utils.ts +17 -0
  1593. package/src/workspace-tree.ts +326 -0
@@ -0,0 +1,2595 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
4
+ import type { AssistantMessage, Model } from "@oh-my-pi/pi-ai";
5
+ import { getBlobsDir, isEnoent, logger, type postmortem, VERSION } from "@oh-my-pi/pi-utils";
6
+ import {
7
+ type Agent,
8
+ type AgentSideConnection,
9
+ type AuthenticateRequest,
10
+ type AuthenticateResponse,
11
+ type AuthMethod,
12
+ type AvailableCommand,
13
+ type ClientCapabilities,
14
+ type CloseSessionRequest,
15
+ type CloseSessionResponse,
16
+ type CreateElicitationResponse,
17
+ type ElicitationContentValue,
18
+ type ElicitationPropertySchema,
19
+ type ForkSessionRequest,
20
+ type ForkSessionResponse,
21
+ type InitializeRequest,
22
+ type InitializeResponse,
23
+ type ListSessionsRequest,
24
+ type ListSessionsResponse,
25
+ type LoadSessionRequest,
26
+ type LoadSessionResponse,
27
+ type McpServer,
28
+ type NewSessionRequest,
29
+ type NewSessionResponse,
30
+ PROTOCOL_VERSION,
31
+ type PromptRequest,
32
+ type PromptResponse,
33
+ type ResumeSessionRequest,
34
+ type ResumeSessionResponse,
35
+ type SessionConfigOption,
36
+ type SessionInfo,
37
+ type SessionModeState,
38
+ type SessionNotification,
39
+ type SessionUpdate,
40
+ type SetSessionConfigOptionRequest,
41
+ type SetSessionConfigOptionResponse,
42
+ type SetSessionModeRequest,
43
+ type SetSessionModeResponse,
44
+ type Usage,
45
+ } from "@oh-my-pi/pi-utils/acp";
46
+ import { disableProvider, enableProvider, reset as resetCapabilities } from "../../capability";
47
+ import { Settings } from "../../config/settings";
48
+ import { clearPluginRootsAndCaches, resolveActiveProjectRegistryPath } from "../../discovery/helpers";
49
+ import {
50
+ type ExtensionUIContext,
51
+ type ExtensionUIDialogOptions,
52
+ getExtensionUISelectOptionLabel,
53
+ } from "../../extensibility/extensions";
54
+ import { runExtensionCompact } from "../../extensibility/extensions/compact-handler";
55
+ import { getSessionSlashCommands } from "../../extensibility/extensions/get-commands-handler";
56
+ import { buildSkillPromptMessage, parseSkillInvocation } from "../../extensibility/skills";
57
+ import { loadSlashCommands } from "../../extensibility/slash-commands";
58
+ import { resolveLocalUrlToPath } from "../../internal-urls";
59
+ import { MCPManager } from "../../mcp/manager";
60
+ import type { MCPServerConfig } from "../../mcp/types";
61
+ import { loadAllExtensions } from "../../modes/components/extensions/state-manager";
62
+ import { theme } from "../../modes/theme/theme";
63
+ import { normalizePlanTitle, type PlanApprovalDetails, resolveApprovedPlan } from "../../plan-mode/approved-plan";
64
+ import type { AgentSession, AgentSessionEvent } from "../../session/agent-session";
65
+ import { BlobStore, resolveImageDataSync } from "../../session/blob-store";
66
+ import { isSilentAbort, SKILL_PROMPT_MESSAGE_TYPE, USER_INTERRUPT_LABEL } from "../../session/messages";
67
+ import type { UsageStatistics } from "../../session/session-entries";
68
+ import type { SessionInfo as StoredSessionInfo } from "../../session/session-listing";
69
+ import { SessionManager } from "../../session/session-manager";
70
+ import { executeAcpBuiltinSlashCommand } from "../../slash-commands/acp-builtins";
71
+ import { buildAvailableSlashCommands, toAcpAvailableCommands } from "../../slash-commands/available-commands";
72
+ import { DEFAULT_STT_MODEL_KEY, STT_MODEL_OPTIONS } from "../../stt/models";
73
+ import { AUTO_THINKING, parseConfiguredThinkingLevel } from "../../thinking";
74
+ import { normalizeLocalScheme } from "../../tools/path-utils";
75
+ import { ToolError } from "../../tools/tool-errors";
76
+ import {
77
+ DEFAULT_TTS_LOCAL_MODEL_KEY,
78
+ DEFAULT_TTS_VOICE,
79
+ TTS_LOCAL_MODELS,
80
+ TTS_LOCAL_VOICE_OPTIONS,
81
+ } from "../../tts/models";
82
+ import { canonicalizeMessage } from "../../utils/thinking-display";
83
+ import { createAcpClientBridge } from "./acp-client-bridge";
84
+ import {
85
+ extractAssistantMessageText,
86
+ mapAgentSessionEventToAcpSessionUpdates,
87
+ normalizeReplayToolArguments,
88
+ } from "./acp-event-mapper";
89
+ import { ACP_TERMINAL_AUTH_FLAG } from "./terminal-auth";
90
+
91
+ const ACP_DEFAULT_MODE_ID = "default";
92
+ const ACP_PLAN_MODE_ID = "plan";
93
+ const DEFAULT_PLAN_FILE_URL = "local://PLAN.md";
94
+ const APPROVE_OPTION = "Approve and execute";
95
+ const REFINE_OPTION = "Refine plan";
96
+ const MODE_CONFIG_ID = "mode";
97
+ const MODEL_CONFIG_ID = "model";
98
+ const THINKING_CONFIG_ID = "thinking";
99
+ const THINKING_OFF = "off";
100
+ const SESSION_PAGE_SIZE = 50;
101
+ const SPEECH_MODELS_LIST_METHOD = "speech.models.list";
102
+ /**
103
+ * Delay between `session/new` (or `session/load` / `session/resume` /
104
+ * `unstable_session/fork`) returning and the agent firing the first
105
+ * notifications against the new session id. Mitigates Zed's
106
+ * `Received session notification for unknown session` race — see
107
+ * `#scheduleBootstrapUpdates`. Exported so the ACP test harness can
108
+ * wait past this guard without hard-coding the literal.
109
+ */
110
+ export const ACP_BOOTSTRAP_RACE_GUARD_MS = 50;
111
+ const ACP_CANCEL_CLEANUP_TIMEOUT_MS = 5_000;
112
+ const ACP_ASYNC_DELIVERY_DRAIN_TIMEOUT_MS = 250;
113
+ const ACP_ASYNC_DELIVERY_DRAIN_MAX_PASSES = 3;
114
+
115
+ type AgentImageContent = {
116
+ type: "image";
117
+ data: string;
118
+ mimeType: string;
119
+ };
120
+
121
+ type PromptQueueState = {
122
+ promise: Promise<void>;
123
+ release: (() => void) | undefined;
124
+ };
125
+ type PromptLifecycleError = Error & { readonly code: "ACP_SESSION_CLOSED" };
126
+
127
+ type PromptTurnState = {
128
+ cancelRequested: boolean;
129
+ settled: boolean;
130
+ /**
131
+ * Delivery of streamed assistant `error` chunks this turn (the mapper
132
+ * surfaces them as `agent_message_chunk`s). Resolves `true` once at least
133
+ * one error chunk reached the client — the `agent_end` error fallback in
134
+ * {@link AcpAgent##flushUnreportedTurnError} awaits it and stays silent on
135
+ * success, so a fallback racing an in-flight delivery can neither duplicate
136
+ * the error nor drop it when delivery fails.
137
+ */
138
+ errorTextDelivery: Promise<boolean> | undefined;
139
+ /**
140
+ * `abort()` is in-flight (or its bounded-timeout race). `undefined` while the turn is
141
+ * running normally and after cleanup completes. The turn occupies `record.promptTurn`
142
+ * for as long as either `!settled` or `cleanup` is set — that combined window is the
143
+ * "turn in flight" predicate (`isPromptTurnInFlight`) every consumer gates on.
144
+ */
145
+ cleanup: Promise<void> | undefined;
146
+ usageBaseline: UsageStatistics;
147
+ unsubscribe: (() => void) | undefined;
148
+ resolve: (value: PromptResponse) => void;
149
+ reject: (reason?: unknown) => void;
150
+ promise: Promise<PromptResponse>;
151
+ };
152
+
153
+ /**
154
+ * A turn is "in flight" from the moment `prompt()` reserves the slot until `settled` is
155
+ * true AND any cancel cleanup has completed. Fork/queue/event gating all depend on this
156
+ * combined window — a settled-but-still-aborting turn is not safe to fork from, queue
157
+ * onto, or forward late events for.
158
+ */
159
+ function isPromptTurnInFlight(turn: PromptTurnState | undefined): turn is PromptTurnState {
160
+ return turn !== undefined && (!turn.settled || turn.cleanup !== undefined);
161
+ }
162
+
163
+ type ManagedSessionRecord = {
164
+ session: AgentSession;
165
+ mcpManager: MCPManager | undefined;
166
+ // Ordered queue of MCP tool refreshes for this record. Rebuilt per
167
+ // `#configureMcpServers` call; drained on reconfigure so a stale in-flight
168
+ // refresh can never land after a newer configuration's tools.
169
+ mcpRefreshChain: Promise<void> | undefined;
170
+ promptTurn: PromptTurnState | undefined;
171
+ promptQueue: PromptQueueState;
172
+ liveMessageId: string | undefined;
173
+ liveMessageProgress: { textEmitted: boolean; thoughtEmitted: boolean } | undefined;
174
+ toolArgsById: Map<string, unknown>;
175
+ extensionsConfigured: boolean;
176
+ // Installed inside `#scheduleBootstrapUpdates` (post-race-guard); released
177
+ // in `#disposeSessionRecord`. Lives independent of any prompt turn.
178
+ lifetimeUnsubscribe: (() => void) | undefined;
179
+ closedError: PromptLifecycleError | undefined;
180
+ promptEventHandlers: Set<Promise<void>>;
181
+ extensionUserMessageTasks: Set<Promise<void>>;
182
+ };
183
+
184
+ type ReplayableMessage = {
185
+ role: string;
186
+ content?: unknown;
187
+ errorMessage?: string;
188
+ toolCallId?: string;
189
+ toolName?: string;
190
+ details?: unknown;
191
+ isError?: boolean;
192
+ };
193
+
194
+ type ReplayableToolItem = {
195
+ type?: unknown;
196
+ id?: unknown;
197
+ name?: unknown;
198
+ arguments?: unknown;
199
+ input?: unknown;
200
+ };
201
+
202
+ type MCPConfigMap = {
203
+ [name: string]: MCPServerConfig;
204
+ };
205
+
206
+ type MCPSource = {
207
+ provider: string;
208
+ providerName: string;
209
+ path: string;
210
+ level: "project";
211
+ };
212
+
213
+ type MCPSourceMap = {
214
+ [name: string]: MCPSource;
215
+ };
216
+
217
+ type CreateAcpSession = (cwd: string) => Promise<AgentSession>;
218
+
219
+ type AcpSpeechOption = {
220
+ value: string;
221
+ label: string;
222
+ description?: string;
223
+ };
224
+
225
+ type AcpSpeechVoiceOption = {
226
+ value: string;
227
+ label: string;
228
+ };
229
+
230
+ type AcpSpeechTtsModelOption = AcpSpeechOption & {
231
+ voices: AcpSpeechVoiceOption[];
232
+ };
233
+
234
+ function buildAcpSpeechModelsCatalog(): Record<string, unknown> {
235
+ const voices = TTS_LOCAL_VOICE_OPTIONS.map(({ value, label }) => ({ value, label }));
236
+ return {
237
+ settings: {
238
+ speechToTextModel: "stt.modelName",
239
+ textToSpeechModel: "tts.localModel",
240
+ textToSpeechVoice: "tts.localVoice",
241
+ speechVoice: "speech.voice",
242
+ },
243
+ defaults: {
244
+ speechToTextModel: DEFAULT_STT_MODEL_KEY,
245
+ textToSpeechModel: DEFAULT_TTS_LOCAL_MODEL_KEY,
246
+ voice: DEFAULT_TTS_VOICE,
247
+ },
248
+ speechToText: {
249
+ setting: "stt.modelName",
250
+ defaultValue: DEFAULT_STT_MODEL_KEY,
251
+ models: STT_MODEL_OPTIONS.map(({ value, label, description }) => ({ value, label, description })),
252
+ },
253
+ textToSpeech: {
254
+ modelSetting: "tts.localModel",
255
+ voiceSetting: "tts.localVoice",
256
+ speechVoiceSetting: "speech.voice",
257
+ defaultModel: DEFAULT_TTS_LOCAL_MODEL_KEY,
258
+ defaultVoice: DEFAULT_TTS_VOICE,
259
+ models: TTS_LOCAL_MODELS.map(
260
+ ({ key, label, description, voices: modelVoices }): AcpSpeechTtsModelOption => ({
261
+ value: key,
262
+ label,
263
+ description,
264
+ voices: modelVoices.map(({ id, label: voiceLabel }) => ({ value: id, label: voiceLabel })),
265
+ }),
266
+ ),
267
+ voices,
268
+ },
269
+ };
270
+ }
271
+
272
+ /**
273
+ * Bridge a single ExtensionUIContext call to the ACP `unstable_createElicitation`
274
+ * surface. Skills/extensions ask for one value at a time (a chosen option, a
275
+ * confirmation, a piece of text), so every elicitation here uses a one-property
276
+ * `value` schema; the caller narrows the resulting `ElicitationContentValue`
277
+ * back to its concrete primitive type.
278
+ *
279
+ * `dialogOptions.signal` short-circuits the elicitation if it is already
280
+ * aborted and races the in-flight request against the abort event. The SDK
281
+ * exposes no `cancel_elicitation` surface for form-mode elicitations
282
+ * (`unstable_completeElicitation` is URL-mode only), so the ACP request itself
283
+ * keeps running on the client side until the user dismisses it — but
284
+ * resolving the local promise unblocks the caller (matches the RPC mode
285
+ * pattern in `requestRpcEditor`). The abort listener is removed once the
286
+ * elicitation settles so that callers which reuse the same signal across many
287
+ * elicitations (e.g. `ask` multi-select loops) don't accumulate listeners and
288
+ * trip Node's `MaxListeners` warning.
289
+ *
290
+ * `dialogOptions.timeout` mirrors `RpcExtensionUIContext.#createDialogPromise`:
291
+ * when the timer fires before the client responds, `onTimeout` is invoked and
292
+ * the caller's promise resolves to the stub fallback. Late SDK responses that
293
+ * arrive after abort/timeout — both rejections and successful `accept`s —
294
+ * are dropped silently (no `logger.warn`) to keep operator logs clean.
295
+ */
296
+ async function elicitFromAcpClient(
297
+ connection: AgentSideConnection,
298
+ sessionId: string,
299
+ method: "select" | "confirm" | "input" | "editor",
300
+ message: string,
301
+ property: ElicitationPropertySchema,
302
+ dialogOptions: ExtensionUIDialogOptions | undefined,
303
+ ): Promise<ElicitationContentValue | undefined> {
304
+ const signal = dialogOptions?.signal;
305
+ if (signal?.aborted) {
306
+ return undefined;
307
+ }
308
+ const { promise, resolve } = Promise.withResolvers<CreateElicitationResponse | undefined>();
309
+ let settled = false;
310
+ let timeoutId: NodeJS.Timeout | undefined;
311
+ const finish = (value: CreateElicitationResponse | undefined) => {
312
+ if (settled) return;
313
+ settled = true;
314
+ if (timeoutId !== undefined) clearTimeout(timeoutId);
315
+ signal?.removeEventListener("abort", onAbort);
316
+ resolve(value);
317
+ };
318
+ const onAbort = () => finish(undefined);
319
+ signal?.addEventListener("abort", onAbort, { once: true });
320
+ if (dialogOptions?.timeout !== undefined) {
321
+ timeoutId = setTimeout(() => {
322
+ if (settled) return;
323
+ try {
324
+ dialogOptions.onTimeout?.();
325
+ } catch (error) {
326
+ // A throwing `onTimeout` must not leave the elicitation promise
327
+ // pending — settle it via `finish` below regardless.
328
+ logger.warn("ACP elicitation onTimeout threw", { sessionId, method, error });
329
+ }
330
+ finish(undefined);
331
+ }, dialogOptions.timeout);
332
+ // A long pending timeout alone shouldn't keep the event loop alive when
333
+ // the rest of the agent has shut down — matches `job-manager.ts` /
334
+ // `executor.ts` timer hygiene. Connection + session lifetimes keep the
335
+ // loop alive on the happy path.
336
+ timeoutId.unref();
337
+ }
338
+ connection
339
+ .unstable_createElicitation({
340
+ mode: "form",
341
+ sessionId,
342
+ message,
343
+ requestedSchema: {
344
+ type: "object",
345
+ properties: { value: property },
346
+ required: ["value"],
347
+ },
348
+ })
349
+ .then(finish, error => {
350
+ // Caller may already have moved on via abort/timeout; suppress noise.
351
+ if (settled) return;
352
+ logger.warn("ACP elicitation failed", { sessionId, method, error });
353
+ finish(undefined);
354
+ });
355
+ const response = await promise;
356
+ if (!isAcceptedElicitation(response) || !response.content) {
357
+ return undefined;
358
+ }
359
+ return response.content.value;
360
+ }
361
+
362
+ /** Narrows a `CreateElicitationResponse` to the accepted-with-content branch; the SDK's `action: string` catch-all arm otherwise defeats literal narrowing on `action !== "accept"`. */
363
+ function isAcceptedElicitation(
364
+ response: CreateElicitationResponse | undefined,
365
+ ): response is Extract<CreateElicitationResponse, { action: "accept" }> {
366
+ return response?.action === "accept";
367
+ }
368
+
369
+ /**
370
+ * Build an {@link ExtensionUIContext} that translates skill/extension UI
371
+ * requests into ACP elicitations against `connection` for the session
372
+ * returned by `getSessionId()`. The id is read lazily at each elicitation
373
+ * because `AgentSession.sessionId` is a getter over `sessionManager` state
374
+ * that mutates when an extension command calls `ctx.newSession` /
375
+ * `ctx.switchSession` — snapshotting it once at factory time would route
376
+ * later elicitations to the pre-switch id. Live reads keep the bridge
377
+ * symmetric with every other `sessionUpdate` call in this file
378
+ * (`record.session.sessionId` is always evaluated at emit time).
379
+ *
380
+ * The non-elicitation surface (custom components, theming, terminal
381
+ * input) remains stubbed — ACP clients render those themselves or not
382
+ * at all. Capability gating respects the client's `initialize`
383
+ * advertisement.
384
+ */
385
+ export function createAcpExtensionUiContext(
386
+ connection: AgentSideConnection,
387
+ getSessionId: () => string,
388
+ clientCapabilities: ClientCapabilities | undefined,
389
+ ): ExtensionUIContext {
390
+ const supportsForm = clientCapabilities?.elicitation?.form != null;
391
+ return {
392
+ select: async (title, options, dialogOptions) => {
393
+ if (!supportsForm) return undefined;
394
+ const value = await elicitFromAcpClient(
395
+ connection,
396
+ getSessionId(),
397
+ "select",
398
+ title,
399
+ { type: "string", enum: options.map(getExtensionUISelectOptionLabel) },
400
+ dialogOptions,
401
+ );
402
+ return typeof value === "string" ? value : undefined;
403
+ },
404
+ confirm: async (title, message, dialogOptions) => {
405
+ if (!supportsForm) return false;
406
+ const value = await elicitFromAcpClient(
407
+ connection,
408
+ getSessionId(),
409
+ "confirm",
410
+ message.trim().length > 0 ? `${title}\n\n${message}` : title,
411
+ { type: "boolean" },
412
+ dialogOptions,
413
+ );
414
+ return typeof value === "boolean" ? value : false;
415
+ },
416
+ input: async (title, placeholder, dialogOptions) => {
417
+ if (!supportsForm) return undefined;
418
+ const value = await elicitFromAcpClient(
419
+ connection,
420
+ getSessionId(),
421
+ "input",
422
+ title,
423
+ // ACP's `StringPropertySchema` has no `placeholder` field, so we
424
+ // surface the placeholder text as `description` — the closest
425
+ // semantic field a client can render alongside the input.
426
+ // Empty / whitespace-only placeholders are treated as absent.
427
+ { type: "string", ...(placeholder?.trim() ? { description: placeholder } : {}) },
428
+ dialogOptions,
429
+ );
430
+ return typeof value === "string" ? value : undefined;
431
+ },
432
+ notify: (message, type) => {
433
+ logger.debug("ACP extension notification", { message, type });
434
+ },
435
+ onTerminalInput: () => () => {},
436
+ setStatus: () => {},
437
+ setWorkingMessage: () => {},
438
+ setWidget: () => {},
439
+ setFooter: () => {},
440
+ setHeader: () => {},
441
+ setTitle: () => {},
442
+ custom: async () => undefined as never,
443
+ pasteToEditor: () => {},
444
+ setEditorText: () => {},
445
+ getEditorText: () => "",
446
+ editor: async (title, prefill, dialogOptions) => {
447
+ if (!supportsForm) return undefined;
448
+ const value = await elicitFromAcpClient(
449
+ connection,
450
+ getSessionId(),
451
+ "editor",
452
+ title,
453
+ { type: "string", ...(prefill ? { default: prefill } : {}) },
454
+ dialogOptions,
455
+ );
456
+ return typeof value === "string" ? value : undefined;
457
+ },
458
+ addAutocompleteProvider: () => {},
459
+ setEditorComponent: () => {},
460
+ get theme() {
461
+ return theme;
462
+ },
463
+ getAllThemes: async () => [],
464
+ getTheme: async () => undefined,
465
+ setTheme: async () => ({ success: false, error: "Theme changes are unavailable in ACP mode" }),
466
+ getToolsExpanded: () => false,
467
+ setToolsExpanded: () => {},
468
+ };
469
+ }
470
+
471
+ export class AcpAgent implements Agent {
472
+ #connection: AgentSideConnection;
473
+ #initialSession: AgentSession | undefined;
474
+ #createSession: CreateAcpSession;
475
+ #sessions = new Map<string, ManagedSessionRecord>();
476
+ #disposePromise: Promise<void> | undefined;
477
+ #cleanupRegistered = false;
478
+ #clientCapabilities: ClientCapabilities | undefined;
479
+ #cancelCleanupTimeoutMs = ACP_CANCEL_CLEANUP_TIMEOUT_MS;
480
+ #blobs = new BlobStore(getBlobsDir());
481
+
482
+ constructor(connection: AgentSideConnection, createSession: CreateAcpSession, initialSession?: AgentSession) {
483
+ this.#connection = connection;
484
+ this.#initialSession = initialSession;
485
+ this.#createSession = createSession;
486
+ }
487
+
488
+ setCancelCleanupTimeoutForTesting(timeoutMs: number): void {
489
+ this.#cancelCleanupTimeoutMs = Math.max(1, timeoutMs);
490
+ }
491
+
492
+ async initialize(params: InitializeRequest): Promise<InitializeResponse> {
493
+ this.#registerConnectionCleanup();
494
+ this.#clientCapabilities = params.clientCapabilities;
495
+ const authMethods: AuthMethod[] = [
496
+ {
497
+ id: "agent",
498
+ name: "Use existing local credentials",
499
+ description: "Authenticate via the provider keys/OAuth state already configured under ~/.omp.",
500
+ },
501
+ ];
502
+ if (params.clientCapabilities?.auth?.terminal === true) {
503
+ authMethods.push({
504
+ type: "terminal",
505
+ id: "terminal",
506
+ name: "Set up Oh My Pi in terminal",
507
+ description: "Launch the omp TUI to add provider keys and select models.",
508
+ args: [ACP_TERMINAL_AUTH_FLAG],
509
+ });
510
+ }
511
+ return {
512
+ protocolVersion: PROTOCOL_VERSION,
513
+ agentInfo: {
514
+ name: "oh-my-pi",
515
+ title: "Oh My Pi",
516
+ version: VERSION,
517
+ },
518
+ authMethods,
519
+ agentCapabilities: {
520
+ loadSession: true,
521
+ mcpCapabilities: {
522
+ http: true,
523
+ sse: true,
524
+ },
525
+ promptCapabilities: {
526
+ embeddedContext: true,
527
+ image: true,
528
+ },
529
+ sessionCapabilities: {
530
+ list: {},
531
+ fork: {},
532
+ resume: {},
533
+ close: {},
534
+ },
535
+ },
536
+ };
537
+ }
538
+
539
+ async authenticate(params: AuthenticateRequest): Promise<AuthenticateResponse> {
540
+ // ACP spec: `methodId` must be one of the methods advertised by `initialize`.
541
+ // Reject anything else so malformed clients fail fast rather than appearing
542
+ // authenticated and surfacing a downstream model failure later.
543
+ const supportsTerminalAuth = this.#clientCapabilities?.auth?.terminal === true;
544
+ const validMethods = supportsTerminalAuth ? ["agent", "terminal"] : ["agent"];
545
+ if (!validMethods.includes(params.methodId)) {
546
+ throw new Error(`Unknown ACP auth method: ${params.methodId}`);
547
+ }
548
+ return {};
549
+ }
550
+
551
+ async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
552
+ this.#assertAbsoluteCwd(params.cwd);
553
+ const record = await this.#createNewSessionRecord(params.cwd, params.mcpServers);
554
+ const response: NewSessionResponse = {
555
+ sessionId: record.session.sessionId,
556
+ configOptions: this.#buildConfigOptions(record.session),
557
+ modes: this.#buildModeState(record.session),
558
+ };
559
+ this.#scheduleBootstrapUpdates(record.session.sessionId);
560
+ return response;
561
+ }
562
+
563
+ async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
564
+ this.#assertAbsoluteCwd(params.cwd);
565
+ const record = await this.#loadManagedSession(params.sessionId, params.cwd, params.mcpServers);
566
+ await this.#replaySessionHistory(record);
567
+ const response: LoadSessionResponse = {
568
+ configOptions: this.#buildConfigOptions(record.session),
569
+ modes: this.#buildModeState(record.session),
570
+ };
571
+ this.#scheduleBootstrapUpdates(record.session.sessionId);
572
+ return response;
573
+ }
574
+
575
+ async listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse> {
576
+ if (params.cwd) {
577
+ this.#assertAbsoluteCwd(params.cwd);
578
+ }
579
+ for (const record of this.#sessions.values()) {
580
+ await record.session.sessionManager.flush();
581
+ }
582
+ const sessions = await this.#listStoredSessions(params.cwd ?? undefined);
583
+ const offset = this.#parseCursor(params.cursor ?? undefined);
584
+ const paged = sessions.slice(offset, offset + SESSION_PAGE_SIZE);
585
+ const nextOffset = offset + paged.length;
586
+ return {
587
+ sessions: paged.map(session => this.#toSessionInfo(session)),
588
+ nextCursor: nextOffset < sessions.length ? String(nextOffset) : undefined,
589
+ };
590
+ }
591
+
592
+ async resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse> {
593
+ this.#assertAbsoluteCwd(params.cwd);
594
+ const record = await this.#resumeManagedSession(params.sessionId, params.cwd, params.mcpServers ?? []);
595
+ const response: ResumeSessionResponse = {
596
+ configOptions: this.#buildConfigOptions(record.session),
597
+ modes: this.#buildModeState(record.session),
598
+ };
599
+ this.#scheduleBootstrapUpdates(record.session.sessionId);
600
+ return response;
601
+ }
602
+
603
+ async unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse> {
604
+ this.#assertAbsoluteCwd(params.cwd);
605
+ const record = await this.#forkManagedSession(params);
606
+ const response: ForkSessionResponse = {
607
+ sessionId: record.session.sessionId,
608
+ configOptions: this.#buildConfigOptions(record.session),
609
+ modes: this.#buildModeState(record.session),
610
+ };
611
+ this.#scheduleBootstrapUpdates(record.session.sessionId);
612
+ return response;
613
+ }
614
+
615
+ async closeSession(params: CloseSessionRequest): Promise<CloseSessionResponse> {
616
+ const record = this.#sessions.get(params.sessionId);
617
+ if (!record) {
618
+ return {};
619
+ }
620
+ await this.#closeManagedSession(params.sessionId, record);
621
+ return {};
622
+ }
623
+
624
+ async setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse> {
625
+ const record = this.#getSessionRecord(params.sessionId);
626
+ this.#applyModeChange(record.session, params.modeId);
627
+ await this.#connection.sessionUpdate({
628
+ sessionId: record.session.sessionId,
629
+ update: this.#buildCurrentModeUpdate(record.session),
630
+ });
631
+ await this.#pushConfigOptionUpdate(record);
632
+ return {};
633
+ }
634
+
635
+ async setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse> {
636
+ const record = this.#getSessionRecord(params.sessionId);
637
+ if (typeof params.value === "boolean") {
638
+ throw new Error(`Unsupported boolean ACP config option: ${params.configId}`);
639
+ }
640
+
641
+ switch (params.configId) {
642
+ case MODE_CONFIG_ID:
643
+ this.#applyModeChange(record.session, params.value);
644
+ break;
645
+ case MODEL_CONFIG_ID:
646
+ await this.#setModelById(record.session, params.value);
647
+ break;
648
+ case THINKING_CONFIG_ID:
649
+ this.#setThinkingLevelById(record.session, params.value);
650
+ break;
651
+ default:
652
+ throw new Error(`Unknown ACP config option: ${params.configId}`);
653
+ }
654
+
655
+ // When mode is changed via the generic config-option API, mirror the
656
+ // `current_mode_update` notification that `setSessionMode` emits so
657
+ // ACP clients tracking session-mode state see a consistent transition.
658
+ if (params.configId === MODE_CONFIG_ID) {
659
+ await this.#connection.sessionUpdate({
660
+ sessionId: record.session.sessionId,
661
+ update: this.#buildCurrentModeUpdate(record.session),
662
+ });
663
+ }
664
+
665
+ // For `model`/`thinking`, `#setModelById`/`#setThinkingLevelById` change
666
+ // the session model/thinking level through AgentSession, which now emits
667
+ // a lifetime event (`model_changed`/`thinking_level_changed`) that
668
+ // `#handleLifetimeEvent` turns into a push once the subscription is
669
+ // installed. Only push here when that subscription is not yet
670
+ // installed, so pre-bootstrap callers still see the change without a
671
+ // post-bootstrap duplicate.
672
+ const handledBySubscription =
673
+ (params.configId === THINKING_CONFIG_ID || params.configId === MODEL_CONFIG_ID) &&
674
+ record.lifetimeUnsubscribe !== undefined;
675
+ if (!handledBySubscription) {
676
+ await this.#pushConfigOptionUpdate(record);
677
+ }
678
+ return { configOptions: this.#buildConfigOptions(record.session) };
679
+ }
680
+
681
+ async prompt(params: PromptRequest): Promise<PromptResponse> {
682
+ const record = this.#getSessionRecord(params.sessionId);
683
+ const activeTurn = record.promptTurn;
684
+ if (activeTurn && !activeTurn.settled && record.session.isStreaming) {
685
+ // New prompt arrived while the previous turn is still in-flight (e.g. the
686
+ // client sent a message immediately after pressing stop, before or without
687
+ // a preceding session/cancel notification). Implicitly cancel the running
688
+ // turn so the new prompt can queue behind the abort cleanup — identical to
689
+ // what cancel() does when called explicitly. #beginCancelCleanup is
690
+ // idempotent, so a concurrent session/cancel notification is harmless.
691
+ // Mirror cancel()'s timeout handling: if abort() hangs past the cleanup
692
+ // timeout, close the managed session instead of leaving it registered
693
+ // with a still-streaming AgentSession. The queued prompt below observes
694
+ // the same cleanup rejection and fails accordingly.
695
+ this.#beginCancelCleanup(record, activeTurn).catch(async (error: unknown) => {
696
+ logger.warn("ACP cancel cleanup timed out; closing session", {
697
+ sessionId: record.session.sessionId,
698
+ error,
699
+ });
700
+ await this.#closeManagedSession(params.sessionId, record);
701
+ });
702
+ }
703
+ return await this.#queuePrompt(record, async () => {
704
+ const previousTurn = record.promptTurn;
705
+ if (previousTurn) {
706
+ // Wait for any prompt that's still settling or whose cancel cleanup is
707
+ // still in flight. We deliberately swallow the prompt rejection (the
708
+ // owning caller already received it) but let cleanup rejections
709
+ // propagate — a timed-out cancel must fail this queued prompt instead
710
+ // of letting it run on a session that is about to be closed.
711
+ await previousTurn.promise.catch(() => undefined);
712
+ await previousTurn.cleanup;
713
+ }
714
+ this.#throwIfRecordClosed(record);
715
+
716
+ const converted = this.#convertPromptBlocks(params.prompt);
717
+ const pendingPrompt = Promise.withResolvers<PromptResponse>();
718
+ record.promptTurn = {
719
+ cancelRequested: false,
720
+ settled: false,
721
+ errorTextDelivery: undefined,
722
+ cleanup: undefined,
723
+ usageBaseline: this.#cloneUsageStatistics(record.session.sessionManager.getUsageStatistics()),
724
+ unsubscribe: undefined,
725
+ resolve: pendingPrompt.resolve,
726
+ reject: pendingPrompt.reject,
727
+ promise: pendingPrompt.promise,
728
+ };
729
+
730
+ record.promptTurn.unsubscribe = record.session.subscribe(event => {
731
+ this.#trackPromptEvent(record, event);
732
+ });
733
+
734
+ this.#runPromptOrCommand(record, converted.text, converted.images).catch((error: unknown) => {
735
+ this.#finishPrompt(record, undefined, error);
736
+ });
737
+
738
+ return await pendingPrompt.promise;
739
+ });
740
+ }
741
+
742
+ async #queuePrompt(record: ManagedSessionRecord, run: () => Promise<PromptResponse>): Promise<PromptResponse> {
743
+ const nextQueue = Promise.withResolvers<void>();
744
+ const releaseQueue = nextQueue.resolve;
745
+ const previousQueue = record.promptQueue;
746
+ record.promptQueue = {
747
+ promise: nextQueue.promise,
748
+ release: releaseQueue,
749
+ };
750
+ await previousQueue.promise;
751
+ this.#throwIfRecordClosed(record);
752
+ try {
753
+ return await run();
754
+ } finally {
755
+ releaseQueue();
756
+ if (record.promptQueue.release === releaseQueue) {
757
+ record.promptQueue.release = undefined;
758
+ }
759
+ }
760
+ }
761
+
762
+ #throwIfRecordClosed(record: ManagedSessionRecord): void {
763
+ if (record.closedError) {
764
+ throw record.closedError;
765
+ }
766
+ }
767
+
768
+ #createPromptLifecycleError(message: string): PromptLifecycleError {
769
+ return Object.assign(new Error(message), { code: "ACP_SESSION_CLOSED" as const });
770
+ }
771
+
772
+ #trackPromptEvent(record: ManagedSessionRecord, event: AgentSessionEvent): void {
773
+ const handling = this.#handlePromptEvent(record, event).catch((error: unknown) => {
774
+ logger.warn("ACP prompt event handler failed", { error });
775
+ });
776
+ record.promptEventHandlers.add(handling);
777
+ void handling.finally(() => {
778
+ record.promptEventHandlers.delete(handling);
779
+ });
780
+ }
781
+
782
+ async #waitForPromptEventHandlers(record: ManagedSessionRecord): Promise<void> {
783
+ while (record.promptEventHandlers.size > 0) {
784
+ await Promise.allSettled(Array.from(record.promptEventHandlers));
785
+ }
786
+ }
787
+
788
+ #trackExtensionUserMessage(record: ManagedSessionRecord, task: Promise<void>): void {
789
+ const tracked = task.catch((error: unknown) => {
790
+ logger.warn("ACP extension sendUserMessage failed", { error });
791
+ });
792
+ record.extensionUserMessageTasks.add(tracked);
793
+ void tracked.finally(() => {
794
+ record.extensionUserMessageTasks.delete(tracked);
795
+ });
796
+ }
797
+
798
+ async #waitForExtensionUserMessages(
799
+ record: ManagedSessionRecord,
800
+ baseline: ReadonlySet<Promise<void>>,
801
+ ): Promise<void> {
802
+ while (true) {
803
+ const pending = Array.from(record.extensionUserMessageTasks).filter(task => !baseline.has(task));
804
+ if (pending.length === 0) {
805
+ return;
806
+ }
807
+ await Promise.allSettled(pending);
808
+ }
809
+ }
810
+
811
+ async #runPromptOrCommand(record: ManagedSessionRecord, text: string, images: AgentImageContent[]): Promise<void> {
812
+ const skillResult = await this.#tryRunSkillCommand(record, text);
813
+ if (skillResult) {
814
+ return;
815
+ }
816
+
817
+ const builtinResult = await executeAcpBuiltinSlashCommand(text, {
818
+ session: record.session,
819
+ sessionManager: record.session.sessionManager,
820
+ settings: record.session.settings,
821
+ cwd: record.session.sessionManager.getCwd(),
822
+ output: output => this.#emitCommandOutput(record, output),
823
+ refreshCommands: () => this.#emitAvailableCommandsUpdate(record),
824
+ reloadPlugins: () => this.#reloadPluginState(record),
825
+ notifyTitleChanged: async () => {
826
+ await this.#connection.sessionUpdate({
827
+ sessionId: record.session.sessionId,
828
+ update: {
829
+ sessionUpdate: "session_info_update",
830
+ title: record.session.sessionName,
831
+ updatedAt: new Date().toISOString(),
832
+ },
833
+ });
834
+ },
835
+ notifyConfigChanged: async () => {
836
+ await this.#pushConfigOptionUpdate(record);
837
+ },
838
+ });
839
+ if (builtinResult !== false) {
840
+ if ("prompt" in builtinResult) {
841
+ await record.session.prompt(builtinResult.prompt, { images });
842
+ return;
843
+ }
844
+ const promptTurn = record.promptTurn;
845
+ this.#finishPrompt(record, {
846
+ stopReason: "end_turn",
847
+ usage: this.#buildTurnUsage(
848
+ promptTurn?.usageBaseline ??
849
+ this.#cloneUsageStatistics(record.session.sessionManager.getUsageStatistics()),
850
+ record.session.sessionManager.getUsageStatistics(),
851
+ ),
852
+ });
853
+ return;
854
+ }
855
+
856
+ const extensionPromptBaseline = new Set(record.extensionUserMessageTasks);
857
+ const agentInvoked = await record.session.prompt(text, { images });
858
+ // Extension and custom-TS commands are handled locally inside session.prompt().
859
+ // An ACP extension command can still call pi.sendUserMessage(), which starts
860
+ // an async nested prompt through the extension runtime. Keep the ACP turn
861
+ // subscribed until those scheduled prompts and their event handlers drain;
862
+ // only then is `false` proof that the slash command was purely local.
863
+ if (!agentInvoked) {
864
+ await this.#waitForExtensionUserMessages(record, extensionPromptBaseline);
865
+ await this.#waitForPromptEventHandlers(record);
866
+ this.#finishPrompt(record, { stopReason: "end_turn" });
867
+ }
868
+ }
869
+
870
+ async #tryRunSkillCommand(record: ManagedSessionRecord, text: string): Promise<boolean> {
871
+ if (!record.session.skillsSettings?.enableSkillCommands) {
872
+ return false;
873
+ }
874
+ const parsed = parseSkillInvocation(text);
875
+ if (!parsed) {
876
+ return false;
877
+ }
878
+ const skill = record.session.skills.find(candidate => candidate.name === parsed.name);
879
+ if (!skill) {
880
+ return false;
881
+ }
882
+ const built = await buildSkillPromptMessage(skill, parsed.args, "user");
883
+ await record.session.promptCustomMessage(
884
+ {
885
+ customType: SKILL_PROMPT_MESSAGE_TYPE,
886
+ content: built.message,
887
+ display: true,
888
+ details: built.details,
889
+ attribution: "user",
890
+ },
891
+ { streamingBehavior: "steer" },
892
+ );
893
+ return true;
894
+ }
895
+
896
+ async cancel(params: { sessionId: string }): Promise<void> {
897
+ const record = this.#getSessionRecord(params.sessionId);
898
+ const promptTurn = record.promptTurn;
899
+ if (!promptTurn || promptTurn.settled) {
900
+ return;
901
+ }
902
+ const cleanup = this.#beginCancelCleanup(record, promptTurn);
903
+ try {
904
+ await cleanup;
905
+ } catch (error: unknown) {
906
+ logger.warn("ACP cancel cleanup timed out; closing session", { sessionId: record.session.sessionId, error });
907
+ await this.#closeManagedSession(record.session.sessionId, record);
908
+ }
909
+ }
910
+
911
+ /**
912
+ * Transition a still-running turn into cancellation: mark intent, drop the live-event
913
+ * subscription, start the bounded `abort()` race, and resolve the ACP prompt response
914
+ * with `stopReason: "cancelled"` so the client sees acceptance immediately. The
915
+ * returned promise is the cleanup barrier — it resolves when `abort()` completes and
916
+ * rejects when the timeout fires. Idempotent: a second call returns the same barrier.
917
+ */
918
+ #beginCancelCleanup(record: ManagedSessionRecord, promptTurn: PromptTurnState): Promise<void> {
919
+ if (promptTurn.cleanup) {
920
+ return promptTurn.cleanup;
921
+ }
922
+ promptTurn.cancelRequested = true;
923
+ promptTurn.unsubscribe?.();
924
+ const cleanup = this.#runCancelCleanup(record, promptTurn);
925
+ promptTurn.cleanup = cleanup;
926
+ this.#finishPrompt(record, {
927
+ stopReason: "cancelled",
928
+ usage: this.#buildTurnUsage(promptTurn.usageBaseline, record.session.sessionManager.getUsageStatistics()),
929
+ });
930
+ return cleanup;
931
+ }
932
+
933
+ async #runCancelCleanup(record: ManagedSessionRecord, promptTurn: PromptTurnState): Promise<void> {
934
+ let timer: NodeJS.Timeout | undefined;
935
+ const timeout = new Promise<never>((_, reject) => {
936
+ timer = setTimeout(() => reject(new Error("ACP cancel cleanup timed out")), this.#cancelCleanupTimeoutMs);
937
+ });
938
+ try {
939
+ await Promise.race([record.session.abort({ reason: USER_INTERRUPT_LABEL }), timeout]);
940
+ } finally {
941
+ if (timer) clearTimeout(timer);
942
+ // Order matters: clear `cleanup` before evicting the slot so the slot-eviction
943
+ // branch matches what `#finishPrompt` saw if it ran first.
944
+ promptTurn.cleanup = undefined;
945
+ if (promptTurn.settled && record.promptTurn === promptTurn) {
946
+ record.promptTurn = undefined;
947
+ }
948
+ }
949
+ }
950
+
951
+ async extMethod(method: string, params: { [key: string]: unknown }): Promise<{ [key: string]: unknown }> {
952
+ switch (method) {
953
+ case SPEECH_MODELS_LIST_METHOD:
954
+ return buildAcpSpeechModelsCatalog();
955
+ case "_omp/sessions/listAll": {
956
+ const limit = typeof params.limit === "number" ? Math.max(1, Math.min(5000, params.limit as number)) : 1000;
957
+ const sessions = await SessionManager.listAll();
958
+ const sorted = sessions.sort((l, r) => r.modified.getTime() - l.modified.getTime()).slice(0, limit);
959
+ return {
960
+ sessions: sorted.map(s => this.#toSessionInfo(s)),
961
+ total: sessions.length,
962
+ };
963
+ }
964
+ case "_omp/projects/list": {
965
+ const sessions = await SessionManager.listAll();
966
+ const buckets = new Map<
967
+ string,
968
+ { cwd: string; sessionCount: number; lastActivityAt: number; lastTitle: string }
969
+ >();
970
+ for (const s of sessions) {
971
+ if (!s.cwd) continue;
972
+ const ts = s.modified.getTime();
973
+ const existing = buckets.get(s.cwd);
974
+ if (existing) {
975
+ existing.sessionCount += 1;
976
+ if (ts > existing.lastActivityAt) {
977
+ existing.lastActivityAt = ts;
978
+ existing.lastTitle = s.title ?? "";
979
+ }
980
+ } else {
981
+ buckets.set(s.cwd, {
982
+ cwd: s.cwd,
983
+ sessionCount: 1,
984
+ lastActivityAt: ts,
985
+ lastTitle: s.title ?? "",
986
+ });
987
+ }
988
+ }
989
+ const projects = Array.from(buckets.values()).sort((a, b) => b.lastActivityAt - a.lastActivityAt);
990
+ return { projects, totalSessions: sessions.length };
991
+ }
992
+ case "_omp/chats/byCwd": {
993
+ const cwd = typeof params.cwd === "string" ? (params.cwd as string) : undefined;
994
+ if (!cwd) throw new Error("cwd required");
995
+ const limit = typeof params.limit === "number" ? Math.max(1, Math.min(500, params.limit as number)) : 100;
996
+ const sessions = await SessionManager.list(cwd);
997
+ const sorted = sessions.sort((l, r) => r.modified.getTime() - l.modified.getTime()).slice(0, limit);
998
+ return { sessions: sorted.map(s => this.#toSessionInfo(s)) };
999
+ }
1000
+ case "_omp/usage": {
1001
+ const [firstRecord] = this.#sessions.values();
1002
+ const target = firstRecord?.session ?? this.#initialSession;
1003
+ if (!target) {
1004
+ return { reports: [] };
1005
+ }
1006
+ const reports = await target.fetchUsageReports();
1007
+ return { reports: reports ?? [] };
1008
+ }
1009
+ case "_omp/extensions": {
1010
+ const cwd = typeof params.cwd === "string" ? (params.cwd as string) : undefined;
1011
+ const sm = await Settings.init();
1012
+ const disabledIds = (sm.get("disabledExtensions") as string[] | undefined) ?? [];
1013
+ const extensions = await loadAllExtensions(cwd, disabledIds);
1014
+ return { extensions: extensions as unknown as Array<{ [key: string]: unknown }> };
1015
+ }
1016
+ case "_omp/extensions/toggle": {
1017
+ const providerId = params.providerId;
1018
+ if (typeof providerId !== "string") throw new Error("providerId required");
1019
+ if (params.enabled === false) {
1020
+ disableProvider(providerId);
1021
+ return { enabled: false };
1022
+ }
1023
+ enableProvider(providerId);
1024
+ return { enabled: true };
1025
+ }
1026
+ default:
1027
+ throw new Error(`Unknown ACP ext method: ${method}`);
1028
+ }
1029
+ }
1030
+
1031
+ async extNotification(_method: string, _params: { [key: string]: unknown }): Promise<void> {}
1032
+
1033
+ get signal(): AbortSignal {
1034
+ return this.#connection.signal;
1035
+ }
1036
+
1037
+ get closed(): Promise<void> {
1038
+ return this.#connection.closed;
1039
+ }
1040
+
1041
+ #registerConnectionCleanup(): void {
1042
+ if (this.#cleanupRegistered) {
1043
+ return;
1044
+ }
1045
+ this.#cleanupRegistered = true;
1046
+ this.#connection.signal.addEventListener(
1047
+ "abort",
1048
+ () => {
1049
+ void this.dispose();
1050
+ },
1051
+ { once: true },
1052
+ );
1053
+ }
1054
+
1055
+ async #createNewSessionRecord(cwd: string, mcpServers: McpServer[]): Promise<ManagedSessionRecord> {
1056
+ const session = await this.#createSession(path.resolve(cwd));
1057
+ try {
1058
+ await session.sessionManager.ensureOnDisk();
1059
+ } catch (error) {
1060
+ await this.#disposeStandaloneSession(session);
1061
+ throw error;
1062
+ }
1063
+ return await this.#registerPreparedSession(session, mcpServers);
1064
+ }
1065
+
1066
+ async #loadManagedSession(sessionId: string, cwd: string, mcpServers: McpServer[]): Promise<ManagedSessionRecord> {
1067
+ const existing = this.#sessions.get(sessionId);
1068
+ if (existing) {
1069
+ this.#assertMatchingCwd(existing.session, cwd);
1070
+ await this.#configureMcpServers(existing, mcpServers);
1071
+ return existing;
1072
+ }
1073
+
1074
+ const storedSession = await this.#findStoredSession(sessionId, cwd);
1075
+ if (!storedSession) {
1076
+ throw new Error(`ACP session not found: ${sessionId}`);
1077
+ }
1078
+ return await this.#openStoredSession(storedSession.path, cwd, mcpServers, sessionId);
1079
+ }
1080
+
1081
+ async #resumeManagedSession(sessionId: string, cwd: string, mcpServers: McpServer[]): Promise<ManagedSessionRecord> {
1082
+ const existing = this.#sessions.get(sessionId);
1083
+ if (existing) {
1084
+ this.#assertMatchingCwd(existing.session, cwd);
1085
+ await this.#configureMcpServers(existing, mcpServers);
1086
+ return existing;
1087
+ }
1088
+
1089
+ const storedSession = await this.#findStoredSession(sessionId, cwd);
1090
+ if (!storedSession) {
1091
+ throw new Error(`ACP session not found: ${sessionId}`);
1092
+ }
1093
+ return await this.#openStoredSession(storedSession.path, cwd, mcpServers, sessionId);
1094
+ }
1095
+
1096
+ async #forkManagedSession(params: ForkSessionRequest): Promise<ManagedSessionRecord> {
1097
+ const sourcePath = await this.#resolveForkSourceSessionPath(params.sessionId);
1098
+ const session = await this.#createSession(path.resolve(params.cwd));
1099
+ try {
1100
+ const success = await session.switchSession(sourcePath);
1101
+ if (!success) {
1102
+ throw new Error(`ACP session fork was cancelled: ${params.sessionId}`);
1103
+ }
1104
+ const forked = await session.fork();
1105
+ if (!forked) {
1106
+ throw new Error(`ACP session fork failed: ${params.sessionId}`);
1107
+ }
1108
+ } catch (error) {
1109
+ await this.#disposeStandaloneSession(session);
1110
+ throw error;
1111
+ }
1112
+ return await this.#registerPreparedSession(session, params.mcpServers ?? []);
1113
+ }
1114
+
1115
+ async #openStoredSession(
1116
+ sessionPath: string,
1117
+ cwd: string,
1118
+ mcpServers: McpServer[],
1119
+ sessionId: string,
1120
+ ): Promise<ManagedSessionRecord> {
1121
+ const session = await this.#createSession(path.resolve(cwd));
1122
+ try {
1123
+ const success = await session.switchSession(sessionPath);
1124
+ if (!success) {
1125
+ throw new Error(`ACP session load was cancelled: ${sessionId}`);
1126
+ }
1127
+ } catch (error) {
1128
+ await this.#disposeStandaloneSession(session);
1129
+ throw error;
1130
+ }
1131
+ return await this.#registerPreparedSession(session, mcpServers);
1132
+ }
1133
+
1134
+ async #registerPreparedSession(session: AgentSession, mcpServers: McpServer[]): Promise<ManagedSessionRecord> {
1135
+ const record = this.#createManagedSessionRecord(session);
1136
+ session.setClientBridge(createAcpClientBridge(this.#connection, session.sessionId, this.#clientCapabilities));
1137
+ // `record.lifetimeUnsubscribe` is installed in `#scheduleBootstrapUpdates`
1138
+ // so it shares the bootstrap race guard — see that comment for why.
1139
+ try {
1140
+ await this.#configureExtensions(record);
1141
+ await this.#configureMcpServers(record, mcpServers);
1142
+ this.#sessions.set(session.sessionId, record);
1143
+ return record;
1144
+ } catch (error) {
1145
+ await this.#disposeSessionRecord(record);
1146
+ throw error;
1147
+ }
1148
+ }
1149
+
1150
+ #createManagedSessionRecord(session: AgentSession): ManagedSessionRecord {
1151
+ return {
1152
+ session,
1153
+ mcpManager: undefined,
1154
+ mcpRefreshChain: undefined,
1155
+ promptTurn: undefined,
1156
+ promptQueue: { promise: Promise.resolve(), release: undefined },
1157
+ liveMessageId: undefined,
1158
+ liveMessageProgress: undefined,
1159
+ toolArgsById: new Map(),
1160
+ extensionsConfigured: false,
1161
+ closedError: undefined,
1162
+ promptEventHandlers: new Set(),
1163
+ extensionUserMessageTasks: new Set(),
1164
+ lifetimeUnsubscribe: undefined,
1165
+ };
1166
+ }
1167
+
1168
+ async #handleLifetimeEvent(record: ManagedSessionRecord, event: AgentSessionEvent): Promise<void> {
1169
+ if (event.type !== "thinking_level_changed" && event.type !== "model_changed") {
1170
+ return;
1171
+ }
1172
+ try {
1173
+ await this.#pushConfigOptionUpdate(record);
1174
+ } catch (error) {
1175
+ logger.warn("Failed to push config_option_update after a lifetime event", {
1176
+ sessionId: record.session.sessionId,
1177
+ eventType: event.type,
1178
+ error,
1179
+ });
1180
+ }
1181
+ }
1182
+
1183
+ #getSessionRecord(sessionId: string): ManagedSessionRecord {
1184
+ const record = this.#sessions.get(sessionId);
1185
+ if (!record) {
1186
+ throw new Error(`Unsupported ACP session: ${sessionId}`);
1187
+ }
1188
+ return record;
1189
+ }
1190
+
1191
+ #assertMatchingCwd(session: AgentSession, cwd: string): void {
1192
+ const expected = path.resolve(cwd);
1193
+ const actual = path.resolve(session.sessionManager.getCwd());
1194
+ if (actual !== expected) {
1195
+ throw new Error(`ACP session ${session.sessionId} is already loaded for ${actual}, not ${expected}`);
1196
+ }
1197
+ }
1198
+
1199
+ async #resolveForkSourceSessionPath(sessionId: string): Promise<string> {
1200
+ const loaded = this.#sessions.get(sessionId);
1201
+ if (loaded) {
1202
+ if (isPromptTurnInFlight(loaded.promptTurn)) {
1203
+ throw new Error(`ACP session fork is unavailable while a prompt is in progress: ${sessionId}`);
1204
+ }
1205
+ await loaded.session.sessionManager.flush();
1206
+ const sessionPath = loaded.session.sessionManager.getSessionFile();
1207
+ if (!sessionPath) {
1208
+ throw new Error(`ACP session cannot be forked before it is persisted: ${sessionId}`);
1209
+ }
1210
+ return sessionPath;
1211
+ }
1212
+
1213
+ const storedSession = await this.#findStoredSessionById(sessionId);
1214
+ if (!storedSession) {
1215
+ throw new Error(`ACP session not found: ${sessionId}`);
1216
+ }
1217
+ return storedSession.path;
1218
+ }
1219
+
1220
+ async #handlePromptEvent(record: ManagedSessionRecord, event: AgentSessionEvent): Promise<void> {
1221
+ const promptTurn = record.promptTurn;
1222
+ if (!promptTurn || promptTurn.settled || promptTurn.cancelRequested) {
1223
+ return;
1224
+ }
1225
+
1226
+ if (event.type === "tool_execution_start" || event.type === "tool_execution_update") {
1227
+ record.toolArgsById.set(event.toolCallId, event.args);
1228
+ }
1229
+
1230
+ this.#prepareLiveAssistantMessage(record, event);
1231
+ const imageDataCache = new Map<string, string>();
1232
+ const resolveImageDataForAcp = (data: string, mimeType: string | undefined): string => {
1233
+ const key = `${mimeType ?? ""}\u0000${data}`;
1234
+ const cached = imageDataCache.get(key);
1235
+ if (cached !== undefined) return cached;
1236
+ const resolved = resolveImageDataSync(this.#blobs, data);
1237
+ imageDataCache.set(key, resolved);
1238
+ return resolved;
1239
+ };
1240
+ const streamedAssistantError =
1241
+ event.type === "message_update" &&
1242
+ event.message.role === "assistant" &&
1243
+ event.assistantMessageEvent.type === "error";
1244
+ for (const notification of mapAgentSessionEventToAcpSessionUpdates(event, record.session.sessionId, {
1245
+ getMessageId: message => this.#getLiveMessageId(record, message),
1246
+ getMessageProgress: message => this.#getLiveMessageProgress(record, message),
1247
+ getToolArgs: toolCallId => record.toolArgsById.get(toolCallId),
1248
+ cwd: record.session.sessionManager.getCwd(),
1249
+ resolveImageData: resolveImageDataForAcp,
1250
+ })) {
1251
+ const delivery = this.#connection.sessionUpdate(notification);
1252
+ if (streamedAssistantError) {
1253
+ // Resolves true only once the error chunk actually reached the
1254
+ // client — a failed delivery keeps the agent_end fallback armed.
1255
+ const outcome = delivery.then(
1256
+ () => true,
1257
+ () => false,
1258
+ );
1259
+ const prior = promptTurn.errorTextDelivery;
1260
+ promptTurn.errorTextDelivery = prior ? Promise.all([prior, outcome]).then(([a, b]) => a || b) : outcome;
1261
+ }
1262
+ await delivery;
1263
+ }
1264
+ if (event.type === "tool_execution_end") {
1265
+ record.toolArgsById.delete(event.toolCallId);
1266
+ }
1267
+ this.#clearLiveAssistantMessageAfterEvent(record, event);
1268
+
1269
+ if (event.type === "agent_end") {
1270
+ await this.#flushMissedFinalAssistantText(record, event);
1271
+ await this.#flushUnreportedTurnError(record, event);
1272
+ await this.#emitEndOfTurnUpdates(record);
1273
+ await this.#waitForAcpPromptIdle(record);
1274
+ record.liveMessageId = undefined;
1275
+ record.liveMessageProgress = undefined;
1276
+ this.#finishPrompt(record, {
1277
+ stopReason: this.#resolveStopReason(event, promptTurn.cancelRequested),
1278
+ usage: this.#buildTurnUsage(promptTurn.usageBaseline, record.session.sessionManager.getUsageStatistics()),
1279
+ });
1280
+ }
1281
+ }
1282
+
1283
+ /**
1284
+ * Deliver the final visible answer when the assistant `message_end` never
1285
+ * reached this prompt turn's subscription. Session event handlers are
1286
+ * fire-and-forget (`Agent#emit` does not await async listeners), and
1287
+ * `agent_end` is flushed through the session's `#endInFlight` path while the
1288
+ * assistant `message_end` fan-out can still be parked on extension delivery —
1289
+ * so `agent_end` can overtake `message_end`. Once the turn finishes,
1290
+ * `#finishPrompt` unsubscribes and the fallback text emission in
1291
+ * `mapAssistantMessageEnd` is lost for good: a client that only received
1292
+ * `agent_thought_chunk`s stays stuck on the thinking block (#4902). The live
1293
+ * message progress records whether visible text ever reached the client; if
1294
+ * it has not, emit the last assistant message's text before the prompt
1295
+ * resolves. A `message_end` that lands during the end-of-turn waits still
1296
+ * takes the normal mapper path and sees `textEmitted` already set, so the
1297
+ * answer is delivered exactly once.
1298
+ */
1299
+ async #flushMissedFinalAssistantText(
1300
+ record: ManagedSessionRecord,
1301
+ event: Extract<AgentSessionEvent, { type: "agent_end" }>,
1302
+ ): Promise<void> {
1303
+ const progress = record.liveMessageProgress;
1304
+ if (!progress || progress.textEmitted) {
1305
+ return;
1306
+ }
1307
+ const lastAssistant = [...event.messages]
1308
+ .reverse()
1309
+ .find((message): message is AssistantMessage => message.role === "assistant");
1310
+ if (!lastAssistant) {
1311
+ return;
1312
+ }
1313
+ const text = extractAssistantMessageText(lastAssistant);
1314
+ if (text.length === 0) {
1315
+ return;
1316
+ }
1317
+ progress.textEmitted = true;
1318
+ await this.#connection.sessionUpdate({
1319
+ sessionId: record.session.sessionId,
1320
+ update: {
1321
+ sessionUpdate: "agent_message_chunk",
1322
+ content: { type: "text", text },
1323
+ messageId: record.liveMessageId,
1324
+ },
1325
+ });
1326
+ }
1327
+
1328
+ /**
1329
+ * Surface a turn-fatal provider error that never reached the client. A
1330
+ * request that fails before streaming any assistant events — e.g. GitHub
1331
+ * Copilot's `HTTP 400 model_not_supported` after retries — emits only
1332
+ * `agent_end` with an empty assistant message carrying `errorMessage`
1333
+ * (`Agent#runLoop`'s catch), so no `message_update`/`message_end` ever maps
1334
+ * to a session update and the client sees the turn end silently. Errors
1335
+ * that did stream are tracked via {@link PromptTurnState.errorTextDelivery};
1336
+ * the fallback awaits that delivery and re-sends only when it failed.
1337
+ */
1338
+ async #flushUnreportedTurnError(
1339
+ record: ManagedSessionRecord,
1340
+ event: Extract<AgentSessionEvent, { type: "agent_end" }>,
1341
+ ): Promise<void> {
1342
+ const streamedDelivery = record.promptTurn?.errorTextDelivery;
1343
+ if (streamedDelivery && (await streamedDelivery)) {
1344
+ return;
1345
+ }
1346
+ const lastAssistant = [...event.messages]
1347
+ .reverse()
1348
+ .find((message): message is AssistantMessage => message.role === "assistant");
1349
+ if (lastAssistant?.stopReason !== "error") {
1350
+ return;
1351
+ }
1352
+ const errorMessage = lastAssistant.errorMessage;
1353
+ if (!errorMessage || isSilentAbort(lastAssistant)) {
1354
+ return;
1355
+ }
1356
+ await this.#connection.sessionUpdate({
1357
+ sessionId: record.session.sessionId,
1358
+ update: {
1359
+ sessionUpdate: "agent_message_chunk",
1360
+ content: { type: "text", text: errorMessage },
1361
+ messageId: record.liveMessageId ?? crypto.randomUUID(),
1362
+ },
1363
+ });
1364
+ }
1365
+
1366
+ async #waitForAcpPromptIdle(record: ManagedSessionRecord): Promise<void> {
1367
+ for (let pass = 0; pass < ACP_ASYNC_DELIVERY_DRAIN_MAX_PASSES; pass++) {
1368
+ await record.session.waitForIdle();
1369
+ const delivered = await record.session.drainAsyncJobDeliveriesForAcp({
1370
+ timeoutMs: ACP_ASYNC_DELIVERY_DRAIN_TIMEOUT_MS,
1371
+ });
1372
+ if (!delivered) {
1373
+ return;
1374
+ }
1375
+ }
1376
+
1377
+ await record.session.waitForIdle();
1378
+ }
1379
+
1380
+ #prepareLiveAssistantMessage(record: ManagedSessionRecord, event: AgentSessionEvent): void {
1381
+ if (
1382
+ (event.type === "message_start" || event.type === "message_update" || event.type === "message_end") &&
1383
+ event.message.role === "assistant" &&
1384
+ (event.type === "message_start" || !record.liveMessageId || !record.liveMessageProgress)
1385
+ ) {
1386
+ record.liveMessageId = crypto.randomUUID();
1387
+ record.liveMessageProgress = { textEmitted: false, thoughtEmitted: false };
1388
+ }
1389
+ }
1390
+
1391
+ /**
1392
+ * Reset live-message tracking once the assistant `message_end` is handled.
1393
+ * The `agent_end` reset happens inside the `agent_end` branch of
1394
+ * `#handlePromptEvent` — after `#flushMissedFinalAssistantText` — so a
1395
+ * `message_end` that arrives during the end-of-turn waits maps against the
1396
+ * real progress instead of resurrecting a fresh one (which would double-emit
1397
+ * the final answer).
1398
+ */
1399
+ #clearLiveAssistantMessageAfterEvent(record: ManagedSessionRecord, event: AgentSessionEvent): void {
1400
+ if (event.type === "message_end" && event.message.role === "assistant") {
1401
+ record.liveMessageId = undefined;
1402
+ record.liveMessageProgress = undefined;
1403
+ }
1404
+ }
1405
+
1406
+ #getLiveMessageId(record: ManagedSessionRecord, message: unknown): string | undefined {
1407
+ if (typeof message !== "object" || message === null) {
1408
+ return undefined;
1409
+ }
1410
+ record.liveMessageId ??= crypto.randomUUID();
1411
+ return record.liveMessageId;
1412
+ }
1413
+
1414
+ #getLiveMessageProgress(
1415
+ record: ManagedSessionRecord,
1416
+ message: unknown,
1417
+ ): { textEmitted: boolean; thoughtEmitted: boolean } | undefined {
1418
+ if (typeof message !== "object" || message === null) {
1419
+ return undefined;
1420
+ }
1421
+ record.liveMessageProgress ??= { textEmitted: false, thoughtEmitted: false };
1422
+ return record.liveMessageProgress;
1423
+ }
1424
+
1425
+ #finishPrompt(record: ManagedSessionRecord, response?: PromptResponse, error?: unknown): void {
1426
+ const promptTurn = record.promptTurn;
1427
+ if (!promptTurn || promptTurn.settled) {
1428
+ return;
1429
+ }
1430
+ promptTurn.settled = true;
1431
+ promptTurn.unsubscribe?.();
1432
+ // Keep the slot occupied until cancel cleanup finishes — `#runCancelCleanup`
1433
+ // evicts the slot in its finally block once both flags say it's safe.
1434
+ if (!promptTurn.cleanup && record.promptTurn === promptTurn) {
1435
+ record.promptTurn = undefined;
1436
+ }
1437
+ if (error !== undefined) {
1438
+ promptTurn.reject(error);
1439
+ return;
1440
+ }
1441
+ promptTurn.resolve(response ?? { stopReason: "end_turn" });
1442
+ }
1443
+
1444
+ #resolveStopReason(
1445
+ event: Extract<AgentSessionEvent, { type: "agent_end" }>,
1446
+ cancelRequested: boolean,
1447
+ ): PromptResponse["stopReason"] {
1448
+ if (cancelRequested) {
1449
+ return "cancelled";
1450
+ }
1451
+ const lastAssistant = [...event.messages]
1452
+ .reverse()
1453
+ .find((message): message is AssistantMessage => message.role === "assistant");
1454
+ const reason = lastAssistant?.stopReason;
1455
+ switch (reason) {
1456
+ case "aborted":
1457
+ return "cancelled";
1458
+ case "length":
1459
+ return "max_tokens";
1460
+ case "error": {
1461
+ const errorMessage = lastAssistant?.errorMessage ?? "";
1462
+ if (/content[_ ]?filter|refus(al|ed)/i.test(errorMessage)) {
1463
+ return "refusal";
1464
+ }
1465
+ return "end_turn";
1466
+ }
1467
+ default:
1468
+ return "end_turn";
1469
+ }
1470
+ }
1471
+
1472
+ async #emitCommandOutput(record: ManagedSessionRecord, text: string): Promise<void> {
1473
+ if (!text) {
1474
+ return;
1475
+ }
1476
+ await this.#connection.sessionUpdate({
1477
+ sessionId: record.session.sessionId,
1478
+ update: {
1479
+ sessionUpdate: "agent_message_chunk",
1480
+ content: { type: "text", text },
1481
+ messageId: crypto.randomUUID(),
1482
+ },
1483
+ });
1484
+ }
1485
+
1486
+ #assertAbsoluteCwd(cwd: string): void {
1487
+ if (!path.isAbsolute(cwd)) {
1488
+ throw new Error(`ACP cwd must be absolute: ${cwd}`);
1489
+ }
1490
+ }
1491
+
1492
+ #convertPromptBlocks(blocks: PromptRequest["prompt"]): { text: string; images: AgentImageContent[] } {
1493
+ const textParts: string[] = [];
1494
+ const images: AgentImageContent[] = [];
1495
+ for (const block of blocks) {
1496
+ switch (block.type) {
1497
+ case "text":
1498
+ textParts.push(block.text);
1499
+ break;
1500
+ case "image":
1501
+ images.push({ type: "image", data: block.data, mimeType: block.mimeType });
1502
+ break;
1503
+ case "resource":
1504
+ if ("text" in block.resource) {
1505
+ textParts.push(block.resource.text);
1506
+ } else if (typeof block.resource.mimeType === "string" && block.resource.mimeType.startsWith("image/")) {
1507
+ // `embeddedContext: true` covers both text and blob resources, but
1508
+ // blobs aren't directly consumable by the LLM. Route image blobs
1509
+ // to the images array so the user's intent survives; everything
1510
+ // else falls back to the URI placeholder below.
1511
+ images.push({ type: "image", data: block.resource.blob, mimeType: block.resource.mimeType });
1512
+ } else {
1513
+ textParts.push(`[embedded resource: ${block.resource.uri}]`);
1514
+ }
1515
+ break;
1516
+ case "resource_link":
1517
+ textParts.push(block.title ?? block.name ?? block.uri);
1518
+ break;
1519
+ case "audio":
1520
+ textParts.push("[audio omitted]");
1521
+ break;
1522
+ }
1523
+ }
1524
+ return {
1525
+ text: textParts.join("\n\n").trim(),
1526
+ images,
1527
+ };
1528
+ }
1529
+
1530
+ async #pushConfigOptionUpdate(record: ManagedSessionRecord): Promise<void> {
1531
+ await this.#pushConfigOptionUpdateForSession(record.session);
1532
+ }
1533
+
1534
+ async #pushConfigOptionUpdateForSession(session: AgentSession): Promise<void> {
1535
+ await this.#connection.sessionUpdate({
1536
+ sessionId: session.sessionId,
1537
+ update: {
1538
+ sessionUpdate: "config_option_update",
1539
+ configOptions: this.#buildConfigOptions(session),
1540
+ },
1541
+ });
1542
+ }
1543
+
1544
+ #buildConfigOptions(session: AgentSession): SessionConfigOption[] {
1545
+ const currentModeId = this.#getCurrentModeId(session);
1546
+ const modeOptions = this.#getAvailableModes(session).map(mode => ({
1547
+ value: mode.id,
1548
+ name: mode.name,
1549
+ description: mode.description,
1550
+ }));
1551
+ const configOptions: SessionConfigOption[] = [
1552
+ {
1553
+ id: MODE_CONFIG_ID,
1554
+ name: "Mode",
1555
+ category: "mode",
1556
+ type: "select",
1557
+ currentValue: currentModeId,
1558
+ options: modeOptions,
1559
+ },
1560
+ ];
1561
+
1562
+ const models = session.getAvailableModels();
1563
+ const currentModel = session.model;
1564
+ if (models.length > 0) {
1565
+ configOptions.push({
1566
+ id: MODEL_CONFIG_ID,
1567
+ name: "Model",
1568
+ category: "model",
1569
+ type: "select",
1570
+ currentValue: currentModel ? this.#toModelId(currentModel) : this.#toModelId(models[0]),
1571
+ options: models.map(model => ({
1572
+ value: this.#toModelId(model),
1573
+ name: model.name,
1574
+ description: `${model.provider}/${model.id}`,
1575
+ })),
1576
+ });
1577
+ }
1578
+
1579
+ configOptions.push({
1580
+ id: THINKING_CONFIG_ID,
1581
+ name: "Thinking",
1582
+ category: "thought_level",
1583
+ type: "select",
1584
+ currentValue: this.#toThinkingConfigValue(
1585
+ session.model?.reasoning ? this.#getConfiguredThinkingLevel(session) : undefined,
1586
+ ),
1587
+ options: this.#buildThinkingOptions(session),
1588
+ });
1589
+ return configOptions;
1590
+ }
1591
+
1592
+ #buildThinkingOptions(session: AgentSession): Array<{ value: string; name: string; description?: string }> {
1593
+ return [
1594
+ { value: THINKING_OFF, name: "Off" },
1595
+ { value: AUTO_THINKING, name: "Auto", description: "Auto-detect per prompt" },
1596
+ ...session.getAvailableThinkingLevels().map(level => ({
1597
+ value: level,
1598
+ name: level,
1599
+ })),
1600
+ ];
1601
+ }
1602
+ #getConfiguredThinkingLevel(session: AgentSession): string | undefined {
1603
+ const configuredThinkingLevel = (session as { configuredThinkingLevel?: () => string | undefined })
1604
+ .configuredThinkingLevel;
1605
+ return typeof configuredThinkingLevel === "function"
1606
+ ? configuredThinkingLevel.call(session)
1607
+ : session.thinkingLevel;
1608
+ }
1609
+
1610
+ #toThinkingConfigValue(value: string | undefined): string {
1611
+ return value && value !== "inherit" ? value : THINKING_OFF;
1612
+ }
1613
+
1614
+ async #setModelById(session: AgentSession, modelId: string): Promise<void> {
1615
+ const model = session.getAvailableModels().find(candidate => this.#toModelId(candidate) === modelId);
1616
+ if (!model) {
1617
+ throw new Error(`Unknown ACP model: ${modelId}`);
1618
+ }
1619
+ await session.setModel(model);
1620
+ }
1621
+
1622
+ #setThinkingLevelById(session: AgentSession, value: string): void {
1623
+ const thinkingLevel = parseConfiguredThinkingLevel(value);
1624
+ if (!thinkingLevel) {
1625
+ throw new Error(`Unknown ACP thinking level: ${value}`);
1626
+ }
1627
+ session.setThinkingLevel(thinkingLevel);
1628
+ }
1629
+
1630
+ #toModelId(model: Model): string {
1631
+ return `${model.provider}/${model.id}`;
1632
+ }
1633
+
1634
+ #getAvailableModes(session: AgentSession): Array<{ id: string; name: string; description: string }> {
1635
+ const modes = [{ id: ACP_DEFAULT_MODE_ID, name: "Default", description: "Standard ACP headless mode" }];
1636
+ if (session.settings.get("plan.enabled")) {
1637
+ modes.push({
1638
+ id: ACP_PLAN_MODE_ID,
1639
+ name: "Plan",
1640
+ description: "Read-only planning mode that drafts a plan to a markdown file before any code changes",
1641
+ });
1642
+ }
1643
+ void session;
1644
+ return modes;
1645
+ }
1646
+
1647
+ #getCurrentModeId(session: AgentSession): string {
1648
+ return session.getPlanModeState()?.enabled ? ACP_PLAN_MODE_ID : ACP_DEFAULT_MODE_ID;
1649
+ }
1650
+
1651
+ #applyModeChange(session: AgentSession, modeId: string): void {
1652
+ const availableModes = this.#getAvailableModes(session);
1653
+ if (!availableModes.some(mode => mode.id === modeId)) {
1654
+ throw new Error(`Unsupported ACP mode: ${modeId}`);
1655
+ }
1656
+ if (modeId === ACP_PLAN_MODE_ID) {
1657
+ const previous = session.getPlanModeState();
1658
+ session.setPlanModeState({
1659
+ enabled: true,
1660
+ planFilePath: previous?.planFilePath ?? DEFAULT_PLAN_FILE_URL,
1661
+ workflow: previous?.workflow ?? "parallel",
1662
+ reentry: previous !== undefined,
1663
+ });
1664
+ // Mirror `InteractiveMode.#enterPlanMode`: register the plan-proposal
1665
+ // handler that consumes `xd://propose` writes from plan mode. Without
1666
+ // this, proposal dispatch falls through and plan mode has no approval
1667
+ // path (issue #1869).
1668
+ session.setPlanProposalHandler?.(title => this.#handleAcpPlanProposal(session, title));
1669
+ } else {
1670
+ session.setPlanProposalHandler?.(null);
1671
+ session.setPlanModeState(undefined);
1672
+ }
1673
+ }
1674
+
1675
+ /**
1676
+ * Plan-proposal handler installed while ACP plan mode is active. The agent
1677
+ * submits the finalized plan by writing its `<slug>`/title to
1678
+ * `xd://propose`; this handler validates the plan file, normalizes the
1679
+ * title, asks the ACP client to confirm (via `unstable_createElicitation`
1680
+ * when supported), and on approval keeps the chosen plan path, exits plan
1681
+ * mode, and notifies the client so the agent regains full tools.
1682
+ *
1683
+ * Mirrors `InteractiveMode.#handlePlanProposal` for the parts the agent sees
1684
+ * (same `PlanApprovalDetails` shape). Clients without form-mode elicitation
1685
+ * get an auto-approve so plan mode is never stranded — the agent always has
1686
+ * a way out.
1687
+ */
1688
+ async #handleAcpPlanProposal(session: AgentSession, title: string): Promise<AgentToolResult<unknown>> {
1689
+ const state = session.getPlanModeState();
1690
+ if (!state?.enabled) {
1691
+ throw new ToolError("Plan mode is not active.");
1692
+ }
1693
+ const {
1694
+ planFilePath,
1695
+ planContent,
1696
+ title: resolvedTitle,
1697
+ } = await resolveApprovedPlan({
1698
+ suppliedTitle: title,
1699
+ statePlanFilePath: state.planFilePath,
1700
+ readPlan: url => this.#readAcpPlanFile(session, url),
1701
+ listPlanFiles: () => this.#listAcpLocalPlanFiles(session),
1702
+ });
1703
+ const approved = await this.#requestAcpPlanApprovalChoice(session.sessionId, resolvedTitle, planContent);
1704
+ const details: PlanApprovalDetails = {
1705
+ planFilePath,
1706
+ title: resolvedTitle,
1707
+ planExists: true,
1708
+ };
1709
+ if (!approved) {
1710
+ // Rejection keeps plan mode active for another planning turn. Promote the
1711
+ // reviewed path into plan-mode state so the next `#buildPlanModeMessage()`
1712
+ // targets the plan just reviewed, not the stale state path.
1713
+ if (state.planFilePath !== planFilePath) {
1714
+ session.setPlanModeState({ ...state, planFilePath });
1715
+ }
1716
+ const normalizedTitle = normalizePlanTitle(resolvedTitle).title;
1717
+ return {
1718
+ content: [
1719
+ {
1720
+ type: "text" as const,
1721
+ text: `Plan refinement requested. Update the plan file, then write ${normalizedTitle} to xd://propose again when ready.`,
1722
+ },
1723
+ ],
1724
+ details,
1725
+ };
1726
+ }
1727
+ // Approved. Set the plan reference so the next turn injects the plan
1728
+ // content as context (the file keeps its agent-chosen name — no rename),
1729
+ // then exit plan mode so the agent regains full tools.
1730
+ session.setPlanReferencePath(planFilePath);
1731
+ session.setPlanProposalHandler?.(null);
1732
+ session.setPlanModeState(undefined);
1733
+ try {
1734
+ await this.#connection.sessionUpdate({
1735
+ sessionId: session.sessionId,
1736
+ update: this.#buildCurrentModeUpdate(session),
1737
+ });
1738
+ await this.#pushConfigOptionUpdateForSession(session);
1739
+ } catch (error) {
1740
+ logger.warn("Failed to emit mode updates after plan approval", {
1741
+ sessionId: session.sessionId,
1742
+ error,
1743
+ });
1744
+ }
1745
+ return {
1746
+ content: [
1747
+ {
1748
+ type: "text" as const,
1749
+ text: `Plan approved at ${planFilePath}. Plan mode exited; proceed with the implementation.`,
1750
+ },
1751
+ ],
1752
+ details,
1753
+ };
1754
+ }
1755
+
1756
+ #resolveAcpPlanFilePath(session: AgentSession, planFilePath: string): string {
1757
+ if (planFilePath.startsWith("local:")) {
1758
+ const normalized = normalizeLocalScheme(planFilePath);
1759
+ return resolveLocalUrlToPath(normalized, {
1760
+ getArtifactsDir: () => session.sessionManager.getArtifactsDir(),
1761
+ getSessionId: () => session.sessionManager.getSessionId(),
1762
+ });
1763
+ }
1764
+ return path.resolve(session.sessionManager.getCwd(), planFilePath);
1765
+ }
1766
+
1767
+ async #readAcpPlanFile(session: AgentSession, planFilePath: string): Promise<string | null> {
1768
+ const resolvedPath = this.#resolveAcpPlanFilePath(session, planFilePath);
1769
+ try {
1770
+ return await Bun.file(resolvedPath).text();
1771
+ } catch (error) {
1772
+ if (isEnoent(error)) {
1773
+ return null;
1774
+ }
1775
+ throw error;
1776
+ }
1777
+ }
1778
+
1779
+ /** `local://` URLs of plan files in the session-local root, newest first —
1780
+ * the `resolveApprovedPlan` fallback for a dropped `extra.title`. */
1781
+ async #listAcpLocalPlanFiles(session: AgentSession): Promise<string[]> {
1782
+ const localRoot = this.#resolveAcpPlanFilePath(session, "local://");
1783
+ try {
1784
+ const entries = await fs.readdir(localRoot, { withFileTypes: true });
1785
+ const plans = await Promise.all(
1786
+ entries
1787
+ .filter(entry => entry.isFile() && /plan\.md$/i.test(entry.name))
1788
+ .map(async entry => {
1789
+ const stat = await fs.stat(path.join(localRoot, entry.name)).catch(() => null);
1790
+ return { url: `local://${entry.name}`, mtime: stat?.mtimeMs ?? 0 };
1791
+ }),
1792
+ );
1793
+ return plans.sort((a, b) => b.mtime - a.mtime).map(plan => plan.url);
1794
+ } catch {
1795
+ return [];
1796
+ }
1797
+ }
1798
+
1799
+ /**
1800
+ * Ask the ACP client to confirm plan approval. Returns `true` only on an
1801
+ * explicit `APPROVE_OPTION` selection. Refine, dismissal (`undefined`), or
1802
+ * any unrecognized value falls through to refine semantics — the caller
1803
+ * keeps plan mode active and surfaces guidance text to the agent. Clients
1804
+ * without `elicitation.form` support auto-approve because there is no
1805
+ * confirmation surface available; without that, plan mode would strand
1806
+ * the agent (the bug this method exists to fix).
1807
+ */
1808
+ async #requestAcpPlanApprovalChoice(sessionId: string, title: string, planContent: string): Promise<boolean> {
1809
+ const supportsForm = this.#clientCapabilities?.elicitation?.form != null;
1810
+ if (!supportsForm) return true;
1811
+ // Include a short preview of the plan so the user has context in the
1812
+ // dialog. Keep the body bounded — Zed renders elicitation messages
1813
+ // inline and a multi-thousand-line plan blows out the dialog.
1814
+ const previewLines = planContent.split("\n").slice(0, 12).join("\n");
1815
+ const ellipsis = planContent.split("\n").length > 12 ? "\n…" : "";
1816
+ const message = `Approve plan "${title}" and start implementation?\n\n${previewLines}${ellipsis}`;
1817
+ const value = await elicitFromAcpClient(
1818
+ this.#connection,
1819
+ sessionId,
1820
+ "select",
1821
+ message,
1822
+ { type: "string", enum: [APPROVE_OPTION, REFINE_OPTION] },
1823
+ undefined,
1824
+ );
1825
+ // Approve ONLY on the explicit approve selection. Dismissal, cancel,
1826
+ // timeout, or any other non-approve response falls through to refine
1827
+ // semantics so closing the dialog can never grant write access.
1828
+ return value === APPROVE_OPTION;
1829
+ }
1830
+
1831
+ #buildModeState(session: AgentSession): SessionModeState {
1832
+ return {
1833
+ availableModes: this.#getAvailableModes(session),
1834
+ currentModeId: this.#getCurrentModeId(session),
1835
+ };
1836
+ }
1837
+
1838
+ #buildCurrentModeUpdate(session: AgentSession): SessionUpdate {
1839
+ return {
1840
+ sessionUpdate: "current_mode_update",
1841
+ currentModeId: this.#getCurrentModeId(session),
1842
+ };
1843
+ }
1844
+
1845
+ async #buildAvailableCommands(session: AgentSession): Promise<AvailableCommand[]> {
1846
+ return toAcpAvailableCommands(await buildAvailableSlashCommands(session));
1847
+ }
1848
+
1849
+ #toSessionInfo(session: StoredSessionInfo): SessionInfo {
1850
+ return {
1851
+ sessionId: session.id,
1852
+ cwd: session.cwd,
1853
+ title: session.title,
1854
+ updatedAt: session.modified.toISOString(),
1855
+ _meta: {
1856
+ messageCount: session.messageCount,
1857
+ size: session.size,
1858
+ },
1859
+ };
1860
+ }
1861
+
1862
+ #scheduleBootstrapUpdates(sessionId: string): void {
1863
+ // Defer first notifications until the response has reached the client.
1864
+ // Zed's agent-client-protocol reader dispatches responses and
1865
+ // notifications to different async tasks; sending the first
1866
+ // `available_commands_update` from `setTimeout(0)` reliably loses the
1867
+ // race against the response handler and Zed logs `Received session
1868
+ // notification for unknown session` then drops the update — leaving
1869
+ // the slash-command palette empty (#1015 follow-up; see
1870
+ // zed-industries/zed#55965 for the same race biting other ACP agents).
1871
+ // `ACP_BOOTSTRAP_RACE_GUARD_MS` is invisible to the operator and large
1872
+ // enough that the response future has scheduled before our timer fires
1873
+ // on stdio-only transports.
1874
+ //
1875
+ // The session-lifetime subscription is installed inside the same timer
1876
+ // so it shares this guard — without it, an extension's `session_start`
1877
+ // handler (or any async work it schedules) calling `setThinkingLevel`
1878
+ // would push a `config_option_update` for a session id the client
1879
+ // hasn't been told about yet. The pre-bootstrap thinking level is
1880
+ // reported in the response's `configOptions`, so deferring the
1881
+ // notification loses no state.
1882
+ setTimeout(() => {
1883
+ if (this.#connection.signal.aborted) {
1884
+ return;
1885
+ }
1886
+ const record = this.#sessions.get(sessionId);
1887
+ if (!record) {
1888
+ return;
1889
+ }
1890
+ if (!record.lifetimeUnsubscribe) {
1891
+ record.lifetimeUnsubscribe = record.session.subscribe(event => {
1892
+ void this.#handleLifetimeEvent(record, event);
1893
+ });
1894
+ }
1895
+ void this.#emitBootstrapUpdates(sessionId, record);
1896
+ }, ACP_BOOTSTRAP_RACE_GUARD_MS);
1897
+ }
1898
+
1899
+ async #emitBootstrapUpdates(sessionId: string, record: ManagedSessionRecord): Promise<void> {
1900
+ if (this.#sessions.get(sessionId) !== record) {
1901
+ return;
1902
+ }
1903
+ await this.#connection.sessionUpdate({
1904
+ sessionId,
1905
+ update: {
1906
+ sessionUpdate: "available_commands_update",
1907
+ availableCommands: await this.#buildAvailableCommands(record.session),
1908
+ },
1909
+ });
1910
+ await this.#connection.sessionUpdate({
1911
+ sessionId,
1912
+ update: {
1913
+ sessionUpdate: "session_info_update",
1914
+ title: record.session.sessionName,
1915
+ updatedAt: record.session.sessionManager.getHeader()?.timestamp,
1916
+ },
1917
+ });
1918
+ }
1919
+
1920
+ async #emitAvailableCommandsUpdate(record: ManagedSessionRecord): Promise<void> {
1921
+ await this.#connection.sessionUpdate({
1922
+ sessionId: record.session.sessionId,
1923
+ update: {
1924
+ sessionUpdate: "available_commands_update",
1925
+ availableCommands: await this.#buildAvailableCommands(record.session),
1926
+ },
1927
+ });
1928
+ }
1929
+
1930
+ /**
1931
+ * Reload plugin/registry state for an ACP session. Mirrors the interactive
1932
+ * `/reload-plugins` and `/move` flows: invalidates the plugin-roots cache,
1933
+ * resets the capability cache, refreshes the session's slash-command state,
1934
+ * then re-advertises commands so the client sees newly installed/disabled
1935
+ * plugins.
1936
+ */
1937
+ async #reloadPluginState(record: ManagedSessionRecord): Promise<void> {
1938
+ const cwd = record.session.sessionManager.getCwd();
1939
+ const projectPath = await resolveActiveProjectRegistryPath(cwd);
1940
+ clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
1941
+ resetCapabilities();
1942
+ await record.session.refreshSkills();
1943
+ const fileCommands = await loadSlashCommands({ cwd });
1944
+ record.session.setSlashCommands(fileCommands);
1945
+ await this.#emitAvailableCommandsUpdate(record);
1946
+ }
1947
+
1948
+ async #emitEndOfTurnUpdates(record: ManagedSessionRecord): Promise<void> {
1949
+ const sessionId = record.session.sessionId;
1950
+
1951
+ const contextUsage = record.session.getContextUsage();
1952
+ if (contextUsage) {
1953
+ const usageStats = record.session.sessionManager.getUsageStatistics();
1954
+ await this.#connection.sessionUpdate({
1955
+ sessionId,
1956
+ update: {
1957
+ sessionUpdate: "usage_update",
1958
+ size: contextUsage.contextWindow,
1959
+ used: contextUsage.tokens ?? 0,
1960
+ cost: usageStats.cost > 0 ? { amount: usageStats.cost, currency: "USD" } : undefined,
1961
+ },
1962
+ });
1963
+ }
1964
+
1965
+ await this.#connection.sessionUpdate({
1966
+ sessionId,
1967
+ update: {
1968
+ sessionUpdate: "session_info_update",
1969
+ title: record.session.sessionName,
1970
+ updatedAt: new Date().toISOString(),
1971
+ },
1972
+ });
1973
+ }
1974
+
1975
+ #cloneUsageStatistics(usage: UsageStatistics): UsageStatistics {
1976
+ return {
1977
+ input: usage.input,
1978
+ output: usage.output,
1979
+ cacheRead: usage.cacheRead,
1980
+ cacheWrite: usage.cacheWrite,
1981
+ totalTokens: usage.totalTokens,
1982
+ orchestrationInput: usage.orchestrationInput,
1983
+ orchestrationOutput: usage.orchestrationOutput,
1984
+ orchestrationCacheRead: usage.orchestrationCacheRead,
1985
+ premiumRequests: usage.premiumRequests,
1986
+ cost: usage.cost,
1987
+ };
1988
+ }
1989
+
1990
+ #buildTurnUsage(previous: UsageStatistics, current: UsageStatistics): Usage | undefined {
1991
+ const inputTokens = Math.max(0, current.input - previous.input);
1992
+ const outputTokens = Math.max(0, current.output - previous.output);
1993
+ const cachedReadTokens = Math.max(0, current.cacheRead - previous.cacheRead);
1994
+ const cachedWriteTokens = Math.max(0, current.cacheWrite - previous.cacheWrite);
1995
+ const totalTokens = Math.max(0, current.totalTokens - previous.totalTokens);
1996
+
1997
+ if (totalTokens === 0) {
1998
+ return undefined;
1999
+ }
2000
+
2001
+ const usage: Usage = {
2002
+ inputTokens,
2003
+ outputTokens,
2004
+ totalTokens,
2005
+ };
2006
+ if (cachedReadTokens > 0) {
2007
+ usage.cachedReadTokens = cachedReadTokens;
2008
+ }
2009
+ if (cachedWriteTokens > 0) {
2010
+ usage.cachedWriteTokens = cachedWriteTokens;
2011
+ }
2012
+ return usage;
2013
+ }
2014
+
2015
+ async #listStoredSessions(cwd?: string): Promise<StoredSessionInfo[]> {
2016
+ const sessions = cwd ? await SessionManager.list(cwd) : await SessionManager.listAll();
2017
+ return sessions.sort((left, right) => right.modified.getTime() - left.modified.getTime());
2018
+ }
2019
+
2020
+ async #findStoredSession(sessionId: string, cwd: string): Promise<StoredSessionInfo | undefined> {
2021
+ const sessions = await this.#listStoredSessions(cwd);
2022
+ return sessions.find(session => session.id === sessionId);
2023
+ }
2024
+
2025
+ async #findStoredSessionById(sessionId: string): Promise<StoredSessionInfo | undefined> {
2026
+ const sessions = await this.#listStoredSessions();
2027
+ return sessions.find(session => session.id === sessionId);
2028
+ }
2029
+
2030
+ #parseCursor(cursor: string | undefined): number {
2031
+ if (!cursor) {
2032
+ return 0;
2033
+ }
2034
+ const parsed = Number.parseInt(cursor, 10);
2035
+ if (!Number.isFinite(parsed) || parsed < 0) {
2036
+ throw new Error(`Invalid ACP session cursor: ${cursor}`);
2037
+ }
2038
+ return parsed;
2039
+ }
2040
+
2041
+ async #replaySessionHistory(record: ManagedSessionRecord): Promise<void> {
2042
+ const cwd = record.session.sessionManager.getCwd();
2043
+ const replayedToolCallIds = new Set<string>();
2044
+ const replayedToolCallArgs = new Map<string, unknown>();
2045
+ for (const message of record.session.sessionManager.buildSessionContext().messages as ReplayableMessage[]) {
2046
+ for (const notification of this.#messageToReplayNotifications(
2047
+ record.session.sessionId,
2048
+ message,
2049
+ cwd,
2050
+ replayedToolCallIds,
2051
+ replayedToolCallArgs,
2052
+ )) {
2053
+ await this.#connection.sessionUpdate(notification);
2054
+ }
2055
+ }
2056
+ }
2057
+
2058
+ #messageToReplayNotifications(
2059
+ sessionId: string,
2060
+ message: ReplayableMessage,
2061
+ cwd: string,
2062
+ replayedToolCallIds: Set<string>,
2063
+ replayedToolCallArgs: Map<string, unknown>,
2064
+ ): SessionNotification[] {
2065
+ if (message.role === "assistant") {
2066
+ return this.#replayAssistantMessage(sessionId, message, cwd, replayedToolCallIds, replayedToolCallArgs);
2067
+ }
2068
+ if (
2069
+ message.role === "user" ||
2070
+ message.role === "developer" ||
2071
+ message.role === "custom" ||
2072
+ message.role === "hookMessage"
2073
+ ) {
2074
+ return this.#wrapReplayContent(
2075
+ sessionId,
2076
+ this.#extractReplayContent(message.content, undefined),
2077
+ "user_message_chunk",
2078
+ crypto.randomUUID(),
2079
+ );
2080
+ }
2081
+ if (
2082
+ message.role === "toolResult" &&
2083
+ typeof message.toolCallId === "string" &&
2084
+ typeof message.toolName === "string"
2085
+ ) {
2086
+ return this.#replayToolResult(
2087
+ sessionId,
2088
+ cwd,
2089
+ {
2090
+ ...message,
2091
+ toolCallId: message.toolCallId,
2092
+ toolName: message.toolName,
2093
+ },
2094
+ {
2095
+ includeStart: !replayedToolCallIds.has(message.toolCallId),
2096
+ toolArgs: replayedToolCallArgs.get(message.toolCallId),
2097
+ },
2098
+ );
2099
+ }
2100
+ if (
2101
+ message.role === "bashExecution" ||
2102
+ message.role === "pythonExecution" ||
2103
+ message.role === "compactionSummary"
2104
+ ) {
2105
+ return this.#wrapReplayContent(
2106
+ sessionId,
2107
+ this.#extractReplayContent(message.content, undefined),
2108
+ "user_message_chunk",
2109
+ crypto.randomUUID(),
2110
+ );
2111
+ }
2112
+ return [];
2113
+ }
2114
+
2115
+ #replayAssistantMessage(
2116
+ sessionId: string,
2117
+ message: ReplayableMessage,
2118
+ cwd: string,
2119
+ replayedToolCallIds: Set<string>,
2120
+ replayedToolCallArgs: Map<string, unknown>,
2121
+ ): SessionNotification[] {
2122
+ const notifications: SessionNotification[] = [];
2123
+ const messageId = crypto.randomUUID();
2124
+ if (Array.isArray(message.content)) {
2125
+ for (const item of message.content) {
2126
+ if (typeof item !== "object" || item === null || !("type" in item)) {
2127
+ continue;
2128
+ }
2129
+ if (item.type === "text" && "text" in item && typeof item.text === "string" && item.text.length > 0) {
2130
+ notifications.push({
2131
+ sessionId,
2132
+ update: {
2133
+ sessionUpdate: "agent_message_chunk",
2134
+ content: { type: "text", text: item.text },
2135
+ messageId,
2136
+ },
2137
+ });
2138
+ continue;
2139
+ }
2140
+ if (
2141
+ item.type === "image" &&
2142
+ "data" in item &&
2143
+ typeof item.data === "string" &&
2144
+ "mimeType" in item &&
2145
+ typeof item.mimeType === "string"
2146
+ ) {
2147
+ notifications.push({
2148
+ sessionId,
2149
+ update: {
2150
+ sessionUpdate: "agent_message_chunk",
2151
+ content: { type: "image", data: item.data, mimeType: item.mimeType },
2152
+ messageId,
2153
+ },
2154
+ });
2155
+ continue;
2156
+ }
2157
+ if (item.type === "thinking" && "thinking" in item && typeof item.thinking === "string") {
2158
+ const thinking = canonicalizeMessage(item.thinking);
2159
+ if (thinking.length === 0) continue;
2160
+ notifications.push({
2161
+ sessionId,
2162
+ update: {
2163
+ sessionUpdate: "agent_thought_chunk",
2164
+ content: { type: "text", text: thinking },
2165
+ messageId,
2166
+ },
2167
+ });
2168
+ continue;
2169
+ }
2170
+ const toolItem = item as ReplayableToolItem;
2171
+ if (
2172
+ (toolItem.type === "toolCall" || toolItem.type === "tool_use") &&
2173
+ typeof toolItem.id === "string" &&
2174
+ typeof toolItem.name === "string"
2175
+ ) {
2176
+ const args = this.#buildReplayAssistantToolArgs(toolItem);
2177
+ notifications.push(
2178
+ ...mapAgentSessionEventToAcpSessionUpdates(
2179
+ {
2180
+ type: "tool_execution_start",
2181
+ toolCallId: toolItem.id,
2182
+ toolName: toolItem.name,
2183
+ args,
2184
+ },
2185
+ sessionId,
2186
+ { cwd },
2187
+ ),
2188
+ );
2189
+ replayedToolCallIds.add(toolItem.id);
2190
+ replayedToolCallArgs.set(toolItem.id, args);
2191
+ }
2192
+ }
2193
+ }
2194
+ if (notifications.length === 0 && message.errorMessage && !isSilentAbort(message)) {
2195
+ notifications.push({
2196
+ sessionId,
2197
+ update: {
2198
+ sessionUpdate: "agent_message_chunk",
2199
+ content: { type: "text", text: message.errorMessage },
2200
+ messageId,
2201
+ },
2202
+ });
2203
+ }
2204
+ return notifications;
2205
+ }
2206
+
2207
+ #buildReplayAssistantToolArgs(item: ReplayableToolItem): unknown {
2208
+ if ("arguments" in item) {
2209
+ return normalizeReplayToolArguments(item.arguments).args;
2210
+ }
2211
+ if (item.type === "tool_use" && "input" in item) {
2212
+ return item.input;
2213
+ }
2214
+ return {};
2215
+ }
2216
+
2217
+ #replayToolResult(
2218
+ sessionId: string,
2219
+ cwd: string,
2220
+ message: Required<Pick<ReplayableMessage, "toolCallId" | "toolName">> & ReplayableMessage,
2221
+ options: { includeStart?: boolean; toolArgs?: unknown } = {},
2222
+ ): SessionNotification[] {
2223
+ const args = this.#buildReplayToolArgs(message.details);
2224
+ const startEvent: AgentSessionEvent = {
2225
+ type: "tool_execution_start",
2226
+ toolCallId: message.toolCallId,
2227
+ toolName: message.toolName,
2228
+ args,
2229
+ };
2230
+ const endEvent: AgentSessionEvent = {
2231
+ type: "tool_execution_end",
2232
+ toolCallId: message.toolCallId,
2233
+ toolName: message.toolName,
2234
+ isError: message.isError === true,
2235
+ result: {
2236
+ content: message.content,
2237
+ details: message.details,
2238
+ errorMessage: message.errorMessage,
2239
+ },
2240
+ };
2241
+ const notifications = mapAgentSessionEventToAcpSessionUpdates(endEvent, sessionId, {
2242
+ cwd,
2243
+ getToolArgs: toolCallId => (toolCallId === message.toolCallId ? options.toolArgs : undefined),
2244
+ resolveImageData: (data, _mimeType) => resolveImageDataSync(this.#blobs, data),
2245
+ });
2246
+ if (options.includeStart === false) {
2247
+ return notifications;
2248
+ }
2249
+ return [...mapAgentSessionEventToAcpSessionUpdates(startEvent, sessionId, { cwd }), ...notifications];
2250
+ }
2251
+
2252
+ #buildReplayToolArgs(details: unknown): { path?: string } {
2253
+ if (typeof details !== "object" || details === null || !("path" in details)) {
2254
+ return {};
2255
+ }
2256
+ const value = (details as { path?: unknown }).path;
2257
+ return typeof value === "string" && value.length > 0 ? { path: value } : {};
2258
+ }
2259
+
2260
+ #wrapReplayContent(
2261
+ sessionId: string,
2262
+ content: PromptRequest["prompt"],
2263
+ kind: "agent_message_chunk" | "user_message_chunk",
2264
+ messageId: string,
2265
+ ): SessionNotification[] {
2266
+ return content.map(block => ({
2267
+ sessionId,
2268
+ update: {
2269
+ sessionUpdate: kind,
2270
+ content: block,
2271
+ messageId,
2272
+ },
2273
+ }));
2274
+ }
2275
+
2276
+ #extractReplayContent(content: unknown, errorMessage: string | undefined): PromptRequest["prompt"] {
2277
+ const replay: PromptRequest["prompt"] = [];
2278
+ if (Array.isArray(content)) {
2279
+ for (const item of content) {
2280
+ if (typeof item !== "object" || item === null || !("type" in item)) {
2281
+ continue;
2282
+ }
2283
+ if (item.type === "text" && "text" in item && typeof item.text === "string" && item.text.length > 0) {
2284
+ replay.push({ type: "text", text: item.text });
2285
+ continue;
2286
+ }
2287
+ if (
2288
+ item.type === "image" &&
2289
+ "data" in item &&
2290
+ "mimeType" in item &&
2291
+ typeof item.data === "string" &&
2292
+ typeof item.mimeType === "string"
2293
+ ) {
2294
+ replay.push({ type: "image", data: item.data, mimeType: item.mimeType });
2295
+ }
2296
+ }
2297
+ }
2298
+ if (replay.length === 0 && errorMessage) {
2299
+ replay.push({ type: "text", text: errorMessage });
2300
+ }
2301
+ return replay;
2302
+ }
2303
+
2304
+ async #configureExtensions(record: ManagedSessionRecord): Promise<void> {
2305
+ if (record.extensionsConfigured) {
2306
+ return;
2307
+ }
2308
+
2309
+ const uiContext = createAcpExtensionUiContext(
2310
+ this.#connection,
2311
+ () => record.session.sessionId,
2312
+ this.#clientCapabilities,
2313
+ );
2314
+ if (this.#clientCapabilities?.elicitation?.form != null) {
2315
+ record.session.setUsageFallbackConfirmer((confirmation, signal) => {
2316
+ const reserve =
2317
+ confirmation.remainingPercent === undefined
2318
+ ? "inside the configured reserve margin"
2319
+ : `${confirmation.remainingPercent.toFixed(1)}% remaining`;
2320
+ return uiContext.confirm(
2321
+ "Coding-plan reserve reached",
2322
+ `${confirmation.from} has ${reserve}. Switch to ${confirmation.to}? Choose No to keep using the current plan.`,
2323
+ { signal },
2324
+ );
2325
+ });
2326
+ }
2327
+
2328
+ const extensionRunner = record.session.extensionRunner;
2329
+ if (!extensionRunner) {
2330
+ record.extensionsConfigured = true;
2331
+ return;
2332
+ }
2333
+
2334
+ extensionRunner.initialize(
2335
+ {
2336
+ sendMessage: (message, options) => {
2337
+ record.session.sendCustomMessage(message, options).catch((error: unknown) => {
2338
+ logger.warn("ACP extension sendMessage failed", { error });
2339
+ });
2340
+ },
2341
+ sendUserMessage: (content, options) => {
2342
+ this.#trackExtensionUserMessage(record, record.session.sendUserMessage(content, options));
2343
+ },
2344
+ appendEntry: (customType, data) => {
2345
+ record.session.sessionManager.appendCustomEntry(customType, data);
2346
+ },
2347
+ setLabel: (targetId, label) => {
2348
+ record.session.sessionManager.appendLabelChange(targetId, label);
2349
+ },
2350
+ getActiveTools: () => record.session.getEnabledToolNames(),
2351
+ getAllTools: () => record.session.getAllToolInfos(),
2352
+ setActiveTools: toolNames => record.session.setActiveToolsByName(toolNames),
2353
+ getCommands: () => getSessionSlashCommands(record.session),
2354
+ setModel: async model => {
2355
+ const apiKey = await record.session.modelRegistry.getApiKey(model);
2356
+ if (!apiKey) {
2357
+ return false;
2358
+ }
2359
+ await record.session.setModel(model);
2360
+ return true;
2361
+ },
2362
+ getThinkingLevel: () => record.session.thinkingLevel,
2363
+ setThinkingLevel: level => record.session.setThinkingLevel(level),
2364
+ getServiceTiers: () => record.session.serviceTierByFamily,
2365
+ setServiceTier: (family, tier) => record.session.setServiceTierFamily(family, tier),
2366
+ getSessionName: () => record.session.sessionManager.getSessionName(),
2367
+ setSessionName: async name => {
2368
+ await record.session.sessionManager.setSessionName(name, "user");
2369
+ },
2370
+ },
2371
+ {
2372
+ getModel: () => record.session.model,
2373
+ isIdle: () => !record.session.isStreaming,
2374
+ abort: () => {
2375
+ void record.session.abort({ reason: USER_INTERRUPT_LABEL });
2376
+ },
2377
+ hasPendingMessages: () => record.session.queuedMessageCount > 0,
2378
+ shutdown: () => {},
2379
+ getContextUsage: () => record.session.getContextUsage(),
2380
+ getSystemPrompt: () => record.session.systemPrompt,
2381
+ compact: instructionsOrOptions => runExtensionCompact(record.session, instructionsOrOptions),
2382
+ },
2383
+ {
2384
+ getContextUsage: () => record.session.getContextUsage(),
2385
+ waitForIdle: () => record.session.agent.waitForIdle(),
2386
+ newSession: async options => {
2387
+ const success = await record.session.newSession({ parentSession: options?.parentSession });
2388
+ if (success && options?.setup) {
2389
+ await options.setup(record.session.sessionManager);
2390
+ }
2391
+ return { cancelled: !success };
2392
+ },
2393
+ branch: async entryId => {
2394
+ const result = await record.session.branch(entryId);
2395
+ return { cancelled: result.cancelled };
2396
+ },
2397
+ navigateTree: async (targetId, options) => {
2398
+ const result = await record.session.navigateTree(targetId, { summarize: options?.summarize });
2399
+ return { cancelled: result.cancelled };
2400
+ },
2401
+ switchSession: async sessionPath => {
2402
+ const success = await record.session.switchSession(sessionPath);
2403
+ return { cancelled: !success };
2404
+ },
2405
+ reload: async () => {
2406
+ await record.session.reload();
2407
+ },
2408
+ compact: instructionsOrOptions => runExtensionCompact(record.session, instructionsOrOptions),
2409
+ },
2410
+ uiContext,
2411
+ );
2412
+ await extensionRunner.emit({ type: "session_start" });
2413
+ record.extensionsConfigured = true;
2414
+ }
2415
+
2416
+ async #configureMcpServers(record: ManagedSessionRecord, servers: McpServer[]): Promise<void> {
2417
+ if (record.mcpManager) {
2418
+ await record.mcpManager.disconnectAll();
2419
+ }
2420
+ // Drain any in-flight refresh queued by a previous configuration: a refresh
2421
+ // that already passed its manager guard could otherwise finish applying a
2422
+ // stale tool set after this reconfiguration installs the new one.
2423
+ await record.mcpRefreshChain;
2424
+ record.mcpRefreshChain = undefined;
2425
+ if (servers.length === 0) {
2426
+ record.mcpManager = undefined;
2427
+ await record.session.refreshMCPTools([]);
2428
+ return;
2429
+ }
2430
+
2431
+ const manager = new MCPManager(record.session.sessionManager.getCwd());
2432
+ // MCP servers connect and reconnect independently, so `onToolsChanged` can fire
2433
+ // several times back to back. Each firing is chained onto `record.mcpRefreshChain`
2434
+ // so refreshes apply in order, and each one re-reads `manager.getTools()` at the
2435
+ // time it actually runs rather than the snapshot from when it was queued — so a
2436
+ // refresh can never apply a stale, smaller tool set after a newer one already landed.
2437
+ // The returned promise propagates failures (the initial awaited refresh below must
2438
+ // fail session setup, as the pre-queue code did); the stored chain swallows them
2439
+ // after logging so background firings only warn and the chain never rejects.
2440
+ const enqueueMcpToolsRefresh = (): Promise<void> => {
2441
+ const run = (record.mcpRefreshChain ?? Promise.resolve()).then(async () => {
2442
+ if (record.mcpManager !== manager) return;
2443
+ await record.session.refreshMCPTools(manager.getTools());
2444
+ });
2445
+ record.mcpRefreshChain = run.catch(error => {
2446
+ logger.warn("ACP MCP tool refresh failed", {
2447
+ error: error instanceof Error ? error.message : String(error),
2448
+ });
2449
+ });
2450
+ return run;
2451
+ };
2452
+ manager.setOnToolsChanged(() => {
2453
+ // Failures are logged once via the stored chain's catch above.
2454
+ enqueueMcpToolsRefresh().catch(() => {});
2455
+ });
2456
+ const configs: MCPConfigMap = {};
2457
+ const sources: MCPSourceMap = {};
2458
+ for (const server of servers) {
2459
+ configs[server.name] = this.#toMcpConfig(server);
2460
+ sources[server.name] = {
2461
+ provider: "acp",
2462
+ providerName: "ACP Client",
2463
+ path: `acp://${server.name}`,
2464
+ level: "project",
2465
+ };
2466
+ }
2467
+
2468
+ const result = await manager.connectServers(configs, sources);
2469
+ if (result.errors.size > 0) {
2470
+ throw new Error(
2471
+ Array.from(result.errors.entries())
2472
+ .map(([name, message]) => `${name}: ${message}`)
2473
+ .join("; "),
2474
+ );
2475
+ }
2476
+
2477
+ record.mcpManager = manager;
2478
+ await enqueueMcpToolsRefresh();
2479
+ }
2480
+
2481
+ #toMcpConfig(server: McpServer): MCPServerConfig {
2482
+ if ("command" in server) {
2483
+ return {
2484
+ type: "stdio",
2485
+ command: server.command,
2486
+ args: server.args,
2487
+ env: this.#toNameValueMap(server.env),
2488
+ };
2489
+ }
2490
+ if (server.type === "http") {
2491
+ return {
2492
+ type: "http",
2493
+ url: server.url,
2494
+ headers: this.#toNameValueMap(server.headers),
2495
+ };
2496
+ }
2497
+ if (server.type === "sse") {
2498
+ return {
2499
+ type: "sse",
2500
+ url: server.url,
2501
+ headers: this.#toNameValueMap(server.headers),
2502
+ };
2503
+ }
2504
+ // The experimental ACP-channel transport (`type: "acp"`) is not advertised in
2505
+ // `mcpCapabilities`, so a spec-compliant client never sends it; reject defensively.
2506
+ throw new Error(`Unsupported MCP server transport: ${server.type}`);
2507
+ }
2508
+
2509
+ #toNameValueMap(values: Array<{ name: string; value: string }>): { [name: string]: string } {
2510
+ const mapped: { [name: string]: string } = {};
2511
+ for (const value of values) {
2512
+ mapped[value.name] = value.value;
2513
+ }
2514
+ return mapped;
2515
+ }
2516
+
2517
+ async #closeManagedSession(sessionId: string, record: ManagedSessionRecord): Promise<void> {
2518
+ record.closedError ??= this.#createPromptLifecycleError("ACP session closed before queued prompt could run");
2519
+ this.#sessions.delete(sessionId);
2520
+ await this.#cancelPromptForClose(record);
2521
+ await this.#disposeSessionRecord(record);
2522
+ }
2523
+
2524
+ async #cancelPromptForClose(record: ManagedSessionRecord): Promise<void> {
2525
+ const promptTurn = record.promptTurn;
2526
+ if (!isPromptTurnInFlight(promptTurn)) {
2527
+ return;
2528
+ }
2529
+ const cleanup = promptTurn.cleanup ?? this.#beginCancelCleanup(record, promptTurn);
2530
+ try {
2531
+ await cleanup;
2532
+ } catch (error) {
2533
+ logger.warn("Failed to abort ACP prompt during session close", { error });
2534
+ }
2535
+ }
2536
+
2537
+ async #disposeSessionRecord(record: ManagedSessionRecord, reason?: postmortem.Reason): Promise<void> {
2538
+ record.lifetimeUnsubscribe?.();
2539
+ if (record.mcpManager) {
2540
+ try {
2541
+ await record.mcpManager.disconnectAll();
2542
+ } catch (error) {
2543
+ logger.warn("Failed to disconnect ACP MCP servers", { error });
2544
+ }
2545
+ record.mcpManager = undefined;
2546
+ }
2547
+ try {
2548
+ await record.session.dispose({ reason });
2549
+ } catch (error) {
2550
+ logger.warn("Failed to dispose ACP session", { error });
2551
+ }
2552
+ }
2553
+
2554
+ async #disposeStandaloneSession(session: AgentSession, reason?: postmortem.Reason): Promise<void> {
2555
+ try {
2556
+ await session.dispose({ reason });
2557
+ } catch (error) {
2558
+ logger.warn("Failed to dispose ACP session", { error });
2559
+ }
2560
+ }
2561
+
2562
+ /** Dispose every session owned by this ACP connection and await persisted teardown. */
2563
+ async dispose(reason?: postmortem.Reason): Promise<void> {
2564
+ if (this.#disposePromise) {
2565
+ await this.#disposePromise;
2566
+ return;
2567
+ }
2568
+
2569
+ this.#disposePromise = (async () => {
2570
+ const records = Array.from(this.#sessions.entries());
2571
+ this.#sessions.clear();
2572
+ await Promise.all(
2573
+ records.map(async ([sessionId, record]) => {
2574
+ try {
2575
+ record.closedError ??= this.#createPromptLifecycleError(
2576
+ "ACP agent disposed before queued prompt could run",
2577
+ );
2578
+ await this.#cancelPromptForClose(record);
2579
+ await this.#disposeSessionRecord(record, reason);
2580
+ } catch (error) {
2581
+ logger.warn("Failed to clean up ACP session", { sessionId, error });
2582
+ }
2583
+ }),
2584
+ );
2585
+
2586
+ const initialSession = this.#initialSession;
2587
+ this.#initialSession = undefined;
2588
+ if (initialSession) {
2589
+ await this.#disposeStandaloneSession(initialSession, reason);
2590
+ }
2591
+ })();
2592
+
2593
+ await this.#disposePromise;
2594
+ }
2595
+ }