@wahack/pi-coding-agent 15.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1152) hide show
  1. package/CHANGELOG.md +10031 -0
  2. package/README.md +36 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +104 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/extensions/README.md +142 -0
  7. package/examples/extensions/api-demo.ts +79 -0
  8. package/examples/extensions/chalk-logger.ts +25 -0
  9. package/examples/extensions/hello.ts +31 -0
  10. package/examples/extensions/pirate.ts +43 -0
  11. package/examples/extensions/plan-mode.ts +549 -0
  12. package/examples/extensions/reload-runtime.ts +38 -0
  13. package/examples/extensions/thinking-note.ts +13 -0
  14. package/examples/extensions/tools.ts +145 -0
  15. package/examples/extensions/with-deps/index.ts +36 -0
  16. package/examples/extensions/with-deps/package-lock.json +31 -0
  17. package/examples/extensions/with-deps/package.json +17 -0
  18. package/examples/hooks/README.md +56 -0
  19. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  20. package/examples/hooks/confirm-destructive.ts +58 -0
  21. package/examples/hooks/custom-compaction.ts +115 -0
  22. package/examples/hooks/dirty-repo-guard.ts +51 -0
  23. package/examples/hooks/file-trigger.ts +40 -0
  24. package/examples/hooks/git-checkpoint.ts +52 -0
  25. package/examples/hooks/handoff.ts +149 -0
  26. package/examples/hooks/permission-gate.ts +33 -0
  27. package/examples/hooks/protected-paths.ts +29 -0
  28. package/examples/hooks/qna.ts +118 -0
  29. package/examples/hooks/status-line.ts +39 -0
  30. package/examples/sdk/01-minimal.ts +21 -0
  31. package/examples/sdk/02-custom-model.ts +49 -0
  32. package/examples/sdk/03-custom-prompt.ts +46 -0
  33. package/examples/sdk/04-skills.ts +43 -0
  34. package/examples/sdk/06-extensions.ts +82 -0
  35. package/examples/sdk/06-hooks.ts +61 -0
  36. package/examples/sdk/07-context-files.ts +35 -0
  37. package/examples/sdk/08-prompt-templates.ts +41 -0
  38. package/examples/sdk/08-slash-commands.ts +46 -0
  39. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  40. package/examples/sdk/11-sessions.ts +47 -0
  41. package/examples/sdk/12-redis-sessions.ts +54 -0
  42. package/examples/sdk/13-sql-sessions.ts +61 -0
  43. package/examples/sdk/README.md +172 -0
  44. package/package.json +554 -0
  45. package/scripts/build-binary.ts +100 -0
  46. package/scripts/bundle-dist.ts +90 -0
  47. package/scripts/format-prompts.ts +68 -0
  48. package/scripts/generate-docs-index.ts +40 -0
  49. package/scripts/generate-template.ts +33 -0
  50. package/scripts/omp +42 -0
  51. package/scripts/omp.ts +19 -0
  52. package/src/async/index.ts +1 -0
  53. package/src/async/job-manager.ts +625 -0
  54. package/src/auto-thinking/classifier.ts +185 -0
  55. package/src/autoresearch/command-resume.md +14 -0
  56. package/src/autoresearch/dashboard.ts +436 -0
  57. package/src/autoresearch/git.ts +319 -0
  58. package/src/autoresearch/helpers.ts +218 -0
  59. package/src/autoresearch/index.ts +536 -0
  60. package/src/autoresearch/prompt-setup.md +43 -0
  61. package/src/autoresearch/prompt.md +103 -0
  62. package/src/autoresearch/resume-message.md +10 -0
  63. package/src/autoresearch/state.ts +273 -0
  64. package/src/autoresearch/storage.ts +699 -0
  65. package/src/autoresearch/tools/init-experiment.ts +272 -0
  66. package/src/autoresearch/tools/log-experiment.ts +524 -0
  67. package/src/autoresearch/tools/run-experiment.ts +407 -0
  68. package/src/autoresearch/tools/update-notes.ts +109 -0
  69. package/src/autoresearch/types.ts +168 -0
  70. package/src/bun-imports.d.ts +28 -0
  71. package/src/capability/context-file.ts +44 -0
  72. package/src/capability/extension-module.ts +34 -0
  73. package/src/capability/extension.ts +47 -0
  74. package/src/capability/fs.ts +117 -0
  75. package/src/capability/hook.ts +40 -0
  76. package/src/capability/index.ts +436 -0
  77. package/src/capability/instruction.ts +37 -0
  78. package/src/capability/mcp.ts +74 -0
  79. package/src/capability/prompt.ts +35 -0
  80. package/src/capability/rule-buckets.ts +66 -0
  81. package/src/capability/rule.ts +261 -0
  82. package/src/capability/settings.ts +34 -0
  83. package/src/capability/skill.ts +63 -0
  84. package/src/capability/slash-command.ts +40 -0
  85. package/src/capability/ssh.ts +41 -0
  86. package/src/capability/system-prompt.ts +34 -0
  87. package/src/capability/tool.ts +38 -0
  88. package/src/capability/types.ts +168 -0
  89. package/src/cli/agents-cli.ts +138 -0
  90. package/src/cli/args.ts +340 -0
  91. package/src/cli/auth-broker-cli.ts +895 -0
  92. package/src/cli/auth-gateway-cli.ts +611 -0
  93. package/src/cli/classify-install-target.ts +76 -0
  94. package/src/cli/claude-trace-cli.ts +795 -0
  95. package/src/cli/commands/init-xdg.ts +27 -0
  96. package/src/cli/completion-gen.ts +550 -0
  97. package/src/cli/config-cli.ts +418 -0
  98. package/src/cli/dry-balance-cli.ts +856 -0
  99. package/src/cli/extension-flags.ts +48 -0
  100. package/src/cli/file-processor.ts +133 -0
  101. package/src/cli/gallery-cli.ts +230 -0
  102. package/src/cli/gallery-fixtures/agentic.ts +407 -0
  103. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  104. package/src/cli/gallery-fixtures/edit.ts +194 -0
  105. package/src/cli/gallery-fixtures/fs.ts +220 -0
  106. package/src/cli/gallery-fixtures/index.ts +40 -0
  107. package/src/cli/gallery-fixtures/interaction.ts +49 -0
  108. package/src/cli/gallery-fixtures/memory.ts +81 -0
  109. package/src/cli/gallery-fixtures/misc.ts +250 -0
  110. package/src/cli/gallery-fixtures/search.ts +213 -0
  111. package/src/cli/gallery-fixtures/shell.ts +167 -0
  112. package/src/cli/gallery-fixtures/types.ts +57 -0
  113. package/src/cli/gallery-fixtures/web.ts +158 -0
  114. package/src/cli/gallery-screenshot.ts +279 -0
  115. package/src/cli/grep-cli.ts +160 -0
  116. package/src/cli/grievances-cli.ts +256 -0
  117. package/src/cli/initial-message.ts +58 -0
  118. package/src/cli/list-models.ts +194 -0
  119. package/src/cli/plugin-cli.ts +996 -0
  120. package/src/cli/read-cli.ts +57 -0
  121. package/src/cli/session-picker.ts +79 -0
  122. package/src/cli/setup-cli.ts +231 -0
  123. package/src/cli/shell-cli.ts +176 -0
  124. package/src/cli/ssh-cli.ts +179 -0
  125. package/src/cli/startup-cwd.ts +68 -0
  126. package/src/cli/stats-cli.ts +238 -0
  127. package/src/cli/tiny-models-cli.ts +127 -0
  128. package/src/cli/update-cli.ts +611 -0
  129. package/src/cli/usage-cli.ts +603 -0
  130. package/src/cli/web-search-cli.ts +132 -0
  131. package/src/cli/worktree-cli.ts +291 -0
  132. package/src/cli-commands.ts +79 -0
  133. package/src/cli.ts +200 -0
  134. package/src/commands/acp.ts +24 -0
  135. package/src/commands/agents.ts +57 -0
  136. package/src/commands/auth-broker.ts +99 -0
  137. package/src/commands/auth-gateway.ts +69 -0
  138. package/src/commands/commit.ts +46 -0
  139. package/src/commands/complete.ts +66 -0
  140. package/src/commands/completions.ts +60 -0
  141. package/src/commands/config.ts +51 -0
  142. package/src/commands/dry-balance.ts +43 -0
  143. package/src/commands/gallery.ts +52 -0
  144. package/src/commands/grep.ts +48 -0
  145. package/src/commands/grievances.ts +51 -0
  146. package/src/commands/install.ts +107 -0
  147. package/src/commands/launch.ts +169 -0
  148. package/src/commands/plugin.ts +78 -0
  149. package/src/commands/read.ts +38 -0
  150. package/src/commands/setup.ts +67 -0
  151. package/src/commands/shell.ts +29 -0
  152. package/src/commands/ssh.ts +60 -0
  153. package/src/commands/stats.ts +29 -0
  154. package/src/commands/tiny-models.ts +36 -0
  155. package/src/commands/update.ts +21 -0
  156. package/src/commands/usage.ts +35 -0
  157. package/src/commands/web-search.ts +42 -0
  158. package/src/commands/worktree.ts +56 -0
  159. package/src/commit/agentic/agent.ts +317 -0
  160. package/src/commit/agentic/fallback.ts +96 -0
  161. package/src/commit/agentic/index.ts +355 -0
  162. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  163. package/src/commit/agentic/prompts/session-user.md +25 -0
  164. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  165. package/src/commit/agentic/prompts/system.md +38 -0
  166. package/src/commit/agentic/state.ts +60 -0
  167. package/src/commit/agentic/tools/analyze-file.ts +146 -0
  168. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  169. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  170. package/src/commit/agentic/tools/git-overview.ts +81 -0
  171. package/src/commit/agentic/tools/index.ts +54 -0
  172. package/src/commit/agentic/tools/propose-changelog.ts +144 -0
  173. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  174. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  175. package/src/commit/agentic/tools/schemas.ts +23 -0
  176. package/src/commit/agentic/tools/split-commit.ts +245 -0
  177. package/src/commit/agentic/topo-sort.ts +44 -0
  178. package/src/commit/agentic/trivial.ts +51 -0
  179. package/src/commit/agentic/validation.ts +183 -0
  180. package/src/commit/analysis/conventional.ts +64 -0
  181. package/src/commit/analysis/index.ts +4 -0
  182. package/src/commit/analysis/scope.ts +242 -0
  183. package/src/commit/analysis/summary.ts +105 -0
  184. package/src/commit/analysis/validation.ts +66 -0
  185. package/src/commit/changelog/detect.ts +40 -0
  186. package/src/commit/changelog/generate.ts +97 -0
  187. package/src/commit/changelog/index.ts +234 -0
  188. package/src/commit/changelog/parse.ts +44 -0
  189. package/src/commit/cli.ts +85 -0
  190. package/src/commit/git/diff.ts +148 -0
  191. package/src/commit/index.ts +5 -0
  192. package/src/commit/map-reduce/index.ts +69 -0
  193. package/src/commit/map-reduce/map-phase.ts +193 -0
  194. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  195. package/src/commit/map-reduce/utils.ts +9 -0
  196. package/src/commit/message.ts +11 -0
  197. package/src/commit/model-selection.ts +92 -0
  198. package/src/commit/pipeline.ts +243 -0
  199. package/src/commit/prompts/analysis-system.md +148 -0
  200. package/src/commit/prompts/analysis-user.md +38 -0
  201. package/src/commit/prompts/changelog-system.md +50 -0
  202. package/src/commit/prompts/changelog-user.md +18 -0
  203. package/src/commit/prompts/file-observer-system.md +24 -0
  204. package/src/commit/prompts/file-observer-user.md +8 -0
  205. package/src/commit/prompts/reduce-system.md +50 -0
  206. package/src/commit/prompts/reduce-user.md +17 -0
  207. package/src/commit/prompts/summary-retry.md +3 -0
  208. package/src/commit/prompts/summary-system.md +38 -0
  209. package/src/commit/prompts/summary-user.md +13 -0
  210. package/src/commit/prompts/types-description.md +2 -0
  211. package/src/commit/shared-llm.ts +77 -0
  212. package/src/commit/types.ts +118 -0
  213. package/src/commit/utils/exclusions.ts +42 -0
  214. package/src/commit/utils.ts +58 -0
  215. package/src/config/api-key-resolver.ts +60 -0
  216. package/src/config/append-only-context-mode.ts +31 -0
  217. package/src/config/config-file.ts +317 -0
  218. package/src/config/file-lock.ts +164 -0
  219. package/src/config/keybindings.ts +628 -0
  220. package/src/config/mcp-schema.json +230 -0
  221. package/src/config/model-discovery.ts +554 -0
  222. package/src/config/model-registry.ts +2090 -0
  223. package/src/config/model-resolver.ts +1502 -0
  224. package/src/config/model-roles.ts +74 -0
  225. package/src/config/models-config-schema.ts +226 -0
  226. package/src/config/models-config.ts +129 -0
  227. package/src/config/prompt-templates.ts +185 -0
  228. package/src/config/resolve-config-value.ts +94 -0
  229. package/src/config/settings-schema.ts +3530 -0
  230. package/src/config/settings.ts +1178 -0
  231. package/src/config.ts +242 -0
  232. package/src/cursor.ts +340 -0
  233. package/src/dap/client.ts +760 -0
  234. package/src/dap/config.ts +189 -0
  235. package/src/dap/defaults.json +212 -0
  236. package/src/dap/index.ts +4 -0
  237. package/src/dap/session.ts +1441 -0
  238. package/src/dap/types.ts +610 -0
  239. package/src/debug/index.ts +515 -0
  240. package/src/debug/log-formatting.ts +58 -0
  241. package/src/debug/log-viewer.ts +908 -0
  242. package/src/debug/profiler.ts +162 -0
  243. package/src/debug/protocol-probe.ts +267 -0
  244. package/src/debug/raw-sse-buffer.ts +273 -0
  245. package/src/debug/raw-sse.ts +292 -0
  246. package/src/debug/report-bundle.ts +374 -0
  247. package/src/debug/system-info.ts +111 -0
  248. package/src/debug/terminal-info.ts +124 -0
  249. package/src/discovery/agents-md.ts +67 -0
  250. package/src/discovery/agents.ts +230 -0
  251. package/src/discovery/at-imports.ts +273 -0
  252. package/src/discovery/builtin-defaults.ts +39 -0
  253. package/src/discovery/builtin-rules/index.ts +54 -0
  254. package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
  255. package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
  256. package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
  257. package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
  258. package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
  259. package/src/discovery/builtin-rules/rs-result-type.md +19 -0
  260. package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
  261. package/src/discovery/builtin-rules/ts-import-type.md +42 -0
  262. package/src/discovery/builtin-rules/ts-no-any.md +56 -0
  263. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +44 -0
  264. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
  265. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  266. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  267. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  268. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
  269. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  270. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  271. package/src/discovery/builtin.ts +906 -0
  272. package/src/discovery/claude-plugins.ts +386 -0
  273. package/src/discovery/claude.ts +584 -0
  274. package/src/discovery/cline.ts +83 -0
  275. package/src/discovery/codex.ts +522 -0
  276. package/src/discovery/cursor.ts +220 -0
  277. package/src/discovery/gemini.ts +383 -0
  278. package/src/discovery/github.ts +154 -0
  279. package/src/discovery/helpers.ts +1016 -0
  280. package/src/discovery/index.ts +81 -0
  281. package/src/discovery/mcp-json.ts +171 -0
  282. package/src/discovery/omp-extension-roots.ts +190 -0
  283. package/src/discovery/omp-plugins.ts +383 -0
  284. package/src/discovery/opencode.ts +398 -0
  285. package/src/discovery/plugin-dir-roots.ts +28 -0
  286. package/src/discovery/ssh.ts +153 -0
  287. package/src/discovery/substitute-plugin-root.ts +29 -0
  288. package/src/discovery/vscode.ts +105 -0
  289. package/src/discovery/windsurf.ts +147 -0
  290. package/src/edit/apply-patch/index.ts +87 -0
  291. package/src/edit/apply-patch/parser.ts +174 -0
  292. package/src/edit/diff.ts +999 -0
  293. package/src/edit/file-snapshot-store.ts +91 -0
  294. package/src/edit/hashline/block-resolver.ts +33 -0
  295. package/src/edit/hashline/diff.ts +290 -0
  296. package/src/edit/hashline/execute.ts +242 -0
  297. package/src/edit/hashline/filesystem.ts +130 -0
  298. package/src/edit/hashline/index.ts +5 -0
  299. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  300. package/src/edit/hashline/params.ts +18 -0
  301. package/src/edit/index.ts +571 -0
  302. package/src/edit/modes/apply-patch.lark +19 -0
  303. package/src/edit/modes/apply-patch.ts +53 -0
  304. package/src/edit/modes/patch.ts +1891 -0
  305. package/src/edit/modes/replace.ts +1137 -0
  306. package/src/edit/normalize.ts +345 -0
  307. package/src/edit/notebook.ts +242 -0
  308. package/src/edit/read-file.ts +25 -0
  309. package/src/edit/renderer.ts +769 -0
  310. package/src/edit/streaming.ts +517 -0
  311. package/src/eval/__tests__/agent-bridge.test.ts +708 -0
  312. package/src/eval/__tests__/bridge-timeout.test.ts +64 -0
  313. package/src/eval/__tests__/budget-bridge.test.ts +69 -0
  314. package/src/eval/__tests__/completion-bridge.test.ts +412 -0
  315. package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
  316. package/src/eval/__tests__/idle-timeout.test.ts +80 -0
  317. package/src/eval/__tests__/js-context-manager.test.ts +241 -0
  318. package/src/eval/__tests__/kernel-spawn.test.ts +103 -0
  319. package/src/eval/agent-bridge.ts +319 -0
  320. package/src/eval/backend.ts +71 -0
  321. package/src/eval/bridge-timeout.ts +44 -0
  322. package/src/eval/budget-bridge.ts +48 -0
  323. package/src/eval/completion-bridge.ts +207 -0
  324. package/src/eval/concurrency-bridge.ts +34 -0
  325. package/src/eval/idle-timeout.ts +91 -0
  326. package/src/eval/index.ts +4 -0
  327. package/src/eval/js/context-manager.ts +502 -0
  328. package/src/eval/js/executor.ts +173 -0
  329. package/src/eval/js/index.ts +51 -0
  330. package/src/eval/js/shared/helpers.ts +283 -0
  331. package/src/eval/js/shared/indirect-eval.ts +30 -0
  332. package/src/eval/js/shared/local-module-loader.ts +342 -0
  333. package/src/eval/js/shared/prelude.ts +2 -0
  334. package/src/eval/js/shared/prelude.txt +246 -0
  335. package/src/eval/js/shared/rewrite-imports.ts +532 -0
  336. package/src/eval/js/shared/runtime.ts +352 -0
  337. package/src/eval/js/shared/types.ts +18 -0
  338. package/src/eval/js/tool-bridge.ts +162 -0
  339. package/src/eval/js/worker-core.ts +132 -0
  340. package/src/eval/js/worker-entry.ts +30 -0
  341. package/src/eval/js/worker-protocol.ts +47 -0
  342. package/src/eval/py/__tests__/prelude.test.ts +19 -0
  343. package/src/eval/py/display.ts +71 -0
  344. package/src/eval/py/executor.ts +742 -0
  345. package/src/eval/py/index.ts +68 -0
  346. package/src/eval/py/kernel.ts +748 -0
  347. package/src/eval/py/prelude.py +658 -0
  348. package/src/eval/py/prelude.ts +3 -0
  349. package/src/eval/py/runner.py +1133 -0
  350. package/src/eval/py/runtime.ts +276 -0
  351. package/src/eval/py/spawn-options.ts +126 -0
  352. package/src/eval/py/tool-bridge.ts +182 -0
  353. package/src/eval/session-id.ts +8 -0
  354. package/src/eval/types.ts +48 -0
  355. package/src/exa/index.ts +2 -0
  356. package/src/exa/mcp-client.ts +370 -0
  357. package/src/exa/types.ts +69 -0
  358. package/src/exec/bash-executor.ts +419 -0
  359. package/src/exec/exec.ts +53 -0
  360. package/src/exec/non-interactive-env.ts +48 -0
  361. package/src/export/custom-share.ts +65 -0
  362. package/src/export/html/index.ts +164 -0
  363. package/src/export/html/template.css +1051 -0
  364. package/src/export/html/template.generated.ts +2 -0
  365. package/src/export/html/template.html +46 -0
  366. package/src/export/html/template.js +2271 -0
  367. package/src/export/html/template.macro.ts +25 -0
  368. package/src/export/html/vendor/highlight.min.js +1213 -0
  369. package/src/export/html/vendor/marked.min.js +6 -0
  370. package/src/export/ttsr.ts +583 -0
  371. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  372. package/src/extensibility/custom-commands/bundled/review/index.ts +489 -0
  373. package/src/extensibility/custom-commands/index.ts +2 -0
  374. package/src/extensibility/custom-commands/loader.ts +238 -0
  375. package/src/extensibility/custom-commands/types.ts +113 -0
  376. package/src/extensibility/custom-tools/index.ts +7 -0
  377. package/src/extensibility/custom-tools/loader.ts +269 -0
  378. package/src/extensibility/custom-tools/types.ts +270 -0
  379. package/src/extensibility/custom-tools/wrapper.ts +47 -0
  380. package/src/extensibility/extensions/compact-handler.ts +40 -0
  381. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  382. package/src/extensibility/extensions/index.ts +16 -0
  383. package/src/extensibility/extensions/loader.ts +572 -0
  384. package/src/extensibility/extensions/runner.ts +922 -0
  385. package/src/extensibility/extensions/types.ts +1322 -0
  386. package/src/extensibility/extensions/wrapper.ts +223 -0
  387. package/src/extensibility/hooks/index.ts +5 -0
  388. package/src/extensibility/hooks/loader.ts +257 -0
  389. package/src/extensibility/hooks/runner.ts +425 -0
  390. package/src/extensibility/hooks/tool-wrapper.ts +107 -0
  391. package/src/extensibility/hooks/types.ts +606 -0
  392. package/src/extensibility/legacy-pi-ai-shim.ts +24 -0
  393. package/src/extensibility/legacy-pi-coding-agent-shim.ts +15 -0
  394. package/src/extensibility/plugins/doctor.ts +65 -0
  395. package/src/extensibility/plugins/git-url.ts +367 -0
  396. package/src/extensibility/plugins/index.ts +9 -0
  397. package/src/extensibility/plugins/installer.ts +192 -0
  398. package/src/extensibility/plugins/legacy-pi-compat.ts +682 -0
  399. package/src/extensibility/plugins/loader.ts +313 -0
  400. package/src/extensibility/plugins/manager.ts +827 -0
  401. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  402. package/src/extensibility/plugins/marketplace/fetcher.ts +317 -0
  403. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  404. package/src/extensibility/plugins/marketplace/manager.ts +770 -0
  405. package/src/extensibility/plugins/marketplace/registry.ts +196 -0
  406. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  407. package/src/extensibility/plugins/marketplace/types.ts +191 -0
  408. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  409. package/src/extensibility/plugins/parser.ts +105 -0
  410. package/src/extensibility/plugins/types.ts +194 -0
  411. package/src/extensibility/shared-events.ts +343 -0
  412. package/src/extensibility/skills.ts +312 -0
  413. package/src/extensibility/slash-commands.ts +227 -0
  414. package/src/extensibility/tool-proxy.ts +25 -0
  415. package/src/extensibility/typebox.ts +418 -0
  416. package/src/extensibility/utils.ts +44 -0
  417. package/src/goals/index.ts +3 -0
  418. package/src/goals/runtime.ts +528 -0
  419. package/src/goals/state.ts +37 -0
  420. package/src/goals/tools/goal-tool.ts +251 -0
  421. package/src/hindsight/backend.ts +354 -0
  422. package/src/hindsight/bank.ts +156 -0
  423. package/src/hindsight/client.ts +598 -0
  424. package/src/hindsight/config.ts +175 -0
  425. package/src/hindsight/content.ts +210 -0
  426. package/src/hindsight/index.ts +8 -0
  427. package/src/hindsight/mental-models.ts +429 -0
  428. package/src/hindsight/seeds.json +32 -0
  429. package/src/hindsight/state.ts +488 -0
  430. package/src/hindsight/transcript.ts +71 -0
  431. package/src/index.ts +59 -0
  432. package/src/internal-urls/agent-protocol.ts +146 -0
  433. package/src/internal-urls/artifact-protocol.ts +107 -0
  434. package/src/internal-urls/docs-index.generated.ts +106 -0
  435. package/src/internal-urls/history-protocol.ts +113 -0
  436. package/src/internal-urls/index.ts +25 -0
  437. package/src/internal-urls/issue-pr-protocol.ts +584 -0
  438. package/src/internal-urls/json-query.ts +126 -0
  439. package/src/internal-urls/local-protocol.ts +287 -0
  440. package/src/internal-urls/mcp-protocol.ts +151 -0
  441. package/src/internal-urls/memory-protocol.ts +169 -0
  442. package/src/internal-urls/omp-protocol.ts +93 -0
  443. package/src/internal-urls/parse.ts +72 -0
  444. package/src/internal-urls/registry-helpers.ts +25 -0
  445. package/src/internal-urls/router.ts +105 -0
  446. package/src/internal-urls/rule-protocol.ts +45 -0
  447. package/src/internal-urls/skill-protocol.ts +96 -0
  448. package/src/internal-urls/types.ts +152 -0
  449. package/src/internal-urls/vault-protocol.ts +936 -0
  450. package/src/irc/bus.ts +292 -0
  451. package/src/lib/xai-http.ts +124 -0
  452. package/src/lsp/client.ts +1193 -0
  453. package/src/lsp/clients/biome-client.ts +264 -0
  454. package/src/lsp/clients/index.ts +50 -0
  455. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  456. package/src/lsp/clients/swiftlint-client.ts +120 -0
  457. package/src/lsp/config.ts +502 -0
  458. package/src/lsp/defaults.json +493 -0
  459. package/src/lsp/diagnostics-ledger.ts +51 -0
  460. package/src/lsp/edits.ts +267 -0
  461. package/src/lsp/index.ts +2477 -0
  462. package/src/lsp/lspmux.ts +233 -0
  463. package/src/lsp/render.ts +694 -0
  464. package/src/lsp/startup-events.ts +13 -0
  465. package/src/lsp/types.ts +455 -0
  466. package/src/lsp/utils.ts +718 -0
  467. package/src/main.ts +1325 -0
  468. package/src/mcp/client.ts +484 -0
  469. package/src/mcp/config-writer.ts +225 -0
  470. package/src/mcp/config.ts +365 -0
  471. package/src/mcp/index.ts +29 -0
  472. package/src/mcp/json-rpc.ts +122 -0
  473. package/src/mcp/loader.ts +124 -0
  474. package/src/mcp/manager.ts +1275 -0
  475. package/src/mcp/oauth-discovery.ts +442 -0
  476. package/src/mcp/oauth-flow.ts +442 -0
  477. package/src/mcp/render.ts +132 -0
  478. package/src/mcp/smithery-auth.ts +104 -0
  479. package/src/mcp/smithery-connect.ts +145 -0
  480. package/src/mcp/smithery-registry.ts +477 -0
  481. package/src/mcp/timeout.ts +59 -0
  482. package/src/mcp/tool-bridge.ts +426 -0
  483. package/src/mcp/tool-cache.ts +117 -0
  484. package/src/mcp/transports/http.ts +519 -0
  485. package/src/mcp/transports/index.ts +6 -0
  486. package/src/mcp/transports/stdio.ts +528 -0
  487. package/src/mcp/types.ts +423 -0
  488. package/src/memories/index.ts +1150 -0
  489. package/src/memories/storage.ts +577 -0
  490. package/src/memory-backend/index.ts +18 -0
  491. package/src/memory-backend/local-backend.ts +39 -0
  492. package/src/memory-backend/off-backend.ts +25 -0
  493. package/src/memory-backend/resolve.ts +25 -0
  494. package/src/memory-backend/runtime.ts +66 -0
  495. package/src/memory-backend/types.ts +166 -0
  496. package/src/mnemopi/backend.ts +547 -0
  497. package/src/mnemopi/config.ts +160 -0
  498. package/src/mnemopi/index.ts +3 -0
  499. package/src/mnemopi/state.ts +584 -0
  500. package/src/modes/acp/acp-agent.ts +2407 -0
  501. package/src/modes/acp/acp-client-bridge.ts +154 -0
  502. package/src/modes/acp/acp-event-mapper.ts +929 -0
  503. package/src/modes/acp/acp-mode.ts +23 -0
  504. package/src/modes/acp/index.ts +2 -0
  505. package/src/modes/acp/terminal-auth.ts +37 -0
  506. package/src/modes/components/agent-dashboard.ts +1206 -0
  507. package/src/modes/components/agent-hub.ts +1071 -0
  508. package/src/modes/components/assistant-message.ts +307 -0
  509. package/src/modes/components/bash-execution.ts +220 -0
  510. package/src/modes/components/bordered-loader.ts +41 -0
  511. package/src/modes/components/branch-summary-message.ts +45 -0
  512. package/src/modes/components/btw-panel.ts +104 -0
  513. package/src/modes/components/chat-block.ts +111 -0
  514. package/src/modes/components/compaction-summary-message.ts +87 -0
  515. package/src/modes/components/copy-selector.ts +206 -0
  516. package/src/modes/components/countdown-timer.ts +75 -0
  517. package/src/modes/components/custom-editor.ts +398 -0
  518. package/src/modes/components/custom-message.ts +63 -0
  519. package/src/modes/components/diff.ts +277 -0
  520. package/src/modes/components/dynamic-border.ts +34 -0
  521. package/src/modes/components/error-banner.ts +33 -0
  522. package/src/modes/components/eval-execution.ts +158 -0
  523. package/src/modes/components/execution-shared.ts +101 -0
  524. package/src/modes/components/extensions/extension-dashboard.ts +399 -0
  525. package/src/modes/components/extensions/extension-list.ts +502 -0
  526. package/src/modes/components/extensions/index.ts +9 -0
  527. package/src/modes/components/extensions/inspector-panel.ts +317 -0
  528. package/src/modes/components/extensions/state-manager.ts +627 -0
  529. package/src/modes/components/extensions/types.ts +186 -0
  530. package/src/modes/components/footer.ts +274 -0
  531. package/src/modes/components/history-search.ts +280 -0
  532. package/src/modes/components/hook-editor.ts +167 -0
  533. package/src/modes/components/hook-input.ts +87 -0
  534. package/src/modes/components/hook-message.ts +66 -0
  535. package/src/modes/components/hook-selector.ts +660 -0
  536. package/src/modes/components/index.ts +38 -0
  537. package/src/modes/components/keybinding-hints.ts +65 -0
  538. package/src/modes/components/late-diagnostics-message.ts +60 -0
  539. package/src/modes/components/login-dialog.ts +164 -0
  540. package/src/modes/components/mcp-add-wizard.ts +1340 -0
  541. package/src/modes/components/message-frame.ts +88 -0
  542. package/src/modes/components/model-selector.ts +1271 -0
  543. package/src/modes/components/oauth-selector.ts +368 -0
  544. package/src/modes/components/omfg-panel.ts +141 -0
  545. package/src/modes/components/overlay-box.ts +108 -0
  546. package/src/modes/components/plan-review-overlay.ts +820 -0
  547. package/src/modes/components/plan-toc.ts +138 -0
  548. package/src/modes/components/plugin-selector.ts +95 -0
  549. package/src/modes/components/plugin-settings.ts +722 -0
  550. package/src/modes/components/queue-mode-selector.ts +56 -0
  551. package/src/modes/components/read-tool-group.ts +670 -0
  552. package/src/modes/components/segment-track.ts +52 -0
  553. package/src/modes/components/session-selector.ts +625 -0
  554. package/src/modes/components/settings-defs.ts +189 -0
  555. package/src/modes/components/settings-selector.ts +651 -0
  556. package/src/modes/components/show-images-selector.ts +45 -0
  557. package/src/modes/components/skill-message.ts +89 -0
  558. package/src/modes/components/status-line/component.ts +869 -0
  559. package/src/modes/components/status-line/context-thresholds.ts +79 -0
  560. package/src/modes/components/status-line/git-utils.ts +42 -0
  561. package/src/modes/components/status-line/index.ts +5 -0
  562. package/src/modes/components/status-line/presets.ts +106 -0
  563. package/src/modes/components/status-line/segments.ts +584 -0
  564. package/src/modes/components/status-line/separators.ts +55 -0
  565. package/src/modes/components/status-line/token-rate.ts +66 -0
  566. package/src/modes/components/status-line/types.ts +108 -0
  567. package/src/modes/components/theme-selector.ts +63 -0
  568. package/src/modes/components/thinking-selector.ts +52 -0
  569. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  570. package/src/modes/components/tips.txt +19 -0
  571. package/src/modes/components/todo-reminder.ts +38 -0
  572. package/src/modes/components/tool-execution.ts +1024 -0
  573. package/src/modes/components/transcript-container.ts +608 -0
  574. package/src/modes/components/tree-selector.ts +978 -0
  575. package/src/modes/components/ttsr-notification.ts +122 -0
  576. package/src/modes/components/user-message-selector.ts +227 -0
  577. package/src/modes/components/user-message.ts +66 -0
  578. package/src/modes/components/visual-truncate.ts +63 -0
  579. package/src/modes/components/welcome.ts +493 -0
  580. package/src/modes/controllers/btw-controller.ts +105 -0
  581. package/src/modes/controllers/command-controller-shared.ts +109 -0
  582. package/src/modes/controllers/command-controller.ts +1566 -0
  583. package/src/modes/controllers/event-controller.ts +1054 -0
  584. package/src/modes/controllers/extension-ui-controller.ts +886 -0
  585. package/src/modes/controllers/input-controller.ts +1073 -0
  586. package/src/modes/controllers/mcp-command-controller.ts +2017 -0
  587. package/src/modes/controllers/omfg-controller.ts +283 -0
  588. package/src/modes/controllers/omfg-rule.ts +647 -0
  589. package/src/modes/controllers/selector-controller.ts +1108 -0
  590. package/src/modes/controllers/ssh-command-controller.ts +384 -0
  591. package/src/modes/controllers/streaming-reveal.ts +279 -0
  592. package/src/modes/controllers/tan-command-controller.ts +173 -0
  593. package/src/modes/controllers/todo-command-controller.ts +485 -0
  594. package/src/modes/data/emojis.json +1 -0
  595. package/src/modes/emoji-autocomplete.ts +285 -0
  596. package/src/modes/gradient-highlight.ts +87 -0
  597. package/src/modes/image-references.ts +117 -0
  598. package/src/modes/index.ts +17 -0
  599. package/src/modes/interactive-mode.ts +3370 -0
  600. package/src/modes/internal-url-autocomplete.ts +143 -0
  601. package/src/modes/loop-limit.ts +140 -0
  602. package/src/modes/magic-keywords.ts +20 -0
  603. package/src/modes/markdown-prose.ts +247 -0
  604. package/src/modes/oauth-manual-input.ts +69 -0
  605. package/src/modes/orchestrate.ts +42 -0
  606. package/src/modes/print-mode.ts +126 -0
  607. package/src/modes/prompt-action-autocomplete.ts +260 -0
  608. package/src/modes/rpc/host-tools.ts +186 -0
  609. package/src/modes/rpc/host-uris.ts +235 -0
  610. package/src/modes/rpc/rpc-client.ts +963 -0
  611. package/src/modes/rpc/rpc-mode.ts +947 -0
  612. package/src/modes/rpc/rpc-subagents.ts +265 -0
  613. package/src/modes/rpc/rpc-types.ts +458 -0
  614. package/src/modes/runtime-init.ts +116 -0
  615. package/src/modes/session-observer-registry.ts +146 -0
  616. package/src/modes/setup-version.ts +11 -0
  617. package/src/modes/setup-wizard/index.ts +99 -0
  618. package/src/modes/setup-wizard/lazy.ts +16 -0
  619. package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
  620. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  621. package/src/modes/setup-wizard/scenes/providers.ts +69 -0
  622. package/src/modes/setup-wizard/scenes/sign-in.ts +205 -0
  623. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  624. package/src/modes/setup-wizard/scenes/theme.ts +299 -0
  625. package/src/modes/setup-wizard/scenes/types.ts +48 -0
  626. package/src/modes/setup-wizard/scenes/web-search.ts +129 -0
  627. package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
  628. package/src/modes/shared.ts +47 -0
  629. package/src/modes/theme/dark.json +95 -0
  630. package/src/modes/theme/defaults/alabaster.json +93 -0
  631. package/src/modes/theme/defaults/amethyst.json +96 -0
  632. package/src/modes/theme/defaults/anthracite.json +93 -0
  633. package/src/modes/theme/defaults/basalt.json +91 -0
  634. package/src/modes/theme/defaults/birch.json +95 -0
  635. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  636. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  637. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  638. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  639. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  640. package/src/modes/theme/defaults/dark-copper.json +95 -0
  641. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  642. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  643. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  644. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  645. package/src/modes/theme/defaults/dark-ember.json +95 -0
  646. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  647. package/src/modes/theme/defaults/dark-forest.json +96 -0
  648. package/src/modes/theme/defaults/dark-github.json +105 -0
  649. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  650. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  651. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  652. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  653. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  654. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  655. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  656. package/src/modes/theme/defaults/dark-nord.json +97 -0
  657. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  658. package/src/modes/theme/defaults/dark-one.json +100 -0
  659. package/src/modes/theme/defaults/dark-poimandres.json +142 -0
  660. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  661. package/src/modes/theme/defaults/dark-reef.json +91 -0
  662. package/src/modes/theme/defaults/dark-retro.json +92 -0
  663. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  664. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  665. package/src/modes/theme/defaults/dark-slate.json +95 -0
  666. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  667. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  668. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  669. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  670. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  671. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  672. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  673. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  674. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  675. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  676. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  677. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  678. package/src/modes/theme/defaults/graphite.json +92 -0
  679. package/src/modes/theme/defaults/index.ts +199 -0
  680. package/src/modes/theme/defaults/light-arctic.json +107 -0
  681. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  682. package/src/modes/theme/defaults/light-canyon.json +91 -0
  683. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  684. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  685. package/src/modes/theme/defaults/light-coral.json +95 -0
  686. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  687. package/src/modes/theme/defaults/light-dawn.json +90 -0
  688. package/src/modes/theme/defaults/light-dunes.json +91 -0
  689. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  690. package/src/modes/theme/defaults/light-forest.json +100 -0
  691. package/src/modes/theme/defaults/light-frost.json +95 -0
  692. package/src/modes/theme/defaults/light-github.json +115 -0
  693. package/src/modes/theme/defaults/light-glacier.json +91 -0
  694. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  695. package/src/modes/theme/defaults/light-haze.json +90 -0
  696. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  697. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  698. package/src/modes/theme/defaults/light-lavender.json +95 -0
  699. package/src/modes/theme/defaults/light-meadow.json +91 -0
  700. package/src/modes/theme/defaults/light-mint.json +95 -0
  701. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  702. package/src/modes/theme/defaults/light-ocean.json +99 -0
  703. package/src/modes/theme/defaults/light-one.json +99 -0
  704. package/src/modes/theme/defaults/light-opal.json +91 -0
  705. package/src/modes/theme/defaults/light-orchard.json +91 -0
  706. package/src/modes/theme/defaults/light-paper.json +95 -0
  707. package/src/modes/theme/defaults/light-poimandres.json +142 -0
  708. package/src/modes/theme/defaults/light-prism.json +90 -0
  709. package/src/modes/theme/defaults/light-retro.json +98 -0
  710. package/src/modes/theme/defaults/light-sand.json +95 -0
  711. package/src/modes/theme/defaults/light-savanna.json +91 -0
  712. package/src/modes/theme/defaults/light-solarized.json +102 -0
  713. package/src/modes/theme/defaults/light-soleil.json +90 -0
  714. package/src/modes/theme/defaults/light-sunset.json +99 -0
  715. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  716. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  717. package/src/modes/theme/defaults/light-wetland.json +91 -0
  718. package/src/modes/theme/defaults/light-zenith.json +89 -0
  719. package/src/modes/theme/defaults/limestone.json +94 -0
  720. package/src/modes/theme/defaults/mahogany.json +97 -0
  721. package/src/modes/theme/defaults/marble.json +93 -0
  722. package/src/modes/theme/defaults/obsidian.json +91 -0
  723. package/src/modes/theme/defaults/onyx.json +91 -0
  724. package/src/modes/theme/defaults/pearl.json +93 -0
  725. package/src/modes/theme/defaults/porcelain.json +91 -0
  726. package/src/modes/theme/defaults/quartz.json +96 -0
  727. package/src/modes/theme/defaults/sandstone.json +95 -0
  728. package/src/modes/theme/defaults/titanium.json +90 -0
  729. package/src/modes/theme/light.json +93 -0
  730. package/src/modes/theme/mermaid-cache.ts +29 -0
  731. package/src/modes/theme/shimmer.ts +235 -0
  732. package/src/modes/theme/theme-schema.json +459 -0
  733. package/src/modes/theme/theme.ts +2676 -0
  734. package/src/modes/turn-budget.ts +31 -0
  735. package/src/modes/types.ts +359 -0
  736. package/src/modes/ultrathink.ts +41 -0
  737. package/src/modes/utils/context-usage.ts +339 -0
  738. package/src/modes/utils/copy-targets.ts +360 -0
  739. package/src/modes/utils/hotkeys-markdown.ts +61 -0
  740. package/src/modes/utils/keybinding-matchers.ts +51 -0
  741. package/src/modes/utils/tools-markdown.ts +27 -0
  742. package/src/modes/utils/ui-helpers.ts +801 -0
  743. package/src/modes/workflow.ts +42 -0
  744. package/src/plan-mode/approved-plan.ts +186 -0
  745. package/src/plan-mode/plan-handoff.ts +37 -0
  746. package/src/plan-mode/plan-protection.ts +31 -0
  747. package/src/plan-mode/state.ts +6 -0
  748. package/src/priority.json +41 -0
  749. package/src/prompts/agents/designer.md +66 -0
  750. package/src/prompts/agents/explore.md +58 -0
  751. package/src/prompts/agents/frontmatter.md +11 -0
  752. package/src/prompts/agents/init.md +33 -0
  753. package/src/prompts/agents/librarian.md +119 -0
  754. package/src/prompts/agents/oracle.md +55 -0
  755. package/src/prompts/agents/plan.md +48 -0
  756. package/src/prompts/agents/reviewer.md +140 -0
  757. package/src/prompts/agents/task.md +16 -0
  758. package/src/prompts/ci-green-request.md +36 -0
  759. package/src/prompts/dry-balance-bench.md +8 -0
  760. package/src/prompts/goals/goal-budget-limit.md +16 -0
  761. package/src/prompts/goals/goal-continuation.md +28 -0
  762. package/src/prompts/goals/goal-mode-active.md +23 -0
  763. package/src/prompts/memories/consolidation.md +30 -0
  764. package/src/prompts/memories/read-path.md +11 -0
  765. package/src/prompts/memories/stage_one_input.md +6 -0
  766. package/src/prompts/memories/stage_one_system.md +21 -0
  767. package/src/prompts/review-custom-request.md +22 -0
  768. package/src/prompts/review-headless-request.md +16 -0
  769. package/src/prompts/review-request.md +69 -0
  770. package/src/prompts/steering/user-interjection.md +10 -0
  771. package/src/prompts/system/agent-creation-architect.md +50 -0
  772. package/src/prompts/system/agent-creation-user.md +6 -0
  773. package/src/prompts/system/auto-continue.md +1 -0
  774. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  775. package/src/prompts/system/auto-thinking-difficulty.md +12 -0
  776. package/src/prompts/system/background-tan-dispatch.md +8 -0
  777. package/src/prompts/system/btw-user.md +8 -0
  778. package/src/prompts/system/commit-message-system.md +14 -0
  779. package/src/prompts/system/custom-system-prompt.md +64 -0
  780. package/src/prompts/system/eager-todo.md +13 -0
  781. package/src/prompts/system/empty-stop-retry.md +6 -0
  782. package/src/prompts/system/irc-incoming.md +7 -0
  783. package/src/prompts/system/manual-continue.md +7 -0
  784. package/src/prompts/system/memory-consolidation-system.md +8 -0
  785. package/src/prompts/system/memory-extraction-system.md +26 -0
  786. package/src/prompts/system/omfg-user.md +50 -0
  787. package/src/prompts/system/orchestrate-notice.md +40 -0
  788. package/src/prompts/system/plan-mode-active.md +109 -0
  789. package/src/prompts/system/plan-mode-approved.md +25 -0
  790. package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
  791. package/src/prompts/system/plan-mode-reference.md +11 -0
  792. package/src/prompts/system/plan-mode-subagent.md +33 -0
  793. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  794. package/src/prompts/system/project-prompt.md +52 -0
  795. package/src/prompts/system/subagent-system-prompt.md +64 -0
  796. package/src/prompts/system/subagent-user-prompt.md +3 -0
  797. package/src/prompts/system/subagent-yield-reminder.md +12 -0
  798. package/src/prompts/system/system-prompt.md +258 -0
  799. package/src/prompts/system/tiny-title-system.md +8 -0
  800. package/src/prompts/system/title-system.md +16 -0
  801. package/src/prompts/system/ttsr-interrupt.md +7 -0
  802. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  803. package/src/prompts/system/ultrathink-notice.md +3 -0
  804. package/src/prompts/system/web-search.md +25 -0
  805. package/src/prompts/system/workflow-notice.md +70 -0
  806. package/src/prompts/tools/apply-patch.md +65 -0
  807. package/src/prompts/tools/ask.md +30 -0
  808. package/src/prompts/tools/ast-edit.md +39 -0
  809. package/src/prompts/tools/ast-grep.md +42 -0
  810. package/src/prompts/tools/async-result.md +8 -0
  811. package/src/prompts/tools/bash.md +46 -0
  812. package/src/prompts/tools/browser.md +73 -0
  813. package/src/prompts/tools/checkpoint.md +16 -0
  814. package/src/prompts/tools/debug.md +34 -0
  815. package/src/prompts/tools/eval.md +92 -0
  816. package/src/prompts/tools/find.md +36 -0
  817. package/src/prompts/tools/github.md +21 -0
  818. package/src/prompts/tools/goal.md +18 -0
  819. package/src/prompts/tools/image-gen.md +7 -0
  820. package/src/prompts/tools/inspect-image-system.md +20 -0
  821. package/src/prompts/tools/inspect-image.md +32 -0
  822. package/src/prompts/tools/irc.md +59 -0
  823. package/src/prompts/tools/job.md +19 -0
  824. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  825. package/src/prompts/tools/lsp.md +42 -0
  826. package/src/prompts/tools/memory-edit.md +8 -0
  827. package/src/prompts/tools/patch.md +70 -0
  828. package/src/prompts/tools/read.md +84 -0
  829. package/src/prompts/tools/recall.md +5 -0
  830. package/src/prompts/tools/reflect.md +5 -0
  831. package/src/prompts/tools/render-mermaid.md +9 -0
  832. package/src/prompts/tools/replace.md +30 -0
  833. package/src/prompts/tools/resolve.md +9 -0
  834. package/src/prompts/tools/retain.md +6 -0
  835. package/src/prompts/tools/rewind.md +13 -0
  836. package/src/prompts/tools/search-tool-bm25.md +32 -0
  837. package/src/prompts/tools/search.md +24 -0
  838. package/src/prompts/tools/ssh.md +31 -0
  839. package/src/prompts/tools/task-summary.md +17 -0
  840. package/src/prompts/tools/task.md +88 -0
  841. package/src/prompts/tools/todo.md +62 -0
  842. package/src/prompts/tools/web-search.md +10 -0
  843. package/src/prompts/tools/write.md +14 -0
  844. package/src/registry/agent-lifecycle.ts +218 -0
  845. package/src/registry/agent-registry.ts +151 -0
  846. package/src/sdk.ts +2558 -0
  847. package/src/secrets/index.ts +123 -0
  848. package/src/secrets/obfuscator.ts +298 -0
  849. package/src/secrets/regex.ts +21 -0
  850. package/src/session/agent-session.ts +10121 -0
  851. package/src/session/agent-storage.ts +455 -0
  852. package/src/session/artifacts.ts +135 -0
  853. package/src/session/auth-broker-config.ts +131 -0
  854. package/src/session/auth-storage.ts +29 -0
  855. package/src/session/blob-store.ts +255 -0
  856. package/src/session/client-bridge.ts +85 -0
  857. package/src/session/history-storage.ts +348 -0
  858. package/src/session/indexed-session-storage.ts +430 -0
  859. package/src/session/messages.ts +541 -0
  860. package/src/session/redis-session-storage.ts +170 -0
  861. package/src/session/session-dump-format.ts +209 -0
  862. package/src/session/session-history-format.ts +246 -0
  863. package/src/session/session-manager.ts +3676 -0
  864. package/src/session/session-storage.ts +529 -0
  865. package/src/session/shake-types.ts +43 -0
  866. package/src/session/sql-session-storage.ts +314 -0
  867. package/src/session/streaming-output.ts +1330 -0
  868. package/src/session/tool-choice-queue.ts +213 -0
  869. package/src/session/yield-queue.ts +173 -0
  870. package/src/slash-commands/acp-builtins.ts +70 -0
  871. package/src/slash-commands/builtin-registry.ts +1798 -0
  872. package/src/slash-commands/helpers/context-report.ts +39 -0
  873. package/src/slash-commands/helpers/format.ts +46 -0
  874. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  875. package/src/slash-commands/helpers/mcp.ts +532 -0
  876. package/src/slash-commands/helpers/parse.ts +85 -0
  877. package/src/slash-commands/helpers/ssh.ts +195 -0
  878. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  879. package/src/slash-commands/helpers/todo.ts +279 -0
  880. package/src/slash-commands/helpers/usage-report.ts +95 -0
  881. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  882. package/src/slash-commands/types.ts +135 -0
  883. package/src/ssh/config-writer.ts +183 -0
  884. package/src/ssh/connection-manager.ts +509 -0
  885. package/src/ssh/ssh-executor.ts +189 -0
  886. package/src/ssh/sshfs-mount.ts +140 -0
  887. package/src/ssh/utils.ts +8 -0
  888. package/src/stt/downloader.ts +71 -0
  889. package/src/stt/index.ts +3 -0
  890. package/src/stt/recorder.ts +351 -0
  891. package/src/stt/setup.ts +52 -0
  892. package/src/stt/stt-controller.ts +160 -0
  893. package/src/stt/transcribe.py +70 -0
  894. package/src/stt/transcriber.ts +91 -0
  895. package/src/stubs/natives/index.ts +814 -0
  896. package/src/stubs/natives/package.json +7 -0
  897. package/src/stubs/tui/index.ts +282 -0
  898. package/src/stubs/tui/package.json +7 -0
  899. package/src/system-prompt.ts +611 -0
  900. package/src/task/agents.ts +167 -0
  901. package/src/task/commands.ts +132 -0
  902. package/src/task/discovery.ts +122 -0
  903. package/src/task/executor.ts +2133 -0
  904. package/src/task/index.ts +1419 -0
  905. package/src/task/name-generator.ts +1577 -0
  906. package/src/task/omp-command.ts +26 -0
  907. package/src/task/output-manager.ts +88 -0
  908. package/src/task/parallel.ts +116 -0
  909. package/src/task/render.ts +1381 -0
  910. package/src/task/repair-args.ts +129 -0
  911. package/src/task/subprocess-tool-registry.ts +88 -0
  912. package/src/task/types.ts +336 -0
  913. package/src/task/worktree.ts +514 -0
  914. package/src/telemetry-export.ts +144 -0
  915. package/src/thinking.ts +167 -0
  916. package/src/tiny/compiled-runtime.ts +179 -0
  917. package/src/tiny/device.ts +111 -0
  918. package/src/tiny/dtype.ts +101 -0
  919. package/src/tiny/models.ts +242 -0
  920. package/src/tiny/text.ts +165 -0
  921. package/src/tiny/title-client.ts +543 -0
  922. package/src/tiny/title-protocol.ts +56 -0
  923. package/src/tiny/worker.ts +568 -0
  924. package/src/tool-discovery/mode.ts +24 -0
  925. package/src/tool-discovery/tool-index.ts +256 -0
  926. package/src/tools/approval.ts +189 -0
  927. package/src/tools/archive-reader.ts +721 -0
  928. package/src/tools/ask.ts +928 -0
  929. package/src/tools/ast-edit.ts +642 -0
  930. package/src/tools/ast-grep.ts +452 -0
  931. package/src/tools/auto-generated-guard.ts +322 -0
  932. package/src/tools/bash-command-fixup.ts +37 -0
  933. package/src/tools/bash-interactive.ts +408 -0
  934. package/src/tools/bash-interceptor.ts +67 -0
  935. package/src/tools/bash-pty-selection.ts +14 -0
  936. package/src/tools/bash-skill-urls.ts +248 -0
  937. package/src/tools/bash.ts +1386 -0
  938. package/src/tools/browser/attach.ts +175 -0
  939. package/src/tools/browser/launch.ts +660 -0
  940. package/src/tools/browser/readable.ts +112 -0
  941. package/src/tools/browser/registry.ts +197 -0
  942. package/src/tools/browser/render.ts +216 -0
  943. package/src/tools/browser/tab-protocol.ts +105 -0
  944. package/src/tools/browser/tab-supervisor.ts +628 -0
  945. package/src/tools/browser/tab-worker-entry.ts +21 -0
  946. package/src/tools/browser/tab-worker.ts +1226 -0
  947. package/src/tools/browser.ts +343 -0
  948. package/src/tools/checkpoint.ts +136 -0
  949. package/src/tools/conflict-detect.ts +718 -0
  950. package/src/tools/context.ts +39 -0
  951. package/src/tools/debug.ts +1067 -0
  952. package/src/tools/eval-backends.ts +27 -0
  953. package/src/tools/eval-render.ts +752 -0
  954. package/src/tools/eval.ts +577 -0
  955. package/src/tools/fetch.ts +1926 -0
  956. package/src/tools/file-recorder.ts +35 -0
  957. package/src/tools/find.ts +609 -0
  958. package/src/tools/fs-cache-invalidation.ts +28 -0
  959. package/src/tools/gh-cache-invalidation.ts +255 -0
  960. package/src/tools/gh-format.ts +12 -0
  961. package/src/tools/gh-renderer.ts +481 -0
  962. package/src/tools/gh.ts +3720 -0
  963. package/src/tools/github-cache.ts +637 -0
  964. package/src/tools/grouped-file-output.ts +210 -0
  965. package/src/tools/image-gen.ts +1517 -0
  966. package/src/tools/index.ts +599 -0
  967. package/src/tools/inspect-image-renderer.ts +132 -0
  968. package/src/tools/inspect-image.ts +174 -0
  969. package/src/tools/irc.ts +723 -0
  970. package/src/tools/job.ts +557 -0
  971. package/src/tools/json-tree.ts +243 -0
  972. package/src/tools/jtd-to-json-schema.ts +219 -0
  973. package/src/tools/jtd-to-typescript.ts +136 -0
  974. package/src/tools/jtd-utils.ts +102 -0
  975. package/src/tools/list-limit.ts +40 -0
  976. package/src/tools/match-line-format.ts +20 -0
  977. package/src/tools/memory-edit.ts +59 -0
  978. package/src/tools/memory-recall.ts +100 -0
  979. package/src/tools/memory-reflect.ts +88 -0
  980. package/src/tools/memory-render.ts +202 -0
  981. package/src/tools/memory-retain.ts +91 -0
  982. package/src/tools/output-meta.ts +754 -0
  983. package/src/tools/output-schema-validator.ts +132 -0
  984. package/src/tools/path-utils.ts +1054 -0
  985. package/src/tools/plan-mode-guard.ts +108 -0
  986. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  987. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  988. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  989. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  990. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  991. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  992. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  993. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  994. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  995. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  996. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  997. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  998. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  999. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  1000. package/src/tools/read.ts +2929 -0
  1001. package/src/tools/render-mermaid.ts +69 -0
  1002. package/src/tools/render-utils.ts +838 -0
  1003. package/src/tools/renderers.ts +77 -0
  1004. package/src/tools/report-tool-issue.ts +534 -0
  1005. package/src/tools/resolve.ts +276 -0
  1006. package/src/tools/review.ts +253 -0
  1007. package/src/tools/search-tool-bm25.ts +351 -0
  1008. package/src/tools/search.ts +1580 -0
  1009. package/src/tools/sqlite-reader.ts +828 -0
  1010. package/src/tools/ssh.ts +349 -0
  1011. package/src/tools/todo.ts +982 -0
  1012. package/src/tools/tool-errors.ts +62 -0
  1013. package/src/tools/tool-result.ts +94 -0
  1014. package/src/tools/tool-timeouts.ts +30 -0
  1015. package/src/tools/tts.ts +133 -0
  1016. package/src/tools/write.ts +1217 -0
  1017. package/src/tools/yield.ts +269 -0
  1018. package/src/tui/code-cell.ts +216 -0
  1019. package/src/tui/file-list.ts +55 -0
  1020. package/src/tui/hyperlink.ts +175 -0
  1021. package/src/tui/index.ts +12 -0
  1022. package/src/tui/output-block.ts +240 -0
  1023. package/src/tui/status-line.ts +54 -0
  1024. package/src/tui/tree-list.ts +84 -0
  1025. package/src/tui/types.ts +15 -0
  1026. package/src/tui/utils.ts +103 -0
  1027. package/src/utils/block-context.ts +312 -0
  1028. package/src/utils/changelog.ts +132 -0
  1029. package/src/utils/clipboard.ts +193 -0
  1030. package/src/utils/command-args.ts +76 -0
  1031. package/src/utils/commit-message-generator.ts +151 -0
  1032. package/src/utils/edit-mode.ts +41 -0
  1033. package/src/utils/enhanced-paste.ts +230 -0
  1034. package/src/utils/event-bus.ts +33 -0
  1035. package/src/utils/external-editor.ts +65 -0
  1036. package/src/utils/file-display-mode.ts +45 -0
  1037. package/src/utils/file-mentions.ts +281 -0
  1038. package/src/utils/git.ts +1833 -0
  1039. package/src/utils/image-loading.ts +132 -0
  1040. package/src/utils/image-resize.ts +309 -0
  1041. package/src/utils/jj.ts +248 -0
  1042. package/src/utils/lang-from-path.ts +239 -0
  1043. package/src/utils/markit.ts +89 -0
  1044. package/src/utils/open.ts +55 -0
  1045. package/src/utils/session-color.ts +68 -0
  1046. package/src/utils/shell-snapshot.ts +187 -0
  1047. package/src/utils/sixel.ts +69 -0
  1048. package/src/utils/title-generator.ts +373 -0
  1049. package/src/utils/tool-choice.ts +33 -0
  1050. package/src/utils/tools-manager.ts +363 -0
  1051. package/src/web/kagi.ts +305 -0
  1052. package/src/web/parallel.ts +353 -0
  1053. package/src/web/scrapers/artifacthub.ts +207 -0
  1054. package/src/web/scrapers/arxiv.ts +83 -0
  1055. package/src/web/scrapers/aur.ts +162 -0
  1056. package/src/web/scrapers/biorxiv.ts +133 -0
  1057. package/src/web/scrapers/bluesky.ts +262 -0
  1058. package/src/web/scrapers/brew.ts +172 -0
  1059. package/src/web/scrapers/cheatsh.ts +68 -0
  1060. package/src/web/scrapers/chocolatey.ts +196 -0
  1061. package/src/web/scrapers/choosealicense.ts +95 -0
  1062. package/src/web/scrapers/cisa-kev.ts +87 -0
  1063. package/src/web/scrapers/clojars.ts +154 -0
  1064. package/src/web/scrapers/coingecko.ts +177 -0
  1065. package/src/web/scrapers/crates-io.ts +97 -0
  1066. package/src/web/scrapers/crossref.ts +136 -0
  1067. package/src/web/scrapers/devto.ts +147 -0
  1068. package/src/web/scrapers/discogs.ts +306 -0
  1069. package/src/web/scrapers/discourse.ts +197 -0
  1070. package/src/web/scrapers/dockerhub.ts +138 -0
  1071. package/src/web/scrapers/docs-rs.ts +653 -0
  1072. package/src/web/scrapers/fdroid.ts +134 -0
  1073. package/src/web/scrapers/firefox-addons.ts +191 -0
  1074. package/src/web/scrapers/flathub.ts +223 -0
  1075. package/src/web/scrapers/github-gist.ts +58 -0
  1076. package/src/web/scrapers/github.ts +704 -0
  1077. package/src/web/scrapers/gitlab.ts +401 -0
  1078. package/src/web/scrapers/go-pkg.ts +266 -0
  1079. package/src/web/scrapers/hackage.ts +140 -0
  1080. package/src/web/scrapers/hackernews.ts +189 -0
  1081. package/src/web/scrapers/hex.ts +105 -0
  1082. package/src/web/scrapers/huggingface.ts +321 -0
  1083. package/src/web/scrapers/iacr.ts +89 -0
  1084. package/src/web/scrapers/index.ts +252 -0
  1085. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1086. package/src/web/scrapers/lemmy.ts +203 -0
  1087. package/src/web/scrapers/lobsters.ts +175 -0
  1088. package/src/web/scrapers/mastodon.ts +292 -0
  1089. package/src/web/scrapers/maven.ts +138 -0
  1090. package/src/web/scrapers/mdn.ts +173 -0
  1091. package/src/web/scrapers/metacpan.ts +222 -0
  1092. package/src/web/scrapers/musicbrainz.ts +250 -0
  1093. package/src/web/scrapers/npm.ts +98 -0
  1094. package/src/web/scrapers/nuget.ts +183 -0
  1095. package/src/web/scrapers/nvd.ts +222 -0
  1096. package/src/web/scrapers/ollama.ts +239 -0
  1097. package/src/web/scrapers/open-vsx.ts +106 -0
  1098. package/src/web/scrapers/opencorporates.ts +292 -0
  1099. package/src/web/scrapers/openlibrary.ts +336 -0
  1100. package/src/web/scrapers/orcid.ts +286 -0
  1101. package/src/web/scrapers/osv.ts +176 -0
  1102. package/src/web/scrapers/packagist.ts +160 -0
  1103. package/src/web/scrapers/pub-dev.ts +143 -0
  1104. package/src/web/scrapers/pubmed.ts +211 -0
  1105. package/src/web/scrapers/pypi.ts +112 -0
  1106. package/src/web/scrapers/rawg.ts +110 -0
  1107. package/src/web/scrapers/readthedocs.ts +120 -0
  1108. package/src/web/scrapers/reddit.ts +95 -0
  1109. package/src/web/scrapers/repology.ts +251 -0
  1110. package/src/web/scrapers/rfc.ts +201 -0
  1111. package/src/web/scrapers/rubygems.ts +103 -0
  1112. package/src/web/scrapers/searchcode.ts +189 -0
  1113. package/src/web/scrapers/sec-edgar.ts +261 -0
  1114. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1115. package/src/web/scrapers/snapcraft.ts +187 -0
  1116. package/src/web/scrapers/sourcegraph.ts +336 -0
  1117. package/src/web/scrapers/spdx.ts +108 -0
  1118. package/src/web/scrapers/spotify.ts +198 -0
  1119. package/src/web/scrapers/stackoverflow.ts +120 -0
  1120. package/src/web/scrapers/terraform.ts +277 -0
  1121. package/src/web/scrapers/tldr.ts +47 -0
  1122. package/src/web/scrapers/twitter.ts +94 -0
  1123. package/src/web/scrapers/types.ts +397 -0
  1124. package/src/web/scrapers/utils.ts +109 -0
  1125. package/src/web/scrapers/vimeo.ts +133 -0
  1126. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1127. package/src/web/scrapers/w3c.ts +156 -0
  1128. package/src/web/scrapers/wikidata.ts +344 -0
  1129. package/src/web/scrapers/wikipedia.ts +84 -0
  1130. package/src/web/scrapers/youtube.ts +325 -0
  1131. package/src/web/search/index.ts +292 -0
  1132. package/src/web/search/provider.ts +157 -0
  1133. package/src/web/search/providers/anthropic.ts +318 -0
  1134. package/src/web/search/providers/base.ts +89 -0
  1135. package/src/web/search/providers/brave.ts +152 -0
  1136. package/src/web/search/providers/codex.ts +591 -0
  1137. package/src/web/search/providers/exa.ts +400 -0
  1138. package/src/web/search/providers/gemini.ts +460 -0
  1139. package/src/web/search/providers/jina.ts +111 -0
  1140. package/src/web/search/providers/kagi.ts +86 -0
  1141. package/src/web/search/providers/kimi.ts +196 -0
  1142. package/src/web/search/providers/parallel.ts +225 -0
  1143. package/src/web/search/providers/perplexity.ts +730 -0
  1144. package/src/web/search/providers/searxng.ts +313 -0
  1145. package/src/web/search/providers/synthetic.ts +114 -0
  1146. package/src/web/search/providers/tavily.ts +176 -0
  1147. package/src/web/search/providers/utils.ts +128 -0
  1148. package/src/web/search/providers/zai.ts +333 -0
  1149. package/src/web/search/render.ts +262 -0
  1150. package/src/web/search/types.ts +482 -0
  1151. package/src/web/search/utils.ts +17 -0
  1152. package/src/workspace-tree.ts +286 -0
@@ -0,0 +1,1054 @@
1
+ import { INTENT_FIELD } from "@oh-my-pi/pi-agent-core";
2
+ import { calculatePromptTokens } from "@oh-my-pi/pi-agent-core/compaction/compaction";
3
+ import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai";
4
+ import { type Component, Loader, TERMINAL } from './stubs/tui/index.ts';
5
+ import { settings } from "../../config/settings";
6
+ import { getFileSnapshotStore } from "../../edit/file-snapshot-store";
7
+ import { AssistantMessageComponent } from "../../modes/components/assistant-message";
8
+ import {
9
+ ReadToolGroupComponent,
10
+ readArgsHaveTarget,
11
+ readArgsTargetInternalUrl,
12
+ } from "../../modes/components/read-tool-group";
13
+ import { TodoReminderComponent } from "../../modes/components/todo-reminder";
14
+ import { ToolExecutionComponent } from "../../modes/components/tool-execution";
15
+ import { TtsrNotificationComponent } from "../../modes/components/ttsr-notification";
16
+ import { getSymbolTheme, theme } from "../../modes/theme/theme";
17
+ import type { InteractiveModeContext, TodoPhase } from "../../modes/types";
18
+ import type { PlanApprovalDetails } from "../../plan-mode/approved-plan";
19
+ import type { AgentSessionEvent } from "../../session/agent-session";
20
+ import { isSilentAbort, readPendingDisplayTag, resolveAbortLabel } from "../../session/messages";
21
+ import type { ResolveToolDetails } from "../../tools/resolve";
22
+ import { interruptHint } from "../shared";
23
+ import { StreamingRevealController } from "./streaming-reveal";
24
+
25
+ type AgentSessionEventKind = AgentSessionEvent["type"];
26
+
27
+ const IRC_MESSAGE_VISIBLE_TTL_MS = 10_000;
28
+ /**
29
+ * Concurrent IRC cards allowed in the transcript's live region. Cards land
30
+ * below a still-live block (a running task), where they cannot commit to
31
+ * native scrollback (commits are prefix-only) — every visible card inflates
32
+ * the live region and pushes the live block's uncommitted rows above the
33
+ * window top, where they are neither on screen nor in history. A swarm burst
34
+ * (several agents coordinating at once) must therefore stay bounded: the
35
+ * oldest live-region card retires as soon as a new one would exceed the cap.
36
+ */
37
+ const MAX_LIVE_IRC_CARDS = 4;
38
+
39
+ /**
40
+ * Loader label shown the instant a user interrupt (Esc) is requested, kept until
41
+ * the agent turn fully unwinds. Esc fires the abort synchronously, but the loop
42
+ * only stops the spinner at `agent_end`, which it cannot reach until every
43
+ * in-flight tool settles its abort in `executeToolCalls` (`Promise.allSettled`).
44
+ * Swapping the steady "Working…" for this acknowledges the keypress instead of
45
+ * reading as an ignored Esc for the seconds a slow tool takes to tear down.
46
+ */
47
+ export const INTERRUPTING_WORKING_MESSAGE = "Interrupting…";
48
+
49
+ type AgentSessionEventHandlers = {
50
+ [E in AgentSessionEventKind]: (event: Extract<AgentSessionEvent, { type: E }>) => Promise<void>;
51
+ };
52
+
53
+ export class EventController {
54
+ #lastReadGroup: ReadToolGroupComponent | undefined = undefined;
55
+ // Count of visible assistant content blocks (rendered non-empty text/thinking)
56
+ // already seen in the current streaming message. A newly appearing one breaks
57
+ // the read run: the rendered reasoning/answer is a visual separator, so reads
58
+ // after it start a fresh group. Empty/absent thinking — common when a model
59
+ // emits one read per completion — does not break it, so a run of consecutive
60
+ // reads collapses into one group even across completion boundaries.
61
+ #lastVisibleBlockCount = 0;
62
+ #renderedCustomMessages = new Set<string>();
63
+ #lastIntent: string | undefined = undefined;
64
+ #backgroundToolCallIds = new Set<string>();
65
+ #agentTurnActive = false;
66
+ #interrupting = false;
67
+ #readToolCallArgs = new Map<string, Record<string, unknown>>();
68
+ #readToolCallAssistantComponents = new Map<string, AssistantMessageComponent>();
69
+ #lastAssistantComponent: AssistantMessageComponent | undefined = undefined;
70
+ // Assistant component whose turn-ending error is currently mirrored in the
71
+ // pinned banner. Its inline `Error: …` line is suppressed while pinned and
72
+ // restored when the banner clears at the next `agent_start` (see
73
+ // #handleMessageEnd / #handleAgentStart).
74
+ #pinnedErrorComponent: AssistantMessageComponent | undefined = undefined;
75
+ #idleCompactionTimer?: NodeJS.Timeout;
76
+ #ircExpiryTimers = new Map<string, NodeJS.Timeout>();
77
+ // Insertion-ordered IRC cards not yet retired; values are the transcript
78
+ // components each card contributed (see #retireIrcCard for the guard).
79
+ #liveIrcCards = new Map<string, Component[]>();
80
+ // Most recent `job` tool block whose result still had every watched job
81
+ // running. Kept un-finalized (live) so the next `job` call displaces it —
82
+ // one persistent poll instead of a stack of "waiting on N jobs" frames —
83
+ // and sealed in place the moment anything else lands below it.
84
+ #displaceablePollComponent: ToolExecutionComponent | undefined = undefined;
85
+ // Most recent TTSR notification block. A new ttsr_triggered event merges its
86
+ // rules into this block while it is still the (live-region) transcript tail.
87
+ #lastTtsrNotification: TtsrNotificationComponent | undefined = undefined;
88
+ #streamingReveal: StreamingRevealController;
89
+ #handlers: AgentSessionEventHandlers;
90
+
91
+ constructor(private ctx: InteractiveModeContext) {
92
+ this.#streamingReveal = new StreamingRevealController({
93
+ getSmoothStreaming: () => this.ctx.settings.get("display.smoothStreaming"),
94
+ getHideThinkingBlock: () => this.ctx.hideThinkingBlock,
95
+ requestRender: () => this.ctx.ui.requestRender(),
96
+ });
97
+ this.#handlers = {
98
+ agent_start: e => this.#handleAgentStart(e),
99
+ agent_end: e => this.#handleAgentEnd(e),
100
+ turn_start: async () => {},
101
+ turn_end: async () => {},
102
+ message_start: e => this.#handleMessageStart(e),
103
+ message_update: e => this.#handleMessageUpdate(e),
104
+ message_end: e => this.#handleMessageEnd(e),
105
+ tool_execution_start: e => this.#handleToolExecutionStart(e),
106
+ tool_execution_update: e => this.#handleToolExecutionUpdate(e),
107
+ tool_execution_end: e => this.#handleToolExecutionEnd(e),
108
+ auto_compaction_start: e => this.#handleAutoCompactionStart(e),
109
+ auto_compaction_end: e => this.#handleAutoCompactionEnd(e),
110
+ auto_retry_start: e => this.#handleAutoRetryStart(e),
111
+ auto_retry_end: e => this.#handleAutoRetryEnd(e),
112
+ retry_fallback_applied: e => this.#handleRetryFallbackApplied(e),
113
+ retry_fallback_succeeded: e => this.#handleRetryFallbackSucceeded(e),
114
+ ttsr_triggered: e => this.#handleTtsrTriggered(e),
115
+ todo_reminder: e => this.#handleTodoReminder(e),
116
+ todo_auto_clear: e => this.#handleTodoAutoClear(e),
117
+ irc_message: e => this.#handleIrcMessage(e),
118
+ notice: e => this.#handleNotice(e),
119
+ thinking_level_changed: async () => {
120
+ this.ctx.statusLine.invalidate();
121
+ this.ctx.updateEditorBorderColor();
122
+ this.ctx.ui.requestRender();
123
+ },
124
+ goal_updated: async () => {},
125
+ } satisfies AgentSessionEventHandlers;
126
+ }
127
+
128
+ dispose(): void {
129
+ this.#streamingReveal.stop();
130
+ this.#cancelIdleCompaction();
131
+ for (const timer of this.#ircExpiryTimers.values()) {
132
+ clearTimeout(timer);
133
+ }
134
+ this.#ircExpiryTimers.clear();
135
+ this.#liveIrcCards.clear();
136
+ }
137
+
138
+ #resetReadGroup(): void {
139
+ this.#lastReadGroup?.finalize();
140
+ this.#lastReadGroup = undefined;
141
+ }
142
+
143
+ #getReadGroup(): ReadToolGroupComponent {
144
+ if (!this.#lastReadGroup) {
145
+ const group = new ReadToolGroupComponent({
146
+ showContentPreview: this.ctx.settings.get("read.toolResultPreview"),
147
+ });
148
+ group.setExpanded(this.ctx.toolOutputExpanded);
149
+ this.ctx.chatContainer.addChild(group);
150
+ this.#lastReadGroup = group;
151
+ }
152
+ return this.#lastReadGroup;
153
+ }
154
+
155
+ #trackReadToolCall(toolCallId: string, args: unknown): void {
156
+ if (!toolCallId) return;
157
+ const normalizedArgs =
158
+ args && typeof args === "object" && !Array.isArray(args) ? (args as Record<string, unknown>) : {};
159
+ this.#readToolCallArgs.set(toolCallId, normalizedArgs);
160
+ const assistantComponent = this.ctx.streamingComponent ?? this.#lastAssistantComponent;
161
+ if (assistantComponent) {
162
+ this.#readToolCallAssistantComponents.set(toolCallId, assistantComponent);
163
+ }
164
+ }
165
+
166
+ #clearReadToolCall(toolCallId: string): void {
167
+ this.#readToolCallArgs.delete(toolCallId);
168
+ this.#readToolCallAssistantComponents.delete(toolCallId);
169
+ }
170
+
171
+ #inlineReadToolImages(
172
+ toolCallId: string,
173
+ result: { content: Array<{ type: string; data?: string; mimeType?: string }> },
174
+ ): boolean {
175
+ if (!settings.get("terminal.showImages")) return false;
176
+ const assistantComponent = this.#readToolCallAssistantComponents.get(toolCallId);
177
+ if (!assistantComponent) return false;
178
+ const images: ImageContent[] = result.content
179
+ .filter(
180
+ (content): content is ImageContent =>
181
+ content.type === "image" && typeof content.data === "string" && typeof content.mimeType === "string",
182
+ )
183
+ .map(content => ({ type: "image", data: content.data, mimeType: content.mimeType }));
184
+ if (images.length === 0) return false;
185
+ assistantComponent.setToolResultImages(toolCallId, images);
186
+ return true;
187
+ }
188
+ #updateWorkingMessageFromIntent(intent: unknown): void {
189
+ if (this.#interrupting) return;
190
+ // Streamed JSON can deliver non-string `_i` (object, number, boolean) before
191
+ // schema validation; `?.` only guards null/undefined, so guard the type too.
192
+ if (typeof intent !== "string") return;
193
+ const trimmed = intent.trim();
194
+ if (!trimmed || trimmed === this.#lastIntent) return;
195
+ this.#lastIntent = trimmed;
196
+ this.ctx.setWorkingMessage(`${trimmed}${interruptHint()}`);
197
+ }
198
+
199
+ /**
200
+ * Acknowledge a user interrupt (Esc) immediately: switch the loader to
201
+ * `INTERRUPTING_WORKING_MESSAGE` and freeze intent-driven working-message
202
+ * updates for the rest of the turn so a late `tool_execution_start` intent
203
+ * cannot repaint a "Working…/<intent>" line over the acknowledgment. Reset at
204
+ * the next `agent_start`. No-op outside an active turn or if already set.
205
+ */
206
+ notifyInterrupting(): void {
207
+ if (!this.#agentTurnActive || this.#interrupting) return;
208
+ this.#interrupting = true;
209
+ this.ctx.setWorkingMessage(INTERRUPTING_WORKING_MESSAGE);
210
+ }
211
+
212
+ subscribeToAgent(): void {
213
+ this.ctx.unsubscribe = this.ctx.session.subscribe(async (event: AgentSessionEvent) => {
214
+ await this.handleEvent(event);
215
+ });
216
+ }
217
+
218
+ async handleEvent(event: AgentSessionEvent): Promise<void> {
219
+ if (!this.ctx.isInitialized) {
220
+ await this.ctx.init();
221
+ }
222
+
223
+ this.ctx.statusLine.invalidate();
224
+ this.ctx.updateEditorTopBorder();
225
+
226
+ const run = this.#handlers[event.type] as (e: AgentSessionEvent) => Promise<void>;
227
+ await run(event);
228
+ }
229
+
230
+ async #handleAgentStart(_event: Extract<AgentSessionEvent, { type: "agent_start" }>): Promise<void> {
231
+ this.#agentTurnActive = true;
232
+ this.#interrupting = false;
233
+ this.#lastIntent = undefined;
234
+ this.#readToolCallArgs.clear();
235
+ this.#readToolCallAssistantComponents.clear();
236
+ this.#resetReadGroup();
237
+ this.#lastAssistantComponent = undefined;
238
+ // Restore the previous turn's inline error in the transcript before dropping
239
+ // the banner, so the error stays in history once the banner is gone.
240
+ this.#pinnedErrorComponent?.setErrorPinned(false);
241
+ this.#pinnedErrorComponent = undefined;
242
+ this.ctx.clearPinnedError();
243
+ if (this.ctx.retryEscapeHandler) {
244
+ this.ctx.editor.onEscape = this.ctx.retryEscapeHandler;
245
+ this.ctx.retryEscapeHandler = undefined;
246
+ }
247
+ if (this.ctx.retryLoader) {
248
+ this.ctx.retryLoader.stop();
249
+ this.ctx.retryLoader = undefined;
250
+ this.ctx.statusContainer.clear();
251
+ }
252
+ this.#cancelIdleCompaction();
253
+ this.ctx.ensureLoadingAnimation();
254
+ this.ctx.ui.requestRender();
255
+ }
256
+
257
+ async #handleMessageStart(event: Extract<AgentSessionEvent, { type: "message_start" }>): Promise<void> {
258
+ if (event.message.role === "hookMessage" || event.message.role === "custom") {
259
+ const signature = `${event.message.role}:${event.message.customType}:${event.message.timestamp}`;
260
+ if (this.#renderedCustomMessages.has(signature)) {
261
+ return;
262
+ }
263
+ this.#renderedCustomMessages.add(signature);
264
+ this.#resetReadGroup();
265
+ this.ctx.addMessageToChat(event.message);
266
+ // Tag-keyed pending-bar refresh: when AgentSession.#handleAgentEvent
267
+ // spliced this dequeued custom message out of #steeringMessages /
268
+ // #followUpMessages (it ran before this emit), the array state is
269
+ // already correct — pendingMessagesContainer just needs to be
270
+ // re-rendered to match. Gated on tag presence so non-queued customs
271
+ // (ttsr-injection, irc:*, async-result, hookMessage) skip the
272
+ // rebuild; their dispatch path never registered a pending chip.
273
+ // Mirrors the user-role refresh at the bottom of this function.
274
+ if (event.message.role === "custom" && readPendingDisplayTag(event.message.details)) {
275
+ this.ctx.updatePendingMessagesDisplay();
276
+ }
277
+ this.ctx.ui.requestRender();
278
+ } else if (event.message.role === "user") {
279
+ const textContent = this.ctx.getUserMessageText(event.message);
280
+ const imageBlocks =
281
+ typeof event.message.content === "string"
282
+ ? []
283
+ : event.message.content.filter(
284
+ (content): content is ImageContent =>
285
+ content.type === "image" &&
286
+ typeof content.data === "string" &&
287
+ typeof content.mimeType === "string",
288
+ );
289
+ const imageCount = imageBlocks.length;
290
+ const signature = `${textContent}\u0000${imageCount}`;
291
+
292
+ this.#resetReadGroup();
293
+ this.#resolveDisplaceablePoll();
294
+ const wasOptimistic = this.ctx.optimisticUserMessageSignature === signature;
295
+ const wasLocallySubmitted = this.ctx.locallySubmittedUserSignatures.delete(signature) || wasOptimistic;
296
+ if (!wasOptimistic) {
297
+ // Append synchronously: #emit dispatches to this listener fire-and-forget
298
+ // (see AgentSession.#emit), so any await between the user message_start and
299
+ // addMessageToChat lets later events (assistant message_start, tool execution
300
+ // start/end) append their components first and scramble transcript order /
301
+ // live-region block boundaries. addMessageToChat materializes clickable image
302
+ // links via the synchronous putBlobSync fallback, so no await is needed here.
303
+ this.ctx.addMessageToChat(event.message);
304
+ }
305
+ if (wasOptimistic) {
306
+ this.ctx.optimisticUserMessageSignature = undefined;
307
+ }
308
+
309
+ // Clear the editor only when the submission did not originate from a
310
+ // local submission (optimistic or queued-while-streaming). Both local
311
+ // paths already cleared the editor at submit time; clearing again here
312
+ // would race with the user typing the next prompt while the previous
313
+ // large redraw lands and erase their in-progress draft (#783).
314
+ if (!event.message.synthetic) {
315
+ if (!wasLocallySubmitted) {
316
+ this.ctx.editor.setText("");
317
+ }
318
+ this.ctx.updatePendingMessagesDisplay();
319
+ }
320
+ this.ctx.ui.requestRender();
321
+ } else if (event.message.role === "fileMention") {
322
+ this.#resetReadGroup();
323
+ this.ctx.addMessageToChat(event.message);
324
+ this.ctx.ui.requestRender();
325
+ } else if (event.message.role === "assistant") {
326
+ this.#lastVisibleBlockCount = 0;
327
+ this.ctx.streamingComponent = new AssistantMessageComponent(
328
+ undefined,
329
+ this.ctx.hideThinkingBlock,
330
+ () => this.ctx.ui.requestRender(),
331
+ this.ctx.session.extensionRunner?.getAssistantThinkingRenderers(),
332
+ this.ctx.ui.imageBudget,
333
+ );
334
+ this.ctx.streamingMessage = event.message;
335
+ this.ctx.chatContainer.addChild(this.ctx.streamingComponent);
336
+ this.#streamingReveal.begin(this.ctx.streamingComponent, this.ctx.streamingMessage);
337
+ this.ctx.ui.requestRender();
338
+ }
339
+ }
340
+
341
+ async #handleIrcMessage(event: Extract<AgentSessionEvent, { type: "irc_message" }>): Promise<void> {
342
+ const signature = `${event.message.role}:${event.message.customType}:${event.message.timestamp}`;
343
+ if (this.#renderedCustomMessages.has(signature)) {
344
+ return;
345
+ }
346
+ this.#renderedCustomMessages.add(signature);
347
+ this.#resetReadGroup();
348
+ const components = this.ctx.addMessageToChat(event.message);
349
+ this.#scheduleIrcExpiry(signature, components);
350
+ this.#enforceIrcCardCap(signature);
351
+ this.ctx.ui.requestRender();
352
+ }
353
+
354
+ #scheduleIrcExpiry(signature: string, components: Component[]): void {
355
+ if (components.length === 0 || this.#ircExpiryTimers.has(signature)) return;
356
+ const timer = setTimeout(() => {
357
+ this.#ircExpiryTimers.delete(signature);
358
+ this.#retireIrcCard(signature);
359
+ }, IRC_MESSAGE_VISIBLE_TTL_MS);
360
+ timer.unref?.();
361
+ this.#ircExpiryTimers.set(signature, timer);
362
+ this.#liveIrcCards.set(signature, components);
363
+ }
364
+
365
+ /**
366
+ * Remove an expired/evicted IRC card — but only while it still sits below a
367
+ * live block, where its rows cannot have entered native scrollback. Once
368
+ * everything above it has finalized, its rows may already be committed;
369
+ * removing them then is an interior deletion of the committed prefix, which
370
+ * the engine can only repair by recommitting every row below the gap —
371
+ * exactly the duplicated-block artifact this guard exists to prevent. Such
372
+ * a card simply stays: it is final history, and the window scrolls past it.
373
+ */
374
+ #retireIrcCard(signature: string): void {
375
+ const components = this.#liveIrcCards.get(signature);
376
+ this.#liveIrcCards.delete(signature);
377
+ if (!components) return;
378
+ let removed = false;
379
+ for (const component of components) {
380
+ if (!this.ctx.chatContainer.isWithinLiveRegion(component)) continue;
381
+ this.ctx.chatContainer.removeChild(component);
382
+ removed = true;
383
+ }
384
+ if (removed) this.ctx.ui.requestRender();
385
+ }
386
+
387
+ /** Evict oldest live-region cards beyond {@link MAX_LIVE_IRC_CARDS}. */
388
+ #enforceIrcCardCap(latestSignature: string): void {
389
+ while (this.#liveIrcCards.size > MAX_LIVE_IRC_CARDS) {
390
+ const oldest = this.#liveIrcCards.keys().next().value;
391
+ if (oldest === undefined || oldest === latestSignature) return;
392
+ const timer = this.#ircExpiryTimers.get(oldest);
393
+ if (timer) {
394
+ clearTimeout(timer);
395
+ this.#ircExpiryTimers.delete(oldest);
396
+ }
397
+ this.#retireIrcCard(oldest);
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Resolve the pending displaceable poll block before the next block lands.
403
+ * A follow-up `job` call displaces it — the stale "waiting on N jobs" frame
404
+ * is removed so repeated polls read as one persistent poll — while anything
405
+ * else seals it in place as final history. Removal is safe only because a
406
+ * displaceable block never finalizes: commits stop at the first live block,
407
+ * so none of its rows have entered native scrollback (see
408
+ * ToolExecutionComponent.isDisplaceableBlock).
409
+ */
410
+ #resolveDisplaceablePoll(nextToolName?: string): void {
411
+ const previous = this.#displaceablePollComponent;
412
+ if (!previous) return;
413
+ this.#displaceablePollComponent = undefined;
414
+ if (nextToolName === "job" && previous.isDisplaceableBlock()) {
415
+ this.ctx.chatContainer.removeChild(previous);
416
+ }
417
+ // Sealing stops the waiting-poll spinner and freezes the block (for a
418
+ // just-removed component it only clears the animation timer).
419
+ previous.seal();
420
+ this.ctx.ui.requestRender();
421
+ }
422
+
423
+ async #handleNotice(event: Extract<AgentSessionEvent, { type: "notice" }>): Promise<void> {
424
+ const message = event.source ? `${event.source}: ${event.message}` : event.message;
425
+ if (event.level === "error") {
426
+ this.ctx.showError(message);
427
+ } else if (event.level === "warning") {
428
+ this.ctx.showWarning(message);
429
+ } else {
430
+ this.ctx.showStatus(message);
431
+ }
432
+ }
433
+
434
+ async #handleMessageUpdate(event: Extract<AgentSessionEvent, { type: "message_update" }>): Promise<void> {
435
+ if (this.ctx.streamingComponent && event.message.role === "assistant") {
436
+ this.ctx.streamingMessage = event.message;
437
+ this.#streamingReveal.setTarget(this.ctx.streamingMessage);
438
+
439
+ const visibleBlockCount = this.ctx.streamingMessage.content.filter(
440
+ content =>
441
+ (content.type === "text" && content.text.trim().length > 0) ||
442
+ (content.type === "thinking" && content.thinking.trim().length > 0),
443
+ ).length;
444
+ if (visibleBlockCount > this.#lastVisibleBlockCount) {
445
+ this.#resetReadGroup();
446
+ this.#lastVisibleBlockCount = visibleBlockCount;
447
+ }
448
+
449
+ // Content blocks stream sequentially: a toolCall block can only begin
450
+ // after every preceding thinking/text block has closed, and the
451
+ // reveal's setTarget above force-completes the visible text for
452
+ // toolCall messages. Finalize the assistant block now instead of at
453
+ // message_end so the transcript's commit-safe run can extend through
454
+ // it into the streaming tool preview below — otherwise a long args
455
+ // stream (a big write/edit/eval) sits below a still-live block and
456
+ // can never reach native scrollback: the head of the preview is
457
+ // neither committed nor on screen and the transcript reads as cut.
458
+ // Skipped when the per-turn usage row is enabled: that row is only
459
+ // known at message_end and appends to this block, which would shift
460
+ // committed tool rows below it every turn (audit recommit →
461
+ // duplicated preview copies in scrollback).
462
+ if (
463
+ this.ctx.streamingMessage.content.some(content => content.type === "toolCall") &&
464
+ !settings.get("display.showTokenUsage")
465
+ ) {
466
+ this.ctx.streamingComponent.markTranscriptBlockFinalized();
467
+ }
468
+ for (const content of this.ctx.streamingMessage.content) {
469
+ if (content.type !== "toolCall") continue;
470
+ if (content.name === "read") {
471
+ if (!readArgsHaveTarget(content.arguments)) {
472
+ // Args still streaming — defer until path is parseable so we can route to the
473
+ // read group (regular files) vs ToolExecutionComponent (internal URLs).
474
+ // Creating either component now would lock the read into the wrong shape.
475
+ continue;
476
+ }
477
+ if (!readArgsTargetInternalUrl(content.arguments)) {
478
+ if (!this.ctx.pendingTools.has(content.id)) this.#resolveDisplaceablePoll(content.name);
479
+ this.#trackReadToolCall(content.id, content.arguments);
480
+ const component = this.ctx.pendingTools.get(content.id);
481
+ if (component) {
482
+ component.updateArgs(content.arguments, content.id);
483
+ } else {
484
+ const group = this.#getReadGroup();
485
+ group.updateArgs(content.arguments, content.id);
486
+ this.ctx.pendingTools.set(content.id, group);
487
+ }
488
+ continue;
489
+ }
490
+ // Internal URL read falls through to ToolExecutionComponent below.
491
+ }
492
+
493
+ // Preserve the raw partial JSON for renderers that need to surface fields before the JSON object closes.
494
+ // Bash uses this to show inline env assignments during streaming instead of popping them in at completion.
495
+ const renderArgs =
496
+ "partialJson" in content
497
+ ? { ...content.arguments, __partialJson: content.partialJson }
498
+ : content.arguments;
499
+ if (!this.ctx.pendingTools.has(content.id)) {
500
+ this.#resolveDisplaceablePoll(content.name);
501
+ this.#resetReadGroup();
502
+ const tool = this.ctx.session.getToolByName(content.name);
503
+ const component = new ToolExecutionComponent(
504
+ content.name,
505
+ renderArgs,
506
+ {
507
+ snapshots: getFileSnapshotStore(this.ctx.session),
508
+ showImages: settings.get("terminal.showImages"),
509
+ editFuzzyThreshold: settings.get("edit.fuzzyThreshold"),
510
+ editAllowFuzzy: settings.get("edit.fuzzyMatch"),
511
+ },
512
+ tool,
513
+ this.ctx.ui,
514
+ this.ctx.sessionManager.getCwd(),
515
+ content.id,
516
+ );
517
+ component.setExpanded(this.ctx.toolOutputExpanded);
518
+ this.ctx.chatContainer.addChild(component);
519
+ this.ctx.pendingTools.set(content.id, component);
520
+ } else {
521
+ const component = this.ctx.pendingTools.get(content.id);
522
+ if (component) {
523
+ component.updateArgs(renderArgs, content.id);
524
+ }
525
+ }
526
+ }
527
+
528
+ // Update working message with intent from streamed tool arguments
529
+ for (const content of this.ctx.streamingMessage.content) {
530
+ if (content.type !== "toolCall") continue;
531
+ const args = content.arguments;
532
+ if (!args || typeof args !== "object") continue;
533
+ if (INTENT_FIELD in args) {
534
+ this.#updateWorkingMessageFromIntent(args[INTENT_FIELD]);
535
+ continue;
536
+ }
537
+ const tool = this.ctx.session.getToolByName(content.name);
538
+ if (typeof tool?.intent !== "function") continue;
539
+ try {
540
+ const derived = tool.intent(args as never)?.trim();
541
+ if (derived) {
542
+ this.#updateWorkingMessageFromIntent(derived);
543
+ }
544
+ } catch {
545
+ // intent function must never break the UI
546
+ }
547
+ }
548
+
549
+ this.ctx.ui.requestRender();
550
+ }
551
+ }
552
+
553
+ async #handleMessageEnd(event: Extract<AgentSessionEvent, { type: "message_end" }>): Promise<void> {
554
+ if (event.message.role === "user") return;
555
+ if (this.ctx.streamingComponent && event.message.role === "assistant") {
556
+ this.ctx.streamingMessage = event.message;
557
+ this.#streamingReveal.stop();
558
+ let errorMessage: string | undefined;
559
+ const aborted = this.ctx.streamingMessage.stopReason === "aborted";
560
+ const silentlyAborted = aborted && isSilentAbort(this.ctx.streamingMessage.errorMessage);
561
+ const ttsrSilenced = aborted && this.ctx.session.isTtsrAbortPending;
562
+ if (aborted && !silentlyAborted && !ttsrSilenced) {
563
+ // Resolve the operator-facing label: a user-interrupt (Esc) abort
564
+ // carries USER_INTERRUPT_LABEL on errorMessage (threaded through the
565
+ // AbortController), which is preserved verbatim; any other abort with
566
+ // no threaded reason falls back to the retry-aware generic label.
567
+ // AgentSession.#handleAgentEvent already stamped SILENT_ABORT_MARKER for
568
+ // the plan-compact transition before this controller ran, so reaching
569
+ // this branch implies the abort was NOT a silent internal transition.
570
+ errorMessage = resolveAbortLabel(this.ctx.streamingMessage.errorMessage, this.ctx.session.retryAttempt);
571
+ this.ctx.streamingMessage.errorMessage = errorMessage;
572
+ }
573
+ if (silentlyAborted || ttsrSilenced) {
574
+ // Silence the streaming render by downgrading stopReason to "stop" for
575
+ // display only — does NOT mutate the persisted message's stopReason
576
+ // (the marker on errorMessage drives replay-side suppression).
577
+ const msgWithoutAbort = { ...this.ctx.streamingMessage, stopReason: "stop" as const };
578
+ this.ctx.streamingComponent.updateContent(msgWithoutAbort);
579
+ } else {
580
+ this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage);
581
+ }
582
+
583
+ if (this.ctx.streamingMessage.stopReason !== "aborted" && this.ctx.streamingMessage.stopReason !== "error") {
584
+ for (const [toolCallId, component] of this.ctx.pendingTools.entries()) {
585
+ component.setArgsComplete(toolCallId);
586
+ }
587
+ } else {
588
+ // The turn ended without running these calls (abort/error/TTSR rewind),
589
+ // so they will never produce a result. Seal them so they stop animating
590
+ // and freeze instead of pinning the transcript live region while a retry
591
+ // streams fresh blocks below them. Background tools keep updating.
592
+ for (const [toolCallId, component] of this.ctx.pendingTools.entries()) {
593
+ if (!this.#backgroundToolCallIds.has(toolCallId) && component instanceof ToolExecutionComponent) {
594
+ component.seal();
595
+ }
596
+ }
597
+ // These calls will never produce a result either, so the tracked
598
+ // waiting poll cannot be displaced anymore — freeze it in place.
599
+ this.#resolveDisplaceablePoll();
600
+ }
601
+ this.#lastAssistantComponent = this.ctx.streamingComponent;
602
+ this.#lastAssistantComponent.setUsageInfo(event.message.usage);
603
+ this.#lastAssistantComponent.markTranscriptBlockFinalized();
604
+ this.ctx.streamingComponent = undefined;
605
+ this.ctx.streamingMessage = undefined;
606
+ // Pin a turn-ending provider error (e.g. Anthropic content-filter block)
607
+ // above the editor so it survives transcript scroll. Cleared at the next
608
+ // turn's agent_start. Suppress the transcript's inline `Error: …` line for
609
+ // the same message while pinned so the error isn't rendered twice.
610
+ if (
611
+ event.message.stopReason === "error" &&
612
+ event.message.errorMessage &&
613
+ !isSilentAbort(event.message.errorMessage)
614
+ ) {
615
+ this.#lastAssistantComponent?.setErrorPinned(true);
616
+ this.#pinnedErrorComponent = this.#lastAssistantComponent;
617
+ this.ctx.showPinnedError(event.message.errorMessage);
618
+ }
619
+ this.ctx.statusLine.invalidate();
620
+ this.ctx.updateEditorTopBorder();
621
+ }
622
+ this.ctx.ui.requestRender();
623
+ }
624
+
625
+ async #handleToolExecutionStart(event: Extract<AgentSessionEvent, { type: "tool_execution_start" }>): Promise<void> {
626
+ this.#updateWorkingMessageFromIntent(event.intent);
627
+ if (!this.ctx.pendingTools.has(event.toolCallId)) {
628
+ this.#resolveDisplaceablePoll(event.toolName);
629
+ if (event.toolName === "read" && readArgsHaveTarget(event.args) && !readArgsTargetInternalUrl(event.args)) {
630
+ this.#trackReadToolCall(event.toolCallId, event.args);
631
+ const component = this.ctx.pendingTools.get(event.toolCallId);
632
+ if (component) {
633
+ component.updateArgs(event.args, event.toolCallId);
634
+ } else {
635
+ const group = this.#getReadGroup();
636
+ group.updateArgs(event.args, event.toolCallId);
637
+ this.ctx.pendingTools.set(event.toolCallId, group);
638
+ }
639
+ this.ctx.ui.requestRender();
640
+ return;
641
+ }
642
+
643
+ this.#resetReadGroup();
644
+ const tool = this.ctx.session.getToolByName(event.toolName);
645
+ const component = new ToolExecutionComponent(
646
+ event.toolName,
647
+ event.args,
648
+ {
649
+ snapshots: getFileSnapshotStore(this.ctx.session),
650
+ showImages: settings.get("terminal.showImages"),
651
+ editFuzzyThreshold: settings.get("edit.fuzzyThreshold"),
652
+ editAllowFuzzy: settings.get("edit.fuzzyMatch"),
653
+ },
654
+ tool,
655
+ this.ctx.ui,
656
+ this.ctx.sessionManager.getCwd(),
657
+ event.toolCallId,
658
+ );
659
+ component.setExpanded(this.ctx.toolOutputExpanded);
660
+ this.ctx.chatContainer.addChild(component);
661
+ this.ctx.pendingTools.set(event.toolCallId, component);
662
+ this.ctx.ui.requestRender();
663
+ }
664
+ }
665
+
666
+ async #handleToolExecutionUpdate(
667
+ event: Extract<AgentSessionEvent, { type: "tool_execution_update" }>,
668
+ ): Promise<void> {
669
+ const component = this.ctx.pendingTools.get(event.toolCallId);
670
+ if (component) {
671
+ const asyncState = (event.partialResult.details as { async?: { state?: string } } | undefined)?.async?.state;
672
+ const isFinalAsyncState = asyncState === "completed" || asyncState === "failed";
673
+ component.updateResult(
674
+ { ...event.partialResult, isError: asyncState === "failed" },
675
+ !isFinalAsyncState,
676
+ event.toolCallId,
677
+ );
678
+ if (isFinalAsyncState) {
679
+ this.ctx.pendingTools.delete(event.toolCallId);
680
+ this.#backgroundToolCallIds.delete(event.toolCallId);
681
+ }
682
+ this.ctx.ui.requestRender();
683
+ }
684
+ }
685
+
686
+ async #handleToolExecutionEnd(event: Extract<AgentSessionEvent, { type: "tool_execution_end" }>): Promise<void> {
687
+ if (event.toolName === "read") {
688
+ if (this.#inlineReadToolImages(event.toolCallId, event.result)) {
689
+ const component = this.ctx.pendingTools.get(event.toolCallId);
690
+ if (component) {
691
+ component.updateResult({ ...event.result, isError: event.isError }, false, event.toolCallId);
692
+ this.ctx.pendingTools.delete(event.toolCallId);
693
+ }
694
+ const asyncState = (event.result.details as { async?: { state?: string } } | undefined)?.async?.state;
695
+ if (asyncState === "running") {
696
+ this.#backgroundToolCallIds.add(event.toolCallId);
697
+ } else {
698
+ this.#backgroundToolCallIds.delete(event.toolCallId);
699
+ this.#clearReadToolCall(event.toolCallId);
700
+ }
701
+ this.ctx.ui.requestRender();
702
+ } else {
703
+ let component = this.ctx.pendingTools.get(event.toolCallId);
704
+ if (!component) {
705
+ const group = this.#getReadGroup();
706
+ const args = this.#readToolCallArgs.get(event.toolCallId);
707
+ if (args) {
708
+ group.updateArgs(args, event.toolCallId);
709
+ }
710
+ component = group;
711
+ this.ctx.pendingTools.set(event.toolCallId, group);
712
+ }
713
+ const asyncState = (event.result.details as { async?: { state?: string } } | undefined)?.async?.state;
714
+ const isBackgroundRunning = asyncState === "running";
715
+ component.updateResult({ ...event.result, isError: event.isError }, isBackgroundRunning, event.toolCallId);
716
+ if (isBackgroundRunning) {
717
+ this.#backgroundToolCallIds.add(event.toolCallId);
718
+ } else {
719
+ this.ctx.pendingTools.delete(event.toolCallId);
720
+ this.#backgroundToolCallIds.delete(event.toolCallId);
721
+ this.#clearReadToolCall(event.toolCallId);
722
+ }
723
+ this.ctx.ui.requestRender();
724
+ }
725
+ } else {
726
+ const component = this.ctx.pendingTools.get(event.toolCallId);
727
+ if (component) {
728
+ const asyncState = (event.result.details as { async?: { state?: string } } | undefined)?.async?.state;
729
+ const isBackgroundRunning = asyncState === "running";
730
+ component.updateResult({ ...event.result, isError: event.isError }, isBackgroundRunning, event.toolCallId);
731
+ if (isBackgroundRunning) {
732
+ this.#backgroundToolCallIds.add(event.toolCallId);
733
+ } else {
734
+ this.ctx.pendingTools.delete(event.toolCallId);
735
+ this.#backgroundToolCallIds.delete(event.toolCallId);
736
+ }
737
+ if (
738
+ event.toolName === "job" &&
739
+ component instanceof ToolExecutionComponent &&
740
+ component.isDisplaceableBlock()
741
+ ) {
742
+ // Remember the waiting poll so the next `job` call can displace it.
743
+ this.#displaceablePollComponent = component;
744
+ }
745
+ this.ctx.ui.requestRender();
746
+ }
747
+ }
748
+ // Update todo display when todo tool completes
749
+ if (event.toolName === "todo" && !event.isError) {
750
+ const details = event.result.details as { phases?: TodoPhase[] } | undefined;
751
+ if (details?.phases) {
752
+ this.ctx.setTodos(details.phases);
753
+ }
754
+ } else if (event.toolName === "todo" && event.isError) {
755
+ const textContent = event.result.content.find(
756
+ (content: { type: string; text?: string }) => content.type === "text",
757
+ )?.text;
758
+ this.ctx.showWarning(
759
+ `Todo update failed${textContent ? `: ${textContent}` : ". Progress may be stale until todo succeeds."}`,
760
+ );
761
+ }
762
+ if (event.toolName === "resolve" && !event.isError) {
763
+ const details = event.result.details as ResolveToolDetails | undefined;
764
+ if (details?.sourceToolName === "plan_approval" && details.action === "apply") {
765
+ const planDetails = details.sourceResultDetails as PlanApprovalDetails | undefined;
766
+ if (planDetails) {
767
+ await this.ctx.handlePlanApproval(planDetails);
768
+ }
769
+ }
770
+ }
771
+ }
772
+ async #handleAgentEnd(_event: Extract<AgentSessionEvent, { type: "agent_end" }>): Promise<void> {
773
+ this.#agentTurnActive = false;
774
+ this.#streamingReveal.stop();
775
+ if (this.ctx.loadingAnimation) {
776
+ this.ctx.loadingAnimation.stop();
777
+ this.ctx.loadingAnimation = undefined;
778
+ this.ctx.statusContainer.clear();
779
+ }
780
+ if (this.ctx.streamingComponent) {
781
+ this.ctx.chatContainer.removeChild(this.ctx.streamingComponent);
782
+ this.ctx.streamingComponent = undefined;
783
+ this.ctx.streamingMessage = undefined;
784
+ }
785
+ await this.ctx.flushPendingModelSwitch();
786
+ for (const toolCallId of Array.from(this.ctx.pendingTools.keys())) {
787
+ if (!this.#backgroundToolCallIds.has(toolCallId)) {
788
+ // A foreground tool still pending at turn end never delivered a result;
789
+ // seal it so it freezes (and stops animating) rather than lingering in
790
+ // the transcript live region as a streaming preview until the next thaw.
791
+ const component = this.ctx.pendingTools.get(toolCallId);
792
+ // A foreground read still pending at turn end shares a group component
793
+ // keyed by every read's id; seal it too so a never-delivered read does
794
+ // not keep the group live (and pinning the live region) indefinitely.
795
+ if (component instanceof ToolExecutionComponent || component instanceof ReadToolGroupComponent) {
796
+ component.seal();
797
+ }
798
+ this.ctx.pendingTools.delete(toolCallId);
799
+ }
800
+ }
801
+ this.#backgroundToolCallIds = new Set(
802
+ Array.from(this.#backgroundToolCallIds).filter(toolCallId => this.ctx.pendingTools.has(toolCallId)),
803
+ );
804
+ this.#readToolCallArgs.clear();
805
+ this.#readToolCallAssistantComponents.clear();
806
+ this.#resetReadGroup();
807
+ // The turn is over: nothing else lands this turn, so the waiting poll is
808
+ // final history — seal it instead of letting its spinner tick while idle.
809
+ this.#resolveDisplaceablePoll();
810
+ this.#lastAssistantComponent = undefined;
811
+ this.ctx.ui.requestRender();
812
+ this.#scheduleIdleCompaction();
813
+ this.sendCompletionNotification();
814
+ }
815
+
816
+ async #handleAutoCompactionStart(
817
+ event: Extract<AgentSessionEvent, { type: "auto_compaction_start" }>,
818
+ ): Promise<void> {
819
+ this.#cancelIdleCompaction();
820
+ this.ctx.autoCompactionEscapeHandler = this.ctx.editor.onEscape;
821
+ this.ctx.editor.onEscape = () => {
822
+ this.ctx.session.abortCompaction();
823
+ };
824
+ this.ctx.statusContainer.clear();
825
+ const reasonText =
826
+ event.reason === "overflow"
827
+ ? "Context overflow detected, "
828
+ : event.reason === "incomplete"
829
+ ? "Response incomplete, "
830
+ : event.reason === "idle"
831
+ ? "Idle "
832
+ : "";
833
+ const actionLabel =
834
+ event.action === "handoff"
835
+ ? "Auto-handoff"
836
+ : event.action === "shake"
837
+ ? "Auto-shake"
838
+ : "Auto context-full maintenance";
839
+ this.ctx.autoCompactionLoader = new Loader(
840
+ this.ctx.ui,
841
+ spinner => theme.fg("accent", spinner),
842
+ text => theme.fg("muted", text),
843
+ `${reasonText}${actionLabel}… (esc to cancel)`,
844
+ getSymbolTheme().spinnerFrames,
845
+ );
846
+ this.ctx.statusContainer.addChild(this.ctx.autoCompactionLoader);
847
+ this.ctx.ui.requestRender();
848
+ }
849
+
850
+ async #handleAutoCompactionEnd(event: Extract<AgentSessionEvent, { type: "auto_compaction_end" }>): Promise<void> {
851
+ this.#cancelIdleCompaction();
852
+ if (this.ctx.autoCompactionEscapeHandler) {
853
+ this.ctx.editor.onEscape = this.ctx.autoCompactionEscapeHandler;
854
+ this.ctx.autoCompactionEscapeHandler = undefined;
855
+ }
856
+ if (this.ctx.autoCompactionLoader) {
857
+ this.ctx.autoCompactionLoader.stop();
858
+ this.ctx.autoCompactionLoader = undefined;
859
+ this.ctx.statusContainer.clear();
860
+ }
861
+ const isHandoffAction = event.action === "handoff";
862
+ const isShakeAction = event.action === "shake";
863
+ if (event.aborted) {
864
+ this.ctx.showStatus(
865
+ isHandoffAction
866
+ ? "Auto-handoff cancelled"
867
+ : isShakeAction
868
+ ? "Auto-shake cancelled"
869
+ : "Auto context-full maintenance cancelled",
870
+ );
871
+ } else if (isShakeAction) {
872
+ // Shake produces no CompactionResult; rebuild on success, suppress benign skips.
873
+ // The fallback path (`errorMessage` set, `skipped` false) means shake reclaimed
874
+ // some tokens before deciding the threshold still wasn't cleared — rebuild so
875
+ // the chat reflects the dropped regions even though a context-full pass follows.
876
+ if (event.errorMessage) {
877
+ if (!event.skipped) {
878
+ this.ctx.rebuildChatFromMessages();
879
+ this.ctx.statusLine.invalidate();
880
+ this.ctx.updateEditorTopBorder();
881
+ }
882
+ this.ctx.showWarning(event.errorMessage);
883
+ } else if (!event.skipped) {
884
+ this.ctx.rebuildChatFromMessages();
885
+ this.ctx.statusLine.invalidate();
886
+ this.ctx.updateEditorTopBorder();
887
+ this.ctx.showStatus("Auto-shake completed");
888
+ }
889
+ } else if (event.result) {
890
+ this.ctx.rebuildChatFromMessages();
891
+ this.ctx.statusLine.invalidate();
892
+ this.ctx.updateEditorTopBorder();
893
+ } else if (event.errorMessage) {
894
+ this.ctx.showWarning(event.errorMessage);
895
+ } else if (isHandoffAction) {
896
+ this.ctx.chatContainer.clear();
897
+ this.ctx.rebuildChatFromMessages();
898
+ this.ctx.statusLine.invalidate();
899
+ this.ctx.updateEditorTopBorder();
900
+ await this.ctx.reloadTodos();
901
+ this.ctx.showStatus("Auto-handoff completed");
902
+ } else if (event.skipped) {
903
+ // Benign skip: no model selected, no candidate models available, or nothing
904
+ // to compact yet. Not a failure — suppress the warning.
905
+ } else {
906
+ this.ctx.showWarning("Auto context-full maintenance failed; continuing without maintenance");
907
+ }
908
+ await this.ctx.flushCompactionQueue({ willRetry: event.willRetry });
909
+ this.ctx.ui.requestRender();
910
+ }
911
+
912
+ async #handleAutoRetryStart(event: Extract<AgentSessionEvent, { type: "auto_retry_start" }>): Promise<void> {
913
+ this.ctx.retryEscapeHandler = this.ctx.editor.onEscape;
914
+ this.ctx.editor.onEscape = () => {
915
+ this.ctx.session.abortRetry();
916
+ };
917
+ this.ctx.statusContainer.clear();
918
+ const delaySeconds = Math.round(event.delayMs / 1000);
919
+ this.ctx.retryLoader = new Loader(
920
+ this.ctx.ui,
921
+ spinner => theme.fg("warning", spinner),
922
+ text => theme.fg("muted", text),
923
+ `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s… (esc to cancel)`,
924
+ getSymbolTheme().spinnerFrames,
925
+ );
926
+ this.ctx.statusContainer.addChild(this.ctx.retryLoader);
927
+ this.ctx.ui.requestRender();
928
+ }
929
+
930
+ async #handleAutoRetryEnd(event: Extract<AgentSessionEvent, { type: "auto_retry_end" }>): Promise<void> {
931
+ if (this.ctx.retryEscapeHandler) {
932
+ this.ctx.editor.onEscape = this.ctx.retryEscapeHandler;
933
+ this.ctx.retryEscapeHandler = undefined;
934
+ }
935
+ if (this.ctx.retryLoader) {
936
+ this.ctx.retryLoader.stop();
937
+ this.ctx.retryLoader = undefined;
938
+ this.ctx.statusContainer.clear();
939
+ }
940
+ if (!event.success) {
941
+ this.ctx.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
942
+ }
943
+ this.ctx.ui.requestRender();
944
+ }
945
+
946
+ async #handleRetryFallbackApplied(
947
+ event: Extract<AgentSessionEvent, { type: "retry_fallback_applied" }>,
948
+ ): Promise<void> {
949
+ this.ctx.showWarning(`Fallback: ${event.from} -> ${event.to}`);
950
+ }
951
+
952
+ async #handleRetryFallbackSucceeded(
953
+ event: Extract<AgentSessionEvent, { type: "retry_fallback_succeeded" }>,
954
+ ): Promise<void> {
955
+ this.ctx.showStatus(`Fallback succeeded on ${event.model}`);
956
+ }
957
+
958
+ async #handleTtsrTriggered(event: Extract<AgentSessionEvent, { type: "ttsr_triggered" }>): Promise<void> {
959
+ // Consecutive notifications (e.g. per-tool matches from one assistant
960
+ // message) merge into the previous block instead of stacking. Mutating an
961
+ // existing block is only safe while it sits inside the live region — a
962
+ // still-mutating block above it means none of its rows have been committed
963
+ // to native scrollback yet (commits are prefix-only and stop at the first
964
+ // live block), so the grown block still repaints.
965
+ const previous = this.#lastTtsrNotification;
966
+ if (
967
+ previous &&
968
+ this.ctx.chatContainer.children.at(-1) === previous &&
969
+ this.ctx.chatContainer.isWithinLiveRegion(previous)
970
+ ) {
971
+ previous.addRules(event.rules);
972
+ this.ctx.ui.requestRender();
973
+ return;
974
+ }
975
+ const component = new TtsrNotificationComponent(event.rules);
976
+ component.setExpanded(this.ctx.toolOutputExpanded);
977
+ this.ctx.present(component);
978
+ this.#lastTtsrNotification = component;
979
+ }
980
+
981
+ async #handleTodoReminder(event: Extract<AgentSessionEvent, { type: "todo_reminder" }>): Promise<void> {
982
+ const component = new TodoReminderComponent(event.todos, event.attempt, event.maxAttempts);
983
+ this.ctx.present(component);
984
+ }
985
+
986
+ async #handleTodoAutoClear(_event: Extract<AgentSessionEvent, { type: "todo_auto_clear" }>): Promise<void> {
987
+ await this.ctx.reloadTodos();
988
+ }
989
+
990
+ #cancelIdleCompaction(): void {
991
+ if (this.#idleCompactionTimer) {
992
+ clearTimeout(this.#idleCompactionTimer);
993
+ this.#idleCompactionTimer = undefined;
994
+ }
995
+ }
996
+
997
+ #scheduleIdleCompaction(): void {
998
+ this.#cancelIdleCompaction();
999
+ // Don't schedule idle work while context maintenance is already running; the
1000
+ // maintenance flow may reset the session before this timer fires.
1001
+ if (this.ctx.session.isCompacting) return;
1002
+
1003
+ const idleSettings = settings.getGroup("compaction");
1004
+ if (!idleSettings.idleEnabled) return;
1005
+
1006
+ // Only if input is empty
1007
+ if (this.ctx.editor.getText().trim()) return;
1008
+
1009
+ const threshold = idleSettings.idleThresholdTokens;
1010
+ if (threshold <= 0) return;
1011
+ if (this.#currentContextTokens() < threshold) return;
1012
+
1013
+ const timeoutMs = Math.max(60, Math.min(3600, idleSettings.idleTimeoutSeconds)) * 1000;
1014
+ this.#idleCompactionTimer = setTimeout(() => {
1015
+ this.#idleCompactionTimer = undefined;
1016
+ // Re-check conditions before firing. Pruning may have run between arming
1017
+ // the timer and now, dropping usage back below the idle threshold.
1018
+ if (this.ctx.session.isStreaming) return;
1019
+ if (this.ctx.session.isCompacting) return;
1020
+ if (this.ctx.editor.getText().trim()) return;
1021
+ if (this.#currentContextTokens() < threshold) return;
1022
+ void this.ctx.session.runIdleCompaction();
1023
+ }, timeoutMs);
1024
+ this.#idleCompactionTimer.unref?.();
1025
+ }
1026
+
1027
+ #currentContextTokens(): number {
1028
+ const lastAssistant = this.ctx.session.agent.state.messages
1029
+ .slice()
1030
+ .reverse()
1031
+ .find((m): m is AssistantMessage => m.role === "assistant" && m.stopReason !== "aborted");
1032
+ return lastAssistant?.usage ? calculatePromptTokens(lastAssistant.usage) : 0;
1033
+ }
1034
+
1035
+ sendCompletionNotification(): void {
1036
+ const notify = settings.get("completion.notify");
1037
+ if (notify === "off") return;
1038
+
1039
+ // Skip when the turn was aborted (e.g. ask cancelled with Ctrl+C) or
1040
+ // errored — those are not "Task complete" events. Mirrors the gate
1041
+ // already used by #currentContextTokens, #handleMessageEnd, and the
1042
+ // retry / TTSR / compaction skip paths across agent-session.ts.
1043
+ const last = this.ctx.session.getLastAssistantMessage?.();
1044
+ if (last?.stopReason === "aborted" || last?.stopReason === "error") return;
1045
+
1046
+ const sessionName = this.ctx.sessionManager.getSessionName();
1047
+ TERMINAL.sendNotification({
1048
+ title: sessionName || "Oh My Pi",
1049
+ body: "Complete",
1050
+ type: "completion",
1051
+ actions: "focus",
1052
+ });
1053
+ }
1054
+ }