oma-coding-agent 1.1.4

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 (1298) hide show
  1. package/CHANGELOG.md +12164 -0
  2. package/README.md +35 -0
  3. package/dist/cli.js +18266 -0
  4. package/examples/README.md +21 -0
  5. package/examples/custom-tools/README.md +104 -0
  6. package/examples/custom-tools/hello/index.ts +20 -0
  7. package/examples/extensions/README.md +142 -0
  8. package/examples/extensions/api-demo.ts +79 -0
  9. package/examples/extensions/chalk-logger.ts +25 -0
  10. package/examples/extensions/hello.ts +31 -0
  11. package/examples/extensions/pirate.ts +43 -0
  12. package/examples/extensions/plan-mode.ts +549 -0
  13. package/examples/extensions/reload-runtime.ts +38 -0
  14. package/examples/extensions/thinking-note.ts +13 -0
  15. package/examples/extensions/tools.ts +145 -0
  16. package/examples/extensions/with-deps/index.ts +36 -0
  17. package/examples/extensions/with-deps/package-lock.json +31 -0
  18. package/examples/extensions/with-deps/package.json +17 -0
  19. package/examples/hooks/README.md +56 -0
  20. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  21. package/examples/hooks/confirm-destructive.ts +58 -0
  22. package/examples/hooks/custom-compaction.ts +115 -0
  23. package/examples/hooks/dirty-repo-guard.ts +51 -0
  24. package/examples/hooks/file-trigger.ts +40 -0
  25. package/examples/hooks/git-checkpoint.ts +52 -0
  26. package/examples/hooks/handoff.ts +149 -0
  27. package/examples/hooks/permission-gate.ts +33 -0
  28. package/examples/hooks/protected-paths.ts +29 -0
  29. package/examples/hooks/qna.ts +118 -0
  30. package/examples/hooks/status-line.ts +39 -0
  31. package/examples/sdk/01-minimal.ts +21 -0
  32. package/examples/sdk/02-custom-model.ts +49 -0
  33. package/examples/sdk/03-custom-prompt.ts +46 -0
  34. package/examples/sdk/04-skills.ts +43 -0
  35. package/examples/sdk/06-extensions.ts +82 -0
  36. package/examples/sdk/06-hooks.ts +61 -0
  37. package/examples/sdk/07-context-files.ts +35 -0
  38. package/examples/sdk/08-prompt-templates.ts +41 -0
  39. package/examples/sdk/08-slash-commands.ts +46 -0
  40. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  41. package/examples/sdk/11-sessions.ts +47 -0
  42. package/examples/sdk/12-redis-sessions.ts +54 -0
  43. package/examples/sdk/13-sql-sessions.ts +61 -0
  44. package/examples/sdk/README.md +172 -0
  45. package/package.json +573 -0
  46. package/scripts/bench-guard.ts +71 -0
  47. package/scripts/build-binary.ts +108 -0
  48. package/scripts/bundle-dist.ts +110 -0
  49. package/scripts/embed-mupdf-wasm.ts +67 -0
  50. package/scripts/format-prompts.ts +68 -0
  51. package/scripts/generate-docs-index.ts +56 -0
  52. package/scripts/generate-share-viewer.ts +34 -0
  53. package/scripts/measure-prompt-tokens.ts +63 -0
  54. package/scripts/omp +42 -0
  55. package/scripts/omp.ts +19 -0
  56. package/src/advisor/__tests__/advisor.test.ts +915 -0
  57. package/src/advisor/advise-tool.ts +165 -0
  58. package/src/advisor/index.ts +4 -0
  59. package/src/advisor/runtime.ts +270 -0
  60. package/src/advisor/transcript-recorder.ts +136 -0
  61. package/src/advisor/watchdog.ts +83 -0
  62. package/src/async/index.ts +1 -0
  63. package/src/async/job-manager.ts +674 -0
  64. package/src/auto-thinking/classifier.ts +190 -0
  65. package/src/autolearn/controller.ts +139 -0
  66. package/src/autolearn/managed-skills.ts +255 -0
  67. package/src/autoresearch/command-resume.md +14 -0
  68. package/src/autoresearch/dashboard.ts +436 -0
  69. package/src/autoresearch/git.ts +319 -0
  70. package/src/autoresearch/helpers.ts +218 -0
  71. package/src/autoresearch/index.ts +536 -0
  72. package/src/autoresearch/prompt-setup.md +43 -0
  73. package/src/autoresearch/prompt.md +103 -0
  74. package/src/autoresearch/resume-message.md +10 -0
  75. package/src/autoresearch/state.ts +273 -0
  76. package/src/autoresearch/storage.ts +700 -0
  77. package/src/autoresearch/tools/init-experiment.ts +269 -0
  78. package/src/autoresearch/tools/log-experiment.ts +521 -0
  79. package/src/autoresearch/tools/run-experiment.ts +407 -0
  80. package/src/autoresearch/tools/update-notes.ts +109 -0
  81. package/src/autoresearch/types.ts +168 -0
  82. package/src/capability/context-file.ts +44 -0
  83. package/src/capability/extension-module.ts +34 -0
  84. package/src/capability/extension.ts +47 -0
  85. package/src/capability/fs.ts +117 -0
  86. package/src/capability/hook.ts +40 -0
  87. package/src/capability/index.ts +436 -0
  88. package/src/capability/instruction.ts +37 -0
  89. package/src/capability/mcp.ts +76 -0
  90. package/src/capability/prompt.ts +35 -0
  91. package/src/capability/rule-buckets.ts +66 -0
  92. package/src/capability/rule.ts +261 -0
  93. package/src/capability/settings.ts +34 -0
  94. package/src/capability/skill.ts +63 -0
  95. package/src/capability/slash-command.ts +40 -0
  96. package/src/capability/ssh.ts +41 -0
  97. package/src/capability/system-prompt.ts +34 -0
  98. package/src/capability/tool.ts +38 -0
  99. package/src/capability/types.ts +168 -0
  100. package/src/cli/agents-cli.ts +138 -0
  101. package/src/cli/args.ts +361 -0
  102. package/src/cli/auth-broker-cli.ts +893 -0
  103. package/src/cli/auth-gateway-cli.ts +608 -0
  104. package/src/cli/bench-cli.ts +552 -0
  105. package/src/cli/classify-install-target.ts +76 -0
  106. package/src/cli/claude-trace-cli.ts +795 -0
  107. package/src/cli/commands/init-xdg.ts +27 -0
  108. package/src/cli/completion-gen.ts +550 -0
  109. package/src/cli/config-cli.ts +418 -0
  110. package/src/cli/dry-balance-cli.ts +858 -0
  111. package/src/cli/extension-flags.ts +48 -0
  112. package/src/cli/file-processor.ts +133 -0
  113. package/src/cli/flag-tables.ts +280 -0
  114. package/src/cli/gallery-cli.ts +231 -0
  115. package/src/cli/gallery-fixtures/agentic.ts +407 -0
  116. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  117. package/src/cli/gallery-fixtures/edit.ts +194 -0
  118. package/src/cli/gallery-fixtures/fs.ts +220 -0
  119. package/src/cli/gallery-fixtures/index.ts +40 -0
  120. package/src/cli/gallery-fixtures/interaction.ts +49 -0
  121. package/src/cli/gallery-fixtures/memory.ts +81 -0
  122. package/src/cli/gallery-fixtures/misc.ts +250 -0
  123. package/src/cli/gallery-fixtures/search.ts +213 -0
  124. package/src/cli/gallery-fixtures/shell.ts +167 -0
  125. package/src/cli/gallery-fixtures/types.ts +57 -0
  126. package/src/cli/gallery-fixtures/web.ts +158 -0
  127. package/src/cli/gallery-screenshot.ts +279 -0
  128. package/src/cli/grep-cli.ts +160 -0
  129. package/src/cli/grievances-cli.ts +256 -0
  130. package/src/cli/initial-message.ts +58 -0
  131. package/src/cli/models-cli.ts +427 -0
  132. package/src/cli/plugin-cli.ts +996 -0
  133. package/src/cli/profile-alias.ts +338 -0
  134. package/src/cli/profile-bootstrap.ts +243 -0
  135. package/src/cli/read-cli.ts +57 -0
  136. package/src/cli/session-picker.ts +80 -0
  137. package/src/cli/setup-cli.ts +332 -0
  138. package/src/cli/setup-model-picker.ts +43 -0
  139. package/src/cli/shell-cli.ts +176 -0
  140. package/src/cli/ssh-cli.ts +179 -0
  141. package/src/cli/startup-cwd.ts +58 -0
  142. package/src/cli/stats-cli.ts +229 -0
  143. package/src/cli/tiny-models-cli.ts +127 -0
  144. package/src/cli/ttsr-cli.ts +995 -0
  145. package/src/cli/update-cli.ts +671 -0
  146. package/src/cli/usage-cli.ts +774 -0
  147. package/src/cli/web-search-cli.ts +132 -0
  148. package/src/cli/worktree-cli.ts +291 -0
  149. package/src/cli-commands.ts +85 -0
  150. package/src/cli.ts +326 -0
  151. package/src/collab/crypto.ts +63 -0
  152. package/src/collab/guest.ts +450 -0
  153. package/src/collab/host.ts +577 -0
  154. package/src/collab/protocol.ts +274 -0
  155. package/src/collab/relay-client.ts +216 -0
  156. package/src/commands/acp.ts +24 -0
  157. package/src/commands/agents.ts +57 -0
  158. package/src/commands/auth-broker.ts +99 -0
  159. package/src/commands/auth-gateway.ts +69 -0
  160. package/src/commands/bench.ts +42 -0
  161. package/src/commands/commit.ts +46 -0
  162. package/src/commands/complete.ts +66 -0
  163. package/src/commands/completions.ts +60 -0
  164. package/src/commands/config.ts +51 -0
  165. package/src/commands/dry-balance.ts +43 -0
  166. package/src/commands/gallery.ts +52 -0
  167. package/src/commands/grep.ts +48 -0
  168. package/src/commands/grievances.ts +51 -0
  169. package/src/commands/install.ts +107 -0
  170. package/src/commands/join.ts +39 -0
  171. package/src/commands/launch.ts +182 -0
  172. package/src/commands/models.ts +61 -0
  173. package/src/commands/plugin.ts +78 -0
  174. package/src/commands/read.ts +38 -0
  175. package/src/commands/say.ts +102 -0
  176. package/src/commands/setup.ts +67 -0
  177. package/src/commands/shell.ts +29 -0
  178. package/src/commands/ssh.ts +60 -0
  179. package/src/commands/stats.ts +29 -0
  180. package/src/commands/tiny-models.ts +36 -0
  181. package/src/commands/token.ts +108 -0
  182. package/src/commands/ttsr.ts +125 -0
  183. package/src/commands/update.ts +21 -0
  184. package/src/commands/usage.ts +43 -0
  185. package/src/commands/web-search.ts +42 -0
  186. package/src/commands/worktree.ts +56 -0
  187. package/src/commit/agentic/agent.ts +318 -0
  188. package/src/commit/agentic/fallback.ts +96 -0
  189. package/src/commit/agentic/index.ts +355 -0
  190. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  191. package/src/commit/agentic/prompts/session-user.md +25 -0
  192. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  193. package/src/commit/agentic/prompts/system.md +38 -0
  194. package/src/commit/agentic/state.ts +60 -0
  195. package/src/commit/agentic/tools/analyze-file.ts +149 -0
  196. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  197. package/src/commit/agentic/tools/git-hunk.ts +52 -0
  198. package/src/commit/agentic/tools/git-overview.ts +81 -0
  199. package/src/commit/agentic/tools/index.ts +54 -0
  200. package/src/commit/agentic/tools/propose-changelog.ts +147 -0
  201. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  202. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  203. package/src/commit/agentic/tools/schemas.ts +11 -0
  204. package/src/commit/agentic/tools/split-commit.ts +241 -0
  205. package/src/commit/agentic/topo-sort.ts +44 -0
  206. package/src/commit/agentic/trivial.ts +51 -0
  207. package/src/commit/agentic/validation.ts +183 -0
  208. package/src/commit/analysis/conventional.ts +64 -0
  209. package/src/commit/analysis/index.ts +4 -0
  210. package/src/commit/analysis/scope.ts +242 -0
  211. package/src/commit/analysis/summary.ts +107 -0
  212. package/src/commit/analysis/validation.ts +66 -0
  213. package/src/commit/changelog/detect.ts +40 -0
  214. package/src/commit/changelog/generate.ts +101 -0
  215. package/src/commit/changelog/index.ts +234 -0
  216. package/src/commit/changelog/parse.ts +44 -0
  217. package/src/commit/cli.ts +85 -0
  218. package/src/commit/git/diff.ts +148 -0
  219. package/src/commit/index.ts +5 -0
  220. package/src/commit/map-reduce/index.ts +69 -0
  221. package/src/commit/map-reduce/map-phase.ts +193 -0
  222. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  223. package/src/commit/map-reduce/utils.ts +9 -0
  224. package/src/commit/message.ts +11 -0
  225. package/src/commit/model-selection.ts +89 -0
  226. package/src/commit/pipeline.ts +243 -0
  227. package/src/commit/prompts/analysis-system.md +148 -0
  228. package/src/commit/prompts/analysis-user.md +38 -0
  229. package/src/commit/prompts/changelog-system.md +50 -0
  230. package/src/commit/prompts/changelog-user.md +18 -0
  231. package/src/commit/prompts/file-observer-system.md +24 -0
  232. package/src/commit/prompts/file-observer-user.md +8 -0
  233. package/src/commit/prompts/reduce-system.md +50 -0
  234. package/src/commit/prompts/reduce-user.md +17 -0
  235. package/src/commit/prompts/summary-retry.md +3 -0
  236. package/src/commit/prompts/summary-system.md +38 -0
  237. package/src/commit/prompts/summary-user.md +13 -0
  238. package/src/commit/prompts/types-description.md +2 -0
  239. package/src/commit/shared-llm.ts +70 -0
  240. package/src/commit/types.ts +118 -0
  241. package/src/commit/utils/exclusions.ts +42 -0
  242. package/src/commit/utils.ts +58 -0
  243. package/src/config/api-key-resolver.ts +67 -0
  244. package/src/config/append-only-context-mode.ts +76 -0
  245. package/src/config/config-file.ts +315 -0
  246. package/src/config/file-lock.ts +164 -0
  247. package/src/config/keybindings.ts +634 -0
  248. package/src/config/mcp-schema.json +238 -0
  249. package/src/config/model-discovery.ts +589 -0
  250. package/src/config/model-registry.ts +2260 -0
  251. package/src/config/model-resolver.ts +1819 -0
  252. package/src/config/model-roles.ts +99 -0
  253. package/src/config/models-config-schema.ts +266 -0
  254. package/src/config/models-config.ts +131 -0
  255. package/src/config/prompt-templates.ts +185 -0
  256. package/src/config/resolve-config-value.ts +94 -0
  257. package/src/config/settings-schema.ts +4740 -0
  258. package/src/config/settings.ts +1243 -0
  259. package/src/config.ts +242 -0
  260. package/src/cursor.ts +340 -0
  261. package/src/dap/client.ts +760 -0
  262. package/src/dap/config.ts +189 -0
  263. package/src/dap/defaults.json +212 -0
  264. package/src/dap/index.ts +4 -0
  265. package/src/dap/session.ts +1441 -0
  266. package/src/dap/types.ts +610 -0
  267. package/src/debug/index.ts +559 -0
  268. package/src/debug/log-formatting.ts +58 -0
  269. package/src/debug/log-viewer.ts +908 -0
  270. package/src/debug/profiler.ts +162 -0
  271. package/src/debug/protocol-probe.ts +267 -0
  272. package/src/debug/raw-sse-buffer.ts +294 -0
  273. package/src/debug/raw-sse.ts +292 -0
  274. package/src/debug/remote-debugger.ts +151 -0
  275. package/src/debug/report-bundle.ts +375 -0
  276. package/src/debug/system-info.ts +111 -0
  277. package/src/debug/terminal-info.ts +124 -0
  278. package/src/discovery/agents-md.ts +67 -0
  279. package/src/discovery/agents.ts +230 -0
  280. package/src/discovery/at-imports.ts +273 -0
  281. package/src/discovery/builtin-defaults.ts +39 -0
  282. package/src/discovery/builtin-rules/index.ts +63 -0
  283. package/src/discovery/builtin-rules/low-end/no-hallucinated-apis.md +14 -0
  284. package/src/discovery/builtin-rules/low-end/no-hallucinated-paths.md +14 -0
  285. package/src/discovery/builtin-rules/low-end/no-premature-completion.md +14 -0
  286. package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
  287. package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
  288. package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
  289. package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
  290. package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
  291. package/src/discovery/builtin-rules/rs-result-type.md +19 -0
  292. package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
  293. package/src/discovery/builtin-rules/ts-import-type.md +42 -0
  294. package/src/discovery/builtin-rules/ts-no-any.md +65 -0
  295. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +44 -0
  296. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
  297. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
  298. package/src/discovery/builtin-rules/ts-no-return-type.md +44 -0
  299. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  300. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  301. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
  302. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  303. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  304. package/src/discovery/builtin.ts +934 -0
  305. package/src/discovery/claude-plugins.ts +386 -0
  306. package/src/discovery/claude.ts +584 -0
  307. package/src/discovery/cline.ts +83 -0
  308. package/src/discovery/codex.ts +522 -0
  309. package/src/discovery/cursor.ts +220 -0
  310. package/src/discovery/gemini.ts +383 -0
  311. package/src/discovery/github.ts +337 -0
  312. package/src/discovery/helpers.ts +1092 -0
  313. package/src/discovery/index.ts +81 -0
  314. package/src/discovery/mcp-json.ts +172 -0
  315. package/src/discovery/omp-extension-roots.ts +190 -0
  316. package/src/discovery/omp-plugins.ts +383 -0
  317. package/src/discovery/opencode.ts +398 -0
  318. package/src/discovery/plugin-dir-roots.ts +28 -0
  319. package/src/discovery/ssh.ts +153 -0
  320. package/src/discovery/substitute-plugin-root.ts +29 -0
  321. package/src/discovery/vscode.ts +105 -0
  322. package/src/discovery/windsurf.ts +147 -0
  323. package/src/edit/apply-patch/index.ts +87 -0
  324. package/src/edit/apply-patch/parser.ts +174 -0
  325. package/src/edit/diff.ts +999 -0
  326. package/src/edit/file-snapshot-store.ts +143 -0
  327. package/src/edit/hashline/block-resolver.ts +33 -0
  328. package/src/edit/hashline/diff.ts +290 -0
  329. package/src/edit/hashline/execute.ts +237 -0
  330. package/src/edit/hashline/filesystem.ts +130 -0
  331. package/src/edit/hashline/index.ts +5 -0
  332. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  333. package/src/edit/hashline/params.ts +19 -0
  334. package/src/edit/index.ts +620 -0
  335. package/src/edit/modes/apply-patch.lark +19 -0
  336. package/src/edit/modes/apply-patch.ts +53 -0
  337. package/src/edit/modes/patch.ts +1888 -0
  338. package/src/edit/modes/replace.ts +1133 -0
  339. package/src/edit/normalize.ts +345 -0
  340. package/src/edit/notebook.ts +242 -0
  341. package/src/edit/read-file.ts +25 -0
  342. package/src/edit/renderer.ts +823 -0
  343. package/src/edit/streaming.ts +517 -0
  344. package/src/eval/__tests__/agent-bridge.test.ts +769 -0
  345. package/src/eval/__tests__/bridge-timeout.test.ts +64 -0
  346. package/src/eval/__tests__/budget-bridge.test.ts +69 -0
  347. package/src/eval/__tests__/completion-bridge.test.ts +412 -0
  348. package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
  349. package/src/eval/__tests__/idle-timeout.test.ts +80 -0
  350. package/src/eval/__tests__/js-context-manager.test.ts +291 -0
  351. package/src/eval/__tests__/kernel-spawn.test.ts +103 -0
  352. package/src/eval/__tests__/prelude-agent.test.ts +73 -0
  353. package/src/eval/agent-bridge.ts +319 -0
  354. package/src/eval/backend.ts +71 -0
  355. package/src/eval/bridge-timeout.ts +44 -0
  356. package/src/eval/budget-bridge.ts +48 -0
  357. package/src/eval/completion-bridge.ts +211 -0
  358. package/src/eval/concurrency-bridge.ts +34 -0
  359. package/src/eval/idle-timeout.ts +91 -0
  360. package/src/eval/index.ts +4 -0
  361. package/src/eval/js/context-manager.ts +621 -0
  362. package/src/eval/js/executor.ts +173 -0
  363. package/src/eval/js/index.ts +51 -0
  364. package/src/eval/js/shared/helpers.ts +283 -0
  365. package/src/eval/js/shared/indirect-eval.ts +30 -0
  366. package/src/eval/js/shared/local-module-loader.ts +342 -0
  367. package/src/eval/js/shared/prelude.ts +2 -0
  368. package/src/eval/js/shared/prelude.txt +307 -0
  369. package/src/eval/js/shared/rewrite-imports.ts +532 -0
  370. package/src/eval/js/shared/runtime.ts +580 -0
  371. package/src/eval/js/shared/types.ts +18 -0
  372. package/src/eval/js/tool-bridge.ts +163 -0
  373. package/src/eval/js/worker-core.ts +151 -0
  374. package/src/eval/js/worker-entry.ts +37 -0
  375. package/src/eval/js/worker-protocol.ts +47 -0
  376. package/src/eval/py/__tests__/prelude.test.ts +19 -0
  377. package/src/eval/py/display.ts +71 -0
  378. package/src/eval/py/executor.ts +742 -0
  379. package/src/eval/py/index.ts +68 -0
  380. package/src/eval/py/kernel.ts +748 -0
  381. package/src/eval/py/prelude.py +683 -0
  382. package/src/eval/py/prelude.ts +3 -0
  383. package/src/eval/py/runner.py +1177 -0
  384. package/src/eval/py/runtime.ts +276 -0
  385. package/src/eval/py/spawn-options.ts +126 -0
  386. package/src/eval/py/tool-bridge.ts +182 -0
  387. package/src/eval/session-id.ts +8 -0
  388. package/src/eval/types.ts +48 -0
  389. package/src/exa/index.ts +2 -0
  390. package/src/exa/mcp-client.ts +370 -0
  391. package/src/exa/types.ts +69 -0
  392. package/src/exec/bash-executor.ts +434 -0
  393. package/src/exec/exec.ts +53 -0
  394. package/src/exec/non-interactive-env.ts +119 -0
  395. package/src/export/custom-share.ts +65 -0
  396. package/src/export/html/index.ts +266 -0
  397. package/src/export/html/share-loader.js +102 -0
  398. package/src/export/html/template.css +1337 -0
  399. package/src/export/html/template.html +49 -0
  400. package/src/export/html/template.js +1626 -0
  401. package/src/export/html/tool-views.generated.js +37 -0
  402. package/src/export/html/vendor/highlight.min.js +1213 -0
  403. package/src/export/html/vendor/marked.min.js +6 -0
  404. package/src/export/share.ts +268 -0
  405. package/src/export/ttsr.ts +583 -0
  406. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  407. package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
  408. package/src/extensibility/custom-commands/index.ts +2 -0
  409. package/src/extensibility/custom-commands/loader.ts +242 -0
  410. package/src/extensibility/custom-commands/types.ts +119 -0
  411. package/src/extensibility/custom-tools/index.ts +7 -0
  412. package/src/extensibility/custom-tools/loader.ts +268 -0
  413. package/src/extensibility/custom-tools/types.ts +277 -0
  414. package/src/extensibility/custom-tools/wrapper.ts +47 -0
  415. package/src/extensibility/extensions/compact-handler.ts +40 -0
  416. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  417. package/src/extensibility/extensions/index.ts +16 -0
  418. package/src/extensibility/extensions/loader.ts +587 -0
  419. package/src/extensibility/extensions/model-api.ts +41 -0
  420. package/src/extensibility/extensions/runner.ts +989 -0
  421. package/src/extensibility/extensions/types.ts +1394 -0
  422. package/src/extensibility/extensions/wrapper.ts +259 -0
  423. package/src/extensibility/hooks/index.ts +6 -0
  424. package/src/extensibility/hooks/loader.ts +262 -0
  425. package/src/extensibility/hooks/runner.ts +425 -0
  426. package/src/extensibility/hooks/tool-wrapper.ts +107 -0
  427. package/src/extensibility/hooks/types.ts +613 -0
  428. package/src/extensibility/legacy-pi-ai-shim.ts +61 -0
  429. package/src/extensibility/legacy-pi-coding-agent-shim.ts +128 -0
  430. package/src/extensibility/plugins/doctor.ts +65 -0
  431. package/src/extensibility/plugins/git-url.ts +367 -0
  432. package/src/extensibility/plugins/index.ts +9 -0
  433. package/src/extensibility/plugins/installer.ts +192 -0
  434. package/src/extensibility/plugins/legacy-pi-compat.ts +712 -0
  435. package/src/extensibility/plugins/loader.ts +458 -0
  436. package/src/extensibility/plugins/manager.ts +1026 -0
  437. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  438. package/src/extensibility/plugins/marketplace/fetcher.ts +315 -0
  439. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  440. package/src/extensibility/plugins/marketplace/manager.ts +770 -0
  441. package/src/extensibility/plugins/marketplace/registry.ts +196 -0
  442. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  443. package/src/extensibility/plugins/marketplace/types.ts +191 -0
  444. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  445. package/src/extensibility/plugins/parser.ts +105 -0
  446. package/src/extensibility/plugins/runtime-config.ts +9 -0
  447. package/src/extensibility/plugins/types.ts +194 -0
  448. package/src/extensibility/shared-events.ts +367 -0
  449. package/src/extensibility/skills.ts +408 -0
  450. package/src/extensibility/slash-commands.ts +131 -0
  451. package/src/extensibility/tool-proxy.ts +28 -0
  452. package/src/extensibility/typebox.ts +945 -0
  453. package/src/extensibility/utils.ts +44 -0
  454. package/src/goals/guided-setup.ts +142 -0
  455. package/src/goals/index.ts +3 -0
  456. package/src/goals/runtime.ts +521 -0
  457. package/src/goals/state.ts +37 -0
  458. package/src/goals/tools/goal-tool.ts +251 -0
  459. package/src/hindsight/backend.ts +354 -0
  460. package/src/hindsight/bank.ts +156 -0
  461. package/src/hindsight/client.ts +623 -0
  462. package/src/hindsight/config.ts +175 -0
  463. package/src/hindsight/content.ts +210 -0
  464. package/src/hindsight/index.ts +8 -0
  465. package/src/hindsight/mental-models.ts +429 -0
  466. package/src/hindsight/seeds.json +32 -0
  467. package/src/hindsight/state.ts +492 -0
  468. package/src/hindsight/transcript.ts +71 -0
  469. package/src/index.ts +66 -0
  470. package/src/internal-urls/agent-protocol.ts +146 -0
  471. package/src/internal-urls/artifact-protocol.ts +107 -0
  472. package/src/internal-urls/docs-index.generated.txt +2 -0
  473. package/src/internal-urls/docs-index.ts +102 -0
  474. package/src/internal-urls/history-protocol.ts +118 -0
  475. package/src/internal-urls/index.ts +25 -0
  476. package/src/internal-urls/issue-pr-protocol.ts +594 -0
  477. package/src/internal-urls/json-query.ts +126 -0
  478. package/src/internal-urls/local-protocol.ts +309 -0
  479. package/src/internal-urls/mcp-protocol.ts +151 -0
  480. package/src/internal-urls/memory-protocol.ts +169 -0
  481. package/src/internal-urls/omp-protocol.ts +94 -0
  482. package/src/internal-urls/parse.ts +72 -0
  483. package/src/internal-urls/registry-helpers.ts +25 -0
  484. package/src/internal-urls/router.ts +105 -0
  485. package/src/internal-urls/rule-protocol.ts +45 -0
  486. package/src/internal-urls/skill-protocol.ts +96 -0
  487. package/src/internal-urls/types.ts +152 -0
  488. package/src/internal-urls/vault-protocol.ts +936 -0
  489. package/src/irc/bus.ts +311 -0
  490. package/src/lib/xai-http.ts +124 -0
  491. package/src/lsp/client.ts +1217 -0
  492. package/src/lsp/clients/biome-client.ts +264 -0
  493. package/src/lsp/clients/index.ts +50 -0
  494. package/src/lsp/clients/lsp-linter-client.ts +85 -0
  495. package/src/lsp/clients/swiftlint-client.ts +120 -0
  496. package/src/lsp/config.ts +502 -0
  497. package/src/lsp/defaults.json +499 -0
  498. package/src/lsp/diagnostics-ledger.ts +51 -0
  499. package/src/lsp/edits.ts +267 -0
  500. package/src/lsp/format-options.ts +119 -0
  501. package/src/lsp/index.ts +2480 -0
  502. package/src/lsp/lspmux.ts +233 -0
  503. package/src/lsp/render.ts +668 -0
  504. package/src/lsp/startup-events.ts +13 -0
  505. package/src/lsp/types.ts +444 -0
  506. package/src/lsp/utils.ts +718 -0
  507. package/src/main.ts +1421 -0
  508. package/src/markit/NOTICE +32 -0
  509. package/src/markit/converters/docx.ts +56 -0
  510. package/src/markit/converters/epub.ts +136 -0
  511. package/src/markit/converters/mammoth.d.ts +24 -0
  512. package/src/markit/converters/pdf/columns.ts +103 -0
  513. package/src/markit/converters/pdf/extract.ts +574 -0
  514. package/src/markit/converters/pdf/grid.ts +780 -0
  515. package/src/markit/converters/pdf/headers.ts +106 -0
  516. package/src/markit/converters/pdf/index.ts +146 -0
  517. package/src/markit/converters/pdf/render.ts +501 -0
  518. package/src/markit/converters/pdf/types.ts +84 -0
  519. package/src/markit/converters/pptx.ts +325 -0
  520. package/src/markit/converters/xlsx.ts +173 -0
  521. package/src/markit/index.ts +2 -0
  522. package/src/markit/registry.ts +59 -0
  523. package/src/markit/types.ts +35 -0
  524. package/src/mcp/client.ts +509 -0
  525. package/src/mcp/config-writer.ts +229 -0
  526. package/src/mcp/config.ts +365 -0
  527. package/src/mcp/index.ts +29 -0
  528. package/src/mcp/json-rpc.ts +122 -0
  529. package/src/mcp/loader.ts +124 -0
  530. package/src/mcp/manager.ts +1326 -0
  531. package/src/mcp/oauth-credentials.ts +104 -0
  532. package/src/mcp/oauth-discovery.ts +467 -0
  533. package/src/mcp/oauth-flow.ts +555 -0
  534. package/src/mcp/render.ts +155 -0
  535. package/src/mcp/smithery-auth.ts +104 -0
  536. package/src/mcp/smithery-connect.ts +145 -0
  537. package/src/mcp/smithery-registry.ts +477 -0
  538. package/src/mcp/startup-events.ts +21 -0
  539. package/src/mcp/timeout.ts +59 -0
  540. package/src/mcp/tool-bridge.ts +429 -0
  541. package/src/mcp/tool-cache.ts +117 -0
  542. package/src/mcp/transports/http.ts +519 -0
  543. package/src/mcp/transports/index.ts +6 -0
  544. package/src/mcp/transports/stdio.ts +606 -0
  545. package/src/mcp/types.ts +427 -0
  546. package/src/memories/index.ts +1281 -0
  547. package/src/memories/storage.ts +578 -0
  548. package/src/memory-backend/index.ts +18 -0
  549. package/src/memory-backend/local-backend.ts +45 -0
  550. package/src/memory-backend/off-backend.ts +25 -0
  551. package/src/memory-backend/resolve.ts +25 -0
  552. package/src/memory-backend/runtime.ts +66 -0
  553. package/src/memory-backend/types.ts +166 -0
  554. package/src/mnemopi/backend.ts +612 -0
  555. package/src/mnemopi/config.ts +265 -0
  556. package/src/mnemopi/embed-client.ts +401 -0
  557. package/src/mnemopi/embed-protocol.ts +35 -0
  558. package/src/mnemopi/embed-worker.ts +113 -0
  559. package/src/mnemopi/index.ts +3 -0
  560. package/src/mnemopi/state.ts +657 -0
  561. package/src/modes/acp/acp-agent.ts +2362 -0
  562. package/src/modes/acp/acp-client-bridge.ts +154 -0
  563. package/src/modes/acp/acp-event-mapper.ts +933 -0
  564. package/src/modes/acp/acp-mode.ts +23 -0
  565. package/src/modes/acp/index.ts +2 -0
  566. package/src/modes/acp/terminal-auth.ts +37 -0
  567. package/src/modes/components/__tests__/skill-message.test.ts +92 -0
  568. package/src/modes/components/advisor-message.ts +99 -0
  569. package/src/modes/components/agent-dashboard.ts +1206 -0
  570. package/src/modes/components/agent-hub.ts +566 -0
  571. package/src/modes/components/agent-transcript-viewer.ts +461 -0
  572. package/src/modes/components/assistant-message.ts +612 -0
  573. package/src/modes/components/background-tan-message.ts +36 -0
  574. package/src/modes/components/bash-execution.ts +220 -0
  575. package/src/modes/components/bordered-loader.ts +41 -0
  576. package/src/modes/components/btw-panel.ts +112 -0
  577. package/src/modes/components/cache-invalidation-marker.ts +110 -0
  578. package/src/modes/components/chat-block.ts +111 -0
  579. package/src/modes/components/chat-transcript-builder.ts +476 -0
  580. package/src/modes/components/collab-prompt-message.ts +32 -0
  581. package/src/modes/components/compaction-summary-message.ts +215 -0
  582. package/src/modes/components/copy-selector.ts +206 -0
  583. package/src/modes/components/countdown-timer.ts +75 -0
  584. package/src/modes/components/custom-editor.test.ts +142 -0
  585. package/src/modes/components/custom-editor.ts +620 -0
  586. package/src/modes/components/custom-message.ts +67 -0
  587. package/src/modes/components/diff.ts +254 -0
  588. package/src/modes/components/dynamic-border.ts +34 -0
  589. package/src/modes/components/error-banner.ts +33 -0
  590. package/src/modes/components/eval-execution.ts +158 -0
  591. package/src/modes/components/execution-shared.ts +101 -0
  592. package/src/modes/components/extensions/extension-dashboard.ts +399 -0
  593. package/src/modes/components/extensions/extension-list.ts +502 -0
  594. package/src/modes/components/extensions/index.ts +9 -0
  595. package/src/modes/components/extensions/inspector-panel.ts +321 -0
  596. package/src/modes/components/extensions/state-manager.ts +627 -0
  597. package/src/modes/components/extensions/types.ts +186 -0
  598. package/src/modes/components/footer.ts +275 -0
  599. package/src/modes/components/history-search.ts +280 -0
  600. package/src/modes/components/hook-editor.ts +167 -0
  601. package/src/modes/components/hook-input.ts +87 -0
  602. package/src/modes/components/hook-message.ts +67 -0
  603. package/src/modes/components/hook-selector.ts +659 -0
  604. package/src/modes/components/index.ts +38 -0
  605. package/src/modes/components/keybinding-hints.ts +65 -0
  606. package/src/modes/components/late-diagnostics-message.ts +60 -0
  607. package/src/modes/components/login-dialog.ts +164 -0
  608. package/src/modes/components/logout-account-selector.ts +130 -0
  609. package/src/modes/components/mcp-add-wizard.ts +1360 -0
  610. package/src/modes/components/message-frame.ts +92 -0
  611. package/src/modes/components/model-selector.ts +1315 -0
  612. package/src/modes/components/oauth-selector.ts +457 -0
  613. package/src/modes/components/omfg-panel.ts +141 -0
  614. package/src/modes/components/overlay-box.ts +109 -0
  615. package/src/modes/components/plan-review-overlay.ts +847 -0
  616. package/src/modes/components/plan-toc.ts +138 -0
  617. package/src/modes/components/plugin-selector.ts +95 -0
  618. package/src/modes/components/plugin-settings.ts +739 -0
  619. package/src/modes/components/queue-mode-selector.ts +56 -0
  620. package/src/modes/components/read-tool-group.ts +676 -0
  621. package/src/modes/components/reset-usage-selector.ts +161 -0
  622. package/src/modes/components/segment-track.ts +89 -0
  623. package/src/modes/components/session-selector.ts +631 -0
  624. package/src/modes/components/settings-defs.ts +225 -0
  625. package/src/modes/components/settings-selector.ts +1095 -0
  626. package/src/modes/components/show-images-selector.ts +45 -0
  627. package/src/modes/components/skill-message.ts +110 -0
  628. package/src/modes/components/snapcompact-shape-preview-doc.md +18 -0
  629. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  630. package/src/modes/components/status-line/component.ts +1001 -0
  631. package/src/modes/components/status-line/context-thresholds.ts +78 -0
  632. package/src/modes/components/status-line/git-utils.ts +42 -0
  633. package/src/modes/components/status-line/index.ts +5 -0
  634. package/src/modes/components/status-line/presets.ts +106 -0
  635. package/src/modes/components/status-line/segments.ts +616 -0
  636. package/src/modes/components/status-line/separators.ts +55 -0
  637. package/src/modes/components/status-line/token-rate.ts +66 -0
  638. package/src/modes/components/status-line/types.ts +124 -0
  639. package/src/modes/components/theme-selector.ts +63 -0
  640. package/src/modes/components/thinking-selector.ts +52 -0
  641. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  642. package/src/modes/components/tips.txt +24 -0
  643. package/src/modes/components/todo-reminder.ts +39 -0
  644. package/src/modes/components/tool-execution.ts +1165 -0
  645. package/src/modes/components/transcript-container.ts +806 -0
  646. package/src/modes/components/tree-selector.ts +994 -0
  647. package/src/modes/components/ttsr-notification.ts +123 -0
  648. package/src/modes/components/usage-row.ts +18 -0
  649. package/src/modes/components/user-message-selector.ts +227 -0
  650. package/src/modes/components/user-message.ts +68 -0
  651. package/src/modes/components/visual-truncate.ts +63 -0
  652. package/src/modes/components/welcome.ts +581 -0
  653. package/src/modes/controllers/btw-controller.ts +173 -0
  654. package/src/modes/controllers/command-controller-shared.ts +109 -0
  655. package/src/modes/controllers/command-controller.ts +1653 -0
  656. package/src/modes/controllers/event-controller.ts +1153 -0
  657. package/src/modes/controllers/extension-ui-controller.ts +893 -0
  658. package/src/modes/controllers/input-controller.ts +1627 -0
  659. package/src/modes/controllers/mcp-command-controller.ts +2162 -0
  660. package/src/modes/controllers/omfg-controller.ts +283 -0
  661. package/src/modes/controllers/omfg-rule.ts +647 -0
  662. package/src/modes/controllers/selector-controller.ts +1285 -0
  663. package/src/modes/controllers/session-focus-controller.ts +112 -0
  664. package/src/modes/controllers/ssh-command-controller.ts +384 -0
  665. package/src/modes/controllers/streaming-reveal.ts +295 -0
  666. package/src/modes/controllers/tan-command-controller.ts +190 -0
  667. package/src/modes/controllers/todo-command-controller.ts +485 -0
  668. package/src/modes/controllers/tool-args-reveal.ts +174 -0
  669. package/src/modes/data/emojis.json +1 -0
  670. package/src/modes/emoji-autocomplete.ts +285 -0
  671. package/src/modes/gradient-highlight.ts +99 -0
  672. package/src/modes/image-references.ts +137 -0
  673. package/src/modes/index.ts +17 -0
  674. package/src/modes/interactive-mode.ts +3940 -0
  675. package/src/modes/internal-url-autocomplete.ts +143 -0
  676. package/src/modes/loop-limit.ts +192 -0
  677. package/src/modes/magic-keywords.ts +42 -0
  678. package/src/modes/markdown-prose.ts +247 -0
  679. package/src/modes/oauth-manual-input.ts +69 -0
  680. package/src/modes/orchestrate.ts +42 -0
  681. package/src/modes/print-mode.ts +130 -0
  682. package/src/modes/prompt-action-autocomplete.ts +260 -0
  683. package/src/modes/rpc/host-tools.ts +186 -0
  684. package/src/modes/rpc/host-uris.ts +235 -0
  685. package/src/modes/rpc/rpc-client.ts +995 -0
  686. package/src/modes/rpc/rpc-mode.ts +1156 -0
  687. package/src/modes/rpc/rpc-subagents.ts +265 -0
  688. package/src/modes/rpc/rpc-types.ts +487 -0
  689. package/src/modes/runtime-init.ts +142 -0
  690. package/src/modes/session-observer-registry.ts +215 -0
  691. package/src/modes/setup-version.ts +11 -0
  692. package/src/modes/setup-wizard/index.ts +101 -0
  693. package/src/modes/setup-wizard/lazy.ts +16 -0
  694. package/src/modes/setup-wizard/scenes/glyph.ts +114 -0
  695. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  696. package/src/modes/setup-wizard/scenes/providers.ts +103 -0
  697. package/src/modes/setup-wizard/scenes/sign-in.ts +286 -0
  698. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  699. package/src/modes/setup-wizard/scenes/theme.ts +326 -0
  700. package/src/modes/setup-wizard/scenes/types.ts +57 -0
  701. package/src/modes/setup-wizard/scenes/web-search.ts +145 -0
  702. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  703. package/src/modes/setup-wizard/wizard-overlay.ts +334 -0
  704. package/src/modes/shared.ts +49 -0
  705. package/src/modes/theme/dark.json +95 -0
  706. package/src/modes/theme/defaults/alabaster.json +93 -0
  707. package/src/modes/theme/defaults/amethyst.json +96 -0
  708. package/src/modes/theme/defaults/anthracite.json +93 -0
  709. package/src/modes/theme/defaults/basalt.json +91 -0
  710. package/src/modes/theme/defaults/birch.json +95 -0
  711. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  712. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  713. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  714. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  715. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  716. package/src/modes/theme/defaults/dark-copper.json +95 -0
  717. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  718. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  719. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  720. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  721. package/src/modes/theme/defaults/dark-ember.json +95 -0
  722. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  723. package/src/modes/theme/defaults/dark-forest.json +96 -0
  724. package/src/modes/theme/defaults/dark-github.json +105 -0
  725. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  726. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  727. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  728. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  729. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  730. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  731. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  732. package/src/modes/theme/defaults/dark-nord.json +97 -0
  733. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  734. package/src/modes/theme/defaults/dark-one.json +100 -0
  735. package/src/modes/theme/defaults/dark-poimandres.json +142 -0
  736. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  737. package/src/modes/theme/defaults/dark-reef.json +91 -0
  738. package/src/modes/theme/defaults/dark-retro.json +92 -0
  739. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  740. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  741. package/src/modes/theme/defaults/dark-slate.json +95 -0
  742. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  743. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  744. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  745. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  746. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  747. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  748. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  749. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  750. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  751. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  752. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  753. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  754. package/src/modes/theme/defaults/graphite.json +92 -0
  755. package/src/modes/theme/defaults/index.ts +199 -0
  756. package/src/modes/theme/defaults/light-arctic.json +107 -0
  757. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  758. package/src/modes/theme/defaults/light-canyon.json +91 -0
  759. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  760. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  761. package/src/modes/theme/defaults/light-coral.json +95 -0
  762. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  763. package/src/modes/theme/defaults/light-dawn.json +90 -0
  764. package/src/modes/theme/defaults/light-dunes.json +91 -0
  765. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  766. package/src/modes/theme/defaults/light-forest.json +100 -0
  767. package/src/modes/theme/defaults/light-frost.json +95 -0
  768. package/src/modes/theme/defaults/light-github.json +115 -0
  769. package/src/modes/theme/defaults/light-glacier.json +91 -0
  770. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  771. package/src/modes/theme/defaults/light-haze.json +90 -0
  772. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  773. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  774. package/src/modes/theme/defaults/light-lavender.json +95 -0
  775. package/src/modes/theme/defaults/light-meadow.json +91 -0
  776. package/src/modes/theme/defaults/light-mint.json +95 -0
  777. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  778. package/src/modes/theme/defaults/light-ocean.json +99 -0
  779. package/src/modes/theme/defaults/light-one.json +99 -0
  780. package/src/modes/theme/defaults/light-opal.json +91 -0
  781. package/src/modes/theme/defaults/light-orchard.json +91 -0
  782. package/src/modes/theme/defaults/light-paper.json +95 -0
  783. package/src/modes/theme/defaults/light-poimandres.json +142 -0
  784. package/src/modes/theme/defaults/light-prism.json +90 -0
  785. package/src/modes/theme/defaults/light-retro.json +98 -0
  786. package/src/modes/theme/defaults/light-sand.json +95 -0
  787. package/src/modes/theme/defaults/light-savanna.json +91 -0
  788. package/src/modes/theme/defaults/light-solarized.json +102 -0
  789. package/src/modes/theme/defaults/light-soleil.json +90 -0
  790. package/src/modes/theme/defaults/light-sunset.json +99 -0
  791. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  792. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  793. package/src/modes/theme/defaults/light-wetland.json +91 -0
  794. package/src/modes/theme/defaults/light-zenith.json +89 -0
  795. package/src/modes/theme/defaults/limestone.json +94 -0
  796. package/src/modes/theme/defaults/mahogany.json +97 -0
  797. package/src/modes/theme/defaults/marble.json +93 -0
  798. package/src/modes/theme/defaults/obsidian.json +91 -0
  799. package/src/modes/theme/defaults/onyx.json +91 -0
  800. package/src/modes/theme/defaults/pearl.json +93 -0
  801. package/src/modes/theme/defaults/porcelain.json +91 -0
  802. package/src/modes/theme/defaults/quartz.json +96 -0
  803. package/src/modes/theme/defaults/sandstone.json +95 -0
  804. package/src/modes/theme/defaults/titanium.json +90 -0
  805. package/src/modes/theme/light.json +93 -0
  806. package/src/modes/theme/mermaid-cache.ts +92 -0
  807. package/src/modes/theme/shimmer.ts +235 -0
  808. package/src/modes/theme/theme-schema.json +459 -0
  809. package/src/modes/theme/theme.ts +2915 -0
  810. package/src/modes/turn-budget.ts +31 -0
  811. package/src/modes/types.ts +406 -0
  812. package/src/modes/ultrathink.ts +41 -0
  813. package/src/modes/utils/context-usage.ts +432 -0
  814. package/src/modes/utils/copy-targets.ts +360 -0
  815. package/src/modes/utils/hotkeys-markdown.ts +62 -0
  816. package/src/modes/utils/keybinding-matchers.ts +51 -0
  817. package/src/modes/utils/tools-markdown.ts +27 -0
  818. package/src/modes/utils/ui-helpers.ts +886 -0
  819. package/src/modes/workflow.ts +42 -0
  820. package/src/plan-mode/approved-plan.ts +186 -0
  821. package/src/plan-mode/plan-handoff.ts +37 -0
  822. package/src/plan-mode/plan-protection.ts +31 -0
  823. package/src/plan-mode/state.ts +6 -0
  824. package/src/priority.json +45 -0
  825. package/src/prompts/advisor/advise-tool.md +3 -0
  826. package/src/prompts/advisor/system.md +113 -0
  827. package/src/prompts/agents/designer.md +74 -0
  828. package/src/prompts/agents/explore.md +58 -0
  829. package/src/prompts/agents/frontmatter.md +11 -0
  830. package/src/prompts/agents/init.md +33 -0
  831. package/src/prompts/agents/librarian.md +119 -0
  832. package/src/prompts/agents/oracle.md +54 -0
  833. package/src/prompts/agents/plan.md +48 -0
  834. package/src/prompts/agents/reviewer.md +139 -0
  835. package/src/prompts/agents/task.md +17 -0
  836. package/src/prompts/bench.md +12 -0
  837. package/src/prompts/ci-green-request.md +36 -0
  838. package/src/prompts/dry-balance-bench.md +8 -0
  839. package/src/prompts/goals/goal-budget-limit.md +16 -0
  840. package/src/prompts/goals/goal-continuation.md +28 -0
  841. package/src/prompts/goals/goal-mode-active.md +23 -0
  842. package/src/prompts/goals/guided-goal-interview.md +8 -0
  843. package/src/prompts/goals/guided-goal-system.md +12 -0
  844. package/src/prompts/low-end/system.md +47 -0
  845. package/src/prompts/memories/consolidation.md +30 -0
  846. package/src/prompts/memories/consolidation_system.md +4 -0
  847. package/src/prompts/memories/read-path.md +17 -0
  848. package/src/prompts/memories/stage_one_input.md +6 -0
  849. package/src/prompts/memories/stage_one_system.md +21 -0
  850. package/src/prompts/review-custom-request.md +22 -0
  851. package/src/prompts/review-headless-request.md +16 -0
  852. package/src/prompts/review-request.md +69 -0
  853. package/src/prompts/steering/user-interjection.md +9 -0
  854. package/src/prompts/system/agent-creation-architect.md +50 -0
  855. package/src/prompts/system/agent-creation-user.md +6 -0
  856. package/src/prompts/system/auto-continue.md +1 -0
  857. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  858. package/src/prompts/system/auto-thinking-difficulty.md +12 -0
  859. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  860. package/src/prompts/system/autolearn-guidance.md +7 -0
  861. package/src/prompts/system/autolearn-nudge.md +3 -0
  862. package/src/prompts/system/background-tan-dispatch.md +8 -0
  863. package/src/prompts/system/btw-user.md +8 -0
  864. package/src/prompts/system/commit-message-system.md +14 -0
  865. package/src/prompts/system/custom-system-prompt.md +64 -0
  866. package/src/prompts/system/eager-task.md +7 -0
  867. package/src/prompts/system/eager-todo.md +18 -0
  868. package/src/prompts/system/empty-stop-retry.md +4 -0
  869. package/src/prompts/system/irc-autoreply.md +6 -0
  870. package/src/prompts/system/irc-incoming.md +7 -0
  871. package/src/prompts/system/manual-continue.md +7 -0
  872. package/src/prompts/system/memory-consolidation-system.md +8 -0
  873. package/src/prompts/system/memory-extraction-system.md +26 -0
  874. package/src/prompts/system/omfg-user.md +50 -0
  875. package/src/prompts/system/orchestrate-notice.md +40 -0
  876. package/src/prompts/system/personalities/default.md +18 -0
  877. package/src/prompts/system/personalities/friendly.md +17 -0
  878. package/src/prompts/system/personalities/pragmatic.md +15 -0
  879. package/src/prompts/system/plan-mode-active.md +109 -0
  880. package/src/prompts/system/plan-mode-approved.md +25 -0
  881. package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
  882. package/src/prompts/system/plan-mode-reference.md +11 -0
  883. package/src/prompts/system/plan-mode-subagent.md +33 -0
  884. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  885. package/src/prompts/system/project-prompt.md +52 -0
  886. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  887. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  888. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  889. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  890. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  891. package/src/prompts/system/subagent-system-prompt.md +71 -0
  892. package/src/prompts/system/subagent-user-prompt.md +3 -0
  893. package/src/prompts/system/subagent-yield-reminder.md +12 -0
  894. package/src/prompts/system/system-prompt.md +251 -0
  895. package/src/prompts/system/tiny-title-system.md +8 -0
  896. package/src/prompts/system/title-marker-instruction.md +1 -0
  897. package/src/prompts/system/title-system-marker.md +16 -0
  898. package/src/prompts/system/title-system.md +16 -0
  899. package/src/prompts/system/ttsr-interrupt.md +7 -0
  900. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  901. package/src/prompts/system/ultrathink-notice.md +3 -0
  902. package/src/prompts/system/unexpected-stop-classifier.md +17 -0
  903. package/src/prompts/system/unexpected-stop-retry.md +4 -0
  904. package/src/prompts/system/web-search.md +25 -0
  905. package/src/prompts/system/workflow-notice.md +70 -0
  906. package/src/prompts/tools/apply-patch.md +65 -0
  907. package/src/prompts/tools/ask.md +22 -0
  908. package/src/prompts/tools/ast-edit.md +22 -0
  909. package/src/prompts/tools/ast-grep.md +25 -0
  910. package/src/prompts/tools/async-result.md +8 -0
  911. package/src/prompts/tools/bash.md +45 -0
  912. package/src/prompts/tools/browser.md +42 -0
  913. package/src/prompts/tools/checkpoint.md +15 -0
  914. package/src/prompts/tools/debug.md +17 -0
  915. package/src/prompts/tools/eval.md +70 -0
  916. package/src/prompts/tools/find.md +19 -0
  917. package/src/prompts/tools/github.md +17 -0
  918. package/src/prompts/tools/goal.md +11 -0
  919. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  920. package/src/prompts/tools/image-attachment-describe.md +10 -0
  921. package/src/prompts/tools/image-gen.md +7 -0
  922. package/src/prompts/tools/inspect-image-system.md +20 -0
  923. package/src/prompts/tools/inspect-image.md +22 -0
  924. package/src/prompts/tools/irc.md +33 -0
  925. package/src/prompts/tools/job.md +17 -0
  926. package/src/prompts/tools/learn.md +7 -0
  927. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  928. package/src/prompts/tools/lsp.md +39 -0
  929. package/src/prompts/tools/manage-skill.md +9 -0
  930. package/src/prompts/tools/memory-edit.md +8 -0
  931. package/src/prompts/tools/patch.md +57 -0
  932. package/src/prompts/tools/read.md +76 -0
  933. package/src/prompts/tools/recall.md +5 -0
  934. package/src/prompts/tools/reflect.md +5 -0
  935. package/src/prompts/tools/replace.md +29 -0
  936. package/src/prompts/tools/resolve.md +4 -0
  937. package/src/prompts/tools/retain.md +6 -0
  938. package/src/prompts/tools/rewind.md +13 -0
  939. package/src/prompts/tools/search-tool-bm25.md +32 -0
  940. package/src/prompts/tools/search.md +22 -0
  941. package/src/prompts/tools/ssh.md +22 -0
  942. package/src/prompts/tools/task-summary.md +17 -0
  943. package/src/prompts/tools/task.md +91 -0
  944. package/src/prompts/tools/todo.md +39 -0
  945. package/src/prompts/tools/web-search.md +6 -0
  946. package/src/prompts/tools/write.md +14 -0
  947. package/src/registry/agent-lifecycle.ts +270 -0
  948. package/src/registry/agent-registry.ts +190 -0
  949. package/src/sdk.ts +2919 -0
  950. package/src/secrets/index.ts +123 -0
  951. package/src/secrets/obfuscator.ts +298 -0
  952. package/src/secrets/regex.ts +21 -0
  953. package/src/session/agent-session.ts +12539 -0
  954. package/src/session/agent-storage.ts +478 -0
  955. package/src/session/artifacts.ts +153 -0
  956. package/src/session/auth-broker-config.ts +92 -0
  957. package/src/session/auth-storage.ts +24 -0
  958. package/src/session/blob-store.ts +255 -0
  959. package/src/session/client-bridge.ts +85 -0
  960. package/src/session/codex-auto-reset.ts +202 -0
  961. package/src/session/compact-modes.ts +105 -0
  962. package/src/session/history-storage.ts +361 -0
  963. package/src/session/indexed-session-storage.ts +427 -0
  964. package/src/session/messages.ts +546 -0
  965. package/src/session/redis-session-storage.ts +170 -0
  966. package/src/session/session-context.ts +399 -0
  967. package/src/session/session-dump-format.ts +216 -0
  968. package/src/session/session-entries.ts +198 -0
  969. package/src/session/session-history-format.ts +308 -0
  970. package/src/session/session-listing.ts +588 -0
  971. package/src/session/session-loader.ts +93 -0
  972. package/src/session/session-manager.ts +1748 -0
  973. package/src/session/session-migrations.ts +78 -0
  974. package/src/session/session-paths.ts +193 -0
  975. package/src/session/session-persistence.ts +147 -0
  976. package/src/session/session-storage.ts +590 -0
  977. package/src/session/shake-types.ts +43 -0
  978. package/src/session/snapcompact-inline.ts +542 -0
  979. package/src/session/snapcompact-savings-journal.ts +113 -0
  980. package/src/session/sql-session-storage.ts +314 -0
  981. package/src/session/streaming-output.ts +1330 -0
  982. package/src/session/tool-choice-queue.ts +290 -0
  983. package/src/session/unexpected-stop-classifier.ts +129 -0
  984. package/src/session/yield-queue.ts +183 -0
  985. package/src/slash-commands/acp-builtins.ts +70 -0
  986. package/src/slash-commands/available-commands.ts +105 -0
  987. package/src/slash-commands/builtin-registry.ts +2332 -0
  988. package/src/slash-commands/helpers/active-oauth-account.ts +44 -0
  989. package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
  990. package/src/slash-commands/helpers/context-report.ts +66 -0
  991. package/src/slash-commands/helpers/format.ts +46 -0
  992. package/src/slash-commands/helpers/logout.ts +88 -0
  993. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  994. package/src/slash-commands/helpers/mcp.ts +532 -0
  995. package/src/slash-commands/helpers/parse.ts +85 -0
  996. package/src/slash-commands/helpers/reset-usage.ts +66 -0
  997. package/src/slash-commands/helpers/ssh.ts +195 -0
  998. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  999. package/src/slash-commands/helpers/todo.ts +279 -0
  1000. package/src/slash-commands/helpers/usage-report.ts +128 -0
  1001. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  1002. package/src/slash-commands/types.ts +135 -0
  1003. package/src/ssh/config-writer.ts +183 -0
  1004. package/src/ssh/connection-manager.ts +510 -0
  1005. package/src/ssh/ssh-executor.ts +189 -0
  1006. package/src/ssh/sshfs-mount.ts +140 -0
  1007. package/src/ssh/utils.ts +8 -0
  1008. package/src/startup-splash.ts +19 -0
  1009. package/src/stt/asr-client.ts +521 -0
  1010. package/src/stt/asr-protocol.ts +65 -0
  1011. package/src/stt/asr-worker.ts +790 -0
  1012. package/src/stt/downloader.ts +138 -0
  1013. package/src/stt/endpointer.ts +259 -0
  1014. package/src/stt/index.ts +7 -0
  1015. package/src/stt/models.ts +150 -0
  1016. package/src/stt/recorder.ts +538 -0
  1017. package/src/stt/stt-controller.ts +380 -0
  1018. package/src/stt/transcriber.ts +60 -0
  1019. package/src/stt/wav.ts +173 -0
  1020. package/src/system-prompt.ts +709 -0
  1021. package/src/task/agents.ts +166 -0
  1022. package/src/task/commands.ts +132 -0
  1023. package/src/task/discovery.ts +122 -0
  1024. package/src/task/executor.ts +2356 -0
  1025. package/src/task/index.ts +1580 -0
  1026. package/src/task/name-generator.ts +1577 -0
  1027. package/src/task/omp-command.ts +26 -0
  1028. package/src/task/output-manager.ts +93 -0
  1029. package/src/task/parallel.ts +116 -0
  1030. package/src/task/persisted-revive.ts +128 -0
  1031. package/src/task/render.ts +1558 -0
  1032. package/src/task/repair-args.ts +129 -0
  1033. package/src/task/subprocess-tool-registry.ts +88 -0
  1034. package/src/task/types.ts +401 -0
  1035. package/src/task/worktree.ts +514 -0
  1036. package/src/telemetry-export.ts +144 -0
  1037. package/src/thinking.ts +187 -0
  1038. package/src/tiny/device.ts +111 -0
  1039. package/src/tiny/dtype.ts +101 -0
  1040. package/src/tiny/models.ts +252 -0
  1041. package/src/tiny/text.ts +169 -0
  1042. package/src/tiny/title-client.ts +538 -0
  1043. package/src/tiny/title-protocol.ts +56 -0
  1044. package/src/tiny/worker.ts +491 -0
  1045. package/src/tool-discovery/mode.ts +24 -0
  1046. package/src/tool-discovery/tool-index.ts +271 -0
  1047. package/src/tools/__tests__/json-tree.test.ts +35 -0
  1048. package/src/tools/approval.ts +189 -0
  1049. package/src/tools/ask.ts +977 -0
  1050. package/src/tools/ast-edit.ts +700 -0
  1051. package/src/tools/ast-grep.ts +483 -0
  1052. package/src/tools/auto-generated-guard.ts +322 -0
  1053. package/src/tools/bash-command-fixup.ts +37 -0
  1054. package/src/tools/bash-interactive.ts +408 -0
  1055. package/src/tools/bash-interceptor.ts +67 -0
  1056. package/src/tools/bash-pty-selection.ts +14 -0
  1057. package/src/tools/bash-skill-urls.ts +248 -0
  1058. package/src/tools/bash.ts +1405 -0
  1059. package/src/tools/browser/attach.ts +194 -0
  1060. package/src/tools/browser/cmux/cmux-tab.ts +1264 -0
  1061. package/src/tools/browser/cmux/rpc.ts +156 -0
  1062. package/src/tools/browser/cmux/socket-client.ts +309 -0
  1063. package/src/tools/browser/launch.ts +673 -0
  1064. package/src/tools/browser/readable.ts +112 -0
  1065. package/src/tools/browser/registry.ts +241 -0
  1066. package/src/tools/browser/render.ts +221 -0
  1067. package/src/tools/browser/tab-protocol.ts +107 -0
  1068. package/src/tools/browser/tab-supervisor.ts +799 -0
  1069. package/src/tools/browser/tab-worker-entry.ts +29 -0
  1070. package/src/tools/browser/tab-worker.ts +1226 -0
  1071. package/src/tools/browser.ts +403 -0
  1072. package/src/tools/builtin-names.ts +34 -0
  1073. package/src/tools/checkpoint.ts +136 -0
  1074. package/src/tools/conflict-detect.ts +718 -0
  1075. package/src/tools/context.ts +39 -0
  1076. package/src/tools/debug.ts +1087 -0
  1077. package/src/tools/eval-backends.ts +27 -0
  1078. package/src/tools/eval-render.ts +762 -0
  1079. package/src/tools/eval.ts +600 -0
  1080. package/src/tools/fetch.ts +1902 -0
  1081. package/src/tools/file-recorder.ts +35 -0
  1082. package/src/tools/find.ts +629 -0
  1083. package/src/tools/fs-cache-invalidation.ts +28 -0
  1084. package/src/tools/gh-cache-invalidation.ts +255 -0
  1085. package/src/tools/gh-format.ts +12 -0
  1086. package/src/tools/gh-renderer.ts +481 -0
  1087. package/src/tools/gh.ts +3752 -0
  1088. package/src/tools/github-cache.ts +663 -0
  1089. package/src/tools/grouped-file-output.ts +210 -0
  1090. package/src/tools/image-gen.ts +1586 -0
  1091. package/src/tools/index.ts +649 -0
  1092. package/src/tools/inspect-image-renderer.ts +132 -0
  1093. package/src/tools/inspect-image.ts +260 -0
  1094. package/src/tools/irc.ts +788 -0
  1095. package/src/tools/job.ts +612 -0
  1096. package/src/tools/json-tree.ts +260 -0
  1097. package/src/tools/jtd-to-json-schema.ts +219 -0
  1098. package/src/tools/jtd-to-typescript.ts +136 -0
  1099. package/src/tools/jtd-utils.ts +102 -0
  1100. package/src/tools/learn.ts +141 -0
  1101. package/src/tools/list-limit.ts +40 -0
  1102. package/src/tools/manage-skill.ts +100 -0
  1103. package/src/tools/match-line-format.ts +20 -0
  1104. package/src/tools/memory-edit.ts +59 -0
  1105. package/src/tools/memory-recall.ts +102 -0
  1106. package/src/tools/memory-reflect.ts +88 -0
  1107. package/src/tools/memory-render.ts +202 -0
  1108. package/src/tools/memory-retain.ts +89 -0
  1109. package/src/tools/output-meta.ts +768 -0
  1110. package/src/tools/output-schema-validator.ts +132 -0
  1111. package/src/tools/path-utils.ts +1116 -0
  1112. package/src/tools/plan-mode-guard.ts +142 -0
  1113. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  1114. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  1115. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  1116. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  1117. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  1118. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  1119. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  1120. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  1121. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  1122. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  1123. package/src/tools/puppeteer/10_stealth_plugins.txt +208 -0
  1124. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  1125. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  1126. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  1127. package/src/tools/read.ts +3124 -0
  1128. package/src/tools/render-utils.ts +895 -0
  1129. package/src/tools/renderers.ts +86 -0
  1130. package/src/tools/report-tool-issue.ts +530 -0
  1131. package/src/tools/resolve.ts +302 -0
  1132. package/src/tools/review.ts +251 -0
  1133. package/src/tools/search-tool-bm25.ts +351 -0
  1134. package/src/tools/search.ts +1583 -0
  1135. package/src/tools/sqlite-reader.ts +828 -0
  1136. package/src/tools/ssh.ts +369 -0
  1137. package/src/tools/todo.ts +938 -0
  1138. package/src/tools/tool-errors.ts +62 -0
  1139. package/src/tools/tool-result.ts +102 -0
  1140. package/src/tools/tool-timeouts.ts +30 -0
  1141. package/src/tools/tts.ts +265 -0
  1142. package/src/tools/write.ts +1182 -0
  1143. package/src/tools/yield.ts +269 -0
  1144. package/src/tts/downloader.ts +64 -0
  1145. package/src/tts/index.ts +8 -0
  1146. package/src/tts/models.ts +137 -0
  1147. package/src/tts/player.ts +137 -0
  1148. package/src/tts/runtime.ts +21 -0
  1149. package/src/tts/streaming-player.ts +266 -0
  1150. package/src/tts/tts-client.ts +642 -0
  1151. package/src/tts/tts-protocol.ts +60 -0
  1152. package/src/tts/tts-worker.ts +505 -0
  1153. package/src/tts/vocalizer.ts +162 -0
  1154. package/src/tts/wav.ts +58 -0
  1155. package/src/tui/code-cell.ts +257 -0
  1156. package/src/tui/file-list.ts +55 -0
  1157. package/src/tui/hyperlink.ts +178 -0
  1158. package/src/tui/index.ts +13 -0
  1159. package/src/tui/output-block.ts +240 -0
  1160. package/src/tui/status-line.ts +54 -0
  1161. package/src/tui/tree-list.ts +133 -0
  1162. package/src/tui/types.ts +15 -0
  1163. package/src/tui/utils.ts +103 -0
  1164. package/src/tui/width-aware-text.ts +58 -0
  1165. package/src/utils/block-context.ts +312 -0
  1166. package/src/utils/changelog.ts +132 -0
  1167. package/src/utils/clipboard.ts +262 -0
  1168. package/src/utils/command-args.ts +76 -0
  1169. package/src/utils/commit-message-generator.ts +147 -0
  1170. package/src/utils/edit-mode.ts +41 -0
  1171. package/src/utils/enhanced-paste.ts +230 -0
  1172. package/src/utils/event-bus.ts +33 -0
  1173. package/src/utils/external-editor.ts +78 -0
  1174. package/src/utils/file-display-mode.ts +45 -0
  1175. package/src/utils/file-mentions.ts +284 -0
  1176. package/src/utils/git.ts +1838 -0
  1177. package/src/utils/image-loading.ts +231 -0
  1178. package/src/utils/image-resize.ts +309 -0
  1179. package/src/utils/image-vision-fallback.ts +197 -0
  1180. package/src/utils/ipc.ts +38 -0
  1181. package/src/utils/jj.ts +248 -0
  1182. package/src/utils/lang-from-path.ts +244 -0
  1183. package/src/utils/markit.ts +143 -0
  1184. package/src/utils/mupdf-wasm-embed.ts +12 -0
  1185. package/src/utils/open.ts +55 -0
  1186. package/src/utils/qrcode.ts +535 -0
  1187. package/src/utils/session-color.ts +142 -0
  1188. package/src/utils/shell-snapshot.ts +187 -0
  1189. package/src/utils/sixel.ts +69 -0
  1190. package/src/utils/thinking-display.ts +11 -0
  1191. package/src/utils/title-generator.ts +416 -0
  1192. package/src/utils/tool-choice.ts +49 -0
  1193. package/src/utils/tools-manager.ts +372 -0
  1194. package/src/utils/turndown.ts +83 -0
  1195. package/src/utils/zip.ts +1091 -0
  1196. package/src/web/kagi.ts +304 -0
  1197. package/src/web/parallel.ts +353 -0
  1198. package/src/web/scrapers/artifacthub.ts +207 -0
  1199. package/src/web/scrapers/arxiv.ts +83 -0
  1200. package/src/web/scrapers/aur.ts +162 -0
  1201. package/src/web/scrapers/biorxiv.ts +133 -0
  1202. package/src/web/scrapers/bluesky.ts +262 -0
  1203. package/src/web/scrapers/brew.ts +172 -0
  1204. package/src/web/scrapers/cheatsh.ts +68 -0
  1205. package/src/web/scrapers/chocolatey.ts +196 -0
  1206. package/src/web/scrapers/choosealicense.ts +95 -0
  1207. package/src/web/scrapers/cisa-kev.ts +87 -0
  1208. package/src/web/scrapers/clojars.ts +154 -0
  1209. package/src/web/scrapers/coingecko.ts +177 -0
  1210. package/src/web/scrapers/crates-io.ts +97 -0
  1211. package/src/web/scrapers/crossref.ts +136 -0
  1212. package/src/web/scrapers/devto.ts +147 -0
  1213. package/src/web/scrapers/discogs.ts +306 -0
  1214. package/src/web/scrapers/discourse.ts +197 -0
  1215. package/src/web/scrapers/dockerhub.ts +138 -0
  1216. package/src/web/scrapers/docs-rs.ts +652 -0
  1217. package/src/web/scrapers/fdroid.ts +134 -0
  1218. package/src/web/scrapers/firefox-addons.ts +191 -0
  1219. package/src/web/scrapers/flathub.ts +223 -0
  1220. package/src/web/scrapers/github-gist.ts +58 -0
  1221. package/src/web/scrapers/github.ts +800 -0
  1222. package/src/web/scrapers/gitlab.ts +401 -0
  1223. package/src/web/scrapers/go-pkg.ts +266 -0
  1224. package/src/web/scrapers/hackage.ts +140 -0
  1225. package/src/web/scrapers/hackernews.ts +189 -0
  1226. package/src/web/scrapers/hex.ts +105 -0
  1227. package/src/web/scrapers/huggingface.ts +321 -0
  1228. package/src/web/scrapers/iacr.ts +89 -0
  1229. package/src/web/scrapers/index.ts +252 -0
  1230. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1231. package/src/web/scrapers/lemmy.ts +203 -0
  1232. package/src/web/scrapers/lobsters.ts +175 -0
  1233. package/src/web/scrapers/mastodon.ts +292 -0
  1234. package/src/web/scrapers/maven.ts +138 -0
  1235. package/src/web/scrapers/mdn.ts +173 -0
  1236. package/src/web/scrapers/metacpan.ts +222 -0
  1237. package/src/web/scrapers/musicbrainz.ts +250 -0
  1238. package/src/web/scrapers/npm.ts +98 -0
  1239. package/src/web/scrapers/nuget.ts +183 -0
  1240. package/src/web/scrapers/nvd.ts +222 -0
  1241. package/src/web/scrapers/ollama.ts +239 -0
  1242. package/src/web/scrapers/open-vsx.ts +106 -0
  1243. package/src/web/scrapers/opencorporates.ts +292 -0
  1244. package/src/web/scrapers/openlibrary.ts +336 -0
  1245. package/src/web/scrapers/orcid.ts +286 -0
  1246. package/src/web/scrapers/osv.ts +176 -0
  1247. package/src/web/scrapers/packagist.ts +160 -0
  1248. package/src/web/scrapers/pub-dev.ts +143 -0
  1249. package/src/web/scrapers/pubmed.ts +211 -0
  1250. package/src/web/scrapers/pypi.ts +112 -0
  1251. package/src/web/scrapers/rawg.ts +110 -0
  1252. package/src/web/scrapers/readthedocs.ts +120 -0
  1253. package/src/web/scrapers/reddit.ts +95 -0
  1254. package/src/web/scrapers/repology.ts +251 -0
  1255. package/src/web/scrapers/rfc.ts +201 -0
  1256. package/src/web/scrapers/rubygems.ts +103 -0
  1257. package/src/web/scrapers/searchcode.ts +189 -0
  1258. package/src/web/scrapers/sec-edgar.ts +261 -0
  1259. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1260. package/src/web/scrapers/snapcraft.ts +187 -0
  1261. package/src/web/scrapers/sourcegraph.ts +336 -0
  1262. package/src/web/scrapers/spdx.ts +108 -0
  1263. package/src/web/scrapers/spotify.ts +198 -0
  1264. package/src/web/scrapers/stackoverflow.ts +120 -0
  1265. package/src/web/scrapers/terraform.ts +277 -0
  1266. package/src/web/scrapers/tldr.ts +47 -0
  1267. package/src/web/scrapers/twitter.ts +94 -0
  1268. package/src/web/scrapers/types.ts +354 -0
  1269. package/src/web/scrapers/utils.ts +109 -0
  1270. package/src/web/scrapers/vimeo.ts +133 -0
  1271. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1272. package/src/web/scrapers/w3c.ts +156 -0
  1273. package/src/web/scrapers/wikidata.ts +344 -0
  1274. package/src/web/scrapers/wikipedia.ts +84 -0
  1275. package/src/web/scrapers/youtube.ts +325 -0
  1276. package/src/web/search/index.ts +317 -0
  1277. package/src/web/search/provider.ts +169 -0
  1278. package/src/web/search/providers/anthropic.ts +343 -0
  1279. package/src/web/search/providers/base.ts +90 -0
  1280. package/src/web/search/providers/brave.ts +152 -0
  1281. package/src/web/search/providers/codex.ts +593 -0
  1282. package/src/web/search/providers/exa.ts +400 -0
  1283. package/src/web/search/providers/gemini.ts +518 -0
  1284. package/src/web/search/providers/jina.ts +111 -0
  1285. package/src/web/search/providers/kagi.ts +86 -0
  1286. package/src/web/search/providers/kimi.ts +196 -0
  1287. package/src/web/search/providers/parallel.ts +225 -0
  1288. package/src/web/search/providers/perplexity-auth.ts +133 -0
  1289. package/src/web/search/providers/perplexity.ts +866 -0
  1290. package/src/web/search/providers/searxng.ts +325 -0
  1291. package/src/web/search/providers/synthetic.ts +114 -0
  1292. package/src/web/search/providers/tavily.ts +176 -0
  1293. package/src/web/search/providers/utils.ts +128 -0
  1294. package/src/web/search/providers/zai.ts +333 -0
  1295. package/src/web/search/render.ts +262 -0
  1296. package/src/web/search/types.ts +462 -0
  1297. package/src/web/search/utils.ts +17 -0
  1298. package/src/workspace-tree.ts +326 -0
@@ -0,0 +1,1748 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { ImageContent, Message, MessageAttribution, ServiceTier, TextContent, Usage } from "@oh-my-pi/pi-ai";
4
+ import {
5
+ directoryExists,
6
+ getBlobsDir,
7
+ getProjectDir,
8
+ getSessionsDir,
9
+ isEnoent,
10
+ logger,
11
+ toError,
12
+ } from "@oh-my-pi/pi-utils";
13
+ import { ArtifactManager } from "./artifacts";
14
+ import { type BlobPutOptions, type BlobPutResult, BlobStore } from "./blob-store";
15
+ import {
16
+ type BashExecutionMessage,
17
+ type CustomMessage,
18
+ type FileMentionMessage,
19
+ type HookMessage,
20
+ type PythonExecutionMessage,
21
+ sanitizeRehydratedOpenAIResponsesAssistantMessage,
22
+ stripInternalDetailsFields,
23
+ } from "./messages";
24
+ import { type BuildSessionContextOptions, buildSessionContext, type SessionContext } from "./session-context";
25
+ import {
26
+ type BranchSummaryEntry,
27
+ type CompactionEntry,
28
+ CURRENT_SESSION_VERSION,
29
+ type CustomEntry,
30
+ type CustomMessageEntry,
31
+ type FileEntry,
32
+ type LabelEntry,
33
+ type MCPToolSelectionEntry,
34
+ type ModeChangeEntry,
35
+ type ModelChangeEntry,
36
+ type NewSessionOptions,
37
+ type ServiceTierChangeEntry,
38
+ type SessionEntry,
39
+ type SessionHeader,
40
+ type SessionInitEntry,
41
+ type SessionMessageEntry,
42
+ type SessionTreeNode,
43
+ type ThinkingLevelChangeEntry,
44
+ type TtsrInjectionEntry,
45
+ type UsageStatistics,
46
+ } from "./session-entries";
47
+ import { findMostRecentSession, listAllSessions, listSessions, type SessionInfo } from "./session-listing";
48
+ import { loadEntriesFromFile, resolveBlobRefsInEntries } from "./session-loader";
49
+ import { generateId, migrateToCurrentVersion } from "./session-migrations";
50
+ import {
51
+ computeDefaultSessionDir,
52
+ readTerminalBreadcrumbEntry,
53
+ resolveManagedSessionRoot,
54
+ writeTerminalBreadcrumb,
55
+ } from "./session-paths";
56
+ import { prepareEntryForPersistence } from "./session-persistence";
57
+ import {
58
+ FileSessionStorage,
59
+ MemorySessionStorage,
60
+ type SessionStorage,
61
+ type SessionStorageWriter,
62
+ } from "./session-storage";
63
+
64
+ const JSONL_SUFFIX_LENGTH = ".jsonl".length;
65
+
66
+ function mintSessionId(): string {
67
+ return Bun.randomUUIDv7();
68
+ }
69
+
70
+ function nowIso(): string {
71
+ return new Date().toISOString();
72
+ }
73
+
74
+ function fileSafeTimestamp(iso: string): string {
75
+ return iso.replace(/[:.]/g, "-");
76
+ }
77
+
78
+ function artifactsDirectoryFor(sessionFile: string | undefined): string | null {
79
+ return sessionFile ? sessionFile.slice(0, -JSONL_SUFFIX_LENGTH) : null;
80
+ }
81
+
82
+ /**
83
+ * Resolve a breadcrumb's recorded session file to its interactive root. Subagent
84
+ * (and other artifact) sessions live inside a parent session's artifacts dir —
85
+ * `<parent>.jsonl` strips its suffix to `<parent>/`, and a child writes
86
+ * `<parent>/<agentId>.jsonl`. A breadcrumb that points at such a child — a
87
+ * pre-fix poisoned crumb left by a subagent that opened in the parent's TTY, or
88
+ * any nested artifact — must resolve back up to the top-level session so
89
+ * `--continue` resumes the real conversation instead of a subagent transcript.
90
+ */
91
+ function resolveBreadcrumbToInteractiveRoot(sessionFile: string): string {
92
+ let current = path.resolve(sessionFile);
93
+ // Walk up while the containing dir is itself a session's artifacts dir
94
+ // (`<dir>.jsonl` exists). Capped to defend against pathological layouts.
95
+ for (let depth = 0; depth < 8; depth++) {
96
+ const parentSessionFile = `${path.dirname(current)}.jsonl`;
97
+ if (!fs.existsSync(parentSessionFile)) return current;
98
+ current = parentSessionFile;
99
+ }
100
+ return current;
101
+ }
102
+
103
+ function emptyUsageStatistics(): UsageStatistics {
104
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, premiumRequests: 0, cost: 0 };
105
+ }
106
+
107
+ function taskUsageFrom(details: unknown): Usage | undefined {
108
+ if (details === null || typeof details !== "object") return undefined;
109
+ const maybeUsage = (details as Record<string, unknown>).usage;
110
+ return maybeUsage !== null && typeof maybeUsage === "object" ? (maybeUsage as Usage) : undefined;
111
+ }
112
+
113
+ function entryUsage(entry: SessionEntry): Usage | undefined {
114
+ if (entry.type !== "message") return undefined;
115
+ const message = entry.message;
116
+ if (message.role === "assistant") return message.usage;
117
+ if (message.role === "toolResult" && message.toolName === "task") return taskUsageFrom(message.details);
118
+ return undefined;
119
+ }
120
+
121
+ function addUsage(target: UsageStatistics, usage: Usage | undefined): void {
122
+ if (!usage) return;
123
+ target.input += usage.input;
124
+ target.output += usage.output;
125
+ target.cacheRead += usage.cacheRead;
126
+ target.cacheWrite += usage.cacheWrite;
127
+ target.premiumRequests += usage.premiumRequests ?? 0;
128
+ target.cost += usage.cost.total;
129
+ }
130
+
131
+ function isAssistantEntry(entry: SessionEntry): boolean {
132
+ return entry.type === "message" && entry.message.role === "assistant";
133
+ }
134
+
135
+ function orderedByTimestamp(a: SessionTreeNode, b: SessionTreeNode): number {
136
+ return new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime();
137
+ }
138
+
139
+ /**
140
+ * Maintains the derived views over a session's entry list: id lookup, the
141
+ * parent→children adjacency, the resolved label map, the active leaf, and the
142
+ * running usage totals. Kept in lockstep with the manager's `#entries` so reads
143
+ * stay O(1)/O(children) instead of rescanning the whole journal.
144
+ */
145
+ class SessionEntryIndex {
146
+ #entriesById = new Map<string, SessionEntry>();
147
+ #children = new Map<string | null, SessionEntry[]>();
148
+ #labels = new Map<string, string>();
149
+ #leaf: string | null = null;
150
+ #usage = emptyUsageStatistics();
151
+
152
+ clear(): void {
153
+ this.#entriesById.clear();
154
+ this.#children.clear();
155
+ this.#labels.clear();
156
+ this.#leaf = null;
157
+ this.#usage = emptyUsageStatistics();
158
+ }
159
+
160
+ rebuild(entries: readonly SessionEntry[]): void {
161
+ this.clear();
162
+ for (const entry of entries) this.insert(entry);
163
+ }
164
+
165
+ insert(entry: SessionEntry): void {
166
+ this.#entriesById.set(entry.id, entry);
167
+ this.#leaf = entry.id;
168
+
169
+ const bucket = this.#children.get(entry.parentId);
170
+ if (bucket) bucket.push(entry);
171
+ else this.#children.set(entry.parentId, [entry]);
172
+
173
+ if (entry.type === "label") {
174
+ if (entry.label) this.#labels.set(entry.targetId, entry.label);
175
+ else this.#labels.delete(entry.targetId);
176
+ }
177
+
178
+ addUsage(this.#usage, entryUsage(entry));
179
+ }
180
+
181
+ has(id: string): boolean {
182
+ return this.#entriesById.has(id);
183
+ }
184
+
185
+ get(id: string): SessionEntry | undefined {
186
+ return this.#entriesById.get(id);
187
+ }
188
+
189
+ /**
190
+ * The live id→entry map. Read-only for callers (lookups + `generateId`
191
+ * collision checks); never mutate it directly — go through `insert`/`rebuild`.
192
+ */
193
+ entriesById(): Map<string, SessionEntry> {
194
+ return this.#entriesById;
195
+ }
196
+
197
+ leafId(): string | null {
198
+ return this.#leaf;
199
+ }
200
+
201
+ leafEntry(): SessionEntry | undefined {
202
+ return this.#leaf ? this.#entriesById.get(this.#leaf) : undefined;
203
+ }
204
+
205
+ setLeaf(id: string | null): void {
206
+ this.#leaf = id;
207
+ }
208
+
209
+ childrenOf(parentId: string): SessionEntry[] {
210
+ return [...(this.#children.get(parentId) ?? [])];
211
+ }
212
+
213
+ labelFor(id: string): string | undefined {
214
+ return this.#labels.get(id);
215
+ }
216
+
217
+ labelsInEffect(): IterableIterator<[string, string]> {
218
+ return this.#labels.entries();
219
+ }
220
+
221
+ usageSnapshot(): UsageStatistics {
222
+ return { ...this.#usage };
223
+ }
224
+
225
+ pathTo(id: string | null | undefined = this.#leaf): SessionEntry[] {
226
+ const branch: SessionEntry[] = [];
227
+ const seen = new Set<string>();
228
+ let cursor = id ? this.#entriesById.get(id) : undefined;
229
+
230
+ while (cursor && !seen.has(cursor.id)) {
231
+ seen.add(cursor.id);
232
+ branch.unshift(cursor);
233
+ cursor = cursor.parentId ? this.#entriesById.get(cursor.parentId) : undefined;
234
+ }
235
+
236
+ return branch;
237
+ }
238
+
239
+ tree(entries: readonly SessionEntry[]): SessionTreeNode[] {
240
+ const nodes = new Map<string, SessionTreeNode>();
241
+ const roots: SessionTreeNode[] = [];
242
+
243
+ for (const entry of entries) {
244
+ nodes.set(entry.id, { entry, children: [], label: this.#labels.get(entry.id) });
245
+ }
246
+
247
+ for (const entry of entries) {
248
+ const node = nodes.get(entry.id)!;
249
+ const parentId = entry.parentId;
250
+ if (parentId === null || parentId === entry.id) {
251
+ roots.push(node);
252
+ continue;
253
+ }
254
+
255
+ const parent = nodes.get(parentId);
256
+ if (parent) parent.children.push(node);
257
+ else roots.push(node);
258
+ }
259
+
260
+ const stack = [...roots];
261
+ while (stack.length > 0) {
262
+ const node = stack.pop()!;
263
+ node.children.sort(orderedByTimestamp);
264
+ stack.push(...node.children);
265
+ }
266
+
267
+ return roots;
268
+ }
269
+ }
270
+
271
+ export type ReadonlySessionManager = Pick<
272
+ SessionManager,
273
+ | "getCwd"
274
+ | "getSessionDir"
275
+ | "getSessionId"
276
+ | "getSessionFile"
277
+ | "getSessionName"
278
+ | "getArtifactsDir"
279
+ | "getArtifactManager"
280
+ | "allocateArtifactPath"
281
+ | "saveArtifact"
282
+ | "getArtifactPath"
283
+ | "getLeafId"
284
+ | "getLeafEntry"
285
+ | "getEntry"
286
+ | "getLabel"
287
+ | "getBranch"
288
+ | "getHeader"
289
+ | "getEntries"
290
+ | "getTree"
291
+ | "getUsageStatistics"
292
+ | "putBlob"
293
+ | "putBlobSync"
294
+ >;
295
+
296
+ interface SessionManagerStateSnapshot {
297
+ cwd: string;
298
+ sessionDir: string;
299
+ sessionId: string;
300
+ sessionName: string | undefined;
301
+ titleSource: "auto" | "user" | undefined;
302
+ sessionFile: string | undefined;
303
+ onDisk: boolean;
304
+ needsRewrite: boolean;
305
+ header: SessionHeader;
306
+ entries: SessionEntry[];
307
+ }
308
+
309
+ interface DiskQueueOptions {
310
+ ignorePriorError?: boolean;
311
+ ignoreEpoch?: boolean;
312
+ epoch?: number;
313
+ }
314
+
315
+ /**
316
+ * Stores and navigates an append-only conversation journal.
317
+ *
318
+ * A session is a JSONL file: one header line followed by entries. Entries form a
319
+ * tree by `(id, parentId)`, and the mutable leaf pointer selects which path is
320
+ * active for future appends and for LLM context construction.
321
+ *
322
+ * Durability is software-crash safe but not power-loss safe: appends are handed
323
+ * to the OS synchronously in-body (so an entry survives an OOM/SIGKILL the
324
+ * instant `appendMessage` returns) but never `fsync`'d. Full-file rewrites go
325
+ * through the storage layer's atomic temp-write+rename so a crash mid-rewrite
326
+ * cannot truncate the prior good file.
327
+ */
328
+ export class SessionManager {
329
+ #cwd: string;
330
+ #sessionDir: string;
331
+ readonly #persist: boolean;
332
+ readonly #storage: SessionStorage;
333
+ readonly #blobs: BlobStore;
334
+
335
+ #sessionId = "";
336
+ #sessionName: string | undefined;
337
+ #titleSource: "auto" | "user" | undefined;
338
+ #sessionFile: string | undefined;
339
+ #header!: SessionHeader;
340
+ #entries: SessionEntry[] = [];
341
+ #index = new SessionEntryIndex();
342
+
343
+ /** File reflects all current entries; appends can go incrementally. */
344
+ #fileIsCurrent = false;
345
+ /** In-memory entries diverged from disk (load-migration/sanitize) → next persist must full-rewrite. */
346
+ #rewriteRequired = false;
347
+ /** Lazy gate crossed (ensureOnDisk / loaded file): every entry must persist from now on. */
348
+ #forceFileCreation = false;
349
+
350
+ /**
351
+ * Collab replication tap: invoked for every appended entry with the
352
+ * in-memory (pre-blob-externalization) entry, so inline images survive.
353
+ */
354
+ onEntryAppended?: (entry: SessionEntry) => void;
355
+
356
+ #turnBudgetTotal: number | null = null;
357
+ #turnBudgetHard = false;
358
+ #turnOutputBaseline = 0;
359
+ #turnEvalOutput = 0;
360
+
361
+ /** The single open append writer; the manager only ever writes one file at a time. */
362
+ #writer: SessionStorageWriter | undefined;
363
+ /** Serializes async disk work (flush/close/atomic rewrite). Appends are synchronous and bypass it. */
364
+ #diskTail: Promise<void> = Promise.resolve();
365
+ #diskFailure: Error | undefined;
366
+ #diskFailureLogged = false;
367
+ /** Bumped on every sync rewrite / chain reset so stale queued tasks become no-ops. */
368
+ #diskEpoch = 0;
369
+
370
+ #artifactManager: ArtifactManager | null = null;
371
+ #artifactManagerSessionFile: string | null = null;
372
+ #adoptedArtifactManager: ArtifactManager | null = null;
373
+ #inMemoryArtifacts: Map<string, string> | null = null;
374
+ #inMemoryArtifactCounter = 0;
375
+
376
+ #suppressBreadcrumb = false;
377
+ #sessionNameChangedCallbacks = new Set<() => void>();
378
+
379
+ private constructor(cwd: string, sessionDir: string, persist: boolean, storage: SessionStorage) {
380
+ this.#cwd = cwd;
381
+ this.#sessionDir = sessionDir;
382
+ this.#persist = persist;
383
+ this.#storage = storage;
384
+ this.#blobs = new BlobStore(getBlobsDir());
385
+
386
+ if (persist && sessionDir) this.#storage.ensureDirSync(sessionDir);
387
+ }
388
+
389
+ #rememberBreadcrumb(cwd: string, sessionFile: string): void {
390
+ if (!this.#suppressBreadcrumb) writeTerminalBreadcrumb(cwd, sessionFile);
391
+ }
392
+
393
+ #clearDiskError(): void {
394
+ this.#diskFailure = undefined;
395
+ this.#diskFailureLogged = false;
396
+ }
397
+
398
+ #noteDiskFailure(errorLike: unknown): Error {
399
+ const error = toError(errorLike);
400
+ if (!this.#diskFailure) this.#diskFailure = error;
401
+
402
+ if (!this.#diskFailureLogged) {
403
+ this.#diskFailureLogged = true;
404
+ logger.error("Session persistence error.", {
405
+ sessionFile: this.#sessionFile,
406
+ error: error.message,
407
+ stack: error.stack,
408
+ });
409
+ }
410
+
411
+ return this.#diskFailure;
412
+ }
413
+
414
+ #scheduleDiskWork(work: () => Promise<void>, options: DiskQueueOptions = {}): Promise<void> {
415
+ const epoch = options.epoch ?? this.#diskEpoch;
416
+ const scheduled = this.#diskTail
417
+ .catch(() => undefined)
418
+ .then(async () => {
419
+ if (!options.ignoreEpoch && epoch !== this.#diskEpoch) return;
420
+ if (this.#diskFailure && !options.ignorePriorError) throw this.#diskFailure;
421
+ await work();
422
+ });
423
+
424
+ const reported = scheduled.catch(err => {
425
+ throw this.#noteDiskFailure(err);
426
+ });
427
+ this.#diskTail = reported.catch(() => undefined);
428
+ return reported;
429
+ }
430
+
431
+ async #drainAndCloseWriter(): Promise<void> {
432
+ try {
433
+ await this.#scheduleDiskWork(
434
+ async () => {
435
+ await this.#closeWriterHandle();
436
+ },
437
+ { ignorePriorError: true, ignoreEpoch: true },
438
+ );
439
+ } finally {
440
+ this.#writer = undefined;
441
+ this.#diskTail = Promise.resolve();
442
+ }
443
+ }
444
+
445
+ #closeWriterEventually(): void {
446
+ const writer = this.#writer;
447
+ this.#writer = undefined;
448
+ if (writer) void writer.close().catch(() => undefined);
449
+ }
450
+
451
+ async #closeWriterHandle(): Promise<void> {
452
+ const writer = this.#writer;
453
+ if (!writer) return;
454
+ this.#writer = undefined;
455
+ await writer.close();
456
+ }
457
+
458
+ #appendWriter(): SessionStorageWriter {
459
+ if (!this.#sessionFile) throw new Error("Cannot open a session writer before a session file exists");
460
+
461
+ if (this.#writer?.isOpen()) return this.#writer;
462
+
463
+ this.#writer = this.#storage.openWriter(this.#sessionFile, {
464
+ flags: "a",
465
+ onError: err => this.#noteDiskFailure(err),
466
+ });
467
+ return this.#writer;
468
+ }
469
+
470
+ #lineFor(entry: FileEntry): string {
471
+ return `${JSON.stringify(prepareEntryForPersistence(entry, this.#blobs))}\n`;
472
+ }
473
+
474
+ #fileBody(): string {
475
+ let body = this.#lineFor(this.#header);
476
+ for (const entry of this.#entries) body += this.#lineFor(entry);
477
+ return body;
478
+ }
479
+
480
+ #historyContainsAssistantMessage(): boolean {
481
+ return this.#entries.some(isAssistantEntry);
482
+ }
483
+
484
+ #shouldHaveSessionFile(): boolean {
485
+ return this.#forceFileCreation || this.#fileIsCurrent || this.#historyContainsAssistantMessage();
486
+ }
487
+
488
+ /**
489
+ * Synchronously rewrite the whole file (header + entries) and keep no open
490
+ * writer; the next append re-opens one. `writeTextSync` returns with the
491
+ * bytes in the kernel page cache, so the file is software-crash durable.
492
+ */
493
+ #rewriteSynchronously(): void {
494
+ if (!this.#persist || !this.#sessionFile || !this.#shouldHaveSessionFile()) return;
495
+
496
+ try {
497
+ const body = this.#fileBody();
498
+ this.#diskEpoch++;
499
+ this.#diskTail = Promise.resolve();
500
+ this.#closeWriterEventually();
501
+ this.#storage.writeTextSync(this.#sessionFile, body);
502
+ this.#fileIsCurrent = true;
503
+ this.#rewriteRequired = false;
504
+ } catch (err) {
505
+ this.#noteDiskFailure(err);
506
+ }
507
+ }
508
+
509
+ /**
510
+ * Rewrite the whole file atomically (temp-write + rename, EPERM-safe) on the
511
+ * disk chain. The body is serialized inside the task — after the writer is
512
+ * closed — so entries appended before the task runs are included.
513
+ */
514
+ async #rewriteAtomically(): Promise<void> {
515
+ if (!this.#persist || !this.#sessionFile) return;
516
+
517
+ const epoch = this.#diskEpoch;
518
+ await this.#scheduleDiskWork(
519
+ async () => {
520
+ await this.#closeWriterHandle();
521
+ const sessionFile = this.#sessionFile;
522
+ if (!sessionFile) return;
523
+ await this.#storage.writeTextAtomic(sessionFile, this.#fileBody());
524
+ this.#fileIsCurrent = true;
525
+ this.#rewriteRequired = false;
526
+ },
527
+ { epoch },
528
+ );
529
+ }
530
+
531
+ #appendToSessionFile(entry: SessionEntry): void {
532
+ if (!this.#persist || !this.#sessionFile) return;
533
+ if (this.#diskFailure) throw this.#diskFailure;
534
+
535
+ // Lazy gate: a brand-new session is not written until it has an assistant
536
+ // message (or someone forced creation), so sessions that never produce
537
+ // output never create a file.
538
+ if (!this.#shouldHaveSessionFile()) {
539
+ this.#fileIsCurrent = false;
540
+ return;
541
+ }
542
+
543
+ // Cold/divergent: not on disk yet, or in-memory entries diverged from the
544
+ // file → rewrite the whole file synchronously and keep going.
545
+ if (!this.#fileIsCurrent || this.#rewriteRequired) {
546
+ this.#rewriteSynchronously();
547
+ return;
548
+ }
549
+
550
+ // Hot path: append synchronously so the entry is durable the instant this
551
+ // returns (file/memory writers perform the write in-body). Never routed
552
+ // through the async disk chain — durability must hold without a flush().
553
+ // A mid-close writer leaves `#writer` undefined, so `#appendWriter` simply
554
+ // opens a fresh append handle and the entry still lands.
555
+ try {
556
+ void this.#appendWriter()
557
+ .append(this.#lineFor(entry))
558
+ .catch(err => this.#noteDiskFailure(err));
559
+ } catch (err) {
560
+ this.#noteDiskFailure(err);
561
+ }
562
+ }
563
+
564
+ #resetToNewSession(options?: NewSessionOptions, forcedSessionFile?: string): string | undefined {
565
+ this.#diskTail = Promise.resolve();
566
+ this.#clearDiskError();
567
+ this.#sessionId = mintSessionId();
568
+ this.#sessionName = undefined;
569
+ this.#titleSource = undefined;
570
+
571
+ const timestamp = nowIso();
572
+ this.#header = {
573
+ type: "session",
574
+ version: CURRENT_SESSION_VERSION,
575
+ id: this.#sessionId,
576
+ timestamp,
577
+ cwd: this.#cwd,
578
+ parentSession: options?.parentSession,
579
+ };
580
+
581
+ this.#entries = [];
582
+ this.#index.clear();
583
+ this.#fileIsCurrent = false;
584
+ this.#rewriteRequired = false;
585
+ this.#forceFileCreation = false;
586
+ this.#turnBudgetTotal = null;
587
+ this.#turnBudgetHard = false;
588
+ this.#turnOutputBaseline = 0;
589
+ this.#turnEvalOutput = 0;
590
+ this.#artifactManager = null;
591
+ this.#artifactManagerSessionFile = null;
592
+ this.#adoptedArtifactManager = null;
593
+ this.#inMemoryArtifacts = null;
594
+ this.#inMemoryArtifactCounter = 0;
595
+
596
+ if (this.#persist) {
597
+ this.#sessionFile =
598
+ forcedSessionFile ??
599
+ path.join(this.#sessionDir, `${fileSafeTimestamp(timestamp)}_${this.#sessionId}.jsonl`);
600
+ this.#rememberBreadcrumb(this.#cwd, this.#sessionFile);
601
+ } else {
602
+ this.#sessionFile = undefined;
603
+ }
604
+
605
+ return this.#sessionFile;
606
+ }
607
+
608
+ #applyEntries(header: SessionHeader, entries: SessionEntry[]): void {
609
+ this.#header = header;
610
+ this.#entries = entries;
611
+ this.#sessionId = header.id;
612
+ this.#sessionName = header.title;
613
+ this.#titleSource = header.titleSource;
614
+ this.#index.rebuild(entries);
615
+ }
616
+
617
+ #freshEntryFields(): { id: string; parentId: string | null; timestamp: string } {
618
+ return {
619
+ id: generateId(this.#index),
620
+ parentId: this.#index.leafId(),
621
+ timestamp: nowIso(),
622
+ };
623
+ }
624
+
625
+ #recordEntry(entry: SessionEntry): void {
626
+ this.#entries.push(entry);
627
+ this.#index.insert(entry);
628
+ this.#appendToSessionFile(entry);
629
+
630
+ const callback = this.onEntryAppended;
631
+ if (callback) {
632
+ try {
633
+ callback(entry);
634
+ } catch (err) {
635
+ logger.warn("collab entry hook failed", { error: String(err) });
636
+ }
637
+ }
638
+ }
639
+
640
+ #draftPath(): string | null {
641
+ const artifactsDir = this.getArtifactsDir();
642
+ return artifactsDir ? path.join(artifactsDir, "draft.txt") : null;
643
+ }
644
+
645
+ #artifactManagerForSession(): ArtifactManager | null {
646
+ if (this.#adoptedArtifactManager) return this.#adoptedArtifactManager;
647
+
648
+ const sessionFile = this.#sessionFile;
649
+ if (!sessionFile) {
650
+ this.#artifactManager = null;
651
+ this.#artifactManagerSessionFile = null;
652
+ return null;
653
+ }
654
+
655
+ if (this.#artifactManager && this.#artifactManagerSessionFile === sessionFile) return this.#artifactManager;
656
+
657
+ this.#artifactManager = new ArtifactManager(sessionFile.slice(0, -JSONL_SUFFIX_LENGTH));
658
+ this.#artifactManagerSessionFile = sessionFile;
659
+ return this.#artifactManager;
660
+ }
661
+
662
+ #notifySessionNameListeners(): void {
663
+ for (const callback of [...this.#sessionNameChangedCallbacks]) {
664
+ try {
665
+ callback();
666
+ } catch (err) {
667
+ logger.warn("SessionManager: session name change hook failed", { error: String(err) });
668
+ }
669
+ }
670
+ }
671
+
672
+ static #cleanTitle(raw: string): string {
673
+ return raw
674
+ .replace(/[\u0000-\u001f\u007f-\u009f]/g, " ")
675
+ .replace(/ +/g, " ")
676
+ .trim();
677
+ }
678
+
679
+ /** Puts a binary blob into the blob store and returns the blob reference. */
680
+ async putBlob(data: Buffer, options?: BlobPutOptions): Promise<BlobPutResult> {
681
+ return this.#blobs.put(data, options);
682
+ }
683
+
684
+ /** Synchronous variant of {@link putBlob} for rebuild-only render paths. */
685
+ putBlobSync(data: Buffer, options?: BlobPutOptions): BlobPutResult {
686
+ return this.#blobs.putSync(data, options);
687
+ }
688
+
689
+ captureState(): SessionManagerStateSnapshot {
690
+ return {
691
+ cwd: this.#cwd,
692
+ sessionDir: this.#sessionDir,
693
+ sessionId: this.#sessionId,
694
+ sessionName: this.#sessionName,
695
+ titleSource: this.#titleSource,
696
+ sessionFile: this.#sessionFile,
697
+ onDisk: this.#fileIsCurrent,
698
+ needsRewrite: this.#rewriteRequired,
699
+ // Snapshot header + entries by reference: switch/reload replaces the
700
+ // active header/array wholesale, so rollback needs no deep clone.
701
+ header: this.#header,
702
+ entries: [...this.#entries],
703
+ };
704
+ }
705
+
706
+ restoreState(snapshot: SessionManagerStateSnapshot): void {
707
+ this.#closeWriterEventually();
708
+ this.#diskTail = Promise.resolve();
709
+ this.#clearDiskError();
710
+
711
+ this.#cwd = snapshot.cwd;
712
+ this.#sessionDir = snapshot.sessionDir;
713
+ this.#sessionFile = snapshot.sessionFile;
714
+ this.#fileIsCurrent = snapshot.onDisk;
715
+ this.#rewriteRequired = snapshot.needsRewrite;
716
+ this.#forceFileCreation = snapshot.onDisk;
717
+ this.#applyEntries(snapshot.header, [...snapshot.entries]);
718
+ this.#sessionName = snapshot.sessionName;
719
+ this.#titleSource = snapshot.titleSource;
720
+ this.#artifactManager = null;
721
+ this.#artifactManagerSessionFile = null;
722
+ this.#adoptedArtifactManager = null;
723
+
724
+ if (this.#sessionFile) this.#rememberBreadcrumb(this.#cwd, this.#sessionFile);
725
+ }
726
+
727
+ /** Switch to a different session file (resume / branch). */
728
+ async setSessionFile(sessionFile: string): Promise<void> {
729
+ await this.#drainAndCloseWriter();
730
+ this.#clearDiskError();
731
+
732
+ const resolvedSessionFile = path.resolve(sessionFile);
733
+ this.#sessionFile = resolvedSessionFile;
734
+ this.#rememberBreadcrumb(this.#cwd, resolvedSessionFile);
735
+
736
+ const fileEntries = await loadEntriesFromFile(resolvedSessionFile, this.#storage);
737
+ if (fileEntries.length === 0) {
738
+ // Explicit but empty/missing path (e.g. --session flag): start fresh but
739
+ // keep the requested path and materialize the header immediately.
740
+ this.#resetToNewSession(undefined, resolvedSessionFile);
741
+ this.#forceFileCreation = true;
742
+ await this.#rewriteAtomically();
743
+ this.#fileIsCurrent = true;
744
+ return;
745
+ }
746
+
747
+ const migrated = migrateToCurrentVersion(fileEntries);
748
+ await resolveBlobRefsInEntries(fileEntries, this.#blobs);
749
+ // loadEntriesFromFile guarantees entries[0] is a valid session header.
750
+ const header = fileEntries[0] as SessionHeader;
751
+
752
+ // Adopt the loaded session's working directory. Sessions live in a dir
753
+ // keyed by their cwd, so resuming a session from another project must
754
+ // re-point cwd/sessionDir at that project — unless that project directory
755
+ // no longer exists on disk, in which case adopting it (and the process
756
+ // chdir interactive mode then performs) would fail with ENOENT. Keep the
757
+ // current cwd so the resumed session stays where the user already is.
758
+ const headerCwd = header.cwd ? path.resolve(header.cwd) : undefined;
759
+ if (headerCwd && headerCwd !== path.resolve(this.#cwd) && (await directoryExists(headerCwd))) {
760
+ this.#cwd = headerCwd;
761
+ this.#sessionDir = path.dirname(resolvedSessionFile);
762
+ this.#rememberBreadcrumb(this.#cwd, resolvedSessionFile);
763
+ }
764
+
765
+ this.#applyEntries(header, fileEntries.slice(1) as SessionEntry[]);
766
+ this.#fileIsCurrent = true;
767
+ this.#rewriteRequired = migrated;
768
+ this.#forceFileCreation = true;
769
+ this.#artifactManager = null;
770
+ this.#artifactManagerSessionFile = null;
771
+
772
+ if (this.sanitizeLoadedOpenAIResponsesReplayMetadata()) this.#rewriteRequired = true;
773
+ }
774
+
775
+ /** Start a new session. Drains and closes any existing writer first. */
776
+ async newSession(options?: NewSessionOptions): Promise<string | undefined> {
777
+ await this.#drainAndCloseWriter();
778
+ return this.#resetToNewSession(options);
779
+ }
780
+
781
+ /** Delete a session file and its artifact directory. ENOENT is treated as success. */
782
+ async dropSession(sessionPath: string): Promise<void> {
783
+ await this.#drainAndCloseWriter();
784
+ try {
785
+ await this.#storage.deleteSessionWithArtifacts(sessionPath);
786
+ } catch (err) {
787
+ if (!isEnoent(err)) throw err;
788
+ }
789
+ }
790
+
791
+ /**
792
+ * Fork the current session into a new file with the same entries.
793
+ * @returns the old and new session file paths, or undefined when not persisting.
794
+ */
795
+ async fork(): Promise<{ oldSessionFile: string; newSessionFile: string } | undefined> {
796
+ if (!this.#persist || !this.#sessionFile) return undefined;
797
+
798
+ const oldSessionFile = this.#sessionFile;
799
+ const parentSessionId = this.#sessionId;
800
+ await this.#drainAndCloseWriter();
801
+ this.#clearDiskError();
802
+
803
+ const timestamp = nowIso();
804
+ this.#sessionId = mintSessionId();
805
+ this.#sessionFile = path.join(this.#sessionDir, `${fileSafeTimestamp(timestamp)}_${this.#sessionId}.jsonl`);
806
+ this.#header = {
807
+ type: "session",
808
+ version: CURRENT_SESSION_VERSION,
809
+ id: this.#sessionId,
810
+ title: this.#header.title ?? this.#sessionName,
811
+ titleSource: this.#header.titleSource ?? this.#titleSource,
812
+ timestamp,
813
+ cwd: this.#cwd,
814
+ parentSession: parentSessionId,
815
+ };
816
+ this.#sessionName = this.#header.title;
817
+ this.#titleSource = this.#header.titleSource;
818
+ this.#fileIsCurrent = false;
819
+ this.#rewriteRequired = false;
820
+ this.#forceFileCreation = true;
821
+ this.#artifactManager = null;
822
+ this.#artifactManagerSessionFile = null;
823
+ this.#rememberBreadcrumb(this.#cwd, this.#sessionFile);
824
+
825
+ await this.#rewriteAtomically();
826
+ return { oldSessionFile, newSessionFile: this.#sessionFile };
827
+ }
828
+
829
+ /**
830
+ * Move the session to a new working directory: relocate the session file and
831
+ * artifacts on disk, update internal references, and rewrite the header cwd.
832
+ */
833
+ async moveTo(newCwd: string, targetSessionDir?: string): Promise<void> {
834
+ const resolvedCwd = path.resolve(newCwd);
835
+ const resolvedTargetDir = targetSessionDir ? path.resolve(targetSessionDir) : undefined;
836
+ if (
837
+ resolvedCwd === path.resolve(this.#cwd) &&
838
+ (!resolvedTargetDir || resolvedTargetDir === path.resolve(this.#sessionDir))
839
+ ) {
840
+ return;
841
+ }
842
+
843
+ const managedRoot = resolveManagedSessionRoot(this.#sessionDir, this.#cwd);
844
+ const nextSessionDir =
845
+ resolvedTargetDir ??
846
+ (managedRoot
847
+ ? computeDefaultSessionDir(resolvedCwd, this.#storage, managedRoot)
848
+ : computeDefaultSessionDir(resolvedCwd, this.#storage));
849
+
850
+ let sessionFileExisted = false;
851
+
852
+ if (this.#persist && this.#sessionFile) {
853
+ this.#storage.ensureDirSync(nextSessionDir);
854
+ await this.#drainAndCloseWriter();
855
+ this.#clearDiskError();
856
+
857
+ const oldSessionFile = this.#sessionFile;
858
+ const newSessionFile = path.join(nextSessionDir, path.basename(oldSessionFile));
859
+ const oldArtifactsDir = artifactsDirectoryFor(oldSessionFile)!;
860
+ const newArtifactsDir = artifactsDirectoryFor(newSessionFile)!;
861
+ const sessionPathChanged = path.resolve(oldSessionFile) !== path.resolve(newSessionFile);
862
+ const artifactPathChanged = path.resolve(oldArtifactsDir) !== path.resolve(newArtifactsDir);
863
+ sessionFileExisted = this.#storage.existsSync(oldSessionFile);
864
+
865
+ let sessionMoved = false;
866
+ let artifactsMoved = false;
867
+
868
+ try {
869
+ if (sessionFileExisted && sessionPathChanged) {
870
+ await fs.promises.rename(oldSessionFile, newSessionFile);
871
+ sessionMoved = true;
872
+ }
873
+
874
+ if (artifactPathChanged) {
875
+ try {
876
+ const artifactStat = await fs.promises.stat(oldArtifactsDir);
877
+ if (artifactStat.isDirectory()) {
878
+ await fs.promises.rename(oldArtifactsDir, newArtifactsDir);
879
+ artifactsMoved = true;
880
+ }
881
+ } catch (err) {
882
+ if (!isEnoent(err)) throw err;
883
+ }
884
+ }
885
+ } catch (err) {
886
+ if (artifactsMoved) {
887
+ try {
888
+ await fs.promises.rename(newArtifactsDir, oldArtifactsDir);
889
+ } catch (rollbackErr) {
890
+ throw new Error(
891
+ `Failed to move artifacts and rollback: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`,
892
+ );
893
+ }
894
+ }
895
+
896
+ if (sessionMoved) {
897
+ try {
898
+ await fs.promises.rename(newSessionFile, oldSessionFile);
899
+ } catch (rollbackErr) {
900
+ throw new Error(
901
+ `Failed to move session file and rollback: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`,
902
+ );
903
+ }
904
+ }
905
+
906
+ throw err;
907
+ }
908
+
909
+ this.#sessionFile = newSessionFile;
910
+ this.#artifactManager = null;
911
+ this.#artifactManagerSessionFile = null;
912
+ }
913
+
914
+ this.#cwd = resolvedCwd;
915
+ this.#sessionDir = nextSessionDir;
916
+ this.#header.cwd = resolvedCwd;
917
+
918
+ // Rewrite at the new location when the file already existed (update cwd) or
919
+ // there is in-memory output worth materializing; otherwise stay lazy.
920
+ const hasAssistant = this.#historyContainsAssistantMessage();
921
+ if (this.#persist && this.#sessionFile && (sessionFileExisted || hasAssistant)) {
922
+ this.#forceFileCreation = true;
923
+ await this.#rewriteAtomically();
924
+ }
925
+
926
+ if (this.#sessionFile) this.#rememberBreadcrumb(resolvedCwd, this.#sessionFile);
927
+ }
928
+
929
+ /**
930
+ * Force the session onto disk even with no assistant message yet (ACP
931
+ * session/new must create a discoverable file immediately).
932
+ */
933
+ async ensureOnDisk(): Promise<void> {
934
+ if (!this.#persist || !this.#sessionFile) return;
935
+ this.#forceFileCreation = true;
936
+ if (this.#fileIsCurrent && !this.#rewriteRequired) return;
937
+ await this.#rewriteAtomically();
938
+ }
939
+
940
+ /** Flush pending writes. Call before switching sessions or on shutdown. */
941
+ async flush(): Promise<void> {
942
+ if (!this.#persist || !this.#sessionFile) return;
943
+ await this.#scheduleDiskWork(async () => {
944
+ if (this.#writer?.isOpen()) await this.#writer.flush();
945
+ });
946
+ if (this.#diskFailure) throw this.#diskFailure;
947
+ }
948
+
949
+ /**
950
+ * Synchronously flush all in-memory entries to disk. Use when the process may
951
+ * exit before an async flush settles (Ctrl+C in the TUI). Software-crash
952
+ * durable; not atomic and not power-loss safe — a same-process crash never
953
+ * lands mid-`writeFileSync`.
954
+ */
955
+ flushSync(): void {
956
+ if (!this.#persist || !this.#sessionFile) return;
957
+ if (this.#diskFailure) throw this.#diskFailure;
958
+ this.#rewriteSynchronously();
959
+ if (this.#diskFailure) throw this.#diskFailure;
960
+ }
961
+
962
+ /** Flush, then close the append writer. */
963
+ async close(): Promise<void> {
964
+ if (!this.#persist) return;
965
+ await this.#scheduleDiskWork(async () => {
966
+ const hadWriter = this.#writer !== undefined;
967
+ await this.#closeWriterHandle();
968
+ if (hadWriter || (this.#sessionFile && this.#storage.existsSync(this.#sessionFile)))
969
+ this.#fileIsCurrent = true;
970
+ });
971
+ if (this.#diskFailure) throw this.#diskFailure;
972
+ }
973
+
974
+ getCwd(): string {
975
+ return this.#cwd;
976
+ }
977
+
978
+ getUsageStatistics(): UsageStatistics {
979
+ return this.#index.usageSnapshot();
980
+ }
981
+
982
+ /**
983
+ * Open a new per-turn budget window: snapshot the cumulative output baseline,
984
+ * reset the eval-subagent counter, and set the (optional) ceiling.
985
+ */
986
+ beginTurnBudget(total: number | null, hard: boolean): void {
987
+ this.#turnBudgetTotal = total;
988
+ this.#turnBudgetHard = hard;
989
+ this.#turnOutputBaseline = this.#index.usageSnapshot().output;
990
+ this.#turnEvalOutput = 0;
991
+ }
992
+
993
+ recordEvalSubagentOutput(output: number): void {
994
+ if (Number.isFinite(output) && output > 0) this.#turnEvalOutput += output;
995
+ }
996
+
997
+ getTurnBudget(): { total: number | null; spent: number; hard: boolean } {
998
+ const mainOutput = Math.max(0, this.#index.usageSnapshot().output - this.#turnOutputBaseline);
999
+ return { total: this.#turnBudgetTotal, spent: mainOutput + this.#turnEvalOutput, hard: this.#turnBudgetHard };
1000
+ }
1001
+
1002
+ getSessionDir(): string {
1003
+ return this.#sessionDir;
1004
+ }
1005
+
1006
+ getSessionId(): string {
1007
+ return this.#sessionId;
1008
+ }
1009
+
1010
+ getSessionFile(): string | undefined {
1011
+ return this.#sessionFile;
1012
+ }
1013
+
1014
+ getArtifactsDir(): string | null {
1015
+ if (this.#adoptedArtifactManager) return this.#adoptedArtifactManager.dir;
1016
+ return artifactsDirectoryFor(this.#sessionFile);
1017
+ }
1018
+
1019
+ adoptArtifactManager(manager: ArtifactManager): void {
1020
+ this.#adoptedArtifactManager = manager;
1021
+ }
1022
+
1023
+ getArtifactManager(): ArtifactManager | null {
1024
+ return this.#artifactManagerForSession();
1025
+ }
1026
+
1027
+ async allocateArtifactPath(toolType: string): Promise<{ id?: string; path?: string }> {
1028
+ return (await this.#artifactManagerForSession()?.allocatePath(toolType)) ?? {};
1029
+ }
1030
+
1031
+ async saveArtifact(content: string, toolType: string): Promise<string | undefined> {
1032
+ const manager = this.#artifactManagerForSession();
1033
+ if (manager) return manager.save(content, toolType);
1034
+
1035
+ // Non-persistent session: keep an in-memory copy so spill truncation works.
1036
+ this.#inMemoryArtifacts ??= new Map();
1037
+ const id = String(this.#inMemoryArtifactCounter++);
1038
+ this.#inMemoryArtifacts.set(id, content);
1039
+ return id;
1040
+ }
1041
+
1042
+ async getArtifactPath(id: string): Promise<string | null> {
1043
+ return (await this.#artifactManagerForSession()?.getPath(id)) ?? null;
1044
+ }
1045
+
1046
+ async saveDraft(text: string): Promise<void> {
1047
+ const draftPath = this.#draftPath();
1048
+ if (!draftPath || !this.#persist) return;
1049
+
1050
+ if (text.length === 0) {
1051
+ try {
1052
+ await this.#storage.unlink(draftPath);
1053
+ } catch (err) {
1054
+ if (!isEnoent(err)) throw err;
1055
+ }
1056
+ return;
1057
+ }
1058
+
1059
+ // Force the header onto disk so resume can find the file this draft attaches to.
1060
+ await this.ensureOnDisk();
1061
+ await this.#storage.writeText(draftPath, text);
1062
+ }
1063
+
1064
+ async consumeDraft(): Promise<string | null> {
1065
+ const draftPath = this.#draftPath();
1066
+ if (!draftPath) return null;
1067
+
1068
+ let draft: string;
1069
+ try {
1070
+ draft = await this.#storage.readText(draftPath);
1071
+ } catch (err) {
1072
+ if (isEnoent(err)) return null;
1073
+ throw err;
1074
+ }
1075
+
1076
+ try {
1077
+ await this.#storage.unlink(draftPath);
1078
+ } catch (err) {
1079
+ if (!isEnoent(err)) throw err;
1080
+ }
1081
+
1082
+ return draft;
1083
+ }
1084
+
1085
+ /** The source that set the session name: "user" (manual/RPC) or "auto" (generated title). */
1086
+ get titleSource(): "auto" | "user" | undefined {
1087
+ return this.#titleSource;
1088
+ }
1089
+
1090
+ getSessionName(): string | undefined {
1091
+ return this.#sessionName;
1092
+ }
1093
+
1094
+ onSessionNameChanged(cb: () => void): () => void {
1095
+ this.#sessionNameChangedCallbacks.add(cb);
1096
+ return () => {
1097
+ this.#sessionNameChangedCallbacks.delete(cb);
1098
+ };
1099
+ }
1100
+
1101
+ /**
1102
+ * Set the session display name.
1103
+ * @param source "user" for explicit renames; "auto" for generated titles.
1104
+ * Auto titles are ignored once the user has set a name.
1105
+ */
1106
+ async setSessionName(name: string, source: "auto" | "user" = "auto"): Promise<boolean> {
1107
+ if (this.#titleSource === "user" && source === "auto") return false;
1108
+
1109
+ const title = SessionManager.#cleanTitle(name);
1110
+ if (!title) return false;
1111
+
1112
+ this.#sessionName = title;
1113
+ this.#titleSource = source;
1114
+ this.#header.title = title;
1115
+ this.#header.titleSource = source;
1116
+
1117
+ if (this.#persist && this.#sessionFile && this.#storage.existsSync(this.#sessionFile)) {
1118
+ await this.#rewriteAtomically();
1119
+ }
1120
+
1121
+ this.#notifySessionNameListeners();
1122
+ return true;
1123
+ }
1124
+
1125
+ /**
1126
+ * Append a foreign (host-authored) entry verbatim, preserving its
1127
+ * `id`/`parentId`. Used by collab guests to mirror the host session.
1128
+ */
1129
+ ingestReplicatedEntry(entry: SessionEntry): void {
1130
+ this.#recordEntry(entry);
1131
+ }
1132
+
1133
+ /**
1134
+ * Snapshot the session for collab replication: the live header plus a deep
1135
+ * copy of every entry (the host mutates entries in place on rewrite paths, so
1136
+ * guests must not share references).
1137
+ */
1138
+ snapshotForReplication(): { header: SessionHeader; entries: SessionEntry[] } {
1139
+ return { header: structuredClone(this.#header), entries: structuredClone(this.#entries) as SessionEntry[] };
1140
+ }
1141
+
1142
+ /**
1143
+ * Append a message as a child of the current leaf, then advance the leaf.
1144
+ * CompactionSummaryMessage / BranchSummaryMessage are rejected here — they are
1145
+ * top-level entries via appendCompaction()/branchWithSummary().
1146
+ */
1147
+ appendMessage(
1148
+ message:
1149
+ | Message
1150
+ | CustomMessage
1151
+ | HookMessage
1152
+ | BashExecutionMessage
1153
+ | PythonExecutionMessage
1154
+ | FileMentionMessage,
1155
+ ): string {
1156
+ const entry: SessionMessageEntry = { type: "message", ...this.#freshEntryFields(), message };
1157
+ this.#recordEntry(entry);
1158
+ return entry.id;
1159
+ }
1160
+
1161
+ appendThinkingLevelChange(thinkingLevel?: string): string {
1162
+ const entry: ThinkingLevelChangeEntry = {
1163
+ type: "thinking_level_change",
1164
+ ...this.#freshEntryFields(),
1165
+ thinkingLevel: thinkingLevel ?? null,
1166
+ };
1167
+ this.#recordEntry(entry);
1168
+ return entry.id;
1169
+ }
1170
+
1171
+ appendServiceTierChange(serviceTier: ServiceTier | null): string {
1172
+ const entry: ServiceTierChangeEntry = { type: "service_tier_change", ...this.#freshEntryFields(), serviceTier };
1173
+ this.#recordEntry(entry);
1174
+ return entry.id;
1175
+ }
1176
+
1177
+ appendModeChange(mode: string, data?: Record<string, unknown>): string {
1178
+ const entry: ModeChangeEntry = { type: "mode_change", ...this.#freshEntryFields(), mode, data };
1179
+ this.#recordEntry(entry);
1180
+ return entry.id;
1181
+ }
1182
+
1183
+ /**
1184
+ * Append a model change as a child of the current leaf, then advance the leaf.
1185
+ * @param model Model in "provider/modelId" format
1186
+ * @param role Optional role (default: "default")
1187
+ */
1188
+ appendModelChange(model: string, role?: string): string {
1189
+ const entry: ModelChangeEntry = { type: "model_change", ...this.#freshEntryFields(), model, role };
1190
+ this.#recordEntry(entry);
1191
+ return entry.id;
1192
+ }
1193
+
1194
+ appendSessionInit(init: {
1195
+ systemPrompt: string;
1196
+ task: string;
1197
+ tools: string[];
1198
+ outputSchema?: unknown;
1199
+ spawns?: string;
1200
+ readSummarize?: boolean;
1201
+ }): string {
1202
+ const entry: SessionInitEntry = { type: "session_init", ...this.#freshEntryFields(), ...init };
1203
+ this.#recordEntry(entry);
1204
+ return entry.id;
1205
+ }
1206
+
1207
+ appendCompaction<T = unknown>(
1208
+ summary: string,
1209
+ shortSummary: string | undefined,
1210
+ firstKeptEntryId: string,
1211
+ tokensBefore: number,
1212
+ details?: T,
1213
+ fromExtension?: boolean,
1214
+ preserveData?: Record<string, unknown>,
1215
+ ): string {
1216
+ const entry: CompactionEntry<T> = {
1217
+ type: "compaction",
1218
+ ...this.#freshEntryFields(),
1219
+ summary,
1220
+ shortSummary,
1221
+ firstKeptEntryId,
1222
+ tokensBefore,
1223
+ details,
1224
+ fromExtension,
1225
+ preserveData,
1226
+ };
1227
+ this.#recordEntry(entry);
1228
+ return entry.id;
1229
+ }
1230
+
1231
+ appendCustomEntry(customType: string, data?: unknown): string {
1232
+ const entry: CustomEntry = { type: "custom", customType, data, ...this.#freshEntryFields() };
1233
+ this.#recordEntry(entry);
1234
+ return entry.id;
1235
+ }
1236
+
1237
+ /**
1238
+ * Rewrite the session file after in-place entry updates (e.g. pruning old tool
1239
+ * outputs). Use sparingly.
1240
+ */
1241
+ async rewriteEntries(): Promise<void> {
1242
+ if (!this.#persist || !this.#sessionFile) return;
1243
+ await this.#rewriteAtomically();
1244
+ }
1245
+
1246
+ /**
1247
+ * Append a custom message entry (for extensions) that participates in LLM context.
1248
+ * @param customType Hook identifier for filtering on reload
1249
+ * @param content Message content (string or TextContent/ImageContent array)
1250
+ * @param display Whether to show in TUI (true = styled display, false = hidden)
1251
+ * @param details Optional extension-specific metadata (not sent to LLM)
1252
+ * @param attribution Who initiated this message for billing/attribution semantics
1253
+ */
1254
+ appendCustomMessageEntry<T = unknown>(
1255
+ customType: string,
1256
+ content: string | (TextContent | ImageContent)[],
1257
+ display: boolean,
1258
+ details?: T,
1259
+ attribution: MessageAttribution = "agent",
1260
+ ): string {
1261
+ const entry: CustomMessageEntry<T> = {
1262
+ type: "custom_message",
1263
+ customType,
1264
+ content,
1265
+ display,
1266
+ // Drop AgentSession-internal transient fields before disk persistence.
1267
+ details: stripInternalDetailsFields(details),
1268
+ attribution,
1269
+ ...this.#freshEntryFields(),
1270
+ };
1271
+ this.#recordEntry(entry);
1272
+ return entry.id;
1273
+ }
1274
+
1275
+ /**
1276
+ * Append an MCP tool selection entry recording the discovery-selected MCP tools.
1277
+ */
1278
+ appendMCPToolSelection(selectedToolNames: string[]): string {
1279
+ const entry: MCPToolSelectionEntry = {
1280
+ type: "mcp_tool_selection",
1281
+ ...this.#freshEntryFields(),
1282
+ selectedToolNames: [...selectedToolNames],
1283
+ };
1284
+ this.#recordEntry(entry);
1285
+ return entry.id;
1286
+ }
1287
+
1288
+ /** Append a TTSR injection entry recording which rules were injected. */
1289
+ appendTtsrInjection(ruleNames: string[]): string {
1290
+ const entry: TtsrInjectionEntry = {
1291
+ type: "ttsr_injection",
1292
+ ...this.#freshEntryFields(),
1293
+ injectedRules: [...ruleNames],
1294
+ };
1295
+ this.#recordEntry(entry);
1296
+ return entry.id;
1297
+ }
1298
+
1299
+ /** All unique TTSR rule names injected on the current branch (root → leaf). */
1300
+ getInjectedTtsrRules(): string[] {
1301
+ const names = new Set<string>();
1302
+ for (const entry of this.getBranch()) {
1303
+ if (entry.type !== "ttsr_injection") continue;
1304
+ for (const name of entry.injectedRules) names.add(name);
1305
+ }
1306
+ return [...names];
1307
+ }
1308
+
1309
+ getLeafId(): string | null {
1310
+ return this.#index.leafId();
1311
+ }
1312
+
1313
+ getLeafEntry(): SessionEntry | undefined {
1314
+ return this.#index.leafEntry();
1315
+ }
1316
+
1317
+ /**
1318
+ * The most recent model role on the current branch, or undefined when no
1319
+ * model change has been recorded.
1320
+ */
1321
+ getLastModelChangeRole(): string | undefined {
1322
+ const branch = this.getBranch();
1323
+ for (let index = branch.length - 1; index >= 0; index--) {
1324
+ const entry = branch[index];
1325
+ if (entry.type === "model_change") return entry.role ?? "default";
1326
+ }
1327
+ return undefined;
1328
+ }
1329
+
1330
+ getEntry(id: string): SessionEntry | undefined {
1331
+ return this.#index.get(id);
1332
+ }
1333
+
1334
+ /** All direct children of an entry. */
1335
+ getChildren(parentId: string): SessionEntry[] {
1336
+ return this.#index.childrenOf(parentId);
1337
+ }
1338
+
1339
+ getLabel(id: string): string | undefined {
1340
+ return this.#index.labelFor(id);
1341
+ }
1342
+
1343
+ /**
1344
+ * Set or clear a label on an entry. Pass undefined/empty to clear.
1345
+ */
1346
+ appendLabelChange(targetId: string, label: string | undefined): string {
1347
+ if (!this.#index.has(targetId)) throw new Error(`Entry ${targetId} not found`);
1348
+
1349
+ const entry: LabelEntry = { type: "label", ...this.#freshEntryFields(), targetId, label };
1350
+ this.#recordEntry(entry);
1351
+ return entry.id;
1352
+ }
1353
+
1354
+ /**
1355
+ * Walk from an entry to root, returning entries in path order. Includes all
1356
+ * entry types; use buildSessionContext() for the resolved LLM messages.
1357
+ */
1358
+ getBranch(fromId?: string): SessionEntry[] {
1359
+ return this.#index.pathTo(fromId ?? this.#index.leafId());
1360
+ }
1361
+
1362
+ /**
1363
+ * Build the session context (LLM messages), or — with `{ transcript: true }` —
1364
+ * the full-history display transcript, from the current leaf path.
1365
+ */
1366
+ buildSessionContext(options?: BuildSessionContextOptions): SessionContext {
1367
+ return buildSessionContext(this.#entries, this.#index.leafId(), this.#index.entriesById(), options);
1368
+ }
1369
+
1370
+ /** Strip stale OpenAI Responses assistant replay metadata from loaded entries. */
1371
+ sanitizeLoadedOpenAIResponsesReplayMetadata(): boolean {
1372
+ let changed = false;
1373
+ for (const entry of this.#entries) {
1374
+ if (entry.type !== "message" || entry.message.role !== "assistant") continue;
1375
+
1376
+ const sanitized = sanitizeRehydratedOpenAIResponsesAssistantMessage(entry.message);
1377
+ if (sanitized === entry.message) continue;
1378
+
1379
+ entry.message = sanitized;
1380
+ changed = true;
1381
+ }
1382
+
1383
+ return changed;
1384
+ }
1385
+
1386
+ getHeader(): SessionHeader | null {
1387
+ return this.#header;
1388
+ }
1389
+
1390
+ /** All session entries (excludes header). Returns a shallow copy. */
1391
+ getEntries(): SessionEntry[] {
1392
+ return [...this.#entries];
1393
+ }
1394
+
1395
+ /**
1396
+ * The session as a tree. A well-formed session has exactly one root; orphaned
1397
+ * entries (broken parent chain) are returned as roots too.
1398
+ */
1399
+ getTree(): SessionTreeNode[] {
1400
+ return this.#index.tree(this.#entries);
1401
+ }
1402
+
1403
+ /**
1404
+ * Move the leaf to an earlier entry so the next append forms a new branch.
1405
+ * Existing entries are never modified or deleted.
1406
+ */
1407
+ branch(branchFromId: string): void {
1408
+ if (!this.#index.has(branchFromId)) throw new Error(`Entry ${branchFromId} not found`);
1409
+ this.#index.setLeaf(branchFromId);
1410
+ }
1411
+
1412
+ /** Reset the leaf to null so the next append creates a new root entry. */
1413
+ resetLeaf(): void {
1414
+ this.#index.setLeaf(null);
1415
+ }
1416
+
1417
+ /** Like branch(), but also records a branch_summary of the abandoned path. */
1418
+ branchWithSummary(branchFromId: string | null, summary: string, details?: unknown, fromExtension?: boolean): string {
1419
+ if (branchFromId !== null && !this.#index.has(branchFromId)) throw new Error(`Entry ${branchFromId} not found`);
1420
+
1421
+ this.#index.setLeaf(branchFromId);
1422
+ const entry: BranchSummaryEntry = {
1423
+ type: "branch_summary",
1424
+ id: generateId(this.#index),
1425
+ parentId: branchFromId,
1426
+ timestamp: nowIso(),
1427
+ fromId: branchFromId ?? "root",
1428
+ summary,
1429
+ details,
1430
+ fromExtension,
1431
+ };
1432
+ this.#recordEntry(entry);
1433
+ return entry.id;
1434
+ }
1435
+
1436
+ /**
1437
+ * Create a new session file containing only the path from root to `leafId`.
1438
+ * Returns the new file path, or undefined when not persisting.
1439
+ */
1440
+ createBranchedSession(leafId: string): string | undefined {
1441
+ const sourceSessionFile = this.#sessionFile;
1442
+ const branchPath = this.getBranch(leafId);
1443
+ if (branchPath.length === 0) throw new Error(`Entry ${leafId} not found`);
1444
+
1445
+ // Drop label entries from the path; recreate them fresh from the resolved map.
1446
+ const entriesToKeep = branchPath.filter(entry => entry.type !== "label");
1447
+ const keptIds = new Set(entriesToKeep.map(entry => entry.id));
1448
+ const labelsToCarry: Array<{ targetId: string; label: string }> = [];
1449
+ for (const [targetId, label] of this.#index.labelsInEffect()) {
1450
+ if (keptIds.has(targetId)) labelsToCarry.push({ targetId, label });
1451
+ }
1452
+
1453
+ const timestamp = nowIso();
1454
+ const newSessionId = mintSessionId();
1455
+ const newSessionFile = path.join(this.#sessionDir, `${fileSafeTimestamp(timestamp)}_${newSessionId}.jsonl`);
1456
+ const header: SessionHeader = {
1457
+ type: "session",
1458
+ version: CURRENT_SESSION_VERSION,
1459
+ id: newSessionId,
1460
+ timestamp,
1461
+ cwd: this.#cwd,
1462
+ parentSession: this.#persist ? sourceSessionFile : undefined,
1463
+ };
1464
+
1465
+ const labels: LabelEntry[] = [];
1466
+ let parentId = entriesToKeep[entriesToKeep.length - 1]?.id ?? null;
1467
+ for (const carried of labelsToCarry) {
1468
+ const labelEntry: LabelEntry = {
1469
+ type: "label",
1470
+ id: generateId(new Set([...keptIds, ...labels.map(entry => entry.id)])),
1471
+ parentId,
1472
+ timestamp: nowIso(),
1473
+ targetId: carried.targetId,
1474
+ label: carried.label,
1475
+ };
1476
+ labels.push(labelEntry);
1477
+ parentId = labelEntry.id;
1478
+ }
1479
+
1480
+ this.#header = header;
1481
+ this.#entries = [...entriesToKeep, ...labels];
1482
+ this.#sessionId = newSessionId;
1483
+ this.#sessionName = header.title;
1484
+ this.#titleSource = header.titleSource;
1485
+ this.#index.rebuild(this.#entries);
1486
+ this.#artifactManager = null;
1487
+ this.#artifactManagerSessionFile = null;
1488
+ this.#forceFileCreation = this.#persist;
1489
+
1490
+ if (!this.#persist) {
1491
+ this.#sessionFile = undefined;
1492
+ this.#fileIsCurrent = false;
1493
+ this.#rewriteRequired = false;
1494
+ return undefined;
1495
+ }
1496
+
1497
+ this.#sessionFile = newSessionFile;
1498
+ this.#rewriteSynchronously();
1499
+ this.#rememberBreadcrumb(this.#cwd, newSessionFile);
1500
+ return newSessionFile;
1501
+ }
1502
+
1503
+ /** Resolve the canonical default session directory for a cwd. */
1504
+ static getDefaultSessionDir(
1505
+ cwd: string,
1506
+ agentDir?: string,
1507
+ storage: SessionStorage = new FileSessionStorage(),
1508
+ ): string {
1509
+ return computeDefaultSessionDir(cwd, storage, getSessionsDir(agentDir));
1510
+ }
1511
+
1512
+ /**
1513
+ * Create a new session.
1514
+ * @param cwd Working directory (stored in the session header)
1515
+ * @param sessionDir Optional session directory; defaults to the cwd-derived dir.
1516
+ */
1517
+ static create(cwd: string, sessionDir?: string, storage: SessionStorage = new FileSessionStorage()): SessionManager {
1518
+ const dir = sessionDir ?? SessionManager.getDefaultSessionDir(cwd, undefined, storage);
1519
+ const manager = new SessionManager(cwd, dir, true, storage);
1520
+ manager.#resetToNewSession();
1521
+ return manager;
1522
+ }
1523
+
1524
+ /**
1525
+ * Fork a session into the current project directory: copy history from another
1526
+ * session file while creating a fresh session file in this sessionDir.
1527
+ *
1528
+ * `options.sessionFile` pins the new session's file path (default: an
1529
+ * auto-named `<timestamp>_<id>.jsonl` in `sessionDir`). Callers that register
1530
+ * the fork as a named agent (e.g. `/tan`) pass `<agentId>.jsonl` so the
1531
+ * persisted-subagent scan keys the agent by the same id the live ref uses.
1532
+ */
1533
+ static async forkFrom(
1534
+ sourcePath: string,
1535
+ cwd: string,
1536
+ sessionDir?: string,
1537
+ storage: SessionStorage = new FileSessionStorage(),
1538
+ options?: { suppressBreadcrumb?: boolean; sessionFile?: string },
1539
+ ): Promise<SessionManager> {
1540
+ const dir = sessionDir ?? SessionManager.getDefaultSessionDir(cwd, undefined, storage);
1541
+ const manager = new SessionManager(cwd, dir, true, storage);
1542
+ manager.#suppressBreadcrumb = options?.suppressBreadcrumb === true;
1543
+
1544
+ const sourceEntries = structuredClone(await loadEntriesFromFile(sourcePath, storage)) as FileEntry[];
1545
+ migrateToCurrentVersion(sourceEntries);
1546
+ await resolveBlobRefsInEntries(sourceEntries, manager.#blobs);
1547
+
1548
+ const sourceHeader = sourceEntries.find(entry => entry.type === "session") as SessionHeader | undefined;
1549
+ const history = sourceEntries.filter(entry => entry.type !== "session") as SessionEntry[];
1550
+ manager.#resetToNewSession({ parentSession: sourceHeader?.id }, options?.sessionFile);
1551
+ manager.#header.title = sourceHeader?.title;
1552
+ manager.#header.titleSource = sourceHeader?.titleSource;
1553
+ manager.#sessionName = manager.#header.title;
1554
+ manager.#titleSource = manager.#header.titleSource;
1555
+ manager.#entries = history;
1556
+ manager.#index.rebuild(history);
1557
+ manager.sanitizeLoadedOpenAIResponsesReplayMetadata();
1558
+ manager.#forceFileCreation = true;
1559
+ await manager.#rewriteAtomically();
1560
+ return manager;
1561
+ }
1562
+
1563
+ /**
1564
+ * Open a specific session file.
1565
+ * @param sessionDir Optional dir for /new or /branch; defaults to the file's parent.
1566
+ * @param options.initialCwd Cwd to use when the file is empty or missing.
1567
+ */
1568
+ static async open(
1569
+ filePath: string,
1570
+ sessionDir?: string,
1571
+ storage: SessionStorage = new FileSessionStorage(),
1572
+ options?: { initialCwd?: string; suppressBreadcrumb?: boolean },
1573
+ ): Promise<SessionManager> {
1574
+ const loaded = await loadEntriesFromFile(filePath, storage);
1575
+ const header = loaded.find(entry => entry.type === "session") as SessionHeader | undefined;
1576
+ // Resume into the session's recorded cwd only when that directory still
1577
+ // exists. A deleted project dir would make the constructor's #cwd — and the
1578
+ // `setProjectDir` chdir interactive mode runs next — point at (and fail on)
1579
+ // a missing path, so fall back to the launch cwd and anchor /new and /branch
1580
+ // there too, keeping the resumed session where the user already is.
1581
+ const recordedCwd = header?.cwd;
1582
+ const recordedCwdUsable = !!recordedCwd && (await directoryExists(recordedCwd));
1583
+ const cwd = recordedCwdUsable ? recordedCwd : (options?.initialCwd ?? getProjectDir());
1584
+ const dir =
1585
+ sessionDir ??
1586
+ (recordedCwd && !recordedCwdUsable
1587
+ ? SessionManager.getDefaultSessionDir(cwd, undefined, storage)
1588
+ : path.dirname(path.resolve(filePath)));
1589
+ const manager = new SessionManager(cwd, dir, true, storage);
1590
+ manager.#suppressBreadcrumb = options?.suppressBreadcrumb === true;
1591
+ await manager.setSessionFile(filePath);
1592
+ return manager;
1593
+ }
1594
+
1595
+ /**
1596
+ * Lock-free peek for cold subagent revival: returns the recorded working
1597
+ * directory (session header) and the latest `session_init` contract (system
1598
+ * prompt / tools / output schema) WITHOUT taking the single-writer lock that
1599
+ * {@link open} acquires — the caller re-opens for the actual revive. Returns
1600
+ * null when the file can't be read; `init` is null for files written before
1601
+ * `session_init` was recorded (no faithful contract to rebuild from).
1602
+ */
1603
+ static async peekSessionInit(
1604
+ filePath: string,
1605
+ storage: SessionStorage = new FileSessionStorage(),
1606
+ ): Promise<{
1607
+ cwd: string;
1608
+ init: {
1609
+ systemPrompt: string;
1610
+ task: string;
1611
+ tools: string[];
1612
+ outputSchema?: unknown;
1613
+ spawns?: string;
1614
+ readSummarize?: boolean;
1615
+ } | null;
1616
+ } | null> {
1617
+ let loaded: FileEntry[];
1618
+ try {
1619
+ loaded = await loadEntriesFromFile(filePath, storage);
1620
+ } catch {
1621
+ return null;
1622
+ }
1623
+ // A missing/empty file has no usable session — nothing to revive from.
1624
+ if (loaded.length === 0) return null;
1625
+ const header = loaded.find(entry => entry.type === "session") as SessionHeader | undefined;
1626
+ let init: {
1627
+ systemPrompt: string;
1628
+ task: string;
1629
+ tools: string[];
1630
+ outputSchema?: unknown;
1631
+ spawns?: string;
1632
+ readSummarize?: boolean;
1633
+ } | null = null;
1634
+ for (let index = loaded.length - 1; index >= 0; index--) {
1635
+ const entry = loaded[index];
1636
+ if (entry.type === "session_init") {
1637
+ init = {
1638
+ systemPrompt: entry.systemPrompt,
1639
+ task: entry.task,
1640
+ tools: entry.tools,
1641
+ outputSchema: entry.outputSchema,
1642
+ readSummarize: entry.readSummarize,
1643
+ spawns: entry.spawns,
1644
+ };
1645
+ break;
1646
+ }
1647
+ }
1648
+ return { cwd: header?.cwd ?? getProjectDir(), init };
1649
+ }
1650
+
1651
+ /** Continue the most recent session, or create a new one if none exists. */
1652
+ static async continueRecent(
1653
+ cwd: string,
1654
+ sessionDir?: string,
1655
+ storage: SessionStorage = new FileSessionStorage(),
1656
+ ): Promise<SessionManager> {
1657
+ const dir = sessionDir ?? SessionManager.getDefaultSessionDir(cwd, undefined, storage);
1658
+ const resolvedCwd = path.resolve(cwd);
1659
+ const breadcrumb = await readTerminalBreadcrumbEntry();
1660
+ let chosenSession: string | null | undefined;
1661
+
1662
+ if (breadcrumb) {
1663
+ // Recover stale crumbs: a subagent open (pre-fix) may have pointed this
1664
+ // terminal's breadcrumb at an artifact child; resume the parent instead.
1665
+ breadcrumb.sessionFile = resolveBreadcrumbToInteractiveRoot(breadcrumb.sessionFile);
1666
+ const breadcrumbCwd = path.resolve(breadcrumb.cwd);
1667
+ if (breadcrumbCwd === resolvedCwd) {
1668
+ chosenSession = breadcrumb.sessionFile;
1669
+ } else {
1670
+ // The terminal's last session started in a different cwd. If that cwd is
1671
+ // gone (worktree move/rename) and this location has no sessions of its
1672
+ // own, re-root the moved session here instead of starting fresh. When an
1673
+ // explicit sessionDir is reused across the move, the stale breadcrumb file
1674
+ // may be the newest entry there; prefer a genuine current-cwd session.
1675
+ let newestInTargetDir = await findMostRecentSession(dir, storage);
1676
+ const breadcrumbFile = path.resolve(breadcrumb.sessionFile);
1677
+ const breadcrumbCwdMissing = !fs.existsSync(breadcrumbCwd);
1678
+ const newestIsBreadcrumb = newestInTargetDir ? path.resolve(newestInTargetDir) === breadcrumbFile : false;
1679
+ let currentProjectAlreadyHasSession = false;
1680
+
1681
+ if (breadcrumbCwdMissing && newestIsBreadcrumb) {
1682
+ const localSession = (await SessionManager.list(cwd, dir, storage)).find(
1683
+ session =>
1684
+ path.resolve(session.path) !== breadcrumbFile &&
1685
+ session.cwd &&
1686
+ path.resolve(session.cwd) === resolvedCwd,
1687
+ );
1688
+ if (localSession) {
1689
+ newestInTargetDir = localSession.path;
1690
+ currentProjectAlreadyHasSession = true;
1691
+ }
1692
+ }
1693
+
1694
+ const looksLikeMovedProject =
1695
+ breadcrumbCwdMissing &&
1696
+ (newestInTargetDir === null || (newestIsBreadcrumb && !currentProjectAlreadyHasSession));
1697
+ if (looksLikeMovedProject) {
1698
+ logger.info("Re-rooting moved session", { from: breadcrumbCwd, to: resolvedCwd });
1699
+ // Anchor at the gone breadcrumb cwd so the moveTo below relocates the
1700
+ // session: open() now falls back to the launch cwd for a missing
1701
+ // recorded cwd, which would no-op moveTo when it equals `cwd`.
1702
+ const manager = await SessionManager.open(breadcrumb.sessionFile, undefined, storage, {
1703
+ initialCwd: breadcrumbCwd,
1704
+ });
1705
+ await manager.moveTo(cwd, sessionDir);
1706
+ return manager;
1707
+ }
1708
+
1709
+ chosenSession = newestInTargetDir;
1710
+ }
1711
+ }
1712
+
1713
+ if (chosenSession === undefined) chosenSession = await findMostRecentSession(dir, storage);
1714
+
1715
+ const manager = new SessionManager(cwd, dir, true, storage);
1716
+ if (chosenSession) await manager.setSessionFile(chosenSession);
1717
+ else manager.#resetToNewSession();
1718
+ return manager;
1719
+ }
1720
+
1721
+ /** Create an in-memory session (no file persistence). */
1722
+ static inMemory(
1723
+ cwd: string = getProjectDir(),
1724
+ storage: SessionStorage = new MemorySessionStorage(),
1725
+ ): SessionManager {
1726
+ const manager = new SessionManager(cwd, "", false, storage);
1727
+ manager.#resetToNewSession();
1728
+ return manager;
1729
+ }
1730
+
1731
+ /**
1732
+ * List sessions for a project directory.
1733
+ * @param sessionDir Optional dir; defaults to the cwd-derived dir.
1734
+ */
1735
+ static async list(
1736
+ cwd: string,
1737
+ sessionDir?: string,
1738
+ storage: SessionStorage = new FileSessionStorage(),
1739
+ ): Promise<SessionInfo[]> {
1740
+ const dir = sessionDir ?? SessionManager.getDefaultSessionDir(cwd, undefined, storage);
1741
+ return listSessions(dir, storage);
1742
+ }
1743
+
1744
+ /** List all sessions across all project directories. */
1745
+ static listAll(storage: SessionStorage = new FileSessionStorage()): Promise<SessionInfo[]> {
1746
+ return listAllSessions(storage);
1747
+ }
1748
+ }