pi-agent-zh 17.1.8

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 (1519) hide show
  1. package/CHANGELOG.md +14459 -0
  2. package/README.md +35 -0
  3. package/dist/CHANGELOG-vt8ene9g.md +14459 -0
  4. package/dist/cli.js +22079 -0
  5. package/dist/template-dys3vk5b.js +1671 -0
  6. package/dist/template-f8wx9vfn.css +1355 -0
  7. package/dist/template-qat058wr.html +55 -0
  8. package/dist/tool-views.generated-jdfmzwmn.js +35 -0
  9. package/examples/README.md +21 -0
  10. package/examples/custom-tools/README.md +104 -0
  11. package/examples/custom-tools/hello/index.ts +20 -0
  12. package/examples/extensions/README.md +142 -0
  13. package/examples/extensions/api-demo.ts +79 -0
  14. package/examples/extensions/chalk-logger.ts +25 -0
  15. package/examples/extensions/hello.ts +31 -0
  16. package/examples/extensions/pirate.ts +43 -0
  17. package/examples/extensions/plan-mode.ts +549 -0
  18. package/examples/extensions/reload-runtime.ts +38 -0
  19. package/examples/extensions/thinking-note.ts +13 -0
  20. package/examples/extensions/tools.ts +145 -0
  21. package/examples/extensions/with-deps/index.ts +36 -0
  22. package/examples/extensions/with-deps/package-lock.json +31 -0
  23. package/examples/extensions/with-deps/package.json +17 -0
  24. package/examples/hooks/README.md +56 -0
  25. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  26. package/examples/hooks/confirm-destructive.ts +58 -0
  27. package/examples/hooks/custom-compaction.ts +115 -0
  28. package/examples/hooks/dirty-repo-guard.ts +51 -0
  29. package/examples/hooks/file-trigger.ts +40 -0
  30. package/examples/hooks/git-checkpoint.ts +52 -0
  31. package/examples/hooks/handoff.ts +149 -0
  32. package/examples/hooks/permission-gate.ts +33 -0
  33. package/examples/hooks/protected-paths.ts +29 -0
  34. package/examples/hooks/qna.ts +118 -0
  35. package/examples/hooks/status-line.ts +39 -0
  36. package/examples/sdk/01-minimal.ts +21 -0
  37. package/examples/sdk/02-custom-model.ts +49 -0
  38. package/examples/sdk/03-custom-prompt.ts +46 -0
  39. package/examples/sdk/04-skills.ts +43 -0
  40. package/examples/sdk/06-extensions.ts +82 -0
  41. package/examples/sdk/06-hooks.ts +61 -0
  42. package/examples/sdk/07-context-files.ts +35 -0
  43. package/examples/sdk/08-prompt-templates.ts +41 -0
  44. package/examples/sdk/08-slash-commands.ts +46 -0
  45. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  46. package/examples/sdk/11-sessions.ts +47 -0
  47. package/examples/sdk/12-redis-sessions.ts +54 -0
  48. package/examples/sdk/13-sql-sessions.ts +61 -0
  49. package/examples/sdk/README.md +169 -0
  50. package/package.json +606 -0
  51. package/scripts/bench-guard.ts +71 -0
  52. package/scripts/bench-title-models.ts +332 -0
  53. package/scripts/build-binary.ts +117 -0
  54. package/scripts/bundle-dist.ts +133 -0
  55. package/scripts/compile-binary.ts +69 -0
  56. package/scripts/embed-mupdf-wasm.ts +67 -0
  57. package/scripts/format-prompts.ts +68 -0
  58. package/scripts/generate-aria-snapshot.ts +134 -0
  59. package/scripts/generate-docs-index.ts +58 -0
  60. package/scripts/generate-share-viewer.ts +34 -0
  61. package/scripts/legacy-pi-virtual-module.ts +195 -0
  62. package/scripts/measure-prompt-tokens.ts +63 -0
  63. package/scripts/omp +42 -0
  64. package/scripts/omp.ts +19 -0
  65. package/src/advisor/advise-tool.ts +216 -0
  66. package/src/advisor/config.ts +341 -0
  67. package/src/advisor/emission-guard.ts +172 -0
  68. package/src/advisor/index.ts +6 -0
  69. package/src/advisor/runtime.ts +1232 -0
  70. package/src/advisor/transcript-recorder.ts +159 -0
  71. package/src/advisor/watchdog.ts +135 -0
  72. package/src/async/index.ts +1 -0
  73. package/src/async/job-manager.ts +821 -0
  74. package/src/auto-thinking/classifier.ts +182 -0
  75. package/src/autolearn/controller.ts +152 -0
  76. package/src/autolearn/managed-skills.ts +255 -0
  77. package/src/autoresearch/command-resume.md +14 -0
  78. package/src/autoresearch/dashboard.ts +436 -0
  79. package/src/autoresearch/git.ts +331 -0
  80. package/src/autoresearch/helpers.ts +218 -0
  81. package/src/autoresearch/index.ts +541 -0
  82. package/src/autoresearch/prompt-setup.md +43 -0
  83. package/src/autoresearch/prompt.md +103 -0
  84. package/src/autoresearch/resume-message.md +10 -0
  85. package/src/autoresearch/state.ts +273 -0
  86. package/src/autoresearch/storage.ts +700 -0
  87. package/src/autoresearch/tools/init-experiment.ts +269 -0
  88. package/src/autoresearch/tools/log-experiment.ts +521 -0
  89. package/src/autoresearch/tools/run-experiment.ts +407 -0
  90. package/src/autoresearch/tools/update-notes.ts +109 -0
  91. package/src/autoresearch/types.ts +168 -0
  92. package/src/capability/context-file.ts +44 -0
  93. package/src/capability/extension-module.ts +34 -0
  94. package/src/capability/extension.ts +47 -0
  95. package/src/capability/fs.ts +117 -0
  96. package/src/capability/hook.ts +40 -0
  97. package/src/capability/index.ts +467 -0
  98. package/src/capability/instruction.ts +37 -0
  99. package/src/capability/mcp.ts +97 -0
  100. package/src/capability/prompt.ts +35 -0
  101. package/src/capability/rule-buckets.ts +66 -0
  102. package/src/capability/rule.ts +298 -0
  103. package/src/capability/settings.ts +34 -0
  104. package/src/capability/skill.ts +63 -0
  105. package/src/capability/slash-command.ts +40 -0
  106. package/src/capability/ssh.ts +41 -0
  107. package/src/capability/system-prompt.ts +34 -0
  108. package/src/capability/tool.ts +38 -0
  109. package/src/capability/types.ts +187 -0
  110. package/src/cleanse/agent.ts +226 -0
  111. package/src/cleanse/balance.ts +79 -0
  112. package/src/cleanse/checkers.ts +996 -0
  113. package/src/cleanse/index.ts +190 -0
  114. package/src/cleanse/loop.ts +51 -0
  115. package/src/cleanse/parsers.ts +726 -0
  116. package/src/cleanse/progress.ts +50 -0
  117. package/src/cleanse/prompts/assignment.md +47 -0
  118. package/src/cleanse/types.ts +72 -0
  119. package/src/cli/agents-cli.ts +138 -0
  120. package/src/cli/args.ts +420 -0
  121. package/src/cli/auth-broker-cli.ts +940 -0
  122. package/src/cli/auth-gateway-cli.ts +674 -0
  123. package/src/cli/bench-cli.ts +990 -0
  124. package/src/cli/classify-install-target.ts +76 -0
  125. package/src/cli/claude-trace-cli.ts +795 -0
  126. package/src/cli/commands/init-xdg.ts +27 -0
  127. package/src/cli/completion-gen.ts +550 -0
  128. package/src/cli/config-cli.ts +459 -0
  129. package/src/cli/dry-balance-cli.ts +864 -0
  130. package/src/cli/extension-flags.ts +48 -0
  131. package/src/cli/file-processor.ts +132 -0
  132. package/src/cli/flag-tables.ts +352 -0
  133. package/src/cli/gallery-cli.ts +272 -0
  134. package/src/cli/gallery-fixtures/agentic.ts +420 -0
  135. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  136. package/src/cli/gallery-fixtures/edit.ts +254 -0
  137. package/src/cli/gallery-fixtures/fs.ts +220 -0
  138. package/src/cli/gallery-fixtures/index.ts +40 -0
  139. package/src/cli/gallery-fixtures/interaction.ts +46 -0
  140. package/src/cli/gallery-fixtures/memory.ts +81 -0
  141. package/src/cli/gallery-fixtures/misc.ts +177 -0
  142. package/src/cli/gallery-fixtures/search.ts +135 -0
  143. package/src/cli/gallery-fixtures/shell.ts +241 -0
  144. package/src/cli/gallery-fixtures/types.ts +57 -0
  145. package/src/cli/gallery-fixtures/web.ts +158 -0
  146. package/src/cli/gallery-screenshot.ts +279 -0
  147. package/src/cli/gc-cli.ts +946 -0
  148. package/src/cli/grep-cli.ts +161 -0
  149. package/src/cli/grievances-cli.ts +256 -0
  150. package/src/cli/initial-message.ts +58 -0
  151. package/src/cli/models-cli.ts +388 -0
  152. package/src/cli/plugin-cli.ts +996 -0
  153. package/src/cli/profile-alias.ts +369 -0
  154. package/src/cli/profile-bootstrap.ts +233 -0
  155. package/src/cli/read-cli.ts +99 -0
  156. package/src/cli/session-picker.ts +93 -0
  157. package/src/cli/setup-cli.ts +320 -0
  158. package/src/cli/setup-model-picker.ts +43 -0
  159. package/src/cli/shell-cli.ts +176 -0
  160. package/src/cli/ssh-cli.ts +179 -0
  161. package/src/cli/startup-cwd.ts +58 -0
  162. package/src/cli/stats-cli.ts +229 -0
  163. package/src/cli/tiny-models-cli.ts +153 -0
  164. package/src/cli/ttsr-cli.ts +995 -0
  165. package/src/cli/update-cli.ts +1219 -0
  166. package/src/cli/usage-cli.ts +1084 -0
  167. package/src/cli/usage-error.ts +7 -0
  168. package/src/cli/web-search-cli.ts +144 -0
  169. package/src/cli/worktree-cli.ts +311 -0
  170. package/src/cli-commands.ts +166 -0
  171. package/src/cli.ts +407 -0
  172. package/src/collab/crypto.ts +63 -0
  173. package/src/collab/display-name.ts +13 -0
  174. package/src/collab/guest.ts +731 -0
  175. package/src/collab/host.ts +691 -0
  176. package/src/collab/protocol.ts +296 -0
  177. package/src/collab/relay-client.ts +282 -0
  178. package/src/collab/replication-shrink.ts +111 -0
  179. package/src/commands/acp.ts +33 -0
  180. package/src/commands/agents.ts +57 -0
  181. package/src/commands/auth-broker.ts +99 -0
  182. package/src/commands/auth-gateway.ts +69 -0
  183. package/src/commands/bench.ts +65 -0
  184. package/src/commands/cleanse.ts +45 -0
  185. package/src/commands/commit.ts +46 -0
  186. package/src/commands/complete.ts +66 -0
  187. package/src/commands/completions.ts +60 -0
  188. package/src/commands/config.ts +51 -0
  189. package/src/commands/dry-balance.ts +43 -0
  190. package/src/commands/gallery.ts +60 -0
  191. package/src/commands/gc.ts +46 -0
  192. package/src/commands/grep.ts +48 -0
  193. package/src/commands/grievances.ts +51 -0
  194. package/src/commands/install.ts +107 -0
  195. package/src/commands/join.ts +39 -0
  196. package/src/commands/launch.ts +212 -0
  197. package/src/commands/models.ts +60 -0
  198. package/src/commands/plugin.ts +78 -0
  199. package/src/commands/read.ts +38 -0
  200. package/src/commands/say.ts +145 -0
  201. package/src/commands/setup.ts +67 -0
  202. package/src/commands/shell.ts +29 -0
  203. package/src/commands/ssh.ts +60 -0
  204. package/src/commands/stats.ts +29 -0
  205. package/src/commands/tiny-models.ts +36 -0
  206. package/src/commands/token.ts +164 -0
  207. package/src/commands/ttsr.ts +125 -0
  208. package/src/commands/update.ts +33 -0
  209. package/src/commands/usage.ts +54 -0
  210. package/src/commands/web-search.ts +42 -0
  211. package/src/commands/worktree.ts +62 -0
  212. package/src/commit/agentic/agent.ts +322 -0
  213. package/src/commit/agentic/fallback.ts +96 -0
  214. package/src/commit/agentic/index.ts +382 -0
  215. package/src/commit/agentic/lock-files.ts +107 -0
  216. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  217. package/src/commit/agentic/prompts/session-user.md +25 -0
  218. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  219. package/src/commit/agentic/prompts/system.md +38 -0
  220. package/src/commit/agentic/state.ts +60 -0
  221. package/src/commit/agentic/tools/analyze-file.ts +148 -0
  222. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  223. package/src/commit/agentic/tools/git-hunk.ts +52 -0
  224. package/src/commit/agentic/tools/git-overview.ts +62 -0
  225. package/src/commit/agentic/tools/index.ts +54 -0
  226. package/src/commit/agentic/tools/propose-changelog.ts +147 -0
  227. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  228. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  229. package/src/commit/agentic/tools/schemas.ts +11 -0
  230. package/src/commit/agentic/tools/split-commit.ts +241 -0
  231. package/src/commit/agentic/topo-sort.ts +44 -0
  232. package/src/commit/agentic/trivial.ts +51 -0
  233. package/src/commit/agentic/validation.ts +183 -0
  234. package/src/commit/analysis/conventional.ts +64 -0
  235. package/src/commit/analysis/index.ts +4 -0
  236. package/src/commit/analysis/scope.ts +242 -0
  237. package/src/commit/analysis/summary.ts +107 -0
  238. package/src/commit/analysis/validation.ts +66 -0
  239. package/src/commit/changelog/detect.ts +40 -0
  240. package/src/commit/changelog/generate.ts +101 -0
  241. package/src/commit/changelog/index.ts +234 -0
  242. package/src/commit/changelog/parse.ts +44 -0
  243. package/src/commit/cli.ts +85 -0
  244. package/src/commit/git/diff.ts +148 -0
  245. package/src/commit/index.ts +5 -0
  246. package/src/commit/map-reduce/index.ts +69 -0
  247. package/src/commit/map-reduce/map-phase.ts +193 -0
  248. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  249. package/src/commit/map-reduce/utils.ts +9 -0
  250. package/src/commit/message.ts +11 -0
  251. package/src/commit/model-selection.ts +95 -0
  252. package/src/commit/pipeline.ts +243 -0
  253. package/src/commit/prompts/analysis-system.md +148 -0
  254. package/src/commit/prompts/analysis-user.md +38 -0
  255. package/src/commit/prompts/changelog-system.md +50 -0
  256. package/src/commit/prompts/changelog-user.md +18 -0
  257. package/src/commit/prompts/file-observer-system.md +24 -0
  258. package/src/commit/prompts/file-observer-user.md +8 -0
  259. package/src/commit/prompts/reduce-system.md +50 -0
  260. package/src/commit/prompts/reduce-user.md +17 -0
  261. package/src/commit/prompts/summary-retry.md +3 -0
  262. package/src/commit/prompts/summary-system.md +38 -0
  263. package/src/commit/prompts/summary-user.md +13 -0
  264. package/src/commit/prompts/types-description.md +2 -0
  265. package/src/commit/shared-llm.ts +70 -0
  266. package/src/commit/types.ts +118 -0
  267. package/src/commit/utils/exclusions.ts +42 -0
  268. package/src/commit/utils.ts +58 -0
  269. package/src/config/api-key-resolver.ts +81 -0
  270. package/src/config/append-only-context-mode.ts +76 -0
  271. package/src/config/config-file.ts +347 -0
  272. package/src/config/file-lock.ts +164 -0
  273. package/src/config/inline-tool-descriptors-mode.ts +26 -0
  274. package/src/config/keybindings.ts +688 -0
  275. package/src/config/mcp-schema.json +247 -0
  276. package/src/config/model-discovery.ts +999 -0
  277. package/src/config/model-registry.ts +2725 -0
  278. package/src/config/model-resolver.ts +2068 -0
  279. package/src/config/model-roles.ts +113 -0
  280. package/src/config/models-config-schema-bundle.ts +313 -0
  281. package/src/config/models-config-schema.ts +14 -0
  282. package/src/config/models-config.ts +130 -0
  283. package/src/config/prompt-templates.ts +205 -0
  284. package/src/config/provider-globals.ts +25 -0
  285. package/src/config/resolve-config-value.ts +94 -0
  286. package/src/config/service-tier.ts +141 -0
  287. package/src/config/settings-schema.ts +5751 -0
  288. package/src/config/settings.ts +2369 -0
  289. package/src/config.ts +242 -0
  290. package/src/cursor.ts +513 -0
  291. package/src/dap/client.ts +1043 -0
  292. package/src/dap/config.ts +480 -0
  293. package/src/dap/defaults.json +212 -0
  294. package/src/dap/index.ts +4 -0
  295. package/src/dap/session.ts +1841 -0
  296. package/src/dap/types.ts +611 -0
  297. package/src/debug/index.ts +584 -0
  298. package/src/debug/log-formatting.ts +58 -0
  299. package/src/debug/log-viewer.ts +966 -0
  300. package/src/debug/profiler.ts +168 -0
  301. package/src/debug/protocol-probe.ts +267 -0
  302. package/src/debug/raw-sse-buffer.ts +421 -0
  303. package/src/debug/raw-sse.ts +312 -0
  304. package/src/debug/remote-debugger.ts +151 -0
  305. package/src/debug/report-bundle.ts +411 -0
  306. package/src/debug/system-info.ts +111 -0
  307. package/src/debug/terminal-info.ts +124 -0
  308. package/src/discovery/agents-md.ts +67 -0
  309. package/src/discovery/agents.ts +230 -0
  310. package/src/discovery/at-imports.ts +273 -0
  311. package/src/discovery/builtin-defaults.ts +39 -0
  312. package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
  313. package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
  314. package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
  315. package/src/discovery/builtin-rules/go-ioutil.md +37 -0
  316. package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
  317. package/src/discovery/builtin-rules/go-new-expr.md +44 -0
  318. package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
  319. package/src/discovery/builtin-rules/go-range-int.md +45 -0
  320. package/src/discovery/builtin-rules/index.ts +74 -0
  321. package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
  322. package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
  323. package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
  324. package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
  325. package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
  326. package/src/discovery/builtin-rules/rs-result-type.md +20 -0
  327. package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
  328. package/src/discovery/builtin-rules/ts-import-type.md +43 -0
  329. package/src/discovery/builtin-rules/ts-no-any.md +66 -0
  330. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
  331. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
  332. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
  333. package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
  334. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  335. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  336. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  337. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
  338. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  339. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  340. package/src/discovery/builtin.ts +935 -0
  341. package/src/discovery/claude-plugins.ts +624 -0
  342. package/src/discovery/claude.ts +588 -0
  343. package/src/discovery/cline.ts +83 -0
  344. package/src/discovery/codex.ts +540 -0
  345. package/src/discovery/cursor.ts +220 -0
  346. package/src/discovery/gemini.ts +383 -0
  347. package/src/discovery/github.ts +337 -0
  348. package/src/discovery/helpers.ts +1159 -0
  349. package/src/discovery/index.ts +81 -0
  350. package/src/discovery/mcp-json.ts +172 -0
  351. package/src/discovery/omp-extension-roots.ts +213 -0
  352. package/src/discovery/omp-plugins.ts +387 -0
  353. package/src/discovery/opencode.ts +441 -0
  354. package/src/discovery/plugin-dir-roots.ts +28 -0
  355. package/src/discovery/ssh.ts +153 -0
  356. package/src/discovery/substitute-plugin-root.ts +77 -0
  357. package/src/discovery/vscode.ts +105 -0
  358. package/src/discovery/windsurf.ts +147 -0
  359. package/src/edit/apply-patch/index.ts +87 -0
  360. package/src/edit/apply-patch/parser.ts +174 -0
  361. package/src/edit/diff.ts +999 -0
  362. package/src/edit/file-snapshot-store.ts +148 -0
  363. package/src/edit/hashline/block-resolver.ts +33 -0
  364. package/src/edit/hashline/diff.ts +356 -0
  365. package/src/edit/hashline/execute.ts +280 -0
  366. package/src/edit/hashline/filesystem.ts +223 -0
  367. package/src/edit/hashline/index.ts +5 -0
  368. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  369. package/src/edit/hashline/params.ts +12 -0
  370. package/src/edit/index.ts +676 -0
  371. package/src/edit/modes/apply-patch.lark +19 -0
  372. package/src/edit/modes/apply-patch.ts +53 -0
  373. package/src/edit/modes/patch.ts +1958 -0
  374. package/src/edit/modes/replace.ts +1137 -0
  375. package/src/edit/normalize.ts +345 -0
  376. package/src/edit/notebook.ts +242 -0
  377. package/src/edit/read-file.ts +25 -0
  378. package/src/edit/renderer.ts +998 -0
  379. package/src/edit/snapshot-details.ts +77 -0
  380. package/src/edit/streaming.ts +698 -0
  381. package/src/eval/agent-bridge.ts +224 -0
  382. package/src/eval/backend-helpers.ts +48 -0
  383. package/src/eval/backend.ts +71 -0
  384. package/src/eval/bridge-timeout.ts +62 -0
  385. package/src/eval/budget-bridge.ts +48 -0
  386. package/src/eval/completion-bridge.ts +211 -0
  387. package/src/eval/concurrency-bridge.ts +34 -0
  388. package/src/eval/executor-base.ts +504 -0
  389. package/src/eval/idle-timeout.ts +91 -0
  390. package/src/eval/index.ts +6 -0
  391. package/src/eval/jl/executor.ts +540 -0
  392. package/src/eval/jl/index.ts +54 -0
  393. package/src/eval/jl/kernel.ts +236 -0
  394. package/src/eval/jl/prelude.jl +736 -0
  395. package/src/eval/jl/prelude.ts +3 -0
  396. package/src/eval/jl/runner.jl +666 -0
  397. package/src/eval/jl/runtime.ts +118 -0
  398. package/src/eval/js/context-manager.ts +701 -0
  399. package/src/eval/js/executor.ts +175 -0
  400. package/src/eval/js/index.ts +40 -0
  401. package/src/eval/js/process-entry.ts +31 -0
  402. package/src/eval/js/shared/helpers.ts +170 -0
  403. package/src/eval/js/shared/indirect-eval.ts +30 -0
  404. package/src/eval/js/shared/local-module-loader.ts +364 -0
  405. package/src/eval/js/shared/prelude.ts +2 -0
  406. package/src/eval/js/shared/prelude.txt +294 -0
  407. package/src/eval/js/shared/rewrite-imports.ts +550 -0
  408. package/src/eval/js/shared/runtime.ts +590 -0
  409. package/src/eval/js/shared/types.ts +18 -0
  410. package/src/eval/js/tool-bridge.ts +163 -0
  411. package/src/eval/js/worker-core.ts +380 -0
  412. package/src/eval/js/worker-entry.ts +37 -0
  413. package/src/eval/js/worker-protocol.ts +47 -0
  414. package/src/eval/kernel-base.ts +569 -0
  415. package/src/eval/py/display.ts +71 -0
  416. package/src/eval/py/executor.ts +603 -0
  417. package/src/eval/py/index.ts +57 -0
  418. package/src/eval/py/kernel.ts +234 -0
  419. package/src/eval/py/prelude.py +672 -0
  420. package/src/eval/py/prelude.ts +3 -0
  421. package/src/eval/py/runner.py +1388 -0
  422. package/src/eval/py/runtime.ts +276 -0
  423. package/src/eval/py/spawn-options.ts +139 -0
  424. package/src/eval/py/tool-bridge.ts +181 -0
  425. package/src/eval/rb/executor.ts +504 -0
  426. package/src/eval/rb/index.ts +54 -0
  427. package/src/eval/rb/kernel.ts +231 -0
  428. package/src/eval/rb/prelude.rb +552 -0
  429. package/src/eval/rb/prelude.ts +3 -0
  430. package/src/eval/rb/runner.rb +581 -0
  431. package/src/eval/rb/runtime.ts +132 -0
  432. package/src/eval/runtime-env.ts +104 -0
  433. package/src/eval/session-id.ts +8 -0
  434. package/src/eval/types.ts +48 -0
  435. package/src/exa/index.ts +2 -0
  436. package/src/exa/mcp-client.ts +370 -0
  437. package/src/exa/types.ts +69 -0
  438. package/src/exec/bash-executor.ts +627 -0
  439. package/src/exec/direnv.ts +145 -0
  440. package/src/exec/exec.ts +53 -0
  441. package/src/exec/non-interactive-env.ts +118 -0
  442. package/src/export/custom-share.ts +65 -0
  443. package/src/export/html/args.ts +20 -0
  444. package/src/export/html/index.ts +313 -0
  445. package/src/export/html/share-loader.js +102 -0
  446. package/src/export/html/template.css +1355 -0
  447. package/src/export/html/template.html +55 -0
  448. package/src/export/html/template.js +1671 -0
  449. package/src/export/html/tool-views.generated.js +35 -0
  450. package/src/export/html/vendor/highlight.min.js +1213 -0
  451. package/src/export/html/vendor/marked.min.js +6 -0
  452. package/src/export/html/web-palette.ts +126 -0
  453. package/src/export/share.ts +682 -0
  454. package/src/export/ttsr.ts +590 -0
  455. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  456. package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
  457. package/src/extensibility/custom-commands/index.ts +2 -0
  458. package/src/extensibility/custom-commands/loader.ts +242 -0
  459. package/src/extensibility/custom-commands/types.ts +119 -0
  460. package/src/extensibility/custom-tools/index.ts +7 -0
  461. package/src/extensibility/custom-tools/loader.ts +301 -0
  462. package/src/extensibility/custom-tools/types.ts +286 -0
  463. package/src/extensibility/custom-tools/wrapper.ts +50 -0
  464. package/src/extensibility/extensions/compact-handler.ts +40 -0
  465. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  466. package/src/extensibility/extensions/index.ts +16 -0
  467. package/src/extensibility/extensions/load-errors.ts +13 -0
  468. package/src/extensibility/extensions/loader.ts +624 -0
  469. package/src/extensibility/extensions/managed-timers.ts +83 -0
  470. package/src/extensibility/extensions/model-api.ts +39 -0
  471. package/src/extensibility/extensions/runner.ts +1209 -0
  472. package/src/extensibility/extensions/types.ts +1526 -0
  473. package/src/extensibility/extensions/wrapper.ts +377 -0
  474. package/src/extensibility/hooks/index.ts +6 -0
  475. package/src/extensibility/hooks/loader.ts +244 -0
  476. package/src/extensibility/hooks/runner.ts +425 -0
  477. package/src/extensibility/hooks/tool-wrapper.ts +124 -0
  478. package/src/extensibility/hooks/types.ts +612 -0
  479. package/src/extensibility/legacy-pi-ai-shim.ts +140 -0
  480. package/src/extensibility/legacy-pi-coding-agent-shim.ts +1384 -0
  481. package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
  482. package/src/extensibility/plugins/bun-git-cache.ts +91 -0
  483. package/src/extensibility/plugins/doctor.ts +65 -0
  484. package/src/extensibility/plugins/git-url.ts +367 -0
  485. package/src/extensibility/plugins/index.ts +9 -0
  486. package/src/extensibility/plugins/installer.ts +201 -0
  487. package/src/extensibility/plugins/legacy-pi-compat.ts +2532 -0
  488. package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
  489. package/src/extensibility/plugins/loader.ts +535 -0
  490. package/src/extensibility/plugins/manager.ts +1142 -0
  491. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  492. package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
  493. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  494. package/src/extensibility/plugins/marketplace/manager.ts +926 -0
  495. package/src/extensibility/plugins/marketplace/registry.ts +196 -0
  496. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  497. package/src/extensibility/plugins/marketplace/types.ts +192 -0
  498. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  499. package/src/extensibility/plugins/parser.ts +107 -0
  500. package/src/extensibility/plugins/runtime-config.ts +9 -0
  501. package/src/extensibility/plugins/types.ts +194 -0
  502. package/src/extensibility/session-handler-types.ts +21 -0
  503. package/src/extensibility/shared-events.ts +401 -0
  504. package/src/extensibility/skills.ts +511 -0
  505. package/src/extensibility/slash-commands.ts +131 -0
  506. package/src/extensibility/tool-event-input.ts +80 -0
  507. package/src/extensibility/tool-proxy.ts +28 -0
  508. package/src/extensibility/typebox.ts +958 -0
  509. package/src/extensibility/utils.ts +184 -0
  510. package/src/goals/index.ts +3 -0
  511. package/src/goals/runtime.ts +521 -0
  512. package/src/goals/state.ts +37 -0
  513. package/src/goals/tools/goal-tool.ts +251 -0
  514. package/src/hindsight/backend.ts +354 -0
  515. package/src/hindsight/bank.ts +156 -0
  516. package/src/hindsight/client.ts +680 -0
  517. package/src/hindsight/config.ts +193 -0
  518. package/src/hindsight/content.ts +266 -0
  519. package/src/hindsight/index.ts +8 -0
  520. package/src/hindsight/mental-models.ts +429 -0
  521. package/src/hindsight/seeds.json +32 -0
  522. package/src/hindsight/state.ts +550 -0
  523. package/src/hindsight/transcript.ts +71 -0
  524. package/src/index.ts +66 -0
  525. package/src/internal-urls/agent-protocol.ts +180 -0
  526. package/src/internal-urls/artifact-protocol.ts +151 -0
  527. package/src/internal-urls/docs-index.ts +102 -0
  528. package/src/internal-urls/filesystem-resource.ts +34 -0
  529. package/src/internal-urls/history-protocol.ts +198 -0
  530. package/src/internal-urls/index.ts +27 -0
  531. package/src/internal-urls/issue-pr-protocol.ts +594 -0
  532. package/src/internal-urls/json-query.ts +126 -0
  533. package/src/internal-urls/local-protocol.ts +470 -0
  534. package/src/internal-urls/mcp-protocol.ts +165 -0
  535. package/src/internal-urls/memory-protocol.ts +347 -0
  536. package/src/internal-urls/omp-protocol.ts +94 -0
  537. package/src/internal-urls/parse.ts +103 -0
  538. package/src/internal-urls/registry-helpers.ts +132 -0
  539. package/src/internal-urls/router.ts +150 -0
  540. package/src/internal-urls/rule-protocol.ts +45 -0
  541. package/src/internal-urls/skill-protocol.ts +117 -0
  542. package/src/internal-urls/ssh-protocol.ts +368 -0
  543. package/src/internal-urls/types.ts +196 -0
  544. package/src/internal-urls/vault-protocol.ts +940 -0
  545. package/src/internal-urls/xd-protocol.ts +46 -0
  546. package/src/irc/bus.ts +380 -0
  547. package/src/jsonrpc/message-framing.ts +142 -0
  548. package/src/launch/broker.ts +1134 -0
  549. package/src/launch/client.ts +350 -0
  550. package/src/launch/paths.ts +17 -0
  551. package/src/launch/presence.ts +82 -0
  552. package/src/launch/protocol.ts +396 -0
  553. package/src/launch/spawn-options.ts +17 -0
  554. package/src/launch/terminal-output.ts +46 -0
  555. package/src/lib/xai-http.ts +150 -0
  556. package/src/live/attestation.ts +91 -0
  557. package/src/live/controller.ts +517 -0
  558. package/src/live/prompts/agent-final-message.md +3 -0
  559. package/src/live/prompts/live-instructions.md +23 -0
  560. package/src/live/protocol.ts +233 -0
  561. package/src/live/transport.ts +422 -0
  562. package/src/live/visualizer.ts +214 -0
  563. package/src/live/voices.ts +18 -0
  564. package/src/lsp/client.ts +1429 -0
  565. package/src/lsp/clients/biome-client.ts +263 -0
  566. package/src/lsp/clients/index.ts +50 -0
  567. package/src/lsp/clients/lsp-linter-client.ts +85 -0
  568. package/src/lsp/clients/swiftlint-client.ts +120 -0
  569. package/src/lsp/config.ts +549 -0
  570. package/src/lsp/defaults.json +499 -0
  571. package/src/lsp/deferred-diagnostics.ts +66 -0
  572. package/src/lsp/diagnostics-ledger.ts +51 -0
  573. package/src/lsp/edits.ts +288 -0
  574. package/src/lsp/format-options.ts +119 -0
  575. package/src/lsp/index.ts +2766 -0
  576. package/src/lsp/lspmux.ts +233 -0
  577. package/src/lsp/render.ts +668 -0
  578. package/src/lsp/startup-events.ts +13 -0
  579. package/src/lsp/types.ts +453 -0
  580. package/src/lsp/utils.ts +718 -0
  581. package/src/main.ts +1647 -0
  582. package/src/markit/NOTICE +32 -0
  583. package/src/markit/converters/docx.ts +56 -0
  584. package/src/markit/converters/epub.ts +136 -0
  585. package/src/markit/converters/mammoth.d.ts +24 -0
  586. package/src/markit/converters/pdf/columns.ts +103 -0
  587. package/src/markit/converters/pdf/extract.ts +598 -0
  588. package/src/markit/converters/pdf/grid.ts +780 -0
  589. package/src/markit/converters/pdf/headers.ts +106 -0
  590. package/src/markit/converters/pdf/index.ts +146 -0
  591. package/src/markit/converters/pdf/render.ts +501 -0
  592. package/src/markit/converters/pdf/types.ts +84 -0
  593. package/src/markit/converters/pptx.ts +325 -0
  594. package/src/markit/converters/xlsx.ts +173 -0
  595. package/src/markit/index.ts +2 -0
  596. package/src/markit/registry.ts +59 -0
  597. package/src/markit/types.ts +35 -0
  598. package/src/mcp/client.ts +511 -0
  599. package/src/mcp/config-writer.ts +377 -0
  600. package/src/mcp/config.ts +381 -0
  601. package/src/mcp/index.ts +29 -0
  602. package/src/mcp/json-rpc.ts +122 -0
  603. package/src/mcp/loader.ts +125 -0
  604. package/src/mcp/manager.ts +1419 -0
  605. package/src/mcp/oauth-credentials.ts +104 -0
  606. package/src/mcp/oauth-discovery.ts +587 -0
  607. package/src/mcp/oauth-flow.ts +820 -0
  608. package/src/mcp/render.ts +214 -0
  609. package/src/mcp/smithery-auth.ts +108 -0
  610. package/src/mcp/smithery-connect.ts +154 -0
  611. package/src/mcp/smithery-registry.ts +500 -0
  612. package/src/mcp/startup-events.ts +116 -0
  613. package/src/mcp/timeout.ts +59 -0
  614. package/src/mcp/tool-bridge.ts +691 -0
  615. package/src/mcp/tool-cache.ts +117 -0
  616. package/src/mcp/transports/http.ts +523 -0
  617. package/src/mcp/transports/index.ts +7 -0
  618. package/src/mcp/transports/sse.ts +377 -0
  619. package/src/mcp/transports/stdio.ts +903 -0
  620. package/src/mcp/types.ts +437 -0
  621. package/src/memories/index.ts +1434 -0
  622. package/src/memories/storage.ts +578 -0
  623. package/src/memory-backend/index.ts +18 -0
  624. package/src/memory-backend/local-backend.ts +47 -0
  625. package/src/memory-backend/off-backend.ts +25 -0
  626. package/src/memory-backend/resolve.ts +25 -0
  627. package/src/memory-backend/runtime.ts +66 -0
  628. package/src/memory-backend/tool-names.ts +2 -0
  629. package/src/memory-backend/types.ts +166 -0
  630. package/src/mnemopi/backend.ts +629 -0
  631. package/src/mnemopi/config.ts +267 -0
  632. package/src/mnemopi/embed-client.ts +246 -0
  633. package/src/mnemopi/embed-protocol.ts +35 -0
  634. package/src/mnemopi/embed-worker.ts +114 -0
  635. package/src/mnemopi/index.ts +3 -0
  636. package/src/mnemopi/state.ts +906 -0
  637. package/src/modes/acp/acp-agent.ts +2577 -0
  638. package/src/modes/acp/acp-client-bridge.ts +154 -0
  639. package/src/modes/acp/acp-event-mapper.ts +1084 -0
  640. package/src/modes/acp/acp-mode.ts +48 -0
  641. package/src/modes/acp/index.ts +2 -0
  642. package/src/modes/acp/terminal-auth.ts +37 -0
  643. package/src/modes/components/advisor-config.ts +635 -0
  644. package/src/modes/components/advisor-message.ts +109 -0
  645. package/src/modes/components/agent-dashboard.ts +1252 -0
  646. package/src/modes/components/agent-hub.ts +648 -0
  647. package/src/modes/components/agent-transcript-viewer.ts +649 -0
  648. package/src/modes/components/ask-dialog.ts +975 -0
  649. package/src/modes/components/assistant-message.ts +972 -0
  650. package/src/modes/components/background-tan-message.ts +36 -0
  651. package/src/modes/components/bash-execution.ts +233 -0
  652. package/src/modes/components/bordered-loader.ts +41 -0
  653. package/src/modes/components/btw-panel.ts +124 -0
  654. package/src/modes/components/cache-invalidation-marker.ts +110 -0
  655. package/src/modes/components/chat-block.ts +111 -0
  656. package/src/modes/components/chat-transcript-builder.ts +495 -0
  657. package/src/modes/components/collab-prompt-message.ts +32 -0
  658. package/src/modes/components/compaction-summary-message.ts +221 -0
  659. package/src/modes/components/copy-selector.ts +218 -0
  660. package/src/modes/components/countdown-timer.ts +75 -0
  661. package/src/modes/components/custom-editor.ts +1027 -0
  662. package/src/modes/components/custom-message.ts +70 -0
  663. package/src/modes/components/diff.ts +254 -0
  664. package/src/modes/components/dynamic-border.ts +37 -0
  665. package/src/modes/components/error-banner.ts +33 -0
  666. package/src/modes/components/eval-execution.ts +169 -0
  667. package/src/modes/components/execution-shared.ts +101 -0
  668. package/src/modes/components/extensions/extension-dashboard.ts +492 -0
  669. package/src/modes/components/extensions/extension-list.ts +507 -0
  670. package/src/modes/components/extensions/index.ts +9 -0
  671. package/src/modes/components/extensions/inspector-panel.ts +321 -0
  672. package/src/modes/components/extensions/state-manager.ts +648 -0
  673. package/src/modes/components/extensions/types.ts +186 -0
  674. package/src/modes/components/footer.ts +276 -0
  675. package/src/modes/components/history-search.ts +268 -0
  676. package/src/modes/components/hook-editor.ts +213 -0
  677. package/src/modes/components/hook-input.ts +87 -0
  678. package/src/modes/components/hook-message.ts +67 -0
  679. package/src/modes/components/hook-selector.ts +691 -0
  680. package/src/modes/components/index.ts +42 -0
  681. package/src/modes/components/keybinding-hints.ts +65 -0
  682. package/src/modes/components/late-diagnostics-message.ts +60 -0
  683. package/src/modes/components/login-dialog.ts +197 -0
  684. package/src/modes/components/logout-account-selector.ts +130 -0
  685. package/src/modes/components/mcp-add-wizard.ts +1413 -0
  686. package/src/modes/components/message-frame.ts +98 -0
  687. package/src/modes/components/model-browser.ts +872 -0
  688. package/src/modes/components/model-hub.ts +2011 -0
  689. package/src/modes/components/model-picker.ts +233 -0
  690. package/src/modes/components/move-overlay.ts +293 -0
  691. package/src/modes/components/oauth-selector.ts +474 -0
  692. package/src/modes/components/omfg-panel.ts +141 -0
  693. package/src/modes/components/overlay-box.ts +109 -0
  694. package/src/modes/components/pause-screen.ts +208 -0
  695. package/src/modes/components/plan-review-overlay.ts +1226 -0
  696. package/src/modes/components/plan-toc.ts +138 -0
  697. package/src/modes/components/plugin-selector.ts +100 -0
  698. package/src/modes/components/plugin-settings.ts +745 -0
  699. package/src/modes/components/queue-mode-selector.ts +61 -0
  700. package/src/modes/components/read-tool-group.ts +678 -0
  701. package/src/modes/components/reset-usage-selector.ts +161 -0
  702. package/src/modes/components/segment-track.ts +89 -0
  703. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  704. package/src/modes/components/selector-helpers.ts +129 -0
  705. package/src/modes/components/session-account-selector.ts +62 -0
  706. package/src/modes/components/session-selector.ts +1019 -0
  707. package/src/modes/components/settings-defs.ts +267 -0
  708. package/src/modes/components/settings-selector.ts +1445 -0
  709. package/src/modes/components/show-images-selector.ts +50 -0
  710. package/src/modes/components/skill-message.ts +110 -0
  711. package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
  712. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  713. package/src/modes/components/status-line/component.ts +1516 -0
  714. package/src/modes/components/status-line/context-thresholds.ts +86 -0
  715. package/src/modes/components/status-line/git-utils.ts +42 -0
  716. package/src/modes/components/status-line/index.ts +5 -0
  717. package/src/modes/components/status-line/presets.ts +106 -0
  718. package/src/modes/components/status-line/segments.ts +710 -0
  719. package/src/modes/components/status-line/separators.ts +55 -0
  720. package/src/modes/components/status-line/types.ts +159 -0
  721. package/src/modes/components/theme-selector.ts +68 -0
  722. package/src/modes/components/thinking-selector.ts +57 -0
  723. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  724. package/src/modes/components/tips.txt +26 -0
  725. package/src/modes/components/todo-reminder.ts +43 -0
  726. package/src/modes/components/tool-execution.ts +1356 -0
  727. package/src/modes/components/transcript-container.ts +527 -0
  728. package/src/modes/components/tree-selector.ts +1008 -0
  729. package/src/modes/components/ttsr-notification.ts +123 -0
  730. package/src/modes/components/usage-row.ts +47 -0
  731. package/src/modes/components/user-message-selector.ts +227 -0
  732. package/src/modes/components/user-message.ts +167 -0
  733. package/src/modes/components/visual-truncate.ts +63 -0
  734. package/src/modes/components/welcome.ts +578 -0
  735. package/src/modes/controllers/btw-controller.ts +198 -0
  736. package/src/modes/controllers/command-controller-shared.ts +109 -0
  737. package/src/modes/controllers/command-controller.ts +1993 -0
  738. package/src/modes/controllers/event-controller.ts +1781 -0
  739. package/src/modes/controllers/extension-ui-controller.ts +1242 -0
  740. package/src/modes/controllers/input-controller.ts +2004 -0
  741. package/src/modes/controllers/live-command-controller.ts +260 -0
  742. package/src/modes/controllers/mcp-command-controller.ts +2403 -0
  743. package/src/modes/controllers/omfg-controller.ts +287 -0
  744. package/src/modes/controllers/omfg-rule.ts +647 -0
  745. package/src/modes/controllers/selector-controller.ts +1968 -0
  746. package/src/modes/controllers/session-focus-controller.ts +117 -0
  747. package/src/modes/controllers/ssh-command-controller.ts +385 -0
  748. package/src/modes/controllers/streaming-reveal.ts +399 -0
  749. package/src/modes/controllers/tan-command-controller.ts +229 -0
  750. package/src/modes/controllers/todo-command-controller.ts +487 -0
  751. package/src/modes/controllers/tool-args-reveal.ts +591 -0
  752. package/src/modes/data/emojis.json +1 -0
  753. package/src/modes/emoji-autocomplete.ts +285 -0
  754. package/src/modes/github-ref-autocomplete.ts +75 -0
  755. package/src/modes/gradient-highlight.ts +99 -0
  756. package/src/modes/image-references.ts +137 -0
  757. package/src/modes/index.ts +10 -0
  758. package/src/modes/interactive-mode.ts +4957 -0
  759. package/src/modes/internal-url-autocomplete.ts +158 -0
  760. package/src/modes/loop-limit.ts +192 -0
  761. package/src/modes/magic-keyword-boundary.ts +23 -0
  762. package/src/modes/magic-keywords.ts +42 -0
  763. package/src/modes/markdown-prose.ts +247 -0
  764. package/src/modes/oauth-manual-input.ts +69 -0
  765. package/src/modes/orchestrate.ts +43 -0
  766. package/src/modes/print-mode.ts +265 -0
  767. package/src/modes/prompt-action-autocomplete.ts +322 -0
  768. package/src/modes/queue-input.ts +132 -0
  769. package/src/modes/rpc/host-tools.ts +204 -0
  770. package/src/modes/rpc/host-uris.ts +235 -0
  771. package/src/modes/rpc/rpc-client.ts +1195 -0
  772. package/src/modes/rpc/rpc-frame.ts +316 -0
  773. package/src/modes/rpc/rpc-input.ts +38 -0
  774. package/src/modes/rpc/rpc-messages.ts +127 -0
  775. package/src/modes/rpc/rpc-mode.ts +1504 -0
  776. package/src/modes/rpc/rpc-subagents.ts +265 -0
  777. package/src/modes/rpc/rpc-types.ts +533 -0
  778. package/src/modes/running-subagent-badge.ts +13 -0
  779. package/src/modes/runtime-init.ts +144 -0
  780. package/src/modes/session-observer-registry.ts +218 -0
  781. package/src/modes/session-teardown.ts +82 -0
  782. package/src/modes/setup-version.ts +11 -0
  783. package/src/modes/setup-wizard/index.ts +103 -0
  784. package/src/modes/setup-wizard/lazy.ts +16 -0
  785. package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
  786. package/src/modes/setup-wizard/scenes/model.ts +132 -0
  787. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  788. package/src/modes/setup-wizard/scenes/providers.ts +105 -0
  789. package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
  790. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  791. package/src/modes/setup-wizard/scenes/theme.ts +330 -0
  792. package/src/modes/setup-wizard/scenes/types.ts +65 -0
  793. package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
  794. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  795. package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
  796. package/src/modes/shared.ts +49 -0
  797. package/src/modes/skill-command.ts +91 -0
  798. package/src/modes/theme/dark.json +95 -0
  799. package/src/modes/theme/defaults/alabaster.json +93 -0
  800. package/src/modes/theme/defaults/amethyst.json +96 -0
  801. package/src/modes/theme/defaults/anthracite.json +93 -0
  802. package/src/modes/theme/defaults/basalt.json +91 -0
  803. package/src/modes/theme/defaults/birch.json +95 -0
  804. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  805. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  806. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  807. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  808. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  809. package/src/modes/theme/defaults/dark-copper.json +95 -0
  810. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  811. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  812. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  813. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  814. package/src/modes/theme/defaults/dark-ember.json +95 -0
  815. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  816. package/src/modes/theme/defaults/dark-forest.json +96 -0
  817. package/src/modes/theme/defaults/dark-github.json +105 -0
  818. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  819. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  820. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  821. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  822. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  823. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  824. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  825. package/src/modes/theme/defaults/dark-nord.json +97 -0
  826. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  827. package/src/modes/theme/defaults/dark-one.json +100 -0
  828. package/src/modes/theme/defaults/dark-poimandres.json +143 -0
  829. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  830. package/src/modes/theme/defaults/dark-reef.json +91 -0
  831. package/src/modes/theme/defaults/dark-retro.json +92 -0
  832. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  833. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  834. package/src/modes/theme/defaults/dark-slate.json +95 -0
  835. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  836. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  837. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  838. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  839. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  840. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  841. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  842. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  843. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  844. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  845. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  846. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  847. package/src/modes/theme/defaults/graphite.json +92 -0
  848. package/src/modes/theme/defaults/index.ts +199 -0
  849. package/src/modes/theme/defaults/light-arctic.json +107 -0
  850. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  851. package/src/modes/theme/defaults/light-canyon.json +91 -0
  852. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  853. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  854. package/src/modes/theme/defaults/light-coral.json +95 -0
  855. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  856. package/src/modes/theme/defaults/light-dawn.json +90 -0
  857. package/src/modes/theme/defaults/light-dunes.json +91 -0
  858. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  859. package/src/modes/theme/defaults/light-forest.json +100 -0
  860. package/src/modes/theme/defaults/light-frost.json +95 -0
  861. package/src/modes/theme/defaults/light-github.json +115 -0
  862. package/src/modes/theme/defaults/light-glacier.json +91 -0
  863. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  864. package/src/modes/theme/defaults/light-haze.json +90 -0
  865. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  866. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  867. package/src/modes/theme/defaults/light-lavender.json +95 -0
  868. package/src/modes/theme/defaults/light-meadow.json +91 -0
  869. package/src/modes/theme/defaults/light-mint.json +95 -0
  870. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  871. package/src/modes/theme/defaults/light-ocean.json +99 -0
  872. package/src/modes/theme/defaults/light-one.json +99 -0
  873. package/src/modes/theme/defaults/light-opal.json +91 -0
  874. package/src/modes/theme/defaults/light-orchard.json +91 -0
  875. package/src/modes/theme/defaults/light-paper.json +95 -0
  876. package/src/modes/theme/defaults/light-poimandres.json +143 -0
  877. package/src/modes/theme/defaults/light-prism.json +90 -0
  878. package/src/modes/theme/defaults/light-retro.json +98 -0
  879. package/src/modes/theme/defaults/light-sand.json +95 -0
  880. package/src/modes/theme/defaults/light-savanna.json +91 -0
  881. package/src/modes/theme/defaults/light-solarized.json +102 -0
  882. package/src/modes/theme/defaults/light-soleil.json +90 -0
  883. package/src/modes/theme/defaults/light-sunset.json +99 -0
  884. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  885. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  886. package/src/modes/theme/defaults/light-wetland.json +91 -0
  887. package/src/modes/theme/defaults/light-zenith.json +89 -0
  888. package/src/modes/theme/defaults/limestone.json +94 -0
  889. package/src/modes/theme/defaults/mahogany.json +97 -0
  890. package/src/modes/theme/defaults/marble.json +93 -0
  891. package/src/modes/theme/defaults/obsidian.json +91 -0
  892. package/src/modes/theme/defaults/onyx.json +91 -0
  893. package/src/modes/theme/defaults/pearl.json +93 -0
  894. package/src/modes/theme/defaults/porcelain.json +91 -0
  895. package/src/modes/theme/defaults/quartz.json +96 -0
  896. package/src/modes/theme/defaults/sandstone.json +95 -0
  897. package/src/modes/theme/defaults/titanium.json +90 -0
  898. package/src/modes/theme/light.json +93 -0
  899. package/src/modes/theme/mermaid-cache.ts +92 -0
  900. package/src/modes/theme/shimmer.ts +305 -0
  901. package/src/modes/theme/theme-schema.json +463 -0
  902. package/src/modes/theme/theme.ts +3019 -0
  903. package/src/modes/turn-budget.ts +31 -0
  904. package/src/modes/types.ts +479 -0
  905. package/src/modes/ultrathink.ts +42 -0
  906. package/src/modes/utils/context-usage.ts +518 -0
  907. package/src/modes/utils/copy-targets.ts +378 -0
  908. package/src/modes/utils/hotkeys-markdown.ts +63 -0
  909. package/src/modes/utils/interactive-context-helpers.ts +30 -0
  910. package/src/modes/utils/keybinding-matchers.ts +86 -0
  911. package/src/modes/utils/tools-markdown.ts +31 -0
  912. package/src/modes/utils/transcript-render-helpers.ts +253 -0
  913. package/src/modes/utils/ui-helpers.ts +963 -0
  914. package/src/modes/warp-events.ts +232 -0
  915. package/src/modes/workflow.ts +49 -0
  916. package/src/plan-mode/approved-plan.ts +194 -0
  917. package/src/plan-mode/model-transition.ts +51 -0
  918. package/src/plan-mode/plan-files.ts +40 -0
  919. package/src/plan-mode/plan-handoff.ts +37 -0
  920. package/src/plan-mode/plan-protection.ts +31 -0
  921. package/src/plan-mode/state.ts +6 -0
  922. package/src/priority.json +60 -0
  923. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  924. package/src/prompts/advisor/advise-tool.md +3 -0
  925. package/src/prompts/advisor/context-files.md +8 -0
  926. package/src/prompts/advisor/system.md +98 -0
  927. package/src/prompts/agents/designer.md +74 -0
  928. package/src/prompts/agents/frontmatter.md +12 -0
  929. package/src/prompts/agents/init.md +33 -0
  930. package/src/prompts/agents/librarian.md +119 -0
  931. package/src/prompts/agents/reviewer.md +139 -0
  932. package/src/prompts/agents/scout.md +58 -0
  933. package/src/prompts/agents/task.md +17 -0
  934. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  935. package/src/prompts/bench/cache-prefix.md +3 -0
  936. package/src/prompts/bench/cache-suffix.md +1 -0
  937. package/src/prompts/bench.md +6 -0
  938. package/src/prompts/ci-green-request.md +36 -0
  939. package/src/prompts/dry-balance-bench.md +8 -0
  940. package/src/prompts/goals/goal-budget-limit.md +16 -0
  941. package/src/prompts/goals/goal-continuation.md +28 -0
  942. package/src/prompts/goals/goal-mode-active.md +23 -0
  943. package/src/prompts/goals/goal-mode-context.md +4 -0
  944. package/src/prompts/goals/goal-todo-context.md +12 -0
  945. package/src/prompts/goals/guided-goal-interview.md +43 -0
  946. package/src/prompts/memories/consolidation.md +30 -0
  947. package/src/prompts/memories/consolidation_system.md +4 -0
  948. package/src/prompts/memories/read-path.md +17 -0
  949. package/src/prompts/memories/stage_one_input.md +6 -0
  950. package/src/prompts/memories/stage_one_system.md +21 -0
  951. package/src/prompts/review-custom-request.md +21 -0
  952. package/src/prompts/review-headless-request.md +16 -0
  953. package/src/prompts/review-request.md +68 -0
  954. package/src/prompts/skills/autoload.md +8 -0
  955. package/src/prompts/skills/user-invocation.md +11 -0
  956. package/src/prompts/steering/parent-irc.md +5 -0
  957. package/src/prompts/steering/user-interjection.md +6 -0
  958. package/src/prompts/system/active-repo-context.md +4 -0
  959. package/src/prompts/system/agent-creation-architect.md +50 -0
  960. package/src/prompts/system/agent-creation-user.md +6 -0
  961. package/src/prompts/system/auto-continue.md +1 -0
  962. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  963. package/src/prompts/system/auto-thinking-difficulty.md +12 -0
  964. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  965. package/src/prompts/system/autolearn-guidance.md +7 -0
  966. package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
  967. package/src/prompts/system/background-tan-dispatch.md +8 -0
  968. package/src/prompts/system/btw-user.md +8 -0
  969. package/src/prompts/system/commit-message-system.md +14 -0
  970. package/src/prompts/system/computer-safety.md +14 -0
  971. package/src/prompts/system/custom-system-prompt.md +64 -0
  972. package/src/prompts/system/eager-task.md +7 -0
  973. package/src/prompts/system/eager-todo.md +18 -0
  974. package/src/prompts/system/empty-stop-retry.md +4 -0
  975. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  976. package/src/prompts/system/interrupted-thinking.md +7 -0
  977. package/src/prompts/system/irc-autoreply.md +6 -0
  978. package/src/prompts/system/irc-incoming.md +9 -0
  979. package/src/prompts/system/manual-continue.md +7 -0
  980. package/src/prompts/system/mcp-xdev-guidance.md +11 -0
  981. package/src/prompts/system/memory-consolidation-system.md +8 -0
  982. package/src/prompts/system/memory-extraction-system.md +26 -0
  983. package/src/prompts/system/mid-run-todo-nudge.md +3 -0
  984. package/src/prompts/system/omfg-user.md +50 -0
  985. package/src/prompts/system/orchestrate-notice.md +40 -0
  986. package/src/prompts/system/personalities/default.md +18 -0
  987. package/src/prompts/system/personalities/friendly.md +17 -0
  988. package/src/prompts/system/personalities/pragmatic.md +15 -0
  989. package/src/prompts/system/plan-mode-active.md +125 -0
  990. package/src/prompts/system/plan-mode-approved.md +22 -0
  991. package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
  992. package/src/prompts/system/plan-mode-reference.md +10 -0
  993. package/src/prompts/system/plan-mode-subagent.md +33 -0
  994. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  995. package/src/prompts/system/plan-yolo-handoff.md +5 -0
  996. package/src/prompts/system/prewalk-checklist.md +7 -0
  997. package/src/prompts/system/prewalk-continue.md +1 -0
  998. package/src/prompts/system/prewalk-plan.md +13 -0
  999. package/src/prompts/system/project-prompt.md +61 -0
  1000. package/src/prompts/system/recap-user.md +9 -0
  1001. package/src/prompts/system/resolve-device-reminder.md +3 -0
  1002. package/src/prompts/system/rewind-report.md +6 -0
  1003. package/src/prompts/system/side-channel-no-tools.md +3 -0
  1004. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  1005. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  1006. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  1007. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  1008. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  1009. package/src/prompts/system/speech-rewrite.md +15 -0
  1010. package/src/prompts/system/subagent-async-pending.md +6 -0
  1011. package/src/prompts/system/subagent-system-prompt.md +73 -0
  1012. package/src/prompts/system/subagent-user-prompt.md +3 -0
  1013. package/src/prompts/system/subagent-yield-reminder.md +23 -0
  1014. package/src/prompts/system/system-prompt.md +283 -0
  1015. package/src/prompts/system/tan-context-switch.md +17 -0
  1016. package/src/prompts/system/task-label.md +23 -0
  1017. package/src/prompts/system/thinking-loop-redirect.md +10 -0
  1018. package/src/prompts/system/title-marker-instruction.md +1 -0
  1019. package/src/prompts/system/title-system.md +16 -0
  1020. package/src/prompts/system/tool-call-loop-redirect.md +8 -0
  1021. package/src/prompts/system/ttsr-interrupt.md +7 -0
  1022. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  1023. package/src/prompts/system/ultrathink-notice.md +3 -0
  1024. package/src/prompts/system/unexpected-stop-classifier.md +17 -0
  1025. package/src/prompts/system/unexpected-stop-retry.md +4 -0
  1026. package/src/prompts/system/vibe-mode-active.md +26 -0
  1027. package/src/prompts/system/web-search.md +25 -0
  1028. package/src/prompts/system/workflow-notice.md +112 -0
  1029. package/src/prompts/system/xdev-mount-notice.md +20 -0
  1030. package/src/prompts/tools/apply-patch.md +65 -0
  1031. package/src/prompts/tools/ask.md +22 -0
  1032. package/src/prompts/tools/ast-edit.md +11 -0
  1033. package/src/prompts/tools/ast-grep.md +19 -0
  1034. package/src/prompts/tools/async-result.md +8 -0
  1035. package/src/prompts/tools/bash.md +25 -0
  1036. package/src/prompts/tools/browser.md +28 -0
  1037. package/src/prompts/tools/checkpoint.md +15 -0
  1038. package/src/prompts/tools/computer.md +26 -0
  1039. package/src/prompts/tools/debug.md +3 -0
  1040. package/src/prompts/tools/eval.md +45 -0
  1041. package/src/prompts/tools/github.md +22 -0
  1042. package/src/prompts/tools/glob.md +15 -0
  1043. package/src/prompts/tools/goal.md +11 -0
  1044. package/src/prompts/tools/grep.md +12 -0
  1045. package/src/prompts/tools/hub.md +34 -0
  1046. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  1047. package/src/prompts/tools/image-attachment-describe.md +10 -0
  1048. package/src/prompts/tools/image-gen.md +7 -0
  1049. package/src/prompts/tools/inspect-image-system.md +20 -0
  1050. package/src/prompts/tools/inspect-image.md +22 -0
  1051. package/src/prompts/tools/learn.md +7 -0
  1052. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  1053. package/src/prompts/tools/lsp.md +19 -0
  1054. package/src/prompts/tools/manage-skill.md +9 -0
  1055. package/src/prompts/tools/memory-edit.md +12 -0
  1056. package/src/prompts/tools/patch.md +57 -0
  1057. package/src/prompts/tools/read.md +27 -0
  1058. package/src/prompts/tools/recall.md +7 -0
  1059. package/src/prompts/tools/reflect.md +5 -0
  1060. package/src/prompts/tools/replace.md +29 -0
  1061. package/src/prompts/tools/retain.md +6 -0
  1062. package/src/prompts/tools/rewind.md +14 -0
  1063. package/src/prompts/tools/task-async-contract.md +1 -0
  1064. package/src/prompts/tools/task-summary.md +20 -0
  1065. package/src/prompts/tools/task.md +83 -0
  1066. package/src/prompts/tools/todo.md +42 -0
  1067. package/src/prompts/tools/vibe-kill.md +3 -0
  1068. package/src/prompts/tools/vibe-list.md +3 -0
  1069. package/src/prompts/tools/vibe-send.md +9 -0
  1070. package/src/prompts/tools/vibe-spawn.md +10 -0
  1071. package/src/prompts/tools/vibe-turn-result.md +19 -0
  1072. package/src/prompts/tools/vibe-wait.md +8 -0
  1073. package/src/prompts/tools/web-search.md +8 -0
  1074. package/src/prompts/tools/write.md +14 -0
  1075. package/src/registry/agent-lifecycle.ts +455 -0
  1076. package/src/registry/agent-registry.ts +217 -0
  1077. package/src/registry/persisted-agents.ts +98 -0
  1078. package/src/sdk.ts +3553 -0
  1079. package/src/secrets/index.ts +246 -0
  1080. package/src/secrets/obfuscator.ts +2574 -0
  1081. package/src/secrets/regex.ts +21 -0
  1082. package/src/session/acp-permission-gate.ts +165 -0
  1083. package/src/session/agent-session-events.ts +66 -0
  1084. package/src/session/agent-session-types.ts +346 -0
  1085. package/src/session/agent-session.ts +8406 -0
  1086. package/src/session/agent-storage.ts +805 -0
  1087. package/src/session/artifacts.ts +154 -0
  1088. package/src/session/async-job-delivery.ts +82 -0
  1089. package/src/session/auth-broker-config.ts +92 -0
  1090. package/src/session/auth-storage.ts +25 -0
  1091. package/src/session/bash-runner.ts +326 -0
  1092. package/src/session/blob-store.ts +295 -0
  1093. package/src/session/checkpoint-entries.ts +81 -0
  1094. package/src/session/client-bridge.ts +85 -0
  1095. package/src/session/codex-auto-reset.ts +202 -0
  1096. package/src/session/compact-modes.ts +105 -0
  1097. package/src/session/eval-runner.ts +217 -0
  1098. package/src/session/exit-diagnostics.ts +310 -0
  1099. package/src/session/history-storage.ts +327 -0
  1100. package/src/session/indexed-session-storage.ts +544 -0
  1101. package/src/session/irc-bridge.ts +203 -0
  1102. package/src/session/messages.ts +1280 -0
  1103. package/src/session/model-controls.ts +750 -0
  1104. package/src/session/prewalk.ts +257 -0
  1105. package/src/session/provider-image-budget.ts +86 -0
  1106. package/src/session/queued-messages.ts +99 -0
  1107. package/src/session/redis-session-storage.ts +257 -0
  1108. package/src/session/retry-fallback-chains.ts +455 -0
  1109. package/src/session/role-models.ts +85 -0
  1110. package/src/session/session-advisors.ts +1754 -0
  1111. package/src/session/session-context.ts +554 -0
  1112. package/src/session/session-dump-format.ts +216 -0
  1113. package/src/session/session-entries.ts +260 -0
  1114. package/src/session/session-handoff.ts +308 -0
  1115. package/src/session/session-history-format.ts +459 -0
  1116. package/src/session/session-listing.ts +715 -0
  1117. package/src/session/session-loader.ts +280 -0
  1118. package/src/session/session-maintenance.ts +2983 -0
  1119. package/src/session/session-manager.ts +2557 -0
  1120. package/src/session/session-memory.ts +222 -0
  1121. package/src/session/session-metadata.ts +53 -0
  1122. package/src/session/session-migrations.ts +78 -0
  1123. package/src/session/session-paths.ts +222 -0
  1124. package/src/session/session-persistence.ts +293 -0
  1125. package/src/session/session-provider-boundary.ts +306 -0
  1126. package/src/session/session-stats.ts +293 -0
  1127. package/src/session/session-storage.ts +734 -0
  1128. package/src/session/session-title-slot.ts +141 -0
  1129. package/src/session/session-tools.ts +1226 -0
  1130. package/src/session/session-workspace.ts +53 -0
  1131. package/src/session/settings-stream-fn.ts +74 -0
  1132. package/src/session/shake-types.ts +43 -0
  1133. package/src/session/snapcompact-inline.ts +545 -0
  1134. package/src/session/snapcompact-savings-journal.ts +113 -0
  1135. package/src/session/sql-session-storage.ts +374 -0
  1136. package/src/session/stream-guards.ts +417 -0
  1137. package/src/session/streaming-output.ts +1459 -0
  1138. package/src/session/todo-tracker.ts +380 -0
  1139. package/src/session/tool-choice-queue.ts +305 -0
  1140. package/src/session/ttsr-coordinator.ts +496 -0
  1141. package/src/session/turn-persistence.ts +142 -0
  1142. package/src/session/turn-recovery.ts +1728 -0
  1143. package/src/session/unexpected-stop-classifier.ts +132 -0
  1144. package/src/session/yield-queue.ts +183 -0
  1145. package/src/slash-commands/acp-builtins.ts +70 -0
  1146. package/src/slash-commands/available-commands.ts +105 -0
  1147. package/src/slash-commands/builtin-registry.ts +2982 -0
  1148. package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
  1149. package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
  1150. package/src/slash-commands/helpers/context-report.ts +66 -0
  1151. package/src/slash-commands/helpers/format.ts +46 -0
  1152. package/src/slash-commands/helpers/logout.ts +108 -0
  1153. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  1154. package/src/slash-commands/helpers/mcp.ts +533 -0
  1155. package/src/slash-commands/helpers/parse.ts +85 -0
  1156. package/src/slash-commands/helpers/reset-usage.ts +66 -0
  1157. package/src/slash-commands/helpers/session-pin.ts +44 -0
  1158. package/src/slash-commands/helpers/ssh.ts +196 -0
  1159. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  1160. package/src/slash-commands/helpers/todo.ts +285 -0
  1161. package/src/slash-commands/helpers/usage-report.ts +198 -0
  1162. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  1163. package/src/slash-commands/types.ts +139 -0
  1164. package/src/ssh/config-writer.ts +183 -0
  1165. package/src/ssh/connection-manager.ts +667 -0
  1166. package/src/ssh/file-transfer.ts +214 -0
  1167. package/src/ssh/sshfs-mount.ts +163 -0
  1168. package/src/ssh/utils.ts +51 -0
  1169. package/src/startup-splash.ts +19 -0
  1170. package/src/stt/asr-client.ts +401 -0
  1171. package/src/stt/asr-protocol.ts +65 -0
  1172. package/src/stt/asr-worker.ts +603 -0
  1173. package/src/stt/downloader.ts +142 -0
  1174. package/src/stt/endpointer.ts +259 -0
  1175. package/src/stt/index.ts +6 -0
  1176. package/src/stt/models.ts +150 -0
  1177. package/src/stt/sherpa-runtime.ts +71 -0
  1178. package/src/stt/stt-controller.ts +320 -0
  1179. package/src/stt/submit-trigger.ts +74 -0
  1180. package/src/subprocess/worker-client.ts +463 -0
  1181. package/src/subprocess/worker-runtime.ts +494 -0
  1182. package/src/system-prompt.ts +894 -0
  1183. package/src/task/agents.ts +168 -0
  1184. package/src/task/commands.ts +132 -0
  1185. package/src/task/discovery.ts +145 -0
  1186. package/src/task/executor.ts +3129 -0
  1187. package/src/task/index.ts +1519 -0
  1188. package/src/task/isolation-ownership.ts +106 -0
  1189. package/src/task/isolation-runner.ts +413 -0
  1190. package/src/task/label.ts +40 -0
  1191. package/src/task/name-generator.ts +1577 -0
  1192. package/src/task/omp-command.ts +26 -0
  1193. package/src/task/output-manager.ts +115 -0
  1194. package/src/task/parallel.ts +221 -0
  1195. package/src/task/persisted-revive.ts +141 -0
  1196. package/src/task/prewalk.ts +6 -0
  1197. package/src/task/prompt-policy.ts +8 -0
  1198. package/src/task/provider-concurrency.ts +100 -0
  1199. package/src/task/render.ts +1814 -0
  1200. package/src/task/renderer.ts +14 -0
  1201. package/src/task/repair-args.ts +118 -0
  1202. package/src/task/spawn-policy.ts +58 -0
  1203. package/src/task/structured-subagent.ts +649 -0
  1204. package/src/task/subprocess-tool-registry.ts +88 -0
  1205. package/src/task/types.ts +551 -0
  1206. package/src/task/worktree.ts +967 -0
  1207. package/src/task/yield-assembly.ts +198 -0
  1208. package/src/telemetry-export.ts +500 -0
  1209. package/src/thinking.ts +364 -0
  1210. package/src/tiny/device.ts +111 -0
  1211. package/src/tiny/dtype.ts +101 -0
  1212. package/src/tiny/message-preproc.ts +155 -0
  1213. package/src/tiny/models.ts +268 -0
  1214. package/src/tiny/text.ts +278 -0
  1215. package/src/tiny/title-client.ts +460 -0
  1216. package/src/tiny/title-protocol.ts +56 -0
  1217. package/src/tiny/worker.ts +354 -0
  1218. package/src/tools/acp-bridge.ts +81 -0
  1219. package/src/tools/approval.ts +245 -0
  1220. package/src/tools/ask.ts +1459 -0
  1221. package/src/tools/ast-edit.ts +718 -0
  1222. package/src/tools/ast-grep.ts +520 -0
  1223. package/src/tools/auto-generated-guard.ts +335 -0
  1224. package/src/tools/bash-interactive.ts +435 -0
  1225. package/src/tools/bash-interceptor.ts +67 -0
  1226. package/src/tools/bash-pty-selection.ts +14 -0
  1227. package/src/tools/bash-skill-urls.ts +335 -0
  1228. package/src/tools/bash.ts +1762 -0
  1229. package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
  1230. package/src/tools/browser/aria/aria-snapshot.ts +131 -0
  1231. package/src/tools/browser/attach.ts +194 -0
  1232. package/src/tools/browser/cmux/cmux-tab.ts +1400 -0
  1233. package/src/tools/browser/cmux/rpc.ts +206 -0
  1234. package/src/tools/browser/cmux/socket-client.ts +445 -0
  1235. package/src/tools/browser/launch.ts +788 -0
  1236. package/src/tools/browser/readable.ts +112 -0
  1237. package/src/tools/browser/registry.ts +287 -0
  1238. package/src/tools/browser/render.ts +226 -0
  1239. package/src/tools/browser/run-cancellation.ts +131 -0
  1240. package/src/tools/browser/run-output.ts +76 -0
  1241. package/src/tools/browser/tab-protocol.ts +115 -0
  1242. package/src/tools/browser/tab-supervisor.ts +1040 -0
  1243. package/src/tools/browser/tab-worker-entry.ts +29 -0
  1244. package/src/tools/browser/tab-worker.ts +1842 -0
  1245. package/src/tools/browser.ts +457 -0
  1246. package/src/tools/builtin-names.ts +66 -0
  1247. package/src/tools/checkpoint.ts +150 -0
  1248. package/src/tools/computer/exposure.ts +38 -0
  1249. package/src/tools/computer/protocol.ts +28 -0
  1250. package/src/tools/computer/supervisor.ts +306 -0
  1251. package/src/tools/computer/worker-entry.ts +34 -0
  1252. package/src/tools/computer/worker.ts +131 -0
  1253. package/src/tools/computer-renderer.ts +108 -0
  1254. package/src/tools/computer.ts +516 -0
  1255. package/src/tools/conflict-detect.ts +815 -0
  1256. package/src/tools/context.ts +46 -0
  1257. package/src/tools/debug.ts +1121 -0
  1258. package/src/tools/default-renderer.ts +139 -0
  1259. package/src/tools/essential-tools.ts +46 -0
  1260. package/src/tools/eval-backends.ts +34 -0
  1261. package/src/tools/eval-format/index.ts +24 -0
  1262. package/src/tools/eval-format/javascript.ts +952 -0
  1263. package/src/tools/eval-format/julia.ts +446 -0
  1264. package/src/tools/eval-format/python.ts +544 -0
  1265. package/src/tools/eval-format/ruby.ts +380 -0
  1266. package/src/tools/eval-render.ts +784 -0
  1267. package/src/tools/eval.ts +774 -0
  1268. package/src/tools/fetch.ts +1889 -0
  1269. package/src/tools/file-recorder.ts +35 -0
  1270. package/src/tools/fs-cache-invalidation.ts +28 -0
  1271. package/src/tools/gh-cache-invalidation.ts +175 -0
  1272. package/src/tools/gh-format.ts +12 -0
  1273. package/src/tools/gh-renderer.ts +484 -0
  1274. package/src/tools/gh.ts +3959 -0
  1275. package/src/tools/github-cache.ts +663 -0
  1276. package/src/tools/glob.ts +682 -0
  1277. package/src/tools/grep.ts +1858 -0
  1278. package/src/tools/grouped-file-output.ts +210 -0
  1279. package/src/tools/hub/index.ts +579 -0
  1280. package/src/tools/hub/jobs.ts +714 -0
  1281. package/src/tools/hub/launch.ts +579 -0
  1282. package/src/tools/hub/messaging.ts +735 -0
  1283. package/src/tools/hub/types.ts +117 -0
  1284. package/src/tools/image-gen.ts +1689 -0
  1285. package/src/tools/image-providers.ts +50 -0
  1286. package/src/tools/index.ts +691 -0
  1287. package/src/tools/inspect-image-renderer.ts +133 -0
  1288. package/src/tools/inspect-image.ts +290 -0
  1289. package/src/tools/json-tree.ts +260 -0
  1290. package/src/tools/jtd-to-json-schema.ts +219 -0
  1291. package/src/tools/jtd-to-typescript.ts +136 -0
  1292. package/src/tools/jtd-utils.ts +102 -0
  1293. package/src/tools/learn.ts +141 -0
  1294. package/src/tools/list-limit.ts +40 -0
  1295. package/src/tools/manage-skill.ts +102 -0
  1296. package/src/tools/match-line-format.ts +20 -0
  1297. package/src/tools/memory-edit.ts +61 -0
  1298. package/src/tools/memory-recall.ts +102 -0
  1299. package/src/tools/memory-reflect.ts +88 -0
  1300. package/src/tools/memory-render.ts +211 -0
  1301. package/src/tools/memory-retain.ts +89 -0
  1302. package/src/tools/output-meta.ts +845 -0
  1303. package/src/tools/output-schema-validator.ts +307 -0
  1304. package/src/tools/path-utils.ts +1381 -0
  1305. package/src/tools/plan-mode-guard.ts +155 -0
  1306. package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
  1307. package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
  1308. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
  1309. package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
  1310. package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
  1311. package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
  1312. package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
  1313. package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
  1314. package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
  1315. package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
  1316. package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
  1317. package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
  1318. package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
  1319. package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
  1320. package/src/tools/read.ts +3742 -0
  1321. package/src/tools/render-utils.ts +924 -0
  1322. package/src/tools/renderers.ts +133 -0
  1323. package/src/tools/report-tool-issue.ts +568 -0
  1324. package/src/tools/resolve.ts +423 -0
  1325. package/src/tools/review.ts +103 -0
  1326. package/src/tools/shell-tokenize.ts +83 -0
  1327. package/src/tools/sqlite-reader.ts +884 -0
  1328. package/src/tools/terminal-output.ts +141 -0
  1329. package/src/tools/todo.ts +1226 -0
  1330. package/src/tools/tool-errors.ts +62 -0
  1331. package/src/tools/tool-result.ts +102 -0
  1332. package/src/tools/tool-timeouts.ts +37 -0
  1333. package/src/tools/tts.ts +266 -0
  1334. package/src/tools/vibe.ts +607 -0
  1335. package/src/tools/write.ts +1640 -0
  1336. package/src/tools/xdev.ts +508 -0
  1337. package/src/tools/yield.ts +486 -0
  1338. package/src/tts/downloader.ts +64 -0
  1339. package/src/tts/index.ts +10 -0
  1340. package/src/tts/models.ts +137 -0
  1341. package/src/tts/runtime.ts +21 -0
  1342. package/src/tts/speakable.ts +392 -0
  1343. package/src/tts/speech-enhancer.ts +206 -0
  1344. package/src/tts/streaming-player.ts +120 -0
  1345. package/src/tts/tts-client.ts +475 -0
  1346. package/src/tts/tts-protocol.ts +69 -0
  1347. package/src/tts/tts-worker.ts +434 -0
  1348. package/src/tts/vocalizer.ts +419 -0
  1349. package/src/tts/wav.ts +58 -0
  1350. package/src/tui/code-cell.ts +268 -0
  1351. package/src/tui/file-list.ts +55 -0
  1352. package/src/tui/hyperlink.ts +178 -0
  1353. package/src/tui/index.ts +13 -0
  1354. package/src/tui/output-block.ts +268 -0
  1355. package/src/tui/status-line.ts +54 -0
  1356. package/src/tui/tree-list.ts +172 -0
  1357. package/src/tui/types.ts +15 -0
  1358. package/src/tui/utils.ts +103 -0
  1359. package/src/tui/width-aware-text.ts +58 -0
  1360. package/src/utils/active-repo-context.ts +143 -0
  1361. package/src/utils/block-context.ts +312 -0
  1362. package/src/utils/changelog.ts +232 -0
  1363. package/src/utils/clipboard.ts +327 -0
  1364. package/src/utils/command-args.ts +76 -0
  1365. package/src/utils/commit-message-generator.ts +148 -0
  1366. package/src/utils/cpuprofile.ts +235 -0
  1367. package/src/utils/edit-mode.ts +61 -0
  1368. package/src/utils/enhanced-paste.ts +230 -0
  1369. package/src/utils/event-bus.ts +33 -0
  1370. package/src/utils/external-editor.ts +78 -0
  1371. package/src/utils/fetch-timeout.ts +10 -0
  1372. package/src/utils/file-display-mode.ts +44 -0
  1373. package/src/utils/file-mentions.ts +293 -0
  1374. package/src/utils/git.ts +2425 -0
  1375. package/src/utils/image-loading.ts +231 -0
  1376. package/src/utils/image-resize.ts +420 -0
  1377. package/src/utils/image-vision-fallback.ts +196 -0
  1378. package/src/utils/inspect-image-mode.ts +39 -0
  1379. package/src/utils/ipc.ts +38 -0
  1380. package/src/utils/jj.ts +416 -0
  1381. package/src/utils/lang-from-path.ts +251 -0
  1382. package/src/utils/local-date.ts +7 -0
  1383. package/src/utils/mac-file-urls.applescript +37 -0
  1384. package/src/utils/markit-cache.ts +166 -0
  1385. package/src/utils/markit.ts +223 -0
  1386. package/src/utils/mupdf-wasm-embed.ts +12 -0
  1387. package/src/utils/open.ts +126 -0
  1388. package/src/utils/profile-tree.ts +111 -0
  1389. package/src/utils/prompt-path.ts +3 -0
  1390. package/src/utils/qrcode.ts +535 -0
  1391. package/src/utils/sample-profile.ts +437 -0
  1392. package/src/utils/session-color.ts +142 -0
  1393. package/src/utils/shell-snapshot-fn-env.sh +63 -0
  1394. package/src/utils/shell-snapshot.ts +316 -0
  1395. package/src/utils/sixel.ts +69 -0
  1396. package/src/utils/thinking-display.ts +163 -0
  1397. package/src/utils/title-generator.ts +597 -0
  1398. package/src/utils/token-rate.ts +72 -0
  1399. package/src/utils/tool-choice.ts +63 -0
  1400. package/src/utils/tools-manager.ts +411 -0
  1401. package/src/utils/turndown.ts +83 -0
  1402. package/src/utils/zip.ts +1106 -0
  1403. package/src/vibe/runtime.ts +1540 -0
  1404. package/src/vibe/state.ts +4 -0
  1405. package/src/web/kagi.ts +304 -0
  1406. package/src/web/parallel.ts +354 -0
  1407. package/src/web/scrapers/artifacthub.ts +207 -0
  1408. package/src/web/scrapers/arxiv.ts +83 -0
  1409. package/src/web/scrapers/aur.ts +162 -0
  1410. package/src/web/scrapers/biorxiv.ts +133 -0
  1411. package/src/web/scrapers/bluesky.ts +262 -0
  1412. package/src/web/scrapers/brew.ts +172 -0
  1413. package/src/web/scrapers/cheatsh.ts +68 -0
  1414. package/src/web/scrapers/chocolatey.ts +196 -0
  1415. package/src/web/scrapers/choosealicense.ts +95 -0
  1416. package/src/web/scrapers/cisa-kev.ts +87 -0
  1417. package/src/web/scrapers/clojars.ts +154 -0
  1418. package/src/web/scrapers/coingecko.ts +177 -0
  1419. package/src/web/scrapers/crates-io.ts +97 -0
  1420. package/src/web/scrapers/crossref.ts +136 -0
  1421. package/src/web/scrapers/devto.ts +147 -0
  1422. package/src/web/scrapers/discogs.ts +306 -0
  1423. package/src/web/scrapers/discourse.ts +197 -0
  1424. package/src/web/scrapers/dockerhub.ts +138 -0
  1425. package/src/web/scrapers/docs-rs.ts +663 -0
  1426. package/src/web/scrapers/fdroid.ts +134 -0
  1427. package/src/web/scrapers/firefox-addons.ts +191 -0
  1428. package/src/web/scrapers/flathub.ts +223 -0
  1429. package/src/web/scrapers/github-gist.ts +58 -0
  1430. package/src/web/scrapers/github.ts +800 -0
  1431. package/src/web/scrapers/gitlab.ts +401 -0
  1432. package/src/web/scrapers/go-pkg.ts +266 -0
  1433. package/src/web/scrapers/hackage.ts +140 -0
  1434. package/src/web/scrapers/hackernews.ts +189 -0
  1435. package/src/web/scrapers/hex.ts +105 -0
  1436. package/src/web/scrapers/huggingface.ts +321 -0
  1437. package/src/web/scrapers/iacr.ts +89 -0
  1438. package/src/web/scrapers/index.ts +252 -0
  1439. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1440. package/src/web/scrapers/lemmy.ts +203 -0
  1441. package/src/web/scrapers/lobsters.ts +175 -0
  1442. package/src/web/scrapers/mastodon.ts +292 -0
  1443. package/src/web/scrapers/maven.ts +138 -0
  1444. package/src/web/scrapers/mdn.ts +173 -0
  1445. package/src/web/scrapers/metacpan.ts +222 -0
  1446. package/src/web/scrapers/musicbrainz.ts +250 -0
  1447. package/src/web/scrapers/npm.ts +98 -0
  1448. package/src/web/scrapers/nuget.ts +183 -0
  1449. package/src/web/scrapers/nvd.ts +222 -0
  1450. package/src/web/scrapers/ollama.ts +239 -0
  1451. package/src/web/scrapers/open-vsx.ts +106 -0
  1452. package/src/web/scrapers/opencorporates.ts +292 -0
  1453. package/src/web/scrapers/openlibrary.ts +336 -0
  1454. package/src/web/scrapers/orcid.ts +286 -0
  1455. package/src/web/scrapers/osv.ts +176 -0
  1456. package/src/web/scrapers/packagist.ts +160 -0
  1457. package/src/web/scrapers/pub-dev.ts +143 -0
  1458. package/src/web/scrapers/pubmed.ts +211 -0
  1459. package/src/web/scrapers/pypi.ts +112 -0
  1460. package/src/web/scrapers/rawg.ts +110 -0
  1461. package/src/web/scrapers/readthedocs.ts +120 -0
  1462. package/src/web/scrapers/reddit.ts +95 -0
  1463. package/src/web/scrapers/repology.ts +251 -0
  1464. package/src/web/scrapers/rfc.ts +201 -0
  1465. package/src/web/scrapers/rubygems.ts +103 -0
  1466. package/src/web/scrapers/searchcode.ts +189 -0
  1467. package/src/web/scrapers/sec-edgar.ts +261 -0
  1468. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1469. package/src/web/scrapers/snapcraft.ts +187 -0
  1470. package/src/web/scrapers/sourcegraph.ts +336 -0
  1471. package/src/web/scrapers/spdx.ts +108 -0
  1472. package/src/web/scrapers/spotify.ts +198 -0
  1473. package/src/web/scrapers/stackoverflow.ts +120 -0
  1474. package/src/web/scrapers/terraform.ts +277 -0
  1475. package/src/web/scrapers/tldr.ts +47 -0
  1476. package/src/web/scrapers/twitter.ts +94 -0
  1477. package/src/web/scrapers/types.ts +354 -0
  1478. package/src/web/scrapers/utils.ts +109 -0
  1479. package/src/web/scrapers/vimeo.ts +133 -0
  1480. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1481. package/src/web/scrapers/w3c.ts +156 -0
  1482. package/src/web/scrapers/wikidata.ts +344 -0
  1483. package/src/web/scrapers/wikipedia.ts +84 -0
  1484. package/src/web/scrapers/youtube.ts +325 -0
  1485. package/src/web/search/index.ts +376 -0
  1486. package/src/web/search/provider.ts +272 -0
  1487. package/src/web/search/providers/anthropic.ts +401 -0
  1488. package/src/web/search/providers/base.ts +110 -0
  1489. package/src/web/search/providers/brave.ts +179 -0
  1490. package/src/web/search/providers/browser-headers.ts +109 -0
  1491. package/src/web/search/providers/browser-page.ts +123 -0
  1492. package/src/web/search/providers/codex.ts +749 -0
  1493. package/src/web/search/providers/duckduckgo.ts +213 -0
  1494. package/src/web/search/providers/ecosia.ts +182 -0
  1495. package/src/web/search/providers/exa.ts +511 -0
  1496. package/src/web/search/providers/firecrawl.ts +211 -0
  1497. package/src/web/search/providers/gemini.ts +628 -0
  1498. package/src/web/search/providers/google.ts +194 -0
  1499. package/src/web/search/providers/jina.ts +134 -0
  1500. package/src/web/search/providers/kagi.ts +95 -0
  1501. package/src/web/search/providers/kimi.ts +217 -0
  1502. package/src/web/search/providers/mojeek.ts +219 -0
  1503. package/src/web/search/providers/parallel.ts +182 -0
  1504. package/src/web/search/providers/perplexity-auth.ts +142 -0
  1505. package/src/web/search/providers/perplexity.ts +994 -0
  1506. package/src/web/search/providers/public.ts +199 -0
  1507. package/src/web/search/providers/searxng.ts +461 -0
  1508. package/src/web/search/providers/startpage.ts +219 -0
  1509. package/src/web/search/providers/synthetic.ts +120 -0
  1510. package/src/web/search/providers/tavily.ts +242 -0
  1511. package/src/web/search/providers/tinyfish.ts +164 -0
  1512. package/src/web/search/providers/utils.ts +128 -0
  1513. package/src/web/search/providers/xai.ts +372 -0
  1514. package/src/web/search/providers/zai.ts +437 -0
  1515. package/src/web/search/query.ts +850 -0
  1516. package/src/web/search/render.ts +262 -0
  1517. package/src/web/search/types.ts +507 -0
  1518. package/src/web/search/utils.ts +17 -0
  1519. package/src/workspace-tree.ts +326 -0
@@ -0,0 +1,1728 @@
1
+ import { scheduler } from "node:timers/promises";
2
+ import {
3
+ type Agent,
4
+ AgentBusyError,
5
+ type AgentMessage,
6
+ isSyntheticToolResultMessage,
7
+ type ThinkingLevel,
8
+ } from "@oh-my-pi/pi-agent-core";
9
+ import type {
10
+ AssistantMessage,
11
+ AssistantRetryRecovery,
12
+ AssistantRetryRecoveryKind,
13
+ CodexCompactionContext,
14
+ Effort,
15
+ Model,
16
+ TextContent,
17
+ ToolChoice,
18
+ } from "@oh-my-pi/pi-ai";
19
+ import { calculateRateLimitBackoffMs, parseRateLimitReason } from "@oh-my-pi/pi-ai";
20
+ import * as AIError from "@oh-my-pi/pi-ai/error";
21
+ import { kCursorExecResolved } from "@oh-my-pi/pi-ai/utils/block-symbols";
22
+ import { isFireworksFastModelId, toFireworksBaseModelId } from "@oh-my-pi/pi-catalog/fireworks-model-id";
23
+ import { extractRetryHint, logger, prompt } from "@oh-my-pi/pi-utils";
24
+ import type { ModelRegistry } from "../config/model-registry";
25
+ import { formatModelStringWithRouting, resolveModelOverride } from "../config/model-resolver";
26
+ import type { Settings } from "../config/settings";
27
+ import type { RecoveredRetryError } from "../extensibility/shared-events";
28
+ import emptyStopRetryTemplate from "../prompts/system/empty-stop-retry.md" with { type: "text" };
29
+ import thinkingLoopRedirectTemplate from "../prompts/system/thinking-loop-redirect.md" with { type: "text" };
30
+ import unexpectedStopRetryTemplate from "../prompts/system/unexpected-stop-retry.md" with { type: "text" };
31
+ import {
32
+ AUTO_THINKING,
33
+ type ConfiguredThinkingLevel,
34
+ clampThinkingLevelToCeiling,
35
+ modelSupportsEffortCeiling,
36
+ } from "../thinking";
37
+ import type { AgentSessionEvent } from "./agent-session-events";
38
+ import type { InitialRetryFallbackState } from "./agent-session-types";
39
+ import { isEmptyErrorTurn } from "./messages";
40
+ import {
41
+ type ActiveRetryFallbackState,
42
+ calculateRetryBackoffDelayMs,
43
+ findRetryFallbackCandidates,
44
+ formatRetryFallbackSelector,
45
+ getRetryFallbackChains,
46
+ getRetryFallbackRevertPolicy,
47
+ parseRetryFallbackSelector,
48
+ type RetryFallbackChains,
49
+ type RetryFallbackResolutionContext,
50
+ type RetryFallbackRevertPolicy,
51
+ type RetryFallbackSelector,
52
+ resolveRetryFallbackChainKey,
53
+ validateRetryFallbackChains,
54
+ } from "./retry-fallback-chains";
55
+ import { getLatestCompactionEntry } from "./session-context";
56
+ import { EPHEMERAL_MODEL_CHANGE_ROLE, type SessionEntry } from "./session-entries";
57
+ import type { SessionManager } from "./session-manager";
58
+ import { sameMessageContent, sessionMessagePersistenceKey } from "./turn-persistence";
59
+ import { classifyUnexpectedStop, isUnexpectedStopCandidate } from "./unexpected-stop-classifier";
60
+
61
+ const THINKING_LOOP_REDIRECT_TYPE = "thinking-loop-redirect";
62
+ const UNEXPECTED_STOP_MAX_RETRIES = 3;
63
+ const UNEXPECTED_STOP_TIMEOUT_MS = 4000;
64
+ const EMPTY_STOP_MAX_RETRIES = 3;
65
+ const SIBLING_UNBLOCK_BUFFER_MS = 1_000;
66
+ const NON_WHITESPACE_RE = /\S/;
67
+
68
+ function hasNonWhitespace(value: string): boolean {
69
+ return NON_WHITESPACE_RE.test(value);
70
+ }
71
+
72
+ function syntheticToolResultTailStart(messages: readonly AgentMessage[]): number {
73
+ let index = messages.length;
74
+ while (index > 0 && isSyntheticToolResultMessage(messages[index - 1])) {
75
+ index--;
76
+ }
77
+ return index;
78
+ }
79
+
80
+ function retryableAssistantTurnEnd(messages: readonly AgentMessage[]): number | undefined {
81
+ const turnEnd = syntheticToolResultTailStart(messages);
82
+ const message = messages[turnEnd - 1];
83
+ if (message?.role !== "assistant") return undefined;
84
+ if (message.stopReason !== "error" && message.stopReason !== "aborted") return undefined;
85
+ return turnEnd;
86
+ }
87
+
88
+ /** Result shape shared with automatic maintenance recovery. */
89
+ export interface RecoveryCompactionResult {
90
+ deferredHandoff: boolean;
91
+ continuationScheduled: boolean;
92
+ automaticContinuationBlocked?: boolean;
93
+ historyRewritten?: boolean;
94
+ }
95
+
96
+ /** Capabilities borrowed from the owning AgentSession. */
97
+ export interface TurnRecoveryHost {
98
+ agent: Agent;
99
+ sessionManager: SessionManager;
100
+ settings: Settings;
101
+ modelRegistry: ModelRegistry;
102
+ configWarnings: string[];
103
+ model(): Model | undefined;
104
+ thinkingLevel(): ThinkingLevel | undefined;
105
+ configuredThinkingLevel(): ConfiguredThinkingLevel | undefined;
106
+ setThinkingLevel(level: ConfiguredThinkingLevel | undefined): void;
107
+ /** Hard per-session effort ceiling; fallback recovery must never raise thinking above it. */
108
+ thinkingLevelCeiling(): Effort | undefined;
109
+ isDisposed(): boolean;
110
+ isStreaming(): boolean;
111
+ isCompacting(): boolean;
112
+ abortInProgress(): boolean;
113
+ streamingEditAbortTriggered(): boolean;
114
+ promptGeneration(): number;
115
+ sessionId(): string;
116
+ emitSessionEvent(event: AgentSessionEvent): Promise<void>;
117
+ scheduleAgentContinue(options: { delayMs?: number; generation?: number; onError?: (error: unknown) => void }): void;
118
+ waitForSessionMessagePersistence(message: AssistantMessage): Promise<void>;
119
+ appendSessionMessage(message: AssistantMessage): void;
120
+ sessionMessageAlreadyPersisted(message: AssistantMessage): boolean;
121
+ setModelWithProviderSessionReset(model: Model): Promise<void>;
122
+ resetCurrentResponsesProviderSession(reason: string): void;
123
+ maybeAutoRedeemCodexReset(): Promise<boolean>;
124
+ runAutoCompaction(
125
+ reason: "overflow" | "threshold" | "idle" | "incomplete",
126
+ willRetry: boolean,
127
+ deferred?: boolean,
128
+ allowDefer?: boolean,
129
+ options?: {
130
+ autoContinue?: boolean;
131
+ triggerContextTokens?: number;
132
+ suppressContinuation?: boolean;
133
+ suppressHandoff?: boolean;
134
+ phase?: CodexCompactionContext["phase"];
135
+ terminalTextAnswer?: boolean;
136
+ },
137
+ ): Promise<RecoveryCompactionResult>;
138
+ withBashBranchTransition<T>(operation: () => T): T;
139
+ }
140
+
141
+ /** Construction-time retry state restored from model selection. */
142
+ export interface TurnRecoveryOptions {
143
+ initialRetryFallback?: InitialRetryFallbackState;
144
+ }
145
+
146
+ type PendingRecoveredRetryError = {
147
+ entryId: string;
148
+ persistenceKey: string;
149
+ recovery: AssistantRetryRecoveryKind;
150
+ attempt: number;
151
+ note: string;
152
+ };
153
+
154
+ /** Owns terminal-stop recovery, automatic retries, and fallback routing. */
155
+ export class TurnRecovery {
156
+ readonly #host: TurnRecoveryHost;
157
+ #retryAbortController: AbortController | undefined;
158
+ #retryAttempt = 0;
159
+ #retryPromise: Promise<void> | undefined;
160
+ #retryResolve: (() => void) | undefined;
161
+ #activeRetryFallback: ActiveRetryFallbackState | undefined;
162
+ #pendingRecoveredRetryErrors: PendingRecoveredRetryError[] = [];
163
+ #emptyStopRetryCount = 0;
164
+ #unexpectedStopRetryCount = 0;
165
+ #acceptTerminalEmptyStopForPrompt = false;
166
+
167
+ constructor(host: TurnRecoveryHost, options: TurnRecoveryOptions = {}) {
168
+ this.#host = host;
169
+ if (options.initialRetryFallback) {
170
+ this.#activeRetryFallback = {
171
+ ...options.initialRetryFallback,
172
+ lastAppliedFallbackThinkingLevel: host.configuredThinkingLevel(),
173
+ pinned: options.initialRetryFallback.pinned ?? false,
174
+ };
175
+ }
176
+ this.#validateRetryFallbackChains();
177
+ }
178
+
179
+ /** Current automatic retry attempt. */
180
+ get attempt(): number {
181
+ return this.#retryAttempt;
182
+ }
183
+
184
+ /** Promise settled when the active retry saga finishes. */
185
+ get retryPromise(): Promise<void> | undefined {
186
+ return this.#retryPromise;
187
+ }
188
+
189
+ /** Resolved selector while fallback routing owns the current model. */
190
+ get retryFallbackModel(): string | undefined {
191
+ const model = this.#host.model();
192
+ return this.#activeRetryFallback && model
193
+ ? formatRetryFallbackSelector(model, this.#host.thinkingLevel())
194
+ : undefined;
195
+ }
196
+
197
+ /** Resets per-prompt recovery counters and terminal-stop acceptance. */
198
+ resetForNewPrompt(): void {
199
+ this.#emptyStopRetryCount = 0;
200
+ this.#unexpectedStopRetryCount = 0;
201
+ this.#acceptTerminalEmptyStopForPrompt = false;
202
+ }
203
+
204
+ /** Sets whether one terminal empty stop is accepted for the current prompt. */
205
+ setAcceptTerminalEmptyStop(accept: boolean): void {
206
+ this.#acceptTerminalEmptyStopForPrompt = accept;
207
+ }
208
+
209
+ /** Closes a successful retry saga and annotates recovered persisted errors. */
210
+ async onAssistantSettledSuccessfully(message: AssistantMessage): Promise<void> {
211
+ if (
212
+ message.stopReason === "error" ||
213
+ message.stopReason === "aborted" ||
214
+ this.#isEmptyAssistantStop(message) ||
215
+ this.#retryAttempt === 0
216
+ ) {
217
+ return;
218
+ }
219
+ const model = this.#host.model();
220
+ if (this.#activeRetryFallback && model) {
221
+ await this.#host.emitSessionEvent({
222
+ type: "retry_fallback_succeeded",
223
+ model: formatRetryFallbackSelector(model, this.#host.thinkingLevel()),
224
+ role: this.#activeRetryFallback.role,
225
+ });
226
+ }
227
+ const recoveredErrors = await this.#markPendingRecoveredRetryErrors(message);
228
+ await this.#host.emitSessionEvent({
229
+ type: "auto_retry_end",
230
+ success: true,
231
+ attempt: this.#retryAttempt,
232
+ recoveredErrors,
233
+ });
234
+ this.#clearPendingRecoveredRetryErrors();
235
+ this.#retryAttempt = 0;
236
+ }
237
+
238
+ /** Closes a failed retry saga when no compaction continuation took ownership. */
239
+ async onErrorSettledWithoutRetry(message: AssistantMessage, compaction: RecoveryCompactionResult): Promise<void> {
240
+ if (message.stopReason !== "error" || this.#retryAttempt === 0 || compaction.continuationScheduled) return;
241
+ const attempt = this.#retryAttempt;
242
+ this.#retryAttempt = 0;
243
+ await this.#host.emitSessionEvent({
244
+ type: "auto_retry_end",
245
+ success: false,
246
+ attempt,
247
+ finalError: message.errorMessage,
248
+ });
249
+ this.#clearPendingRecoveredRetryErrors();
250
+ }
251
+
252
+ /** Persists an otherwise skipped terminal empty error turn. */
253
+ persistTerminalEmptyErrorTurn(message: AssistantMessage): Promise<void> {
254
+ return this.#persistTerminalEmptyErrorTurn(message);
255
+ }
256
+
257
+ /** Handles empty terminal assistant turns and schedules bounded recovery. */
258
+ handleEmptyAssistantStop(message: AssistantMessage): Promise<boolean> {
259
+ return this.#handleEmptyAssistantStop(message);
260
+ }
261
+
262
+ /** Classifies suspicious terminal stops and schedules bounded recovery. */
263
+ handleUnexpectedAssistantStop(message: AssistantMessage): Promise<boolean> {
264
+ return this.#handleUnexpectedAssistantStop(message);
265
+ }
266
+
267
+ /** Removes a persisted failed assistant turn after its persistence slot settles. */
268
+ dropPersistedAssistantTurn(message: AssistantMessage): Promise<void> {
269
+ return this.#dropPersistedAssistantTurn(message);
270
+ }
271
+
272
+ /** Runs recovery compaction and restores the failed turn when no rewrite occurs. */
273
+ runRecoveryCompactionWithRollback(
274
+ reason: "overflow" | "incomplete",
275
+ message: AssistantMessage,
276
+ allowDefer: boolean,
277
+ options: { autoContinue: boolean; triggerContextTokens?: number },
278
+ ): Promise<RecoveryCompactionResult> {
279
+ return this.#runRecoveryCompactionWithRollback(reason, message, allowDefer, options);
280
+ }
281
+
282
+ /** Restores the configured primary after fallback cooldown expiry. */
283
+ maybeRestoreRetryFallbackPrimary(): Promise<void> {
284
+ return this.#maybeRestoreRetryFallbackPrimary();
285
+ }
286
+
287
+ /** Applies automatic retry, credential rotation, and model fallback policy. */
288
+ handleRetryableError(
289
+ message: AssistantMessage,
290
+ options?: {
291
+ allowModelFallback?: boolean;
292
+ fireworksFastFallback?: boolean;
293
+ hardErrorFallback?: boolean;
294
+ preserveFailedTurn?: boolean;
295
+ },
296
+ ): Promise<boolean> {
297
+ return this.#handleRetryableError(message, options);
298
+ }
299
+
300
+ /** Prompts after transient overlap with a prior agent run. */
301
+ promptAgentWithIdleRetry(messages: AgentMessage[], options?: { toolChoice?: ToolChoice }): Promise<void> {
302
+ return this.#promptAgentWithIdleRetry(messages, options);
303
+ }
304
+
305
+ /** Parses provider retry and rate-limit reset hints into a delay. */
306
+ parseRetryAfterMsFromError(errorMessage: string): number | undefined {
307
+ return this.#parseRetryAfterMsFromError(errorMessage);
308
+ }
309
+
310
+ /** Resolve the pending retry promise */
311
+ resolveRetry(): void {
312
+ if (this.#retryResolve) {
313
+ this.#retryResolve();
314
+ this.#retryResolve = undefined;
315
+ this.#retryPromise = undefined;
316
+ }
317
+ }
318
+
319
+ #clearPendingRecoveredRetryErrors(): void {
320
+ this.#pendingRecoveredRetryErrors = [];
321
+ }
322
+
323
+ /**
324
+ * Durably record a terminal empty error turn (`stopReason: "error"` with no
325
+ * substantive content) that `#persistSessionMessageIfMissing` skipped, so the
326
+ * session JSONL keeps a record of why the run stopped instead of ending at the
327
+ * last tool result. A no-op for non-empty/non-error turns and idempotent via
328
+ * the already-persisted guard; the turn is dropped from active context by the
329
+ * caller (or `isProviderRefusalMessage`/`isEmptyErrorTurn` filters) so it is
330
+ * never replayed on the wire. Used by the retry-lifecycle dead-ends and the
331
+ * non-retry terminal error tail.
332
+ */
333
+ async #persistTerminalEmptyErrorTurn(message: AssistantMessage): Promise<void> {
334
+ await this.#host.waitForSessionMessagePersistence(message);
335
+ if (!isEmptyErrorTurn(message)) return;
336
+ if (this.#host.sessionMessageAlreadyPersisted(message)) return;
337
+ this.#host.appendSessionMessage(message);
338
+ }
339
+
340
+ #retryRecoveryKind(
341
+ id: number,
342
+ switchedCredential: boolean,
343
+ switchedModel: boolean,
344
+ delayMs: number,
345
+ ): AssistantRetryRecoveryKind {
346
+ if (switchedCredential) return "credential";
347
+ if (switchedModel) return "model";
348
+ if (AIError.is(id, AIError.Flag.UsageLimit) && delayMs > 0) return "wait";
349
+ return "plain";
350
+ }
351
+
352
+ #retryRecoveryNote(recovery: AssistantRetryRecoveryKind, rateLimited: boolean): string {
353
+ const parts: string[] = [];
354
+ if (rateLimited) {
355
+ parts.push("rate-limited");
356
+ } else if (recovery === "plain") {
357
+ parts.push("error");
358
+ }
359
+ if (recovery === "credential") {
360
+ parts.push("switched account");
361
+ } else if (recovery === "model") {
362
+ parts.push("switched model");
363
+ } else if (recovery === "wait") {
364
+ parts.push("waited");
365
+ }
366
+ parts.push("retried");
367
+ return parts.join("; ");
368
+ }
369
+
370
+ async #recordPendingRecoveredRetryError(
371
+ message: AssistantMessage,
372
+ id: number,
373
+ options: { switchedCredential: boolean; switchedModel: boolean; delayMs: number },
374
+ ): Promise<void> {
375
+ await this.persistTerminalEmptyErrorTurn(message);
376
+ const persistenceKey = sessionMessagePersistenceKey(message);
377
+ if (!persistenceKey) return;
378
+ let branchEntry: SessionEntry | undefined;
379
+ for (const entry of this.#host.sessionManager.getBranch().slice().reverse()) {
380
+ if (entry.type !== "message" || entry.message.role !== "assistant") continue;
381
+ if (sessionMessagePersistenceKey(entry.message) !== persistenceKey) continue;
382
+ if (!sameMessageContent(entry.message, message) && !this.#isSameAssistantMessage(entry.message, message)) {
383
+ continue;
384
+ }
385
+ branchEntry = entry;
386
+ break;
387
+ }
388
+ if (!branchEntry) return;
389
+ if (this.#pendingRecoveredRetryErrors.some(error => error.entryId === branchEntry.id)) return;
390
+ const rateLimited = AIError.is(id, AIError.Flag.UsageLimit);
391
+ const recovery = this.#retryRecoveryKind(id, options.switchedCredential, options.switchedModel, options.delayMs);
392
+ const note = this.#retryRecoveryNote(recovery, rateLimited);
393
+ this.#pendingRecoveredRetryErrors.push({
394
+ entryId: branchEntry.id,
395
+ persistenceKey,
396
+ recovery,
397
+ attempt: this.#retryAttempt,
398
+ note,
399
+ });
400
+ }
401
+
402
+ async #markPendingRecoveredRetryErrors(supersedingMessage: AssistantMessage): Promise<RecoveredRetryError[]> {
403
+ if (this.#pendingRecoveredRetryErrors.length === 0) return [];
404
+ const branch = this.#host.sessionManager.getBranch();
405
+ const branchById = new Map<string, SessionEntry>();
406
+ for (const entry of branch) {
407
+ branchById.set(entry.id, entry);
408
+ }
409
+ const recoveredAt = new Date().toISOString();
410
+ const supersededBy: AssistantRetryRecovery["supersededBy"] = {
411
+ timestamp: supersedingMessage.timestamp,
412
+ provider: supersedingMessage.provider,
413
+ model: supersedingMessage.model,
414
+ };
415
+ if (supersedingMessage.responseId) {
416
+ supersededBy.responseId = supersedingMessage.responseId;
417
+ }
418
+ const recoveredErrors: RecoveredRetryError[] = [];
419
+ for (const pending of this.#pendingRecoveredRetryErrors) {
420
+ let entry = branchById.get(pending.entryId);
421
+ if (entry?.type !== "message" || entry.message.role !== "assistant") {
422
+ entry = branch
423
+ .slice()
424
+ .reverse()
425
+ .find(
426
+ candidate =>
427
+ candidate.type === "message" &&
428
+ candidate.message.role === "assistant" &&
429
+ sessionMessagePersistenceKey(candidate.message) === pending.persistenceKey,
430
+ );
431
+ }
432
+ if (entry?.type !== "message" || entry.message.role !== "assistant") continue;
433
+ const retryRecovery: AssistantRetryRecovery = {
434
+ kind: "auto-retry",
435
+ status: "recovered",
436
+ attempt: pending.attempt,
437
+ recoveredAt,
438
+ recovery: pending.recovery,
439
+ note: pending.note,
440
+ supersededBy,
441
+ };
442
+ entry.message.retryRecovery = retryRecovery;
443
+ recoveredErrors.push({
444
+ entryId: entry.id,
445
+ persistenceKey: pending.persistenceKey,
446
+ note: retryRecovery.note,
447
+ retryRecovery,
448
+ });
449
+ }
450
+ if (recoveredErrors.length > 0) {
451
+ await this.#host.sessionManager.rewriteEntries();
452
+ }
453
+ return recoveredErrors;
454
+ }
455
+
456
+ async #handleEmptyAssistantStop(assistantMessage: AssistantMessage): Promise<boolean> {
457
+ if (!this.#isEmptyAssistantStop(assistantMessage)) {
458
+ this.#emptyStopRetryCount = 0;
459
+ return false;
460
+ }
461
+
462
+ if (this.#acceptTerminalEmptyStopForPrompt && assistantMessage.stopReason === "stop") {
463
+ this.#acceptTerminalEmptyStopForPrompt = false;
464
+ this.#discardAcceptedTerminalEmptyStop(assistantMessage);
465
+ this.#emptyStopRetryCount = 0;
466
+ return false;
467
+ }
468
+
469
+ this.#emptyStopRetryCount++;
470
+ if (this.#emptyStopRetryCount > EMPTY_STOP_MAX_RETRIES) {
471
+ const attempts = this.#emptyStopRetryCount - 1;
472
+ const finalError =
473
+ "Assistant returned empty stop after retry cap; try switching models or `/shake images` to remove archived frames";
474
+ logger.warn(finalError, {
475
+ attempts,
476
+ model: assistantMessage.model,
477
+ provider: assistantMessage.provider,
478
+ });
479
+ await this.#host.emitSessionEvent({
480
+ type: "auto_retry_end",
481
+ success: false,
482
+ attempt: this.#retryAttempt > 0 ? this.#retryAttempt : attempts,
483
+ finalError,
484
+ });
485
+ this.#clearPendingRecoveredRetryErrors();
486
+ this.#retryAttempt = 0;
487
+ this.resolveRetry();
488
+ // A zero-content turn carries no transcript value, while its provider usage
489
+ // can anchor the next prompt at the full failed-request size and re-trigger
490
+ // compaction at the same boundary. Remove every capped empty stop; toolUse
491
+ // orphans still need this for Anthropic message-history validity.
492
+ await this.dropPersistedAssistantTurn(assistantMessage);
493
+ return false;
494
+ }
495
+ this.discardAssistantTurn(assistantMessage);
496
+ this.#host.agent.appendMessage({
497
+ role: "developer",
498
+ content: [{ type: "text", text: this.#emptyStopRetryReminder() }],
499
+ attribution: "agent",
500
+ timestamp: Date.now(),
501
+ });
502
+ this.#host.scheduleAgentContinue({ generation: this.#host.promptGeneration() });
503
+ return true;
504
+ }
505
+
506
+ #isEmptyAssistantStop(assistantMessage: AssistantMessage): boolean {
507
+ switch (assistantMessage.stopReason) {
508
+ case "stop":
509
+ // Unsigned thinking alone is not actionable, but a signature is
510
+ // provider-authenticated content and makes the stop terminal.
511
+ for (const content of assistantMessage.content) {
512
+ if (content.type === "toolCall") return false;
513
+ if (content.type === "text" && hasNonWhitespace(content.text)) return false;
514
+ if (content.type === "thinking" && hasNonWhitespace(content.thinkingSignature ?? "")) return false;
515
+ }
516
+ return true;
517
+ case "toolUse":
518
+ // An orphaned toolUse stop (no tool_use block) corrupts Anthropic history:
519
+ // a later tool_result has nothing to anchor to. Thinking alone cannot anchor
520
+ // a tool_result, so it does not rescue a toolUse stop here.
521
+ for (const content of assistantMessage.content) {
522
+ if (content.type === "toolCall") return false;
523
+ if (content.type === "text" && hasNonWhitespace(content.text)) return false;
524
+ }
525
+ return true;
526
+ default:
527
+ return false;
528
+ }
529
+ }
530
+
531
+ #emptyStopRetryReminder(): string {
532
+ return prompt.render(emptyStopRetryTemplate, {
533
+ retryCount: this.#emptyStopRetryCount,
534
+ maxRetries: EMPTY_STOP_MAX_RETRIES,
535
+ });
536
+ }
537
+ async #handleUnexpectedAssistantStop(assistantMessage: AssistantMessage): Promise<boolean> {
538
+ if (!this.#host.settings.get("features.unexpectedStopDetection")) {
539
+ return false;
540
+ }
541
+ if (!isUnexpectedStopCandidate(assistantMessage)) {
542
+ this.#unexpectedStopRetryCount = 0;
543
+ return false;
544
+ }
545
+
546
+ const text = assistantMessage.content
547
+ .filter((content): content is TextContent => content.type === "text")
548
+ .map(content => content.text)
549
+ .join("\n");
550
+ if (!/\S/.test(text)) {
551
+ this.#unexpectedStopRetryCount = 0;
552
+ return false;
553
+ }
554
+
555
+ const controller = new AbortController();
556
+ const timeout = setTimeout(() => controller.abort(), UNEXPECTED_STOP_TIMEOUT_MS);
557
+ let classification: boolean | undefined;
558
+ try {
559
+ classification = await classifyUnexpectedStop(text, {
560
+ settings: this.#host.settings,
561
+ registry: this.#host.modelRegistry,
562
+ sessionId: this.#host.sessionId(),
563
+ metadataResolver: (provider: string) => this.#host.agent.metadataForProvider(provider),
564
+ signal: controller.signal,
565
+ });
566
+ } finally {
567
+ clearTimeout(timeout);
568
+ }
569
+
570
+ if (classification !== true) {
571
+ this.#unexpectedStopRetryCount = 0;
572
+ return false;
573
+ }
574
+
575
+ this.#unexpectedStopRetryCount++;
576
+ if (this.#unexpectedStopRetryCount > UNEXPECTED_STOP_MAX_RETRIES) {
577
+ logger.warn("Assistant returned unexpected stop after retry cap", {
578
+ attempts: this.#unexpectedStopRetryCount - 1,
579
+ model: assistantMessage.model,
580
+ provider: assistantMessage.provider,
581
+ });
582
+ this.#unexpectedStopRetryCount = 0;
583
+ return false;
584
+ }
585
+
586
+ this.#host.agent.appendMessage({
587
+ role: "developer",
588
+ content: [{ type: "text", text: this.#unexpectedStopRetryReminder() }],
589
+ attribution: "agent",
590
+ timestamp: Date.now(),
591
+ });
592
+ this.#host.scheduleAgentContinue({ generation: this.#host.promptGeneration() });
593
+ return true;
594
+ }
595
+
596
+ #unexpectedStopRetryReminder(): string {
597
+ return prompt.render(unexpectedStopRetryTemplate, {
598
+ retryCount: this.#unexpectedStopRetryCount,
599
+ maxRetries: UNEXPECTED_STOP_MAX_RETRIES,
600
+ });
601
+ }
602
+
603
+ removeAssistantMessageFromActiveContext(
604
+ assistantMessage: AssistantMessage,
605
+ reason = "assistant-context-cleanup",
606
+ ): void {
607
+ const messages = this.#host.agent.state.messages;
608
+ const lastMessage = messages[messages.length - 1];
609
+ const lastAssistant: AssistantMessage | undefined = lastMessage?.role === "assistant" ? lastMessage : undefined;
610
+ if (lastAssistant !== undefined && this.#isSameAssistantMessage(lastAssistant, assistantMessage)) {
611
+ this.#host.agent.replaceMessages(messages.slice(0, -1));
612
+ return;
613
+ }
614
+ // A miss means the failed turn is still in active context (or was never
615
+ // there); log just enough to explain why the identity check failed.
616
+ logger.debug("agent active context assistant removal missed", {
617
+ reason,
618
+ lastRole: lastMessage?.role,
619
+ candidateTimestamp: assistantMessage.timestamp,
620
+ lastTimestamp: lastAssistant?.timestamp,
621
+ candidateStopReason: assistantMessage.stopReason,
622
+ lastStopReason: lastAssistant?.stopReason,
623
+ });
624
+ }
625
+
626
+ /**
627
+ * Drop a recoverable assistant turn from the persisted session branch once a
628
+ * recovery path (context promotion or compaction) is committed. Waits for the
629
+ * in-flight `message_end` persistence slot first so the branch entry exists
630
+ * before we reparent past it. Active context removal is the caller's
631
+ * responsibility — recovery paths clear it eagerly so the retry never
632
+ * replays the failed turn, while no-recovery paths leave the persisted entry
633
+ * (and the user-visible transcript line) in place.
634
+ */
635
+ async #dropPersistedAssistantTurn(assistantMessage: AssistantMessage): Promise<void> {
636
+ await this.#host.waitForSessionMessagePersistence(assistantMessage);
637
+ this.discardAssistantTurn(assistantMessage);
638
+ }
639
+
640
+ /**
641
+ * Drop the failed assistant turn from persisted history, run
642
+ * {@link #runAutoCompaction} for an `overflow` / `incomplete` recovery, and
643
+ * restore the assistant entry if compaction did not actually commit
644
+ * anything (no usable model/preparation, hook cancel, compaction error,
645
+ * or a no-progress automatic-continuation block before any summary was
646
+ * written).
647
+ *
648
+ * Compaction has to see a clean branch — otherwise its `prepareCompaction`
649
+ * pass would keep the failed turn in the kept region and the retry would
650
+ * replay it. But a return that was not paired with a fresh compaction
651
+ * summary or a successful history rewrite means no recovery is in progress,
652
+ * even if queued user input gets drained next. Restoring the failed turn
653
+ * before that continuation preserves the visible stop reason and rebuilds the
654
+ * active assistant tail that `Agent.continue()` needs to dequeue follow-ups.
655
+ */
656
+ async #runRecoveryCompactionWithRollback(
657
+ reason: "overflow" | "incomplete",
658
+ assistantMessage: AssistantMessage,
659
+ allowDefer: boolean,
660
+ options: { autoContinue: boolean; triggerContextTokens?: number },
661
+ ): Promise<RecoveryCompactionResult> {
662
+ const compactionEntryBefore = getLatestCompactionEntry(this.#host.sessionManager.getBranch());
663
+ await this.dropPersistedAssistantTurn(assistantMessage);
664
+ const result = await this.#host.runAutoCompaction(reason, true, false, allowDefer, {
665
+ autoContinue: options.autoContinue,
666
+ triggerContextTokens: options.triggerContextTokens,
667
+ phase: "mid_turn",
668
+ });
669
+ const compactionEntryAfter = getLatestCompactionEntry(this.#host.sessionManager.getBranch());
670
+ if (result.historyRewritten !== true && compactionEntryAfter === compactionEntryBefore) {
671
+ this.#restoreFailedAssistantTurn(assistantMessage);
672
+ }
673
+ return result;
674
+ }
675
+
676
+ #restoreFailedAssistantTurn(assistantMessage: AssistantMessage): void {
677
+ if (!isEmptyErrorTurn(assistantMessage)) this.#host.sessionManager.appendMessage(assistantMessage);
678
+ const lastMessage = this.#host.agent.state.messages.at(-1);
679
+ if (
680
+ lastMessage?.role === "assistant" &&
681
+ this.#isSameAssistantMessage(lastMessage as AssistantMessage, assistantMessage)
682
+ ) {
683
+ return;
684
+ }
685
+ this.#host.agent.appendMessage(assistantMessage);
686
+ }
687
+
688
+ #discardAcceptedTerminalEmptyStop(assistantMessage: AssistantMessage): void {
689
+ const branch = this.#host.sessionManager.getBranch();
690
+ const branchEntry = branch
691
+ .slice()
692
+ .reverse()
693
+ .find(
694
+ entry =>
695
+ entry.type === "message" &&
696
+ entry.message.role === "assistant" &&
697
+ this.#isSameAssistantMessage(entry.message, assistantMessage),
698
+ );
699
+ const parentEntry =
700
+ branchEntry?.parentId === null || branchEntry?.parentId === undefined
701
+ ? undefined
702
+ : branch.find(entry => entry.id === branchEntry.parentId);
703
+ const prunePrompt = parentEntry?.type === "custom_message";
704
+
705
+ this.removeAssistantMessageFromActiveContext(assistantMessage, "accepted-terminal-empty-stop");
706
+ if (prunePrompt && this.#host.agent.state.messages.at(-1)?.role === "custom") {
707
+ this.#host.agent.replaceMessages(this.#host.agent.state.messages.slice(0, -1));
708
+ }
709
+
710
+ if (!branchEntry) return;
711
+ const targetParentId = prunePrompt ? parentEntry.parentId : branchEntry.parentId;
712
+ this.#host.withBashBranchTransition(() => {
713
+ if (targetParentId === null) {
714
+ this.#host.sessionManager.resetLeaf();
715
+ } else {
716
+ this.#host.sessionManager.branch(targetParentId);
717
+ }
718
+ });
719
+ this.#host.sessionManager.appendCustomEntry("accepted-terminal-empty-stop");
720
+ }
721
+
722
+ /**
723
+ * Drop an assistant turn from BOTH the live agent context and the persisted
724
+ * session branch (reparenting the leaf to the turn's parent), so a discarded
725
+ * turn does not resurface on reload. Used for empty/reasoning-only stops and
726
+ * the Gemini header-runaway interrupt, which must not replay a partial,
727
+ * loop-fueling thinking block.
728
+ */
729
+ discardAssistantTurn(assistantMessage: AssistantMessage): void {
730
+ this.removeAssistantMessageFromActiveContext(assistantMessage);
731
+
732
+ const branchEntry = this.#host.sessionManager
733
+ .getBranch()
734
+ .slice()
735
+ .reverse()
736
+ .find(
737
+ entry =>
738
+ entry.type === "message" &&
739
+ entry.message.role === "assistant" &&
740
+ this.#isSameAssistantMessage(entry.message as AssistantMessage, assistantMessage),
741
+ );
742
+ if (!branchEntry) {
743
+ return;
744
+ }
745
+ this.#host.withBashBranchTransition(() => {
746
+ if (branchEntry.parentId === null) {
747
+ this.#host.sessionManager.resetLeaf();
748
+ } else {
749
+ this.#host.sessionManager.branch(branchEntry.parentId);
750
+ }
751
+ });
752
+ }
753
+
754
+ #isSameAssistantMessage(left: AssistantMessage, right: AssistantMessage): boolean {
755
+ return (
756
+ left === right ||
757
+ (left.timestamp === right.timestamp &&
758
+ left.provider === right.provider &&
759
+ left.model === right.model &&
760
+ left.stopReason === right.stopReason)
761
+ );
762
+ }
763
+
764
+ /**
765
+ * Classify retry decisions against the active session model. Test stream
766
+ * shims and provider adapters can emit generic assistant metadata, but retry
767
+ * policy belongs to the model that was actually requested for this turn.
768
+ */
769
+ #classifyRetryMessage(message: AssistantMessage): number {
770
+ const activeModel = this.#host.model();
771
+ if (!activeModel || message.api === activeModel.api) {
772
+ return AIError.classifyMessage(message);
773
+ }
774
+
775
+ const id = AIError.classifyMessage({
776
+ api: activeModel.api,
777
+ errorId: message.errorId,
778
+ errorMessage: message.errorMessage,
779
+ errorStatus: message.errorStatus,
780
+ });
781
+ message.errorId = id;
782
+ return id;
783
+ }
784
+
785
+ /**
786
+ * Retry an empty, reason-less provider abort: a turn with no content that
787
+ * carries the generic sentinel (bare `abort()`), whether the provider
788
+ * finalized it as `stopReason: "aborted"` or leaked it as `stopReason:
789
+ * "error"` (a stalled/dropped stream reported as an error rather than an
790
+ * abort — issue #5375). Only fires while the session is neither aborting nor
791
+ * tearing down. A user/lifecycle abort (`#abortInProgress`), a dispose-driven
792
+ * abort (`#isDisposed`), or a session-induced streaming-edit guard abort
793
+ * (`StreamingEditGuard.abortTriggered` — auto-generated-file guard or failed-patch
794
+ * preview) is deliberate and MUST settle the turn instead: routing it through
795
+ * retry would orphan `#retryPromise` on a continuation the guard skips
796
+ * (hanging the in-flight `prompt()`) or silently undo the guard's intended
797
+ * abort. Deliberate user interrupts (`UserInterrupt`) and silent aborts carry
798
+ * their own marker, not the generic sentinel, so they never match here.
799
+ */
800
+ isRetryableReasonlessAbort(message: AssistantMessage): boolean {
801
+ if (
802
+ (message.stopReason !== "aborted" && message.stopReason !== "error") ||
803
+ message.content.length !== 0 ||
804
+ this.#host.abortInProgress() ||
805
+ this.#host.isDisposed() ||
806
+ this.#host.streamingEditAbortTriggered()
807
+ ) {
808
+ return false;
809
+ }
810
+
811
+ const id = this.#classifyRetryMessage(message);
812
+ if (message.stopReason === "aborted" && AIError.is(id, AIError.Flag.Abort)) return true;
813
+ if (message.errorMessage !== "Request was aborted" && message.errorMessage !== "Request was aborted.") {
814
+ return false;
815
+ }
816
+
817
+ message.errorId = AIError.create(AIError.Flag.Abort);
818
+ return true;
819
+ }
820
+
821
+ /**
822
+ * Check if an error is retryable (transient errors or usage limits).
823
+ * Context overflow is NOT retryable (handled by compaction instead).
824
+ * Usage-limit errors are retryable because the retry handler performs credential switching.
825
+ */
826
+ isRetryableError(message: AssistantMessage): boolean {
827
+ if (message.stopReason !== "error") return false;
828
+
829
+ const id = this.#classifyRetryMessage(message);
830
+ // Context overflow is handled by compaction, not retry
831
+ const contextWindow = this.#host.model()?.contextWindow ?? 0;
832
+ if (AIError.isContextOverflow(message, contextWindow)) return false;
833
+
834
+ if (this.isClassifierRefusal(message)) return true;
835
+ return AIError.retriable(id, { replayUnsafe: this.#hasReplayUnsafeToolOutput(message) });
836
+ }
837
+
838
+ /**
839
+ * Classify a reasonless abort or stream stall whose emitted tool calls all
840
+ * have results. The failed assistant/tool-result pair stays in context so
841
+ * continuation cannot replay completed side effects; synthetic results tell
842
+ * the next turn that an unexecuted call must be reissued.
843
+ */
844
+ classifyResolvedInterruptedToolTurn(message: AssistantMessage): "reasonless-abort" | "stream-stall" | undefined {
845
+ const id = this.#classifyRetryMessage(message);
846
+ const genericAbort =
847
+ message.errorMessage === "Request was aborted" || message.errorMessage === "Request was aborted.";
848
+ const reasonlessAbort =
849
+ (message.stopReason === "aborted" || message.stopReason === "error") &&
850
+ !this.#host.abortInProgress() &&
851
+ !this.#host.isDisposed() &&
852
+ !this.#host.streamingEditAbortTriggered() &&
853
+ ((message.stopReason === "aborted" && AIError.is(id, AIError.Flag.Abort)) || genericAbort);
854
+ const streamStall =
855
+ message.stopReason === "error" &&
856
+ message.errorMessage?.toLowerCase().includes("stream stall") === true &&
857
+ AIError.retriable(id);
858
+ if (!reasonlessAbort && !streamStall) return undefined;
859
+ if (reasonlessAbort && genericAbort) message.errorId = AIError.create(AIError.Flag.Abort);
860
+
861
+ // The Cursor server-execution marker gate applies only to the stream-stall
862
+ // path: an unmarked/unresolved Cursor block there means the server has not
863
+ // finished executing, so resuming would race it. A reasonless abort instead
864
+ // ends the turn and the agent loop pairs every un-run call (Cursor's unmarked
865
+ // `todo`/MCP blocks included) with a synthetic `executed: false` result, so
866
+ // the tool-result reconciliation below is the safety gate and the marker is
867
+ // irrelevant.
868
+ const resolvedToolCallIds: string[] = [];
869
+ for (const block of message.content) {
870
+ if (block.type !== "toolCall") continue;
871
+ if (
872
+ streamStall &&
873
+ message.provider === "cursor" &&
874
+ (!(kCursorExecResolved in block) || block[kCursorExecResolved] !== true)
875
+ ) {
876
+ return undefined;
877
+ }
878
+ resolvedToolCallIds.push(block.id);
879
+ }
880
+ if (resolvedToolCallIds.length === 0) return undefined;
881
+
882
+ const messages = this.#host.agent.state.messages;
883
+ let assistantIndex = -1;
884
+ for (let i = messages.length - 1; i >= 0; i--) {
885
+ const candidate = messages[i];
886
+ if (candidate.role === "assistant" && this.#isSameAssistantMessage(candidate, message)) {
887
+ assistantIndex = i;
888
+ break;
889
+ }
890
+ }
891
+ if (assistantIndex < 0) return undefined;
892
+
893
+ const unresolvedToolCallIds = new Set(resolvedToolCallIds);
894
+ for (let i = assistantIndex + 1; i < messages.length; i++) {
895
+ const candidate = messages[i];
896
+ if (candidate.role === "toolResult") unresolvedToolCallIds.delete(candidate.toolCallId);
897
+ }
898
+ if (unresolvedToolCallIds.size > 0) return undefined;
899
+ return reasonlessAbort ? "reasonless-abort" : "stream-stall";
900
+ }
901
+ /**
902
+ * Retried turns remove the failed assistant message from active context.
903
+ * Text/thinking-only partials are safe to discard and replay. Retained
904
+ * tool calls are not: a completed tool call may already have emitted its
905
+ * tool result after this assistant message, so replaying can duplicate work.
906
+ */
907
+ #hasReplayUnsafeToolOutput(message: AssistantMessage): boolean {
908
+ return message.content.some(block => block.type === "toolCall");
909
+ }
910
+
911
+ /**
912
+ * OpenRouter can repeatedly close Gemini streams at the reasoning-to-payload
913
+ * transition. One retry covers a transient edge failure; the normal ten-retry
914
+ * budget would otherwise re-run the same expensive reasoning cycle unchanged.
915
+ */
916
+ #isOpenRouterThinkingStreamClose(message: AssistantMessage): boolean {
917
+ return (
918
+ message.provider === "openrouter" &&
919
+ /server_error:\s*stream closed with reason:\s*error/i.test(message.errorMessage ?? "") &&
920
+ message.content.some(block => block.type === "thinking" && block.thinking.trim().length > 0)
921
+ );
922
+ }
923
+
924
+ /** Checks whether a provider error represents a classifier refusal. */
925
+ isClassifierRefusal(message: AssistantMessage): boolean {
926
+ if (message.stopReason !== "error") return false;
927
+ const stopType = message.stopDetails?.type;
928
+ return stopType === "refusal" || stopType === "sensitive";
929
+ }
930
+
931
+ #getRetryFallbackResolutionContext(): RetryFallbackResolutionContext {
932
+ return {
933
+ chains: this.#getRetryFallbackChains(),
934
+ getModelRole: role => this.#host.settings.getModelRole(role),
935
+ modelLookup: this.#host.modelRegistry,
936
+ };
937
+ }
938
+
939
+ #getRetryFallbackChains(): RetryFallbackChains {
940
+ return getRetryFallbackChains(this.#host.settings);
941
+ }
942
+
943
+ #validateRetryFallbackChains(): void {
944
+ validateRetryFallbackChains(this.#host.settings, this.#host.modelRegistry, message =>
945
+ this.#host.configWarnings.push(message),
946
+ );
947
+ }
948
+
949
+ #getRetryFallbackRevertPolicy(): RetryFallbackRevertPolicy {
950
+ return getRetryFallbackRevertPolicy(this.#host.settings);
951
+ }
952
+
953
+ /** Clears fallback ownership after an explicit model change. */
954
+ clearActiveRetryFallback(): void {
955
+ this.#activeRetryFallback = undefined;
956
+ }
957
+
958
+ /** Checks whether a fallback selector remains in cooldown. */
959
+ isRetryFallbackSelectorSuppressed(selector: RetryFallbackSelector): boolean {
960
+ return this.#host.modelRegistry.isSelectorSuppressed(selector.raw);
961
+ }
962
+
963
+ /** Records the cooldown that should suppress a failing selector. */
964
+ noteRetryFallbackCooldown(currentSelector: string, retryAfterMs: number | undefined, errorMessage: string): void {
965
+ let cooldownMs = retryAfterMs;
966
+ if (!cooldownMs || cooldownMs <= 0) {
967
+ const reason = parseRateLimitReason(errorMessage);
968
+ cooldownMs = reason === "UNKNOWN" ? 5 * 60 * 1000 : calculateRateLimitBackoffMs(reason);
969
+ }
970
+ this.#host.modelRegistry.suppressSelector(currentSelector, Date.now() + cooldownMs);
971
+ }
972
+
973
+ /**
974
+ * Map the failing model selector to the chain key that owns it, by
975
+ * specificity: an exact model-selector key, then a `provider/*` wildcard,
976
+ * then a model role whose current assignment matches, then `default`.
977
+ * Model-oriented keys win over roles so a chain follows the model across
978
+ * role reassignments.
979
+ */
980
+ resolveRetryFallbackRole(
981
+ currentSelector: string,
982
+ currentModel: Model | null | undefined = this.#host.model(),
983
+ ): string | undefined {
984
+ return resolveRetryFallbackChainKey(this.#getRetryFallbackResolutionContext(), currentSelector, currentModel);
985
+ }
986
+
987
+ /** Finds fallback candidates that follow the active selector. */
988
+ findRetryFallbackCandidates(
989
+ role: string,
990
+ currentSelector: string,
991
+ currentModel: Model | null | undefined = this.#host.model(),
992
+ ): RetryFallbackSelector[] {
993
+ return findRetryFallbackCandidates(
994
+ this.#getRetryFallbackResolutionContext(),
995
+ role,
996
+ currentSelector,
997
+ currentModel,
998
+ );
999
+ }
1000
+
1001
+ async applyRetryFallbackCandidate(
1002
+ role: string,
1003
+ selector: RetryFallbackSelector,
1004
+ currentSelector: string,
1005
+ options?: { pinFallback?: boolean; apiKey?: string; signal?: AbortSignal },
1006
+ ): Promise<void> {
1007
+ const resolved = resolveModelOverride([selector.raw], this.#host.modelRegistry, this.#host.settings);
1008
+ const candidate = resolved.model ?? this.#host.modelRegistry.find(selector.provider, selector.id);
1009
+ if (!candidate) {
1010
+ throw new Error(`Retry fallback model not found: ${selector.raw}`);
1011
+ }
1012
+ const apiKey =
1013
+ options?.apiKey ?? (await this.#host.modelRegistry.getApiKey(candidate, this.#host.sessionId(), options));
1014
+ if (!apiKey) {
1015
+ throw new Error(`No API key for retry fallback ${selector.raw}`);
1016
+ }
1017
+ if (options?.signal?.aborted) return;
1018
+
1019
+ // Capture the configured selector (auto-aware) so a fallback chain preserves
1020
+ // `auto` instead of collapsing it to the level it resolved to this turn.
1021
+ const currentThinkingLevel = this.#host.configuredThinkingLevel();
1022
+ const requestedThinkingLevel = selector.thinkingLevel ?? currentThinkingLevel;
1023
+ // A fallback selector's explicit level (or the carried level after the
1024
+ // replacement model's floor clamp) must never exceed the session's
1025
+ // per-spawn effort ceiling.
1026
+ const nextThinkingLevel =
1027
+ requestedThinkingLevel === AUTO_THINKING
1028
+ ? requestedThinkingLevel
1029
+ : clampThinkingLevelToCeiling(candidate, requestedThinkingLevel, this.#host.thinkingLevelCeiling());
1030
+ const candidateSelector = formatModelStringWithRouting(candidate);
1031
+ await this.#host.setModelWithProviderSessionReset(candidate);
1032
+ this.#host.sessionManager.appendModelChange(candidateSelector, EPHEMERAL_MODEL_CHANGE_ROLE);
1033
+ this.#host.settings.getStorage()?.recordModelUsage(candidateSelector);
1034
+ this.#host.setThinkingLevel(nextThinkingLevel);
1035
+ if (!this.#activeRetryFallback) {
1036
+ this.#activeRetryFallback = {
1037
+ role,
1038
+ originalSelector: currentSelector,
1039
+ originalThinkingLevel: currentThinkingLevel,
1040
+ lastAppliedFallbackThinkingLevel: nextThinkingLevel,
1041
+ pinned: options?.pinFallback === true,
1042
+ };
1043
+ } else {
1044
+ this.#activeRetryFallback.lastAppliedFallbackThinkingLevel = nextThinkingLevel;
1045
+ this.#activeRetryFallback.pinned = this.#activeRetryFallback.pinned || options?.pinFallback === true;
1046
+ }
1047
+ await this.#host.emitSessionEvent({
1048
+ type: "retry_fallback_applied",
1049
+ from: currentSelector,
1050
+ to: selector.raw,
1051
+ role,
1052
+ });
1053
+ }
1054
+
1055
+ async #tryRetryModelFallback(currentSelector: string, options?: { pinFallback?: boolean }): Promise<boolean> {
1056
+ const role = this.#activeRetryFallback?.role ?? this.resolveRetryFallbackRole(currentSelector);
1057
+ if (!role) return false;
1058
+
1059
+ const ceiling = this.#host.thinkingLevelCeiling();
1060
+ for (const selector of this.findRetryFallbackCandidates(role, currentSelector)) {
1061
+ if (this.isRetryFallbackSelectorSuppressed(selector)) continue;
1062
+ const resolved = resolveModelOverride([selector.raw], this.#host.modelRegistry, this.#host.settings);
1063
+ const candidate = resolved.model ?? this.#host.modelRegistry.find(selector.provider, selector.id);
1064
+ if (!candidate) continue;
1065
+ // A candidate whose effort floor exceeds the per-spawn ceiling would be
1066
+ // clamped UP past the cap by its model floor — skip it entirely.
1067
+ if (ceiling !== undefined && !modelSupportsEffortCeiling(candidate, ceiling)) continue;
1068
+ const apiKey = await this.#host.modelRegistry.getApiKey(candidate, this.#host.sessionId());
1069
+ if (!apiKey) continue;
1070
+ await this.applyRetryFallbackCandidate(role, selector, currentSelector, options);
1071
+ return true;
1072
+ }
1073
+
1074
+ return false;
1075
+ }
1076
+
1077
+ /** The active model when it is a Fireworks Fast (`-fast`) variant, else undefined. */
1078
+ #activeFireworksFastModel(): Model | undefined {
1079
+ const model = this.#host.model();
1080
+ return model?.provider === "fireworks" && isFireworksFastModelId(model.id) ? model : undefined;
1081
+ }
1082
+
1083
+ /**
1084
+ * True when the current turn failed on a Fireworks Fast (`-fast`) model in a
1085
+ * way that should degrade to the reliable base (Standard) model. Fast is a
1086
+ * speed-optimized router with no SLA, so any *pre-content* failure — a
1087
+ * transient overload/5xx or a hard "router/model not found / unsupported" —
1088
+ * is worth retrying on the base id. Skips failures the base model shares:
1089
+ * context overflow (compaction's job), usage limits and auth errors (same
1090
+ * account/key), and turns that already emitted a tool call (replaying would
1091
+ * duplicate work). Requires the base model to exist in the registry.
1092
+ */
1093
+ isFireworksFastFallbackEligible(message: AssistantMessage): boolean {
1094
+ const model = this.#activeFireworksFastModel();
1095
+ if (!model) return false;
1096
+ if (message.stopReason !== "error") return false;
1097
+ if (message.content.some(block => block.type === "toolCall")) return false;
1098
+ // A content refusal/sensitivity stop is the model's decision, not a route
1099
+ // failure — switching to the base model would just re-trigger it.
1100
+ if (this.isClassifierRefusal(message)) return false;
1101
+ const id = this.#classifyRetryMessage(message);
1102
+ if (AIError.isContextOverflow(message, model.contextWindow ?? 0)) return false;
1103
+ if (AIError.is(id, AIError.Flag.UsageLimit)) return false;
1104
+ if (AIError.is(id, AIError.Flag.AuthFailed)) return false;
1105
+ return this.#host.modelRegistry.find("fireworks", toFireworksBaseModelId(model.id)) !== undefined;
1106
+ }
1107
+
1108
+ /**
1109
+ * True when a turn failed with a hard (non-retryable) provider error but a
1110
+ * configured `retry.fallbackChains` entry covers the active model: the same
1111
+ * model is not worth retrying, yet a DIFFERENT model is a fresh chance, so
1112
+ * the chain is consulted before the error becomes final. Skips failures a
1113
+ * model switch cannot fix or must not replay: cancellations (abort-flavored
1114
+ * errors are not model faults), context overflow (compaction's job),
1115
+ * classifier refusals (chain consult is handled on the retryable path with
1116
+ * `pinFallback`), and turns that already emitted a tool call (replaying
1117
+ * could duplicate work).
1118
+ */
1119
+ isHardErrorFallbackEligible(message: AssistantMessage): boolean {
1120
+ if (message.stopReason !== "error") return false;
1121
+ const model = this.#host.model();
1122
+ if (!model) return false;
1123
+ const retrySettings = this.#host.settings.getGroup("retry");
1124
+ if (!retrySettings.enabled || !retrySettings.modelFallback) return false;
1125
+ if (this.isClassifierRefusal(message)) return false;
1126
+ const id = this.#classifyRetryMessage(message);
1127
+ if (AIError.is(id, AIError.Flag.Abort) || AIError.is(id, AIError.Flag.UserInterrupt)) return false;
1128
+ if (AIError.isContextOverflow(message, model.contextWindow ?? 0)) return false;
1129
+ if (this.#hasReplayUnsafeToolOutput(message)) return false;
1130
+ const currentSelector = formatRetryFallbackSelector(model, this.#host.thinkingLevel());
1131
+ const role = this.#activeRetryFallback?.role ?? this.resolveRetryFallbackRole(currentSelector);
1132
+ if (!role) return false;
1133
+ return this.findRetryFallbackCandidates(role, currentSelector).length > 0;
1134
+ }
1135
+
1136
+ /**
1137
+ * Switch the active model from a Fireworks Fast (`-fast`) variant to its base
1138
+ * (Standard) id and stick there for the rest of the session — the auto
1139
+ * fallback that makes Fast a safe default. Returns false when the current
1140
+ * model is not a fast variant, the base id is missing, or it has no key.
1141
+ */
1142
+ async #tryFireworksFastFallback(currentSelector: string): Promise<boolean> {
1143
+ const model = this.#activeFireworksFastModel();
1144
+ if (!model) return false;
1145
+ const baseModel = this.#host.modelRegistry.find("fireworks", toFireworksBaseModelId(model.id));
1146
+ if (!baseModel) return false;
1147
+ const apiKey = await this.#host.modelRegistry.getApiKey(baseModel, this.#host.sessionId());
1148
+ if (!apiKey) return false;
1149
+ const baseSelector = formatModelStringWithRouting(baseModel);
1150
+ await this.#host.setModelWithProviderSessionReset(baseModel);
1151
+ this.#host.sessionManager.appendModelChange(baseSelector, EPHEMERAL_MODEL_CHANGE_ROLE);
1152
+ this.#host.settings.getStorage()?.recordModelUsage(baseSelector);
1153
+ await this.#host.emitSessionEvent({
1154
+ type: "retry_fallback_applied",
1155
+ from: currentSelector,
1156
+ to: baseSelector,
1157
+ role: "fireworks-fast",
1158
+ });
1159
+ return true;
1160
+ }
1161
+
1162
+ async #maybeRestoreRetryFallbackPrimary(): Promise<void> {
1163
+ if (!this.#activeRetryFallback) return;
1164
+ if (this.#activeRetryFallback.pinned) return;
1165
+ if (this.#getRetryFallbackRevertPolicy() !== "cooldown-expiry") return;
1166
+
1167
+ const {
1168
+ originalSelector: originalSelectorRaw,
1169
+ originalThinkingLevel,
1170
+ lastAppliedFallbackThinkingLevel,
1171
+ } = this.#activeRetryFallback;
1172
+ const originalSelector = parseRetryFallbackSelector(originalSelectorRaw, this.#host.modelRegistry);
1173
+ if (!originalSelector) {
1174
+ this.clearActiveRetryFallback();
1175
+ return;
1176
+ }
1177
+
1178
+ const currentModel = this.#host.model();
1179
+ if (!currentModel) return;
1180
+ const currentSelector = formatRetryFallbackSelector(currentModel, this.#host.thinkingLevel());
1181
+ if (currentSelector === originalSelector.raw) {
1182
+ if (!this.isRetryFallbackSelectorSuppressed(originalSelector)) {
1183
+ this.clearActiveRetryFallback();
1184
+ }
1185
+ return;
1186
+ }
1187
+ if (this.isRetryFallbackSelectorSuppressed(originalSelector)) return;
1188
+
1189
+ const resolvedPrimary = resolveModelOverride(
1190
+ [originalSelector.raw],
1191
+ this.#host.modelRegistry,
1192
+ this.#host.settings,
1193
+ );
1194
+ const primaryModel =
1195
+ resolvedPrimary.model ?? this.#host.modelRegistry.find(originalSelector.provider, originalSelector.id);
1196
+ if (!primaryModel) return;
1197
+ const apiKey = await this.#host.modelRegistry.getApiKey(primaryModel, this.#host.sessionId());
1198
+ if (!apiKey) return;
1199
+
1200
+ const currentThinkingLevel = this.#host.configuredThinkingLevel();
1201
+ const thinkingToApply =
1202
+ currentThinkingLevel === lastAppliedFallbackThinkingLevel ? originalThinkingLevel : currentThinkingLevel;
1203
+ const primarySelector = formatModelStringWithRouting(primaryModel);
1204
+ await this.#host.setModelWithProviderSessionReset(primaryModel);
1205
+ this.#host.sessionManager.appendModelChange(primarySelector, EPHEMERAL_MODEL_CHANGE_ROLE);
1206
+ this.#host.settings.getStorage()?.recordModelUsage(primarySelector);
1207
+ this.#host.setThinkingLevel(thinkingToApply);
1208
+ this.clearActiveRetryFallback();
1209
+ }
1210
+
1211
+ #parseRetryAfterMsFromError(errorMessage: string): number | undefined {
1212
+ const now = Date.now();
1213
+ const retryAfterMsMatch = /retry-after-ms\s*[:=]\s*(\d+)/i.exec(errorMessage);
1214
+ if (retryAfterMsMatch) {
1215
+ return Math.max(0, Number(retryAfterMsMatch[1]));
1216
+ }
1217
+
1218
+ const retryAfterMatch = /retry-after\s*[:=]\s*([^\s,;]+)/i.exec(errorMessage);
1219
+ if (retryAfterMatch) {
1220
+ const value = retryAfterMatch[1];
1221
+ const seconds = Number(value);
1222
+ if (!Number.isNaN(seconds)) {
1223
+ return Math.max(0, seconds * 1000);
1224
+ }
1225
+ const dateMs = Date.parse(value);
1226
+ if (!Number.isNaN(dateMs)) {
1227
+ return Math.max(0, dateMs - now);
1228
+ }
1229
+ }
1230
+
1231
+ const retryHintMs = extractRetryHint(undefined, errorMessage);
1232
+ if (retryHintMs !== undefined) {
1233
+ return retryHintMs;
1234
+ }
1235
+
1236
+ const resetMsMatch = /x-ratelimit-reset-ms\s*[:=]\s*(\d+)/i.exec(errorMessage);
1237
+ if (resetMsMatch) {
1238
+ const resetMs = Number(resetMsMatch[1]);
1239
+ if (!Number.isNaN(resetMs)) {
1240
+ if (resetMs > 1_000_000_000_000) {
1241
+ return Math.max(0, resetMs - now);
1242
+ }
1243
+ return Math.max(0, resetMs);
1244
+ }
1245
+ }
1246
+
1247
+ const resetMatch = /x-ratelimit-reset\s*[:=]\s*(\d+)/i.exec(errorMessage);
1248
+ if (resetMatch) {
1249
+ const resetSeconds = Number(resetMatch[1]);
1250
+ if (!Number.isNaN(resetSeconds)) {
1251
+ if (resetSeconds > 1_000_000_000) {
1252
+ return Math.max(0, resetSeconds * 1000 - now);
1253
+ }
1254
+ return Math.max(0, resetSeconds * 1000);
1255
+ }
1256
+ }
1257
+
1258
+ // Smart Fallback if no exact headers found
1259
+ return undefined;
1260
+ }
1261
+
1262
+ /**
1263
+ * Handle retryable errors with exponential backoff, credential rotation, and
1264
+ * model-fallback chains. Also entered for NON-retryable errors when a switch
1265
+ * is the recovery (`fireworksFastFallback`, `hardErrorFallback`): then a
1266
+ * successful model switch retries immediately, and a failed switch surfaces
1267
+ * the error without a same-model backoff retry.
1268
+ * @returns true if retry was initiated, false if max retries exceeded or disabled
1269
+ */
1270
+ async #handleRetryableError(
1271
+ message: AssistantMessage,
1272
+ options?: {
1273
+ allowModelFallback?: boolean;
1274
+ fireworksFastFallback?: boolean;
1275
+ hardErrorFallback?: boolean;
1276
+ preserveFailedTurn?: boolean;
1277
+ },
1278
+ ): Promise<boolean> {
1279
+ const retrySettings = this.#host.settings.getGroup("retry");
1280
+ // The Fireworks Fast→base degrade is an intrinsic model-selection safety net,
1281
+ // not a retry loop, so it runs even when the user disabled retries: it switches
1282
+ // the model once and lets the base turn proceed.
1283
+ if (!retrySettings.enabled && !options?.fireworksFastFallback) return false;
1284
+ const classifierRefusal = this.isClassifierRefusal(message);
1285
+
1286
+ const generation = this.#host.promptGeneration();
1287
+ this.#retryAttempt++;
1288
+
1289
+ // Create retry promise on first attempt so waitForRetry() can await it
1290
+ // Ensure only one promise exists (avoid orphaned promises from concurrent calls)
1291
+ if (!this.#retryPromise) {
1292
+ const { promise, resolve } = Promise.withResolvers<void>();
1293
+ this.#retryPromise = promise;
1294
+ this.#retryResolve = resolve;
1295
+ }
1296
+
1297
+ // All attempts on the current model are spent. Don't fail yet: the
1298
+ // fallback chain below gets one last consult. Credential rotation can
1299
+ // consume the entire budget without the fallback branch ever running
1300
+ // (every rotation sets switchedCredential and skips it), so without
1301
+ // this last resort a provider-wide usage cap never fails over to the
1302
+ // configured chain.
1303
+ const maxRetries = this.#isOpenRouterThinkingStreamClose(message)
1304
+ ? Math.min(retrySettings.maxRetries, 1)
1305
+ : retrySettings.maxRetries;
1306
+ const retryBudgetExhausted = this.#retryAttempt > maxRetries;
1307
+
1308
+ const errorMessage = message.errorMessage || "Unknown error";
1309
+ const id = this.#classifyRetryMessage(message);
1310
+ const staleOpenAIResponsesReplayError = AIError.is(id, AIError.Flag.StaleResponsesItem);
1311
+ const parsedRetryAfterMs = this.#parseRetryAfterMsFromError(errorMessage);
1312
+ let delayMs = staleOpenAIResponsesReplayError
1313
+ ? 0
1314
+ : calculateRetryBackoffDelayMs(retrySettings.baseDelayMs, this.#retryAttempt);
1315
+ let switchedCredential = false;
1316
+ let switchedModel = false;
1317
+ // Set when a usage-limit error pinned the wait to credential
1318
+ // availability — suppresses the generic retry-after bump below.
1319
+ let usageLimitWaitMs: number | undefined;
1320
+
1321
+ if (staleOpenAIResponsesReplayError) {
1322
+ this.#host.resetCurrentResponsesProviderSession("stale replay error");
1323
+ }
1324
+
1325
+ const activeModel = this.#host.model();
1326
+ if (
1327
+ !retryBudgetExhausted &&
1328
+ activeModel &&
1329
+ !staleOpenAIResponsesReplayError &&
1330
+ AIError.is(id, AIError.Flag.UsageLimit)
1331
+ ) {
1332
+ const retryAfterMs = parsedRetryAfterMs ?? calculateRateLimitBackoffMs(parseRateLimitReason(errorMessage));
1333
+ const outcome = await this.#host.modelRegistry.authStorage.markUsageLimitReached(
1334
+ activeModel.provider,
1335
+ this.#host.sessionId(),
1336
+ {
1337
+ retryAfterMs,
1338
+ baseUrl: activeModel.baseUrl,
1339
+ modelId: activeModel.id,
1340
+ },
1341
+ );
1342
+ if (outcome.switched) {
1343
+ switchedCredential = true;
1344
+ delayMs = 0;
1345
+ } else if (await this.#host.maybeAutoRedeemCodexReset()) {
1346
+ // A live usage-limit 429 on the active Codex account, with a banked
1347
+ // reset and the opt-in setting on: spend the reset and retry
1348
+ // immediately instead of waiting out the window. Runs after the
1349
+ // free sibling-switch above and before model fallback below.
1350
+ switchedCredential = true;
1351
+ delayMs = 0;
1352
+ } else {
1353
+ // No sibling credential is usable right now. Wait for whichever
1354
+ // comes first: the provider's retry-after window for the current
1355
+ // account, or the earliest moment a temporarily blocked sibling
1356
+ // frees up (e.g. a 60s post-401 block or a 5-min usage-probe
1357
+ // block) — the next attempt's getApiKey re-ranks and picks it up.
1358
+ // Without this, one short-lived sibling block escalates a
1359
+ // recoverable situation into the provider's multi-hour wait and
1360
+ // trips the fail-fast cap below.
1361
+ usageLimitWaitMs = retryAfterMs;
1362
+ if (outcome.retryAtMs !== undefined) {
1363
+ const siblingWaitMs = Math.max(0, outcome.retryAtMs - Date.now()) + SIBLING_UNBLOCK_BUFFER_MS;
1364
+ if (siblingWaitMs < usageLimitWaitMs) {
1365
+ usageLimitWaitMs = siblingWaitMs;
1366
+ }
1367
+ }
1368
+ if (usageLimitWaitMs > delayMs) {
1369
+ delayMs = usageLimitWaitMs;
1370
+ }
1371
+ }
1372
+ }
1373
+
1374
+ const allowModelFallback = options?.allowModelFallback !== false;
1375
+ const currentModel = this.#host.model();
1376
+ const currentSelector = currentModel
1377
+ ? formatRetryFallbackSelector(currentModel, this.#host.thinkingLevel())
1378
+ : undefined;
1379
+ if (!staleOpenAIResponsesReplayError && !switchedCredential && currentSelector) {
1380
+ // A refusal chain stops at the retry budget: the exhausted-attempt
1381
+ // last resort is for provider failures, not classifier decisions.
1382
+ if (allowModelFallback && retrySettings.modelFallback && !(retryBudgetExhausted && classifierRefusal)) {
1383
+ if (!classifierRefusal) {
1384
+ this.noteRetryFallbackCooldown(currentSelector, parsedRetryAfterMs, errorMessage);
1385
+ }
1386
+ switchedModel = await this.#tryRetryModelFallback(currentSelector, { pinFallback: classifierRefusal });
1387
+ }
1388
+ // Auto fallback from a Fireworks Fast variant to its base model. Independent
1389
+ // of the role-fallback setting: it's intrinsic to the Fast contract (speed
1390
+ // best-effort, degrade to Standard on failure) and triggers on hard router
1391
+ // errors the generic retry classifier would otherwise reject.
1392
+ if (!switchedModel && allowModelFallback && options?.fireworksFastFallback) {
1393
+ switchedModel = await this.#tryFireworksFastFallback(currentSelector);
1394
+ }
1395
+ if (switchedModel) {
1396
+ delayMs = 0;
1397
+ } else if (usageLimitWaitMs === undefined && parsedRetryAfterMs && parsedRetryAfterMs > delayMs) {
1398
+ delayMs = parsedRetryAfterMs;
1399
+ }
1400
+ }
1401
+ if (retryBudgetExhausted) {
1402
+ if (!switchedModel) {
1403
+ await this.persistTerminalEmptyErrorTurn(message);
1404
+ // Max retries exceeded and no fallback model to switch to: emit
1405
+ // final failure and reset.
1406
+ await this.#host.emitSessionEvent({
1407
+ type: "auto_retry_end",
1408
+ success: false,
1409
+ attempt: this.#retryAttempt - 1,
1410
+ finalError: message.errorMessage,
1411
+ });
1412
+ this.#clearPendingRecoveredRetryErrors();
1413
+ this.#retryAttempt = 0;
1414
+ this.resolveRetry(); // Resolve so waitForRetry() completes
1415
+ return false;
1416
+ }
1417
+ // The fallback model gets a fresh retry budget — leaving the spent
1418
+ // counter in place would exhaust it again on its first error.
1419
+ this.#retryAttempt = 1;
1420
+ }
1421
+ if (classifierRefusal && !switchedModel) {
1422
+ // A prior attempt in this saga already announced `auto_retry_start`
1423
+ // (retryAttempt was incremented for each call to this method, so > 1
1424
+ // means at least one earlier attempt started the loop) but this
1425
+ // attempt is not going to retry — the saga must close with its own
1426
+ // `auto_retry_end` so subscribers tracking retry-outstanding state
1427
+ // (e.g. suppressing a duplicate error toast) don't stay latched on
1428
+ // an announcement that never resolves.
1429
+ if (this.#retryAttempt > 1) {
1430
+ await this.persistTerminalEmptyErrorTurn(message);
1431
+ await this.#host.emitSessionEvent({
1432
+ type: "auto_retry_end",
1433
+ success: false,
1434
+ attempt: this.#retryAttempt - 1,
1435
+ finalError: errorMessage,
1436
+ });
1437
+ this.#clearPendingRecoveredRetryErrors();
1438
+ }
1439
+ this.#retryAttempt = 0;
1440
+ this.resolveRetry();
1441
+ return false;
1442
+ }
1443
+ // A fallback switch was the whole reason we entered (Fast→base degrade or
1444
+ // a hard-error chain consult) but it could not happen (e.g. no candidate
1445
+ // has a credential). Don't fall through to backing-off and retrying the
1446
+ // failing model for an error the generic classifier wouldn't retry —
1447
+ // surface it instead.
1448
+ if (
1449
+ (options?.fireworksFastFallback || options?.hardErrorFallback) &&
1450
+ !switchedModel &&
1451
+ !this.isRetryableError(message)
1452
+ ) {
1453
+ // Same auto_retry_end backstop as the classifier-refusal branch above.
1454
+ if (this.#retryAttempt > 1) {
1455
+ await this.persistTerminalEmptyErrorTurn(message);
1456
+ await this.#host.emitSessionEvent({
1457
+ type: "auto_retry_end",
1458
+ success: false,
1459
+ attempt: this.#retryAttempt - 1,
1460
+ finalError: errorMessage,
1461
+ });
1462
+ this.#clearPendingRecoveredRetryErrors();
1463
+ }
1464
+ this.#retryAttempt = 0;
1465
+ this.resolveRetry();
1466
+ return false;
1467
+ }
1468
+
1469
+ // Fail-fast cap: if the provider asks us to wait longer than
1470
+ // retry.maxDelayMs and we have no fallback credential or model to
1471
+ // switch to, surface the error instead of sleeping. Defends against
1472
+ // 3-hour Anthropic rate-limit windows that would otherwise leave a
1473
+ // subagent (or interactive session) silently hung. The original
1474
+ // assistant error message is preserved in agent state so the caller
1475
+ // can act on it.
1476
+ const maxDelayMs = retrySettings.maxDelayMs;
1477
+ if (maxDelayMs > 0 && delayMs > maxDelayMs && !switchedCredential && !switchedModel) {
1478
+ await this.persistTerminalEmptyErrorTurn(message);
1479
+ const attempt = this.#retryAttempt;
1480
+ this.#retryAttempt = 0;
1481
+ await this.#host.emitSessionEvent({
1482
+ type: "auto_retry_end",
1483
+ success: false,
1484
+ attempt,
1485
+ finalError: `Provider requested ${delayMs}ms wait, exceeds retry.maxDelayMs (${maxDelayMs}ms). Original error: ${errorMessage}`,
1486
+ });
1487
+ this.#clearPendingRecoveredRetryErrors();
1488
+ this.resolveRetry();
1489
+ return false;
1490
+ }
1491
+
1492
+ await this.#recordPendingRecoveredRetryError(message, id, { switchedCredential, switchedModel, delayMs });
1493
+
1494
+ await this.#host.emitSessionEvent({
1495
+ type: "auto_retry_start",
1496
+ attempt: this.#retryAttempt,
1497
+ maxAttempts: maxRetries,
1498
+ delayMs,
1499
+ errorMessage,
1500
+ errorId: message.errorId,
1501
+ });
1502
+
1503
+ // Resolved stream-stall tools have already emitted results. Keep that failed
1504
+ // turn intact so continuation cannot repeat their side effects.
1505
+ if (!options?.preserveFailedTurn) {
1506
+ this.removeAssistantMessageFromActiveContext(message, "auto-retry");
1507
+ }
1508
+
1509
+ // A thinking/response loop retried into identical context loops again. Inject a
1510
+ // hidden redirect so the retried turn sees a directive to break the repeated
1511
+ // pattern instead of re-sampling the same stalled reasoning.
1512
+ this.#maybeInjectThinkingLoopRedirect(id);
1513
+
1514
+ // Wait with exponential backoff (abortable).
1515
+ const retryAbortController = new AbortController();
1516
+ this.#retryAbortController?.abort();
1517
+ this.#retryAbortController = retryAbortController;
1518
+ try {
1519
+ await scheduler.wait(delayMs, { signal: retryAbortController.signal });
1520
+ } catch {
1521
+ if (this.#retryAbortController !== retryAbortController) {
1522
+ return false;
1523
+ }
1524
+ // Aborted during sleep - emit end event so UI can clean up
1525
+ const attempt = this.#retryAttempt;
1526
+ this.#retryAttempt = 0;
1527
+ this.#retryAbortController = undefined;
1528
+ await this.#host.emitSessionEvent({
1529
+ type: "auto_retry_end",
1530
+ success: false,
1531
+ attempt,
1532
+ finalError: "Retry cancelled",
1533
+ });
1534
+ this.#clearPendingRecoveredRetryErrors();
1535
+ this.resolveRetry();
1536
+ return false;
1537
+ }
1538
+ if (this.#retryAbortController === retryAbortController) {
1539
+ this.#retryAbortController = undefined;
1540
+ }
1541
+
1542
+ // The identity-keyed removal above can miss when a context rebuild
1543
+ // recreated the failed turn's message object between settle and retry
1544
+ // (fresh identity, same failed tail — issue #5382). Agent.continue()
1545
+ // rejects any assistant tail, so a missed removal fails the scheduled
1546
+ // retry locally before a provider request is ever made. Re-check the
1547
+ // tail after the backoff (covering rebuilds during the sleep too) and
1548
+ // strip a still-failed assistant tail by position. Never in
1549
+ // preserveFailedTurn mode — the kept turn ends in synthetic tool
1550
+ // results that continue() accepts — and never once a newer prompt owns
1551
+ // the session.
1552
+ if (!options?.preserveFailedTurn && this.#host.promptGeneration() === generation) {
1553
+ this.#stripFailedAssistantTail();
1554
+ }
1555
+
1556
+ // Retry via continue() outside the agent_end event callback chain. A
1557
+ // continuation that still fails locally must close the retry saga —
1558
+ // otherwise auto_retry_end never fires, retryPromise stays pending, and
1559
+ // the in-flight prompt() (and the TUI retry indicator) hang forever.
1560
+ this.#host.scheduleAgentContinue({
1561
+ delayMs: 1,
1562
+ generation,
1563
+ onError: error => void this.#failRetryAfterLocalContinueError(message, error),
1564
+ });
1565
+
1566
+ return true;
1567
+ }
1568
+
1569
+ /**
1570
+ * Positional backstop for {@link removeAssistantMessageFromActiveContext}:
1571
+ * when the identity check missed, the failed assistant turn is still the
1572
+ * active tail and the scheduled continue() would reject it. An
1573
+ * error/aborted-stopped assistant tail is never legal continuation input
1574
+ * and no recovery path wants it replayed on the wire, so drop it by
1575
+ * position; any healthy tail is left untouched.
1576
+ */
1577
+ #stripFailedAssistantTail(): void {
1578
+ const messages = this.#host.agent.state.messages;
1579
+ const tail = messages[messages.length - 1];
1580
+ if (tail?.role !== "assistant") return;
1581
+ if (tail.stopReason !== "error" && tail.stopReason !== "aborted") return;
1582
+ logger.debug("agent active context failed assistant tail stripped positionally", {
1583
+ stopReason: tail.stopReason,
1584
+ timestamp: tail.timestamp,
1585
+ });
1586
+ this.#host.agent.replaceMessages(messages.slice(0, -1));
1587
+ }
1588
+
1589
+ /**
1590
+ * Close the retry saga when the scheduled continue() failed locally (no
1591
+ * provider request was made). Mirrors the other retry dead-ends: emit the
1592
+ * closing `auto_retry_end` so subscribers stop showing retry progress, and
1593
+ * resolve the retry promise so the in-flight prompt() unwinds (issue #5382).
1594
+ */
1595
+ async #failRetryAfterLocalContinueError(message: AssistantMessage, error: unknown): Promise<void> {
1596
+ if (this.#retryAttempt === 0) return;
1597
+ const attempt = this.#retryAttempt;
1598
+ this.#retryAttempt = 0;
1599
+ const localError = error instanceof Error ? error.message : String(error);
1600
+ await this.persistTerminalEmptyErrorTurn(message);
1601
+ await this.#host.emitSessionEvent({
1602
+ type: "auto_retry_end",
1603
+ success: false,
1604
+ attempt,
1605
+ finalError: `Retry continuation failed locally: ${localError}. Original error: ${message.errorMessage ?? "Unknown error"}`,
1606
+ });
1607
+ this.#clearPendingRecoveredRetryErrors();
1608
+ this.resolveRetry();
1609
+ }
1610
+
1611
+ /**
1612
+ * Inject a hidden redirect notice when a thinking/response loop is being retried, so
1613
+ * the retried turn carries an instruction to break the repeated pattern instead of
1614
+ * re-sampling the same stalled context. Injected on every {@link AIError.Flag.ThinkingLoop}
1615
+ * retry (the failed assistant is dropped each attempt, so the notice does not accumulate
1616
+ * unboundedly). No-op unless `id` carries the ThinkingLoop flag and the loop guard is
1617
+ * enabled. The notice is generic on purpose — the detector's detail can quote raw model
1618
+ * text, which must not be interpolated into a higher-priority developer message.
1619
+ */
1620
+ #maybeInjectThinkingLoopRedirect(id: number): void {
1621
+ if (!AIError.is(id, AIError.Flag.ThinkingLoop)) return;
1622
+ if (this.#host.settings.get("model.loopGuard.enabled") !== true) return;
1623
+ this.#host.agent.appendMessage({
1624
+ role: "custom",
1625
+ customType: THINKING_LOOP_REDIRECT_TYPE,
1626
+ content: thinkingLoopRedirectTemplate,
1627
+ display: false,
1628
+ attribution: "agent",
1629
+ timestamp: Date.now(),
1630
+ });
1631
+ this.#host.sessionManager.appendCustomMessageEntry(
1632
+ THINKING_LOOP_REDIRECT_TYPE,
1633
+ thinkingLoopRedirectTemplate,
1634
+ false,
1635
+ undefined,
1636
+ "agent",
1637
+ );
1638
+ }
1639
+
1640
+ /**
1641
+ * Cancel in-progress retry.
1642
+ */
1643
+ abortRetry(): void {
1644
+ this.#retryAbortController?.abort();
1645
+ // Note: _retryAttempt is reset in the catch block of _autoRetry
1646
+ this.resolveRetry();
1647
+ }
1648
+
1649
+ async #promptAgentWithIdleRetry(messages: AgentMessage[], options?: { toolChoice?: ToolChoice }): Promise<void> {
1650
+ const deadline = Date.now() + 30_000;
1651
+ for (;;) {
1652
+ try {
1653
+ await this.#host.agent.prompt(messages, options);
1654
+ return;
1655
+ } catch (err) {
1656
+ if (!(err instanceof AgentBusyError)) {
1657
+ throw err;
1658
+ }
1659
+ if (Date.now() >= deadline) {
1660
+ throw new Error("Timed out waiting for prior agent run to finish before prompting.");
1661
+ }
1662
+ await this.#host.agent.waitForIdle();
1663
+ }
1664
+ }
1665
+ }
1666
+
1667
+ /** Whether auto-retry is currently in progress */
1668
+ get isRetrying(): boolean {
1669
+ return this.#retryPromise !== undefined;
1670
+ }
1671
+
1672
+ /** Whether auto-retry is enabled */
1673
+ get autoRetryEnabled(): boolean {
1674
+ return this.#host.settings.get("retry.enabled") ?? true;
1675
+ }
1676
+
1677
+ /**
1678
+ * Toggle auto-retry setting.
1679
+ */
1680
+ setAutoRetryEnabled(enabled: boolean): void {
1681
+ this.#host.settings.set("retry.enabled", enabled);
1682
+ }
1683
+ /**
1684
+ * Manually retry the last failed assistant turn.
1685
+ * Removes the error message from active agent state when present and
1686
+ * re-attempts with a fresh retry budget.
1687
+ *
1688
+ * A stream that stalls or aborts mid-tool-call ends the turn with
1689
+ * `stopReason: "error" | "aborted"` and then appends one synthetic
1690
+ * {@link isSyntheticToolResultMessage tool_result} per emitted tool call to
1691
+ * preserve the provider's tool_use/tool_result pairing (see
1692
+ * `createAbortedToolResult` in `agent-loop.ts`). Those placeholders trail the
1693
+ * failed assistant turn, so the retry lookback walks back over them before
1694
+ * checking the assistant message; it strips both the placeholders and the
1695
+ * failed turn before re-attempting.
1696
+ *
1697
+ * A restored session deliberately omits failed assistant turns from provider
1698
+ * context. In that case, the persisted display transcript remains the source
1699
+ * of truth for whether the current branch has a retryable failed tail.
1700
+ *
1701
+ * @returns true if retry was initiated, false if no failed turn to retry or agent is busy
1702
+ */
1703
+ async retry(): Promise<boolean> {
1704
+ if (this.#host.isStreaming() || this.#host.isCompacting() || this.isRetrying) return false;
1705
+
1706
+ const messages = this.#host.agent.state.messages;
1707
+ const activeTurnEnd = retryableAssistantTurnEnd(messages);
1708
+ if (activeTurnEnd !== undefined) {
1709
+ // Remove the failed/aborted assistant message plus its synthetic tool
1710
+ // results (same as auto-retry does before re-attempting).
1711
+ this.#host.agent.replaceMessages(messages.slice(0, activeTurnEnd - 1));
1712
+ } else {
1713
+ // A restored session already dropped the failed assistant turn (and its
1714
+ // paired synthetic tool results) from provider context, so the persisted
1715
+ // display transcript is the source of truth for a retryable failed tail.
1716
+ const transcriptMessages = this.#host.sessionManager.buildSessionContext({ transcript: true }).messages;
1717
+ if (retryableAssistantTurnEnd(transcriptMessages) === undefined) return false;
1718
+ }
1719
+
1720
+ // Reset retry budget for a fresh attempt
1721
+ this.#retryAttempt = 0;
1722
+
1723
+ // Re-attempt the turn
1724
+ this.#host.scheduleAgentContinue({ delayMs: 1 });
1725
+
1726
+ return true;
1727
+ }
1728
+ }