@kr78/pi-coding-agent 17.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1593) hide show
  1. package/CHANGELOG.md +14861 -0
  2. package/README.md +35 -0
  3. package/dist/CHANGELOG-chtdxh1z.md +14861 -0
  4. package/dist/cli.js +20483 -0
  5. package/dist/template-c2hyaytt.js +1653 -0
  6. package/dist/template-f8wx9vfn.css +1355 -0
  7. package/dist/template-qat058wr.html +55 -0
  8. package/dist/tool-views.generated-jdfmzwmn.js +35 -0
  9. package/examples/README.md +21 -0
  10. package/examples/custom-tools/README.md +104 -0
  11. package/examples/custom-tools/hello/index.ts +20 -0
  12. package/examples/extensions/README.md +142 -0
  13. package/examples/extensions/api-demo.ts +78 -0
  14. package/examples/extensions/chalk-logger.ts +25 -0
  15. package/examples/extensions/hello.ts +31 -0
  16. package/examples/extensions/pirate.ts +43 -0
  17. package/examples/extensions/plan-mode.ts +549 -0
  18. package/examples/extensions/reload-runtime.ts +38 -0
  19. package/examples/extensions/thinking-note.ts +13 -0
  20. package/examples/extensions/tools.ts +145 -0
  21. package/examples/extensions/with-deps/index.ts +36 -0
  22. package/examples/extensions/with-deps/package-lock.json +31 -0
  23. package/examples/extensions/with-deps/package.json +17 -0
  24. package/examples/hooks/README.md +56 -0
  25. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  26. package/examples/hooks/confirm-destructive.ts +58 -0
  27. package/examples/hooks/custom-compaction.ts +115 -0
  28. package/examples/hooks/dirty-repo-guard.ts +51 -0
  29. package/examples/hooks/file-trigger.ts +40 -0
  30. package/examples/hooks/git-checkpoint.ts +52 -0
  31. package/examples/hooks/handoff.ts +149 -0
  32. package/examples/hooks/permission-gate.ts +33 -0
  33. package/examples/hooks/protected-paths.ts +29 -0
  34. package/examples/hooks/qna.ts +118 -0
  35. package/examples/hooks/status-line.ts +39 -0
  36. package/examples/sdk/01-minimal.ts +21 -0
  37. package/examples/sdk/02-custom-model.ts +49 -0
  38. package/examples/sdk/03-custom-prompt.ts +46 -0
  39. package/examples/sdk/04-skills.ts +43 -0
  40. package/examples/sdk/06-extensions.ts +82 -0
  41. package/examples/sdk/06-hooks.ts +61 -0
  42. package/examples/sdk/07-context-files.ts +35 -0
  43. package/examples/sdk/08-prompt-templates.ts +41 -0
  44. package/examples/sdk/08-slash-commands.ts +46 -0
  45. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  46. package/examples/sdk/11-sessions.ts +47 -0
  47. package/examples/sdk/12-redis-sessions.ts +54 -0
  48. package/examples/sdk/13-sql-sessions.ts +61 -0
  49. package/examples/sdk/README.md +169 -0
  50. package/package.json +587 -0
  51. package/scripts/bench-guard.ts +71 -0
  52. package/scripts/bench-title-models.ts +332 -0
  53. package/scripts/build-binary.ts +117 -0
  54. package/scripts/bundle-dist.ts +123 -0
  55. package/scripts/compile-binary.ts +69 -0
  56. package/scripts/embed-mupdf-wasm.ts +67 -0
  57. package/scripts/format-prompts.ts +68 -0
  58. package/scripts/generate-aria-snapshot.ts +134 -0
  59. package/scripts/generate-docs-index.ts +58 -0
  60. package/scripts/generate-share-viewer.ts +34 -0
  61. package/scripts/legacy-pi-virtual-module.ts +205 -0
  62. package/scripts/measure-prompt-tokens.ts +63 -0
  63. package/scripts/omp +51 -0
  64. package/scripts/omp.ts +19 -0
  65. package/scripts/security-compare.ts +40 -0
  66. package/src/advisor/advise-tool.ts +234 -0
  67. package/src/advisor/config.ts +341 -0
  68. package/src/advisor/emission-guard.ts +172 -0
  69. package/src/advisor/index.ts +6 -0
  70. package/src/advisor/runtime.ts +1412 -0
  71. package/src/advisor/transcript-recorder.ts +215 -0
  72. package/src/advisor/watchdog.ts +135 -0
  73. package/src/async/index.ts +1 -0
  74. package/src/async/job-manager.ts +847 -0
  75. package/src/auto-thinking/classifier.ts +220 -0
  76. package/src/autolearn/controller.ts +152 -0
  77. package/src/autolearn/managed-skills.ts +255 -0
  78. package/src/autoresearch/command-resume.md +14 -0
  79. package/src/autoresearch/dashboard.ts +436 -0
  80. package/src/autoresearch/git.ts +331 -0
  81. package/src/autoresearch/helpers.ts +218 -0
  82. package/src/autoresearch/index.ts +541 -0
  83. package/src/autoresearch/prompt-setup.md +43 -0
  84. package/src/autoresearch/prompt.md +103 -0
  85. package/src/autoresearch/resume-message.md +10 -0
  86. package/src/autoresearch/state.ts +273 -0
  87. package/src/autoresearch/storage.ts +700 -0
  88. package/src/autoresearch/tools/init-experiment.ts +268 -0
  89. package/src/autoresearch/tools/log-experiment.ts +520 -0
  90. package/src/autoresearch/tools/run-experiment.ts +407 -0
  91. package/src/autoresearch/tools/update-notes.ts +109 -0
  92. package/src/autoresearch/types.ts +168 -0
  93. package/src/capability/context-file.ts +44 -0
  94. package/src/capability/extension-module.ts +34 -0
  95. package/src/capability/extension.ts +47 -0
  96. package/src/capability/fs.ts +117 -0
  97. package/src/capability/hook.ts +40 -0
  98. package/src/capability/index.ts +467 -0
  99. package/src/capability/instruction.ts +37 -0
  100. package/src/capability/mcp.ts +115 -0
  101. package/src/capability/prompt.ts +35 -0
  102. package/src/capability/rule-buckets.ts +66 -0
  103. package/src/capability/rule.ts +298 -0
  104. package/src/capability/settings.ts +34 -0
  105. package/src/capability/skill.ts +69 -0
  106. package/src/capability/slash-command.ts +40 -0
  107. package/src/capability/ssh.ts +41 -0
  108. package/src/capability/system-prompt.ts +34 -0
  109. package/src/capability/tool.ts +38 -0
  110. package/src/capability/types.ts +187 -0
  111. package/src/cleanse/agent.ts +226 -0
  112. package/src/cleanse/balance.ts +79 -0
  113. package/src/cleanse/checkers.ts +996 -0
  114. package/src/cleanse/index.ts +190 -0
  115. package/src/cleanse/loop.ts +51 -0
  116. package/src/cleanse/parsers.ts +726 -0
  117. package/src/cleanse/progress.ts +50 -0
  118. package/src/cleanse/prompts/assignment.md +47 -0
  119. package/src/cleanse/types.ts +72 -0
  120. package/src/cli/agents-cli.ts +138 -0
  121. package/src/cli/args.ts +368 -0
  122. package/src/cli/auth-broker-cli.ts +940 -0
  123. package/src/cli/auth-gateway-cli.ts +674 -0
  124. package/src/cli/bench-cli.ts +990 -0
  125. package/src/cli/browser-relay-cli.ts +119 -0
  126. package/src/cli/classify-install-target.ts +76 -0
  127. package/src/cli/claude-trace-cli.ts +795 -0
  128. package/src/cli/command-help.ts +107 -0
  129. package/src/cli/commands/init-xdg.ts +27 -0
  130. package/src/cli/completion-gen.ts +550 -0
  131. package/src/cli/config-cli.ts +459 -0
  132. package/src/cli/dry-balance-cli.ts +864 -0
  133. package/src/cli/extension-flags.ts +48 -0
  134. package/src/cli/file-processor.ts +132 -0
  135. package/src/cli/flag-tables.ts +367 -0
  136. package/src/cli/gallery-cli.ts +272 -0
  137. package/src/cli/gallery-fixtures/agentic.ts +420 -0
  138. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  139. package/src/cli/gallery-fixtures/edit.ts +254 -0
  140. package/src/cli/gallery-fixtures/fs.ts +245 -0
  141. package/src/cli/gallery-fixtures/index.ts +40 -0
  142. package/src/cli/gallery-fixtures/interaction.ts +46 -0
  143. package/src/cli/gallery-fixtures/memory.ts +81 -0
  144. package/src/cli/gallery-fixtures/misc.ts +177 -0
  145. package/src/cli/gallery-fixtures/search.ts +135 -0
  146. package/src/cli/gallery-fixtures/shell.ts +241 -0
  147. package/src/cli/gallery-fixtures/types.ts +57 -0
  148. package/src/cli/gallery-fixtures/web.ts +158 -0
  149. package/src/cli/gallery-screenshot.ts +279 -0
  150. package/src/cli/gc-cli.ts +1566 -0
  151. package/src/cli/grep-cli.ts +161 -0
  152. package/src/cli/grievances-cli.ts +256 -0
  153. package/src/cli/help-extra.ts +89 -0
  154. package/src/cli/initial-message.ts +58 -0
  155. package/src/cli/models-cli.ts +385 -0
  156. package/src/cli/plugin-cli.ts +996 -0
  157. package/src/cli/profile-alias.ts +369 -0
  158. package/src/cli/profile-bootstrap.ts +233 -0
  159. package/src/cli/read-cli.ts +99 -0
  160. package/src/cli/session-picker.ts +110 -0
  161. package/src/cli/setup-cli.ts +312 -0
  162. package/src/cli/setup-model-picker.ts +43 -0
  163. package/src/cli/shell-cli.ts +176 -0
  164. package/src/cli/ssh-cli.ts +179 -0
  165. package/src/cli/startup-cwd.ts +58 -0
  166. package/src/cli/stats-cli.ts +229 -0
  167. package/src/cli/thinking-levels.ts +7 -0
  168. package/src/cli/tiny-models-cli.ts +153 -0
  169. package/src/cli/ttsr-cli.ts +1013 -0
  170. package/src/cli/update-cli.ts +1184 -0
  171. package/src/cli/usage-cli.ts +1084 -0
  172. package/src/cli/usage-error.ts +7 -0
  173. package/src/cli/web-search-cli.ts +144 -0
  174. package/src/cli/worktree-cli.ts +311 -0
  175. package/src/cli-commands.ts +308 -0
  176. package/src/cli.ts +428 -0
  177. package/src/collab/crypto.ts +63 -0
  178. package/src/collab/display-name.ts +13 -0
  179. package/src/collab/guest.ts +764 -0
  180. package/src/collab/host.ts +692 -0
  181. package/src/collab/protocol.ts +296 -0
  182. package/src/collab/relay-client.ts +282 -0
  183. package/src/collab/replication-shrink.ts +111 -0
  184. package/src/commands/acp.ts +35 -0
  185. package/src/commands/agents.ts +58 -0
  186. package/src/commands/auth-broker.ts +100 -0
  187. package/src/commands/auth-gateway.ts +70 -0
  188. package/src/commands/bench.ts +64 -0
  189. package/src/commands/browser-relay.ts +53 -0
  190. package/src/commands/cleanse.ts +45 -0
  191. package/src/commands/commit.ts +47 -0
  192. package/src/commands/complete.ts +67 -0
  193. package/src/commands/completions.ts +61 -0
  194. package/src/commands/config.ts +52 -0
  195. package/src/commands/dry-balance.ts +43 -0
  196. package/src/commands/gallery.ts +61 -0
  197. package/src/commands/gc.ts +47 -0
  198. package/src/commands/grep.ts +49 -0
  199. package/src/commands/grievances.ts +52 -0
  200. package/src/commands/install.ts +107 -0
  201. package/src/commands/join.ts +40 -0
  202. package/src/commands/launch-help.ts +116 -0
  203. package/src/commands/launch.ts +34 -0
  204. package/src/commands/models.ts +61 -0
  205. package/src/commands/plugin.ts +79 -0
  206. package/src/commands/read.ts +39 -0
  207. package/src/commands/say.ts +146 -0
  208. package/src/commands/setup.ts +68 -0
  209. package/src/commands/share.ts +71 -0
  210. package/src/commands/shell.ts +30 -0
  211. package/src/commands/ssh.ts +61 -0
  212. package/src/commands/stats.ts +30 -0
  213. package/src/commands/tiny-models.ts +36 -0
  214. package/src/commands/token.ts +164 -0
  215. package/src/commands/ttsr.ts +125 -0
  216. package/src/commands/update.ts +34 -0
  217. package/src/commands/usage.ts +55 -0
  218. package/src/commands/web-search.ts +43 -0
  219. package/src/commands/worktree.ts +63 -0
  220. package/src/commit/agentic/agent.ts +322 -0
  221. package/src/commit/agentic/fallback.ts +96 -0
  222. package/src/commit/agentic/index.ts +383 -0
  223. package/src/commit/agentic/lock-files.ts +107 -0
  224. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  225. package/src/commit/agentic/prompts/session-user.md +25 -0
  226. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  227. package/src/commit/agentic/prompts/system.md +38 -0
  228. package/src/commit/agentic/state.ts +60 -0
  229. package/src/commit/agentic/tools/analyze-file.ts +148 -0
  230. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  231. package/src/commit/agentic/tools/git-hunk.ts +52 -0
  232. package/src/commit/agentic/tools/git-overview.ts +62 -0
  233. package/src/commit/agentic/tools/index.ts +54 -0
  234. package/src/commit/agentic/tools/propose-changelog.ts +147 -0
  235. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  236. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  237. package/src/commit/agentic/tools/schemas.ts +11 -0
  238. package/src/commit/agentic/tools/split-commit.ts +241 -0
  239. package/src/commit/agentic/topo-sort.ts +44 -0
  240. package/src/commit/agentic/trivial.ts +51 -0
  241. package/src/commit/agentic/validation.ts +183 -0
  242. package/src/commit/analysis/conventional.ts +64 -0
  243. package/src/commit/analysis/index.ts +4 -0
  244. package/src/commit/analysis/scope.ts +242 -0
  245. package/src/commit/analysis/summary.ts +107 -0
  246. package/src/commit/analysis/validation.ts +66 -0
  247. package/src/commit/changelog/detect.ts +40 -0
  248. package/src/commit/changelog/generate.ts +101 -0
  249. package/src/commit/changelog/index.ts +234 -0
  250. package/src/commit/changelog/parse.ts +44 -0
  251. package/src/commit/cli.ts +85 -0
  252. package/src/commit/git/diff.ts +148 -0
  253. package/src/commit/index.ts +5 -0
  254. package/src/commit/map-reduce/index.ts +69 -0
  255. package/src/commit/map-reduce/map-phase.ts +193 -0
  256. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  257. package/src/commit/map-reduce/utils.ts +9 -0
  258. package/src/commit/message.ts +11 -0
  259. package/src/commit/model-selection.ts +95 -0
  260. package/src/commit/pipeline.ts +244 -0
  261. package/src/commit/prompts/analysis-system.md +148 -0
  262. package/src/commit/prompts/analysis-user.md +38 -0
  263. package/src/commit/prompts/changelog-system.md +50 -0
  264. package/src/commit/prompts/changelog-user.md +18 -0
  265. package/src/commit/prompts/file-observer-system.md +24 -0
  266. package/src/commit/prompts/file-observer-user.md +8 -0
  267. package/src/commit/prompts/reduce-system.md +50 -0
  268. package/src/commit/prompts/reduce-user.md +17 -0
  269. package/src/commit/prompts/summary-retry.md +3 -0
  270. package/src/commit/prompts/summary-system.md +38 -0
  271. package/src/commit/prompts/summary-user.md +13 -0
  272. package/src/commit/prompts/types-description.md +2 -0
  273. package/src/commit/shared-llm.ts +70 -0
  274. package/src/commit/types.ts +118 -0
  275. package/src/commit/utils/exclusions.ts +42 -0
  276. package/src/commit/utils.ts +58 -0
  277. package/src/config/api-key-resolver.ts +81 -0
  278. package/src/config/append-only-context-mode.ts +76 -0
  279. package/src/config/config-file.ts +347 -0
  280. package/src/config/inline-tool-descriptors-mode.ts +26 -0
  281. package/src/config/keybindings.ts +703 -0
  282. package/src/config/mcp-schema.json +252 -0
  283. package/src/config/model-discovery.ts +1077 -0
  284. package/src/config/model-registry.ts +2822 -0
  285. package/src/config/model-resolver.ts +2115 -0
  286. package/src/config/model-roles.ts +113 -0
  287. package/src/config/models-config-schema-bundle.ts +315 -0
  288. package/src/config/models-config-schema.ts +14 -0
  289. package/src/config/models-config.ts +130 -0
  290. package/src/config/prompt-templates.ts +205 -0
  291. package/src/config/provider-globals.ts +25 -0
  292. package/src/config/resolve-config-value.ts +95 -0
  293. package/src/config/service-tier.ts +146 -0
  294. package/src/config/settings-schema.ts +5887 -0
  295. package/src/config/settings.ts +2394 -0
  296. package/src/config.ts +242 -0
  297. package/src/cursor-bridge-tools.ts +81 -0
  298. package/src/cursor.ts +960 -0
  299. package/src/dap/client.ts +1043 -0
  300. package/src/dap/config.ts +480 -0
  301. package/src/dap/defaults.json +212 -0
  302. package/src/dap/index.ts +4 -0
  303. package/src/dap/session.ts +1841 -0
  304. package/src/dap/types.ts +611 -0
  305. package/src/debug/index.ts +584 -0
  306. package/src/debug/log-formatting.ts +58 -0
  307. package/src/debug/log-viewer.ts +966 -0
  308. package/src/debug/profiler.ts +168 -0
  309. package/src/debug/protocol-probe.ts +267 -0
  310. package/src/debug/raw-sse-buffer.ts +421 -0
  311. package/src/debug/raw-sse.ts +312 -0
  312. package/src/debug/remote-debugger.ts +151 -0
  313. package/src/debug/report-bundle.ts +411 -0
  314. package/src/debug/system-info.ts +111 -0
  315. package/src/debug/terminal-info.ts +124 -0
  316. package/src/discovery/agent-plugin-format.ts +551 -0
  317. package/src/discovery/agent-plugins.ts +341 -0
  318. package/src/discovery/agents-md.ts +67 -0
  319. package/src/discovery/agents.ts +300 -0
  320. package/src/discovery/at-imports.ts +273 -0
  321. package/src/discovery/builtin-defaults.ts +39 -0
  322. package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
  323. package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
  324. package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
  325. package/src/discovery/builtin-rules/go-ioutil.md +37 -0
  326. package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
  327. package/src/discovery/builtin-rules/go-new-expr.md +44 -0
  328. package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
  329. package/src/discovery/builtin-rules/go-range-int.md +45 -0
  330. package/src/discovery/builtin-rules/index.ts +74 -0
  331. package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
  332. package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
  333. package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
  334. package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
  335. package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
  336. package/src/discovery/builtin-rules/rs-result-type.md +20 -0
  337. package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
  338. package/src/discovery/builtin-rules/ts-import-type.md +43 -0
  339. package/src/discovery/builtin-rules/ts-no-any.md +66 -0
  340. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
  341. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
  342. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +56 -0
  343. package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
  344. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  345. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  346. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  347. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
  348. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  349. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  350. package/src/discovery/builtin.ts +945 -0
  351. package/src/discovery/claude-plugins.ts +640 -0
  352. package/src/discovery/claude.ts +591 -0
  353. package/src/discovery/cline.ts +83 -0
  354. package/src/discovery/codex.ts +553 -0
  355. package/src/discovery/contained-path.ts +78 -0
  356. package/src/discovery/cursor.ts +223 -0
  357. package/src/discovery/gemini.ts +386 -0
  358. package/src/discovery/github.ts +337 -0
  359. package/src/discovery/helpers.ts +1183 -0
  360. package/src/discovery/index.ts +82 -0
  361. package/src/discovery/mcp-json.ts +182 -0
  362. package/src/discovery/omp-extension-roots.ts +272 -0
  363. package/src/discovery/omp-plugins.ts +409 -0
  364. package/src/discovery/opencode.ts +441 -0
  365. package/src/discovery/plugin-dir-roots.ts +28 -0
  366. package/src/discovery/ssh.ts +153 -0
  367. package/src/discovery/substitute-plugin-root.ts +77 -0
  368. package/src/discovery/vscode.ts +106 -0
  369. package/src/discovery/windsurf.ts +149 -0
  370. package/src/edit/apply-patch/index.ts +87 -0
  371. package/src/edit/apply-patch/parser.ts +174 -0
  372. package/src/edit/diff.ts +1008 -0
  373. package/src/edit/edit-clipboard.ts +23 -0
  374. package/src/edit/file-snapshot-store.ts +148 -0
  375. package/src/edit/hashline/block-resolver.ts +33 -0
  376. package/src/edit/hashline/diff.ts +402 -0
  377. package/src/edit/hashline/execute.ts +313 -0
  378. package/src/edit/hashline/filesystem.ts +247 -0
  379. package/src/edit/hashline/index.ts +5 -0
  380. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  381. package/src/edit/hashline/params.ts +12 -0
  382. package/src/edit/index.ts +699 -0
  383. package/src/edit/modes/apply-patch.lark +19 -0
  384. package/src/edit/modes/apply-patch.ts +53 -0
  385. package/src/edit/modes/patch.ts +1958 -0
  386. package/src/edit/modes/replace.ts +1191 -0
  387. package/src/edit/normalize.ts +345 -0
  388. package/src/edit/notebook.ts +242 -0
  389. package/src/edit/read-file.ts +25 -0
  390. package/src/edit/renderer.ts +998 -0
  391. package/src/edit/snapshot-details.ts +77 -0
  392. package/src/edit/streaming.ts +710 -0
  393. package/src/eval/agent-bridge.ts +224 -0
  394. package/src/eval/backend-helpers.ts +48 -0
  395. package/src/eval/backend.ts +71 -0
  396. package/src/eval/bridge-timeout.ts +65 -0
  397. package/src/eval/budget-bridge.ts +48 -0
  398. package/src/eval/completion-bridge.ts +212 -0
  399. package/src/eval/concurrency-bridge.ts +34 -0
  400. package/src/eval/executor-base.ts +562 -0
  401. package/src/eval/idle-timeout.ts +91 -0
  402. package/src/eval/index.ts +6 -0
  403. package/src/eval/jl/executor.ts +551 -0
  404. package/src/eval/jl/index.ts +54 -0
  405. package/src/eval/jl/kernel.ts +236 -0
  406. package/src/eval/jl/prelude.jl +736 -0
  407. package/src/eval/jl/prelude.ts +3 -0
  408. package/src/eval/jl/runner.jl +666 -0
  409. package/src/eval/jl/runtime.ts +118 -0
  410. package/src/eval/js/context-manager.ts +872 -0
  411. package/src/eval/js/executor.ts +178 -0
  412. package/src/eval/js/index.ts +41 -0
  413. package/src/eval/js/process-entry.ts +31 -0
  414. package/src/eval/js/shared/helpers.ts +170 -0
  415. package/src/eval/js/shared/indirect-eval.ts +30 -0
  416. package/src/eval/js/shared/local-module-loader.ts +364 -0
  417. package/src/eval/js/shared/prelude.ts +2 -0
  418. package/src/eval/js/shared/prelude.txt +294 -0
  419. package/src/eval/js/shared/rewrite-imports.ts +550 -0
  420. package/src/eval/js/shared/runtime.ts +590 -0
  421. package/src/eval/js/shared/types.ts +18 -0
  422. package/src/eval/js/tool-bridge.ts +163 -0
  423. package/src/eval/js/worker-core.ts +380 -0
  424. package/src/eval/js/worker-entry.ts +37 -0
  425. package/src/eval/js/worker-protocol.ts +47 -0
  426. package/src/eval/kernel-base.ts +569 -0
  427. package/src/eval/py/display.ts +71 -0
  428. package/src/eval/py/executor.ts +643 -0
  429. package/src/eval/py/index.ts +57 -0
  430. package/src/eval/py/kernel.ts +235 -0
  431. package/src/eval/py/prelude.py +673 -0
  432. package/src/eval/py/prelude.ts +3 -0
  433. package/src/eval/py/runner.py +1425 -0
  434. package/src/eval/py/runtime.ts +276 -0
  435. package/src/eval/py/spawn-options.ts +134 -0
  436. package/src/eval/py/tool-bridge.ts +201 -0
  437. package/src/eval/rb/executor.ts +511 -0
  438. package/src/eval/rb/index.ts +54 -0
  439. package/src/eval/rb/kernel.ts +231 -0
  440. package/src/eval/rb/prelude.rb +551 -0
  441. package/src/eval/rb/prelude.ts +3 -0
  442. package/src/eval/rb/runner.rb +581 -0
  443. package/src/eval/rb/runtime.ts +132 -0
  444. package/src/eval/runtime-env.ts +104 -0
  445. package/src/eval/session-id.ts +8 -0
  446. package/src/eval/types.ts +48 -0
  447. package/src/exa/index.ts +2 -0
  448. package/src/exa/mcp-client.ts +370 -0
  449. package/src/exa/types.ts +69 -0
  450. package/src/exec/bash-executor.ts +627 -0
  451. package/src/exec/direnv.ts +145 -0
  452. package/src/exec/exec.ts +53 -0
  453. package/src/exec/non-interactive-env.ts +118 -0
  454. package/src/export/custom-share.ts +65 -0
  455. package/src/export/html/args.ts +20 -0
  456. package/src/export/html/index.ts +320 -0
  457. package/src/export/html/share-loader.js +102 -0
  458. package/src/export/html/template.css +1355 -0
  459. package/src/export/html/template.html +55 -0
  460. package/src/export/html/template.js +1653 -0
  461. package/src/export/html/tool-views.generated.js +35 -0
  462. package/src/export/html/vendor/highlight.min.js +1213 -0
  463. package/src/export/html/vendor/marked.min.js +6 -0
  464. package/src/export/html/web-palette.ts +126 -0
  465. package/src/export/share.ts +686 -0
  466. package/src/export/ttsr.ts +590 -0
  467. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  468. package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
  469. package/src/extensibility/custom-commands/index.ts +2 -0
  470. package/src/extensibility/custom-commands/loader.ts +242 -0
  471. package/src/extensibility/custom-commands/types.ts +119 -0
  472. package/src/extensibility/custom-tools/index.ts +7 -0
  473. package/src/extensibility/custom-tools/loader.ts +301 -0
  474. package/src/extensibility/custom-tools/types.ts +287 -0
  475. package/src/extensibility/custom-tools/wrapper.ts +50 -0
  476. package/src/extensibility/extensions/compact-handler.ts +40 -0
  477. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  478. package/src/extensibility/extensions/index.ts +16 -0
  479. package/src/extensibility/extensions/load-errors.ts +13 -0
  480. package/src/extensibility/extensions/loader.ts +663 -0
  481. package/src/extensibility/extensions/managed-timers.ts +83 -0
  482. package/src/extensibility/extensions/model-api.ts +39 -0
  483. package/src/extensibility/extensions/runner.ts +1396 -0
  484. package/src/extensibility/extensions/types.ts +1603 -0
  485. package/src/extensibility/extensions/wrapper.ts +395 -0
  486. package/src/extensibility/hooks/index.ts +6 -0
  487. package/src/extensibility/hooks/loader.ts +243 -0
  488. package/src/extensibility/hooks/runner.ts +425 -0
  489. package/src/extensibility/hooks/tool-wrapper.ts +124 -0
  490. package/src/extensibility/hooks/types.ts +612 -0
  491. package/src/extensibility/legacy-pi-ai-shim.ts +161 -0
  492. package/src/extensibility/legacy-pi-coding-agent-shim.ts +1460 -0
  493. package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
  494. package/src/extensibility/legacy-typebox.ts +101 -0
  495. package/src/extensibility/plugins/bun-git-cache.ts +91 -0
  496. package/src/extensibility/plugins/doctor.ts +65 -0
  497. package/src/extensibility/plugins/git-url.ts +367 -0
  498. package/src/extensibility/plugins/index.ts +9 -0
  499. package/src/extensibility/plugins/installer.ts +201 -0
  500. package/src/extensibility/plugins/legacy-pi-compat.ts +2643 -0
  501. package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
  502. package/src/extensibility/plugins/loader.ts +535 -0
  503. package/src/extensibility/plugins/manager.ts +1142 -0
  504. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  505. package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
  506. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  507. package/src/extensibility/plugins/marketplace/manager.ts +927 -0
  508. package/src/extensibility/plugins/marketplace/registry.ts +192 -0
  509. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  510. package/src/extensibility/plugins/marketplace/types.ts +192 -0
  511. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  512. package/src/extensibility/plugins/parser.ts +107 -0
  513. package/src/extensibility/plugins/runtime-config.ts +9 -0
  514. package/src/extensibility/plugins/types.ts +194 -0
  515. package/src/extensibility/session-handler-types.ts +21 -0
  516. package/src/extensibility/shared-events.ts +401 -0
  517. package/src/extensibility/skills.ts +531 -0
  518. package/src/extensibility/slash-commands.ts +131 -0
  519. package/src/extensibility/tool-event-input.ts +80 -0
  520. package/src/extensibility/tool-proxy.ts +28 -0
  521. package/src/extensibility/utils.ts +192 -0
  522. package/src/goals/index.ts +3 -0
  523. package/src/goals/runtime.ts +521 -0
  524. package/src/goals/state.ts +37 -0
  525. package/src/goals/tools/goal-tool.ts +251 -0
  526. package/src/hindsight/backend.ts +354 -0
  527. package/src/hindsight/bank.ts +156 -0
  528. package/src/hindsight/client.ts +680 -0
  529. package/src/hindsight/config.ts +193 -0
  530. package/src/hindsight/content.ts +266 -0
  531. package/src/hindsight/index.ts +8 -0
  532. package/src/hindsight/mental-models.ts +429 -0
  533. package/src/hindsight/seeds.json +32 -0
  534. package/src/hindsight/state.ts +530 -0
  535. package/src/hindsight/transcript.ts +71 -0
  536. package/src/index.ts +66 -0
  537. package/src/internal-urls/agent-protocol.ts +180 -0
  538. package/src/internal-urls/artifact-protocol.ts +151 -0
  539. package/src/internal-urls/docs-index.ts +102 -0
  540. package/src/internal-urls/filesystem-resource.ts +34 -0
  541. package/src/internal-urls/history-protocol.ts +198 -0
  542. package/src/internal-urls/index.ts +28 -0
  543. package/src/internal-urls/issue-pr-protocol.ts +594 -0
  544. package/src/internal-urls/json-query.ts +126 -0
  545. package/src/internal-urls/local-protocol.ts +471 -0
  546. package/src/internal-urls/mcp-protocol.ts +168 -0
  547. package/src/internal-urls/memory-protocol.ts +380 -0
  548. package/src/internal-urls/omp-protocol.ts +94 -0
  549. package/src/internal-urls/parse.ts +103 -0
  550. package/src/internal-urls/registry-helpers.ts +132 -0
  551. package/src/internal-urls/router.ts +153 -0
  552. package/src/internal-urls/rule-protocol.ts +45 -0
  553. package/src/internal-urls/security-protocol.ts +261 -0
  554. package/src/internal-urls/skill-protocol.ts +131 -0
  555. package/src/internal-urls/ssh-protocol.ts +368 -0
  556. package/src/internal-urls/types.ts +196 -0
  557. package/src/internal-urls/vault-protocol.ts +940 -0
  558. package/src/internal-urls/xd-protocol.ts +46 -0
  559. package/src/irc/bus.ts +380 -0
  560. package/src/jsonrpc/message-framing.ts +142 -0
  561. package/src/launch/broker.ts +1368 -0
  562. package/src/launch/client.ts +521 -0
  563. package/src/launch/ensure.ts +73 -0
  564. package/src/launch/paths.ts +14 -0
  565. package/src/launch/presence.ts +82 -0
  566. package/src/launch/protocol.ts +448 -0
  567. package/src/launch/spawn-options.ts +17 -0
  568. package/src/launch/terminal-output-worker-client.ts +53 -0
  569. package/src/launch/terminal-output-worker-protocol.ts +11 -0
  570. package/src/launch/terminal-output-worker.ts +23 -0
  571. package/src/launch/terminal-output.ts +46 -0
  572. package/src/lib/xai-http.ts +150 -0
  573. package/src/live/attestation.ts +91 -0
  574. package/src/live/controller.ts +517 -0
  575. package/src/live/prompts/agent-final-message.md +3 -0
  576. package/src/live/prompts/live-instructions.md +23 -0
  577. package/src/live/protocol.ts +233 -0
  578. package/src/live/transport.ts +422 -0
  579. package/src/live/visualizer.ts +221 -0
  580. package/src/live/voices.ts +18 -0
  581. package/src/lsp/client.ts +1465 -0
  582. package/src/lsp/clients/biome-client.ts +263 -0
  583. package/src/lsp/clients/index.ts +50 -0
  584. package/src/lsp/clients/lsp-linter-client.ts +85 -0
  585. package/src/lsp/clients/swiftlint-client.ts +120 -0
  586. package/src/lsp/config.ts +549 -0
  587. package/src/lsp/defaults.json +499 -0
  588. package/src/lsp/deferred-diagnostics.ts +66 -0
  589. package/src/lsp/diagnostics-ledger.ts +51 -0
  590. package/src/lsp/edits.ts +288 -0
  591. package/src/lsp/format-options.ts +119 -0
  592. package/src/lsp/index.ts +2821 -0
  593. package/src/lsp/lspmux.ts +233 -0
  594. package/src/lsp/mux/daemon.ts +348 -0
  595. package/src/lsp/mux/protocol.ts +96 -0
  596. package/src/lsp/mux/server.ts +797 -0
  597. package/src/lsp/render.ts +668 -0
  598. package/src/lsp/startup-events.ts +13 -0
  599. package/src/lsp/types.ts +479 -0
  600. package/src/lsp/utils.ts +747 -0
  601. package/src/main.ts +1799 -0
  602. package/src/markit/NOTICE +32 -0
  603. package/src/markit/converters/docx.ts +56 -0
  604. package/src/markit/converters/epub.ts +136 -0
  605. package/src/markit/converters/pdf/columns.ts +103 -0
  606. package/src/markit/converters/pdf/extract.ts +598 -0
  607. package/src/markit/converters/pdf/grid.ts +780 -0
  608. package/src/markit/converters/pdf/headers.ts +106 -0
  609. package/src/markit/converters/pdf/index.ts +146 -0
  610. package/src/markit/converters/pdf/render.ts +501 -0
  611. package/src/markit/converters/pdf/types.ts +84 -0
  612. package/src/markit/converters/pptx.ts +325 -0
  613. package/src/markit/converters/xlsx.ts +173 -0
  614. package/src/markit/index.ts +2 -0
  615. package/src/markit/registry.ts +59 -0
  616. package/src/markit/types.ts +35 -0
  617. package/src/mcp/client.ts +511 -0
  618. package/src/mcp/config-writer.ts +377 -0
  619. package/src/mcp/config.ts +385 -0
  620. package/src/mcp/index.ts +29 -0
  621. package/src/mcp/json-rpc.ts +122 -0
  622. package/src/mcp/loader.ts +125 -0
  623. package/src/mcp/manager.ts +1524 -0
  624. package/src/mcp/oauth-credentials.ts +104 -0
  625. package/src/mcp/oauth-discovery.ts +587 -0
  626. package/src/mcp/oauth-flow.ts +830 -0
  627. package/src/mcp/render.ts +214 -0
  628. package/src/mcp/request-id.ts +24 -0
  629. package/src/mcp/smithery-auth.ts +108 -0
  630. package/src/mcp/smithery-connect.ts +154 -0
  631. package/src/mcp/smithery-registry.ts +500 -0
  632. package/src/mcp/startup-events.ts +116 -0
  633. package/src/mcp/timeout.ts +59 -0
  634. package/src/mcp/tool-bridge.ts +691 -0
  635. package/src/mcp/tool-cache.ts +117 -0
  636. package/src/mcp/transports/header-policy.ts +95 -0
  637. package/src/mcp/transports/http.ts +508 -0
  638. package/src/mcp/transports/index.ts +7 -0
  639. package/src/mcp/transports/sse.ts +372 -0
  640. package/src/mcp/transports/stdio.ts +905 -0
  641. package/src/mcp/types.ts +467 -0
  642. package/src/memories/index.ts +1434 -0
  643. package/src/memories/storage.ts +578 -0
  644. package/src/memory-backend/index.ts +19 -0
  645. package/src/memory-backend/local-backend.ts +47 -0
  646. package/src/memory-backend/messages.ts +19 -0
  647. package/src/memory-backend/off-backend.ts +25 -0
  648. package/src/memory-backend/resolve.ts +25 -0
  649. package/src/memory-backend/runtime.ts +66 -0
  650. package/src/memory-backend/tool-names.ts +2 -0
  651. package/src/memory-backend/types.ts +166 -0
  652. package/src/mnemopi/backend.ts +629 -0
  653. package/src/mnemopi/config.ts +267 -0
  654. package/src/mnemopi/embed-client.ts +293 -0
  655. package/src/mnemopi/embed-protocol.ts +35 -0
  656. package/src/mnemopi/embed-worker.ts +114 -0
  657. package/src/mnemopi/index.ts +3 -0
  658. package/src/mnemopi/state.ts +940 -0
  659. package/src/modes/acp/acp-agent.ts +2595 -0
  660. package/src/modes/acp/acp-client-bridge.ts +154 -0
  661. package/src/modes/acp/acp-event-mapper.ts +1084 -0
  662. package/src/modes/acp/acp-mode.ts +48 -0
  663. package/src/modes/acp/index.ts +2 -0
  664. package/src/modes/acp/terminal-auth.ts +37 -0
  665. package/src/modes/components/advisor-config.ts +635 -0
  666. package/src/modes/components/advisor-message.ts +109 -0
  667. package/src/modes/components/agent-dashboard.ts +1252 -0
  668. package/src/modes/components/agent-hub-projection.ts +248 -0
  669. package/src/modes/components/agent-hub-renderer.ts +194 -0
  670. package/src/modes/components/agent-hub.ts +1126 -0
  671. package/src/modes/components/agent-transcript-viewer.ts +647 -0
  672. package/src/modes/components/ask-dialog.ts +1017 -0
  673. package/src/modes/components/assistant-message.ts +978 -0
  674. package/src/modes/components/background-tan-message.ts +36 -0
  675. package/src/modes/components/bash-execution.ts +233 -0
  676. package/src/modes/components/bordered-loader.ts +41 -0
  677. package/src/modes/components/btw-panel.ts +161 -0
  678. package/src/modes/components/cache-invalidation-marker.ts +110 -0
  679. package/src/modes/components/chat-block.ts +111 -0
  680. package/src/modes/components/chat-transcript-builder.ts +518 -0
  681. package/src/modes/components/codex-reset-fireworks.ts +369 -0
  682. package/src/modes/components/collab-prompt-message.ts +32 -0
  683. package/src/modes/components/compaction-summary-message.ts +221 -0
  684. package/src/modes/components/copy-selector.ts +218 -0
  685. package/src/modes/components/countdown-timer.ts +75 -0
  686. package/src/modes/components/custom-editor.ts +1036 -0
  687. package/src/modes/components/custom-message.ts +70 -0
  688. package/src/modes/components/diff.ts +254 -0
  689. package/src/modes/components/dynamic-border.ts +37 -0
  690. package/src/modes/components/error-banner.ts +33 -0
  691. package/src/modes/components/eval-execution.ts +169 -0
  692. package/src/modes/components/execution-shared.ts +101 -0
  693. package/src/modes/components/extensions/extension-dashboard.ts +492 -0
  694. package/src/modes/components/extensions/extension-list.ts +507 -0
  695. package/src/modes/components/extensions/index.ts +9 -0
  696. package/src/modes/components/extensions/inspector-panel.ts +326 -0
  697. package/src/modes/components/extensions/state-manager.ts +648 -0
  698. package/src/modes/components/extensions/types.ts +186 -0
  699. package/src/modes/components/footer.ts +274 -0
  700. package/src/modes/components/history-search.ts +268 -0
  701. package/src/modes/components/hook-editor.ts +213 -0
  702. package/src/modes/components/hook-input.ts +87 -0
  703. package/src/modes/components/hook-message.ts +67 -0
  704. package/src/modes/components/hook-selector.ts +691 -0
  705. package/src/modes/components/index.ts +42 -0
  706. package/src/modes/components/keybinding-hints.ts +56 -0
  707. package/src/modes/components/late-diagnostics-message.ts +60 -0
  708. package/src/modes/components/login-dialog.ts +197 -0
  709. package/src/modes/components/logout-account-selector.ts +130 -0
  710. package/src/modes/components/mcp-add-wizard.ts +1413 -0
  711. package/src/modes/components/message-frame.ts +98 -0
  712. package/src/modes/components/model-browser.ts +888 -0
  713. package/src/modes/components/model-hub.ts +2014 -0
  714. package/src/modes/components/model-picker.ts +237 -0
  715. package/src/modes/components/move-overlay.ts +293 -0
  716. package/src/modes/components/oauth-selector.ts +474 -0
  717. package/src/modes/components/omfg-panel.ts +141 -0
  718. package/src/modes/components/overlay-box.ts +109 -0
  719. package/src/modes/components/pause-screen.ts +208 -0
  720. package/src/modes/components/plan-review-overlay.ts +1226 -0
  721. package/src/modes/components/plan-toc.ts +138 -0
  722. package/src/modes/components/plugin-selector.ts +100 -0
  723. package/src/modes/components/plugin-settings.ts +745 -0
  724. package/src/modes/components/queue-mode-selector.ts +61 -0
  725. package/src/modes/components/read-tool-group.ts +856 -0
  726. package/src/modes/components/reset-usage-selector.ts +161 -0
  727. package/src/modes/components/segment-track.ts +89 -0
  728. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  729. package/src/modes/components/selector-helpers.ts +129 -0
  730. package/src/modes/components/session-account-selector.ts +62 -0
  731. package/src/modes/components/session-selector.ts +1035 -0
  732. package/src/modes/components/settings-defs.ts +267 -0
  733. package/src/modes/components/settings-selector.ts +1445 -0
  734. package/src/modes/components/show-images-selector.ts +50 -0
  735. package/src/modes/components/skill-message.ts +110 -0
  736. package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
  737. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  738. package/src/modes/components/status-line/component.ts +1843 -0
  739. package/src/modes/components/status-line/context-thresholds.ts +86 -0
  740. package/src/modes/components/status-line/git-utils.ts +42 -0
  741. package/src/modes/components/status-line/index.ts +5 -0
  742. package/src/modes/components/status-line/presets.ts +106 -0
  743. package/src/modes/components/status-line/segments.ts +710 -0
  744. package/src/modes/components/status-line/separators.ts +55 -0
  745. package/src/modes/components/status-line/types.ts +159 -0
  746. package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
  747. package/src/modes/components/theme-selector.ts +68 -0
  748. package/src/modes/components/thinking-selector.ts +57 -0
  749. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  750. package/src/modes/components/tips.txt +26 -0
  751. package/src/modes/components/todo-reminder.ts +43 -0
  752. package/src/modes/components/tool-execution.ts +1467 -0
  753. package/src/modes/components/transcript-container.ts +527 -0
  754. package/src/modes/components/tree-selector.ts +1006 -0
  755. package/src/modes/components/ttsr-notification.ts +123 -0
  756. package/src/modes/components/usage-row.ts +52 -0
  757. package/src/modes/components/user-message-selector.ts +227 -0
  758. package/src/modes/components/user-message.ts +167 -0
  759. package/src/modes/components/visual-truncate.ts +63 -0
  760. package/src/modes/components/welcome.ts +578 -0
  761. package/src/modes/controllers/btw-controller.ts +246 -0
  762. package/src/modes/controllers/command-controller-shared.ts +109 -0
  763. package/src/modes/controllers/command-controller.ts +2024 -0
  764. package/src/modes/controllers/event-controller.ts +2173 -0
  765. package/src/modes/controllers/extension-ui-controller.ts +1243 -0
  766. package/src/modes/controllers/input-controller.ts +2037 -0
  767. package/src/modes/controllers/live-command-controller.ts +261 -0
  768. package/src/modes/controllers/mcp-command-controller.ts +2552 -0
  769. package/src/modes/controllers/omfg-controller.ts +287 -0
  770. package/src/modes/controllers/omfg-rule.ts +647 -0
  771. package/src/modes/controllers/selector-controller.ts +2058 -0
  772. package/src/modes/controllers/session-focus-controller.ts +117 -0
  773. package/src/modes/controllers/ssh-command-controller.ts +385 -0
  774. package/src/modes/controllers/streaming-reveal.ts +399 -0
  775. package/src/modes/controllers/tan-command-controller.ts +243 -0
  776. package/src/modes/controllers/todo-command-controller.ts +487 -0
  777. package/src/modes/controllers/tool-args-reveal.ts +591 -0
  778. package/src/modes/data/emojis.json +1 -0
  779. package/src/modes/emoji-autocomplete.ts +285 -0
  780. package/src/modes/github-ref-autocomplete.ts +75 -0
  781. package/src/modes/gradient-highlight.ts +99 -0
  782. package/src/modes/image-references.ts +137 -0
  783. package/src/modes/index.ts +10 -0
  784. package/src/modes/interactive-mode.ts +5055 -0
  785. package/src/modes/internal-url-autocomplete.ts +158 -0
  786. package/src/modes/loop-limit.ts +192 -0
  787. package/src/modes/magic-keyword-boundary.ts +23 -0
  788. package/src/modes/magic-keywords.ts +42 -0
  789. package/src/modes/markdown-prose.ts +247 -0
  790. package/src/modes/oauth-manual-input.ts +69 -0
  791. package/src/modes/orchestrate.ts +43 -0
  792. package/src/modes/print-mode.ts +282 -0
  793. package/src/modes/prompt-action-autocomplete.ts +322 -0
  794. package/src/modes/queue-input.ts +132 -0
  795. package/src/modes/rpc/host-tools.ts +204 -0
  796. package/src/modes/rpc/host-uris.ts +241 -0
  797. package/src/modes/rpc/rpc-client.ts +1213 -0
  798. package/src/modes/rpc/rpc-frame.ts +316 -0
  799. package/src/modes/rpc/rpc-input.ts +38 -0
  800. package/src/modes/rpc/rpc-messages.ts +127 -0
  801. package/src/modes/rpc/rpc-mode.ts +1519 -0
  802. package/src/modes/rpc/rpc-subagents.ts +265 -0
  803. package/src/modes/rpc/rpc-types.ts +544 -0
  804. package/src/modes/running-subagent-badge.ts +13 -0
  805. package/src/modes/runtime-init.ts +144 -0
  806. package/src/modes/session-observer-registry.ts +223 -0
  807. package/src/modes/session-teardown.ts +82 -0
  808. package/src/modes/setup-version.ts +11 -0
  809. package/src/modes/setup-wizard/index.ts +103 -0
  810. package/src/modes/setup-wizard/lazy.ts +16 -0
  811. package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
  812. package/src/modes/setup-wizard/scenes/model.ts +132 -0
  813. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  814. package/src/modes/setup-wizard/scenes/providers.ts +105 -0
  815. package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
  816. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  817. package/src/modes/setup-wizard/scenes/theme.ts +330 -0
  818. package/src/modes/setup-wizard/scenes/types.ts +65 -0
  819. package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
  820. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  821. package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
  822. package/src/modes/shared.ts +49 -0
  823. package/src/modes/skill-command.ts +91 -0
  824. package/src/modes/theme/dark.json +95 -0
  825. package/src/modes/theme/defaults/alabaster.json +93 -0
  826. package/src/modes/theme/defaults/amethyst.json +96 -0
  827. package/src/modes/theme/defaults/anthracite.json +93 -0
  828. package/src/modes/theme/defaults/basalt.json +91 -0
  829. package/src/modes/theme/defaults/birch.json +95 -0
  830. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  831. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  832. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  833. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  834. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  835. package/src/modes/theme/defaults/dark-copper.json +95 -0
  836. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  837. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  838. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  839. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  840. package/src/modes/theme/defaults/dark-ember.json +95 -0
  841. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  842. package/src/modes/theme/defaults/dark-forest.json +96 -0
  843. package/src/modes/theme/defaults/dark-github.json +105 -0
  844. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  845. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  846. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  847. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  848. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  849. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  850. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  851. package/src/modes/theme/defaults/dark-nord.json +97 -0
  852. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  853. package/src/modes/theme/defaults/dark-one.json +100 -0
  854. package/src/modes/theme/defaults/dark-poimandres.json +143 -0
  855. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  856. package/src/modes/theme/defaults/dark-reef.json +91 -0
  857. package/src/modes/theme/defaults/dark-retro.json +92 -0
  858. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  859. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  860. package/src/modes/theme/defaults/dark-slate.json +95 -0
  861. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  862. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  863. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  864. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  865. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  866. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  867. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  868. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  869. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  870. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  871. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  872. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  873. package/src/modes/theme/defaults/graphite.json +92 -0
  874. package/src/modes/theme/defaults/index.ts +199 -0
  875. package/src/modes/theme/defaults/light-arctic.json +107 -0
  876. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  877. package/src/modes/theme/defaults/light-canyon.json +91 -0
  878. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  879. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  880. package/src/modes/theme/defaults/light-coral.json +95 -0
  881. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  882. package/src/modes/theme/defaults/light-dawn.json +90 -0
  883. package/src/modes/theme/defaults/light-dunes.json +91 -0
  884. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  885. package/src/modes/theme/defaults/light-forest.json +100 -0
  886. package/src/modes/theme/defaults/light-frost.json +95 -0
  887. package/src/modes/theme/defaults/light-github.json +115 -0
  888. package/src/modes/theme/defaults/light-glacier.json +91 -0
  889. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  890. package/src/modes/theme/defaults/light-haze.json +90 -0
  891. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  892. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  893. package/src/modes/theme/defaults/light-lavender.json +95 -0
  894. package/src/modes/theme/defaults/light-meadow.json +91 -0
  895. package/src/modes/theme/defaults/light-mint.json +95 -0
  896. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  897. package/src/modes/theme/defaults/light-ocean.json +99 -0
  898. package/src/modes/theme/defaults/light-one.json +99 -0
  899. package/src/modes/theme/defaults/light-opal.json +91 -0
  900. package/src/modes/theme/defaults/light-orchard.json +91 -0
  901. package/src/modes/theme/defaults/light-paper.json +95 -0
  902. package/src/modes/theme/defaults/light-poimandres.json +143 -0
  903. package/src/modes/theme/defaults/light-prism.json +90 -0
  904. package/src/modes/theme/defaults/light-retro.json +98 -0
  905. package/src/modes/theme/defaults/light-sand.json +95 -0
  906. package/src/modes/theme/defaults/light-savanna.json +91 -0
  907. package/src/modes/theme/defaults/light-solarized.json +102 -0
  908. package/src/modes/theme/defaults/light-soleil.json +90 -0
  909. package/src/modes/theme/defaults/light-sunset.json +99 -0
  910. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  911. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  912. package/src/modes/theme/defaults/light-wetland.json +91 -0
  913. package/src/modes/theme/defaults/light-zenith.json +89 -0
  914. package/src/modes/theme/defaults/limestone.json +94 -0
  915. package/src/modes/theme/defaults/mahogany.json +97 -0
  916. package/src/modes/theme/defaults/marble.json +93 -0
  917. package/src/modes/theme/defaults/obsidian.json +91 -0
  918. package/src/modes/theme/defaults/onyx.json +91 -0
  919. package/src/modes/theme/defaults/pearl.json +93 -0
  920. package/src/modes/theme/defaults/porcelain.json +91 -0
  921. package/src/modes/theme/defaults/quartz.json +96 -0
  922. package/src/modes/theme/defaults/sandstone.json +95 -0
  923. package/src/modes/theme/defaults/titanium.json +90 -0
  924. package/src/modes/theme/light.json +93 -0
  925. package/src/modes/theme/mermaid-cache.ts +92 -0
  926. package/src/modes/theme/shimmer.ts +305 -0
  927. package/src/modes/theme/theme-schema.json +463 -0
  928. package/src/modes/theme/theme.ts +3171 -0
  929. package/src/modes/turn-budget.ts +31 -0
  930. package/src/modes/types.ts +494 -0
  931. package/src/modes/ultrathink.ts +42 -0
  932. package/src/modes/utils/context-usage.ts +518 -0
  933. package/src/modes/utils/copy-targets.ts +378 -0
  934. package/src/modes/utils/hotkeys-markdown.ts +65 -0
  935. package/src/modes/utils/interactive-context-helpers.ts +31 -0
  936. package/src/modes/utils/keybinding-matchers.ts +86 -0
  937. package/src/modes/utils/tools-markdown.ts +31 -0
  938. package/src/modes/utils/transcript-render-helpers.ts +253 -0
  939. package/src/modes/utils/ui-helpers.ts +976 -0
  940. package/src/modes/warp-events.ts +232 -0
  941. package/src/modes/workflow.ts +55 -0
  942. package/src/plan-mode/approved-plan.ts +194 -0
  943. package/src/plan-mode/model-transition.ts +51 -0
  944. package/src/plan-mode/plan-files.ts +40 -0
  945. package/src/plan-mode/plan-handoff.ts +37 -0
  946. package/src/plan-mode/plan-protection.ts +31 -0
  947. package/src/plan-mode/state.ts +6 -0
  948. package/src/priority.json +60 -0
  949. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  950. package/src/prompts/advisor/advise-tool.md +3 -0
  951. package/src/prompts/advisor/context-files.md +8 -0
  952. package/src/prompts/advisor/system.md +98 -0
  953. package/src/prompts/agents/designer.md +74 -0
  954. package/src/prompts/agents/frontmatter.md +12 -0
  955. package/src/prompts/agents/init.md +33 -0
  956. package/src/prompts/agents/librarian.md +119 -0
  957. package/src/prompts/agents/reviewer.md +139 -0
  958. package/src/prompts/agents/scout.md +58 -0
  959. package/src/prompts/agents/security-reviewer.md +75 -0
  960. package/src/prompts/agents/task.md +17 -0
  961. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  962. package/src/prompts/bench/cache-prefix.md +3 -0
  963. package/src/prompts/bench/cache-suffix.md +1 -0
  964. package/src/prompts/bench.md +6 -0
  965. package/src/prompts/ci-green-request.md +36 -0
  966. package/src/prompts/dry-balance-bench.md +8 -0
  967. package/src/prompts/goals/goal-budget-limit.md +16 -0
  968. package/src/prompts/goals/goal-continuation.md +28 -0
  969. package/src/prompts/goals/goal-mode-active.md +23 -0
  970. package/src/prompts/goals/goal-mode-context.md +4 -0
  971. package/src/prompts/goals/goal-todo-context.md +12 -0
  972. package/src/prompts/goals/guided-goal-interview.md +43 -0
  973. package/src/prompts/memories/consolidation.md +30 -0
  974. package/src/prompts/memories/consolidation_system.md +4 -0
  975. package/src/prompts/memories/read-path.md +17 -0
  976. package/src/prompts/memories/stage_one_input.md +6 -0
  977. package/src/prompts/memories/stage_one_system.md +21 -0
  978. package/src/prompts/review-custom-request.md +21 -0
  979. package/src/prompts/review-headless-request.md +16 -0
  980. package/src/prompts/review-request.md +68 -0
  981. package/src/prompts/security/scan-coordinator.md +7 -0
  982. package/src/prompts/security/scan-request.md +21 -0
  983. package/src/prompts/security/validate-request.md +8 -0
  984. package/src/prompts/session/launch-completion.md +1 -0
  985. package/src/prompts/skills/autoload.md +8 -0
  986. package/src/prompts/skills/user-invocation.md +11 -0
  987. package/src/prompts/steering/parent-irc.md +5 -0
  988. package/src/prompts/steering/user-interjection.md +6 -0
  989. package/src/prompts/system/active-repo-context.md +4 -0
  990. package/src/prompts/system/agent-creation-architect.md +50 -0
  991. package/src/prompts/system/agent-creation-user.md +6 -0
  992. package/src/prompts/system/auto-continue.md +1 -0
  993. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  994. package/src/prompts/system/auto-thinking-difficulty.md +14 -0
  995. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  996. package/src/prompts/system/autolearn-guidance.md +7 -0
  997. package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
  998. package/src/prompts/system/background-tan-dispatch.md +8 -0
  999. package/src/prompts/system/btw-user.md +8 -0
  1000. package/src/prompts/system/commit-message-system.md +14 -0
  1001. package/src/prompts/system/computer-safety.md +14 -0
  1002. package/src/prompts/system/custom-system-prompt.md +64 -0
  1003. package/src/prompts/system/eager-task.md +7 -0
  1004. package/src/prompts/system/eager-todo.md +18 -0
  1005. package/src/prompts/system/empty-stop-retry.md +4 -0
  1006. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  1007. package/src/prompts/system/interrupted-thinking.md +7 -0
  1008. package/src/prompts/system/irc-autoreply.md +6 -0
  1009. package/src/prompts/system/irc-incoming.md +9 -0
  1010. package/src/prompts/system/manual-continue.md +7 -0
  1011. package/src/prompts/system/mcp-xdev-guidance.md +11 -0
  1012. package/src/prompts/system/memory-consolidation-system.md +8 -0
  1013. package/src/prompts/system/memory-extraction-system.md +26 -0
  1014. package/src/prompts/system/mid-run-todo-nudge.md +3 -0
  1015. package/src/prompts/system/omfg-user.md +50 -0
  1016. package/src/prompts/system/orchestrate-notice.md +40 -0
  1017. package/src/prompts/system/personalities/default.md +18 -0
  1018. package/src/prompts/system/personalities/friendly.md +17 -0
  1019. package/src/prompts/system/personalities/pragmatic.md +15 -0
  1020. package/src/prompts/system/plan-mode-active.md +125 -0
  1021. package/src/prompts/system/plan-mode-approved.md +22 -0
  1022. package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
  1023. package/src/prompts/system/plan-mode-reference.md +10 -0
  1024. package/src/prompts/system/plan-mode-subagent.md +33 -0
  1025. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  1026. package/src/prompts/system/plan-yolo-handoff.md +5 -0
  1027. package/src/prompts/system/prewalk-checklist.md +7 -0
  1028. package/src/prompts/system/prewalk-continue.md +1 -0
  1029. package/src/prompts/system/prewalk-plan.md +13 -0
  1030. package/src/prompts/system/project-prompt.md +61 -0
  1031. package/src/prompts/system/recap-user.md +9 -0
  1032. package/src/prompts/system/resolve-device-reminder.md +3 -0
  1033. package/src/prompts/system/rewind-report.md +6 -0
  1034. package/src/prompts/system/side-channel-no-tools.md +3 -0
  1035. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  1036. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  1037. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  1038. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  1039. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  1040. package/src/prompts/system/speech-rewrite.md +15 -0
  1041. package/src/prompts/system/subagent-async-pending.md +6 -0
  1042. package/src/prompts/system/subagent-system-prompt.md +73 -0
  1043. package/src/prompts/system/subagent-user-prompt.md +3 -0
  1044. package/src/prompts/system/subagent-yield-reminder.md +23 -0
  1045. package/src/prompts/system/system-prompt.md +263 -0
  1046. package/src/prompts/system/tan-context-switch.md +17 -0
  1047. package/src/prompts/system/task-label.md +23 -0
  1048. package/src/prompts/system/thinking-loop-redirect.md +10 -0
  1049. package/src/prompts/system/title-marker-instruction.md +1 -0
  1050. package/src/prompts/system/title-system.md +16 -0
  1051. package/src/prompts/system/tool-call-loop-redirect.md +8 -0
  1052. package/src/prompts/system/ttsr-interrupt.md +7 -0
  1053. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  1054. package/src/prompts/system/ultrathink-notice.md +3 -0
  1055. package/src/prompts/system/unexpected-stop-classifier.md +17 -0
  1056. package/src/prompts/system/unexpected-stop-retry.md +4 -0
  1057. package/src/prompts/system/vibe-mode-active.md +26 -0
  1058. package/src/prompts/system/web-search.md +25 -0
  1059. package/src/prompts/system/workflow-notice.md +112 -0
  1060. package/src/prompts/system/xdev-mount-notice.md +20 -0
  1061. package/src/prompts/tools/apply-patch.md +65 -0
  1062. package/src/prompts/tools/ask.md +22 -0
  1063. package/src/prompts/tools/ast-edit.md +11 -0
  1064. package/src/prompts/tools/ast-grep.md +19 -0
  1065. package/src/prompts/tools/async-result.md +8 -0
  1066. package/src/prompts/tools/bash.md +23 -0
  1067. package/src/prompts/tools/browser.md +29 -0
  1068. package/src/prompts/tools/checkpoint.md +15 -0
  1069. package/src/prompts/tools/computer.md +26 -0
  1070. package/src/prompts/tools/debug.md +3 -0
  1071. package/src/prompts/tools/eval.md +45 -0
  1072. package/src/prompts/tools/github.md +22 -0
  1073. package/src/prompts/tools/glob.md +16 -0
  1074. package/src/prompts/tools/goal.md +11 -0
  1075. package/src/prompts/tools/grep.md +13 -0
  1076. package/src/prompts/tools/hub.md +34 -0
  1077. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  1078. package/src/prompts/tools/image-attachment-describe.md +10 -0
  1079. package/src/prompts/tools/image-gen.md +7 -0
  1080. package/src/prompts/tools/inspect-image-system.md +20 -0
  1081. package/src/prompts/tools/inspect-image.md +22 -0
  1082. package/src/prompts/tools/learn.md +7 -0
  1083. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  1084. package/src/prompts/tools/lsp.md +19 -0
  1085. package/src/prompts/tools/manage-skill.md +9 -0
  1086. package/src/prompts/tools/memory-edit.md +12 -0
  1087. package/src/prompts/tools/patch.md +57 -0
  1088. package/src/prompts/tools/read.md +27 -0
  1089. package/src/prompts/tools/recall.md +7 -0
  1090. package/src/prompts/tools/reflect.md +5 -0
  1091. package/src/prompts/tools/replace.md +30 -0
  1092. package/src/prompts/tools/retain.md +6 -0
  1093. package/src/prompts/tools/rewind.md +14 -0
  1094. package/src/prompts/tools/security-publish.md +1 -0
  1095. package/src/prompts/tools/security-scan.md +1 -0
  1096. package/src/prompts/tools/task-async-contract.md +1 -0
  1097. package/src/prompts/tools/task-summary.md +20 -0
  1098. package/src/prompts/tools/task.md +83 -0
  1099. package/src/prompts/tools/todo.md +42 -0
  1100. package/src/prompts/tools/vibe-kill.md +3 -0
  1101. package/src/prompts/tools/vibe-list.md +3 -0
  1102. package/src/prompts/tools/vibe-send.md +9 -0
  1103. package/src/prompts/tools/vibe-spawn.md +10 -0
  1104. package/src/prompts/tools/vibe-turn-result.md +19 -0
  1105. package/src/prompts/tools/vibe-wait.md +8 -0
  1106. package/src/prompts/tools/web-search.md +8 -0
  1107. package/src/prompts/tools/write.md +14 -0
  1108. package/src/registry/agent-lifecycle.ts +505 -0
  1109. package/src/registry/agent-registry.ts +287 -0
  1110. package/src/registry/persisted-agents.ts +423 -0
  1111. package/src/sdk.ts +3820 -0
  1112. package/src/secrets/index.ts +375 -0
  1113. package/src/secrets/obfuscator.ts +2629 -0
  1114. package/src/secrets/regex.ts +21 -0
  1115. package/src/security/auth.ts +98 -0
  1116. package/src/security/cloud.ts +686 -0
  1117. package/src/security/comparison.ts +255 -0
  1118. package/src/security/contracts/ids.ts +111 -0
  1119. package/src/security/contracts/index.ts +4 -0
  1120. package/src/security/contracts/schemas.ts +219 -0
  1121. package/src/security/contracts/types.ts +254 -0
  1122. package/src/security/contracts/validation.ts +69 -0
  1123. package/src/security/coordinator.ts +708 -0
  1124. package/src/security/importers/codex-security.ts +387 -0
  1125. package/src/security/importers/index.ts +2 -0
  1126. package/src/security/importers/sarif.ts +357 -0
  1127. package/src/security/index.ts +13 -0
  1128. package/src/security/preflight.ts +405 -0
  1129. package/src/security/provenance.ts +106 -0
  1130. package/src/security/publication.ts +326 -0
  1131. package/src/security/remediation.ts +93 -0
  1132. package/src/security/resource-output.ts +50 -0
  1133. package/src/security/sarif.ts +78 -0
  1134. package/src/security/store.ts +430 -0
  1135. package/src/session/acp-permission-gate.ts +165 -0
  1136. package/src/session/agent-session-events.ts +67 -0
  1137. package/src/session/agent-session-types.ts +407 -0
  1138. package/src/session/agent-session.ts +9000 -0
  1139. package/src/session/agent-storage.ts +807 -0
  1140. package/src/session/artifacts.ts +154 -0
  1141. package/src/session/async-job-delivery.ts +82 -0
  1142. package/src/session/auth-broker-config.ts +92 -0
  1143. package/src/session/auth-storage.ts +25 -0
  1144. package/src/session/bash-runner.ts +326 -0
  1145. package/src/session/blob-store.ts +295 -0
  1146. package/src/session/checkpoint-entries.ts +81 -0
  1147. package/src/session/claude-session-store.ts +426 -0
  1148. package/src/session/client-bridge.ts +85 -0
  1149. package/src/session/codex-auto-reset.ts +673 -0
  1150. package/src/session/codex-session-store.ts +673 -0
  1151. package/src/session/compact-modes.ts +105 -0
  1152. package/src/session/credential-pin.ts +93 -0
  1153. package/src/session/eval-runner.ts +219 -0
  1154. package/src/session/exit-diagnostics.ts +310 -0
  1155. package/src/session/foreign-session-import.ts +52 -0
  1156. package/src/session/foreign-session-jsonl.ts +29 -0
  1157. package/src/session/foreign-session-store.ts +26 -0
  1158. package/src/session/history-storage.ts +329 -0
  1159. package/src/session/indexed-session-storage.ts +553 -0
  1160. package/src/session/irc-bridge.ts +203 -0
  1161. package/src/session/launch-completion.ts +37 -0
  1162. package/src/session/messages.ts +1304 -0
  1163. package/src/session/model-controls.ts +757 -0
  1164. package/src/session/prewalk.ts +279 -0
  1165. package/src/session/provider-image-budget.ts +86 -0
  1166. package/src/session/queued-messages.ts +99 -0
  1167. package/src/session/redis-session-storage.ts +257 -0
  1168. package/src/session/retry-fallback-chains.ts +455 -0
  1169. package/src/session/role-models.ts +85 -0
  1170. package/src/session/session-advisors.ts +1896 -0
  1171. package/src/session/session-context.ts +584 -0
  1172. package/src/session/session-dump-format.ts +216 -0
  1173. package/src/session/session-entries.ts +306 -0
  1174. package/src/session/session-handoff.ts +325 -0
  1175. package/src/session/session-history-format.ts +459 -0
  1176. package/src/session/session-listing.ts +715 -0
  1177. package/src/session/session-loader.ts +362 -0
  1178. package/src/session/session-maintenance.ts +3115 -0
  1179. package/src/session/session-manager.ts +2747 -0
  1180. package/src/session/session-memory.ts +222 -0
  1181. package/src/session/session-metadata.ts +53 -0
  1182. package/src/session/session-migrations.ts +78 -0
  1183. package/src/session/session-paths.ts +280 -0
  1184. package/src/session/session-persistence.ts +293 -0
  1185. package/src/session/session-provider-boundary.ts +306 -0
  1186. package/src/session/session-stats.ts +349 -0
  1187. package/src/session/session-storage.ts +774 -0
  1188. package/src/session/session-title-slot.ts +141 -0
  1189. package/src/session/session-tools.ts +1385 -0
  1190. package/src/session/session-workspace.ts +53 -0
  1191. package/src/session/settings-stream-fn.ts +79 -0
  1192. package/src/session/shake-types.ts +43 -0
  1193. package/src/session/snapcompact-inline.ts +545 -0
  1194. package/src/session/snapcompact-savings-journal.ts +113 -0
  1195. package/src/session/sql-session-storage.ts +374 -0
  1196. package/src/session/stream-guards.ts +417 -0
  1197. package/src/session/streaming-output.ts +1459 -0
  1198. package/src/session/todo-tracker.ts +380 -0
  1199. package/src/session/tool-choice-queue.ts +305 -0
  1200. package/src/session/ttsr-coordinator.ts +496 -0
  1201. package/src/session/turn-persistence.ts +142 -0
  1202. package/src/session/turn-recovery.ts +2005 -0
  1203. package/src/session/unexpected-stop-classifier.ts +141 -0
  1204. package/src/session/yield-queue.ts +288 -0
  1205. package/src/slash-commands/acp-builtins.ts +70 -0
  1206. package/src/slash-commands/available-commands.ts +105 -0
  1207. package/src/slash-commands/builtin-registry.ts +3149 -0
  1208. package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
  1209. package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
  1210. package/src/slash-commands/helpers/context-report.ts +66 -0
  1211. package/src/slash-commands/helpers/format.ts +46 -0
  1212. package/src/slash-commands/helpers/logout.ts +108 -0
  1213. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  1214. package/src/slash-commands/helpers/mcp.ts +533 -0
  1215. package/src/slash-commands/helpers/parse.ts +85 -0
  1216. package/src/slash-commands/helpers/reset-usage.ts +68 -0
  1217. package/src/slash-commands/helpers/security.ts +451 -0
  1218. package/src/slash-commands/helpers/session-pin.ts +44 -0
  1219. package/src/slash-commands/helpers/ssh.ts +196 -0
  1220. package/src/slash-commands/helpers/stats-dashboard.ts +86 -0
  1221. package/src/slash-commands/helpers/todo.ts +285 -0
  1222. package/src/slash-commands/helpers/usage-report.ts +198 -0
  1223. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  1224. package/src/slash-commands/types.ts +139 -0
  1225. package/src/ssh/config-writer.ts +183 -0
  1226. package/src/ssh/connection-manager.ts +667 -0
  1227. package/src/ssh/file-transfer.ts +214 -0
  1228. package/src/ssh/sshfs-mount.ts +163 -0
  1229. package/src/ssh/utils.ts +51 -0
  1230. package/src/startup-splash.ts +19 -0
  1231. package/src/stt/asr-client.ts +401 -0
  1232. package/src/stt/asr-protocol.ts +65 -0
  1233. package/src/stt/asr-worker.ts +603 -0
  1234. package/src/stt/downloader.ts +142 -0
  1235. package/src/stt/endpointer.ts +259 -0
  1236. package/src/stt/index.ts +6 -0
  1237. package/src/stt/models.ts +150 -0
  1238. package/src/stt/sherpa-runtime.ts +71 -0
  1239. package/src/stt/stt-controller.ts +320 -0
  1240. package/src/stt/submit-trigger.ts +74 -0
  1241. package/src/subprocess/worker-client.ts +463 -0
  1242. package/src/subprocess/worker-runtime.ts +494 -0
  1243. package/src/system-prompt.ts +921 -0
  1244. package/src/task/agents.ts +170 -0
  1245. package/src/task/commands.ts +132 -0
  1246. package/src/task/discovery.ts +145 -0
  1247. package/src/task/executor.ts +3431 -0
  1248. package/src/task/index.ts +1515 -0
  1249. package/src/task/isolation-ownership.ts +106 -0
  1250. package/src/task/isolation-runner.ts +444 -0
  1251. package/src/task/label.ts +40 -0
  1252. package/src/task/name-generator.ts +1577 -0
  1253. package/src/task/omp-command.ts +26 -0
  1254. package/src/task/output-manager.ts +115 -0
  1255. package/src/task/parallel.ts +221 -0
  1256. package/src/task/persisted-revive.ts +173 -0
  1257. package/src/task/prewalk.ts +6 -0
  1258. package/src/task/prompt-policy.ts +8 -0
  1259. package/src/task/provider-concurrency.ts +100 -0
  1260. package/src/task/read-only-policy.ts +27 -0
  1261. package/src/task/render.ts +1830 -0
  1262. package/src/task/renderer.ts +14 -0
  1263. package/src/task/repair-args.ts +118 -0
  1264. package/src/task/spawn-policy.ts +72 -0
  1265. package/src/task/structured-subagent.ts +676 -0
  1266. package/src/task/subprocess-tool-registry.ts +88 -0
  1267. package/src/task/types.ts +555 -0
  1268. package/src/task/worktree.ts +967 -0
  1269. package/src/task/yield-assembly.ts +198 -0
  1270. package/src/telemetry-export.ts +504 -0
  1271. package/src/thinking.ts +377 -0
  1272. package/src/tiny/device.ts +111 -0
  1273. package/src/tiny/dtype.ts +101 -0
  1274. package/src/tiny/message-preproc.ts +155 -0
  1275. package/src/tiny/models.ts +268 -0
  1276. package/src/tiny/text.ts +290 -0
  1277. package/src/tiny/title-client.ts +460 -0
  1278. package/src/tiny/title-protocol.ts +56 -0
  1279. package/src/tiny/worker.ts +354 -0
  1280. package/src/tools/acp-bridge.ts +125 -0
  1281. package/src/tools/approval.ts +245 -0
  1282. package/src/tools/ask.ts +1459 -0
  1283. package/src/tools/ast-edit.ts +718 -0
  1284. package/src/tools/ast-grep.ts +526 -0
  1285. package/src/tools/auto-generated-guard.ts +335 -0
  1286. package/src/tools/bash-interactive.ts +435 -0
  1287. package/src/tools/bash-interceptor.ts +148 -0
  1288. package/src/tools/bash-pty-selection.ts +14 -0
  1289. package/src/tools/bash-skill-urls.ts +350 -0
  1290. package/src/tools/bash.ts +1826 -0
  1291. package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
  1292. package/src/tools/browser/aria/aria-snapshot.ts +131 -0
  1293. package/src/tools/browser/attach.ts +219 -0
  1294. package/src/tools/browser/cmux/cmux-tab.ts +1531 -0
  1295. package/src/tools/browser/cmux/rpc.ts +200 -0
  1296. package/src/tools/browser/cmux/socket-client.ts +445 -0
  1297. package/src/tools/browser/launch.ts +939 -0
  1298. package/src/tools/browser/readable.ts +112 -0
  1299. package/src/tools/browser/registry.ts +447 -0
  1300. package/src/tools/browser/relay/bridge.ts +945 -0
  1301. package/src/tools/browser/relay/daemon.ts +117 -0
  1302. package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
  1303. package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
  1304. package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
  1305. package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
  1306. package/src/tools/browser/relay/kind.ts +41 -0
  1307. package/src/tools/browser/relay/protocol.ts +54 -0
  1308. package/src/tools/browser/relay/server.ts +141 -0
  1309. package/src/tools/browser/render.ts +229 -0
  1310. package/src/tools/browser/run-output.ts +76 -0
  1311. package/src/tools/browser/shared-daemon.ts +139 -0
  1312. package/src/tools/browser/tab-protocol.ts +123 -0
  1313. package/src/tools/browser/tab-supervisor.ts +1077 -0
  1314. package/src/tools/browser/tab-worker-entry.ts +29 -0
  1315. package/src/tools/browser/tab-worker.ts +1982 -0
  1316. package/src/tools/browser.ts +487 -0
  1317. package/src/tools/builtin-names.ts +67 -0
  1318. package/src/tools/checkpoint.ts +137 -0
  1319. package/src/tools/computer/exposure.ts +14 -0
  1320. package/src/tools/computer/protocol.ts +69 -0
  1321. package/src/tools/computer/supervisor.ts +409 -0
  1322. package/src/tools/computer/worker-entry.ts +39 -0
  1323. package/src/tools/computer/worker.ts +745 -0
  1324. package/src/tools/computer-renderer.ts +147 -0
  1325. package/src/tools/computer.ts +222 -0
  1326. package/src/tools/conflict-detect.ts +815 -0
  1327. package/src/tools/context.ts +49 -0
  1328. package/src/tools/debug.ts +1121 -0
  1329. package/src/tools/default-renderer.ts +154 -0
  1330. package/src/tools/essential-tools.ts +46 -0
  1331. package/src/tools/eval-backends.ts +34 -0
  1332. package/src/tools/eval-format/index.ts +24 -0
  1333. package/src/tools/eval-format/javascript.ts +952 -0
  1334. package/src/tools/eval-format/julia.ts +446 -0
  1335. package/src/tools/eval-format/python.ts +544 -0
  1336. package/src/tools/eval-format/ruby.ts +380 -0
  1337. package/src/tools/eval-render.ts +784 -0
  1338. package/src/tools/eval.ts +774 -0
  1339. package/src/tools/fetch.ts +1889 -0
  1340. package/src/tools/file-recorder.ts +35 -0
  1341. package/src/tools/fs-cache-invalidation.ts +28 -0
  1342. package/src/tools/gh-cache-invalidation.ts +175 -0
  1343. package/src/tools/gh-format.ts +12 -0
  1344. package/src/tools/gh-renderer.ts +484 -0
  1345. package/src/tools/gh.ts +3958 -0
  1346. package/src/tools/github-cache.ts +663 -0
  1347. package/src/tools/glob.ts +691 -0
  1348. package/src/tools/grep.ts +1920 -0
  1349. package/src/tools/grouped-file-output.ts +210 -0
  1350. package/src/tools/hub/index.ts +579 -0
  1351. package/src/tools/hub/jobs.ts +714 -0
  1352. package/src/tools/hub/launch.ts +690 -0
  1353. package/src/tools/hub/messaging.ts +735 -0
  1354. package/src/tools/hub/types.ts +117 -0
  1355. package/src/tools/image-gen.ts +1689 -0
  1356. package/src/tools/image-providers.ts +50 -0
  1357. package/src/tools/index.ts +736 -0
  1358. package/src/tools/inspect-image-renderer.ts +133 -0
  1359. package/src/tools/inspect-image.ts +316 -0
  1360. package/src/tools/json-tree.ts +260 -0
  1361. package/src/tools/jtd-to-json-schema.ts +219 -0
  1362. package/src/tools/jtd-to-typescript.ts +136 -0
  1363. package/src/tools/jtd-utils.ts +102 -0
  1364. package/src/tools/learn.ts +141 -0
  1365. package/src/tools/list-limit.ts +40 -0
  1366. package/src/tools/manage-skill.ts +102 -0
  1367. package/src/tools/match-line-format.ts +20 -0
  1368. package/src/tools/memory-edit.ts +61 -0
  1369. package/src/tools/memory-recall.ts +102 -0
  1370. package/src/tools/memory-reflect.ts +88 -0
  1371. package/src/tools/memory-render.ts +211 -0
  1372. package/src/tools/memory-retain.ts +89 -0
  1373. package/src/tools/output-meta.ts +860 -0
  1374. package/src/tools/output-schema-validator.ts +307 -0
  1375. package/src/tools/path-utils.ts +1489 -0
  1376. package/src/tools/plan-mode-guard.ts +155 -0
  1377. package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
  1378. package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
  1379. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
  1380. package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
  1381. package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
  1382. package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
  1383. package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
  1384. package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
  1385. package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
  1386. package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
  1387. package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
  1388. package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
  1389. package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
  1390. package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
  1391. package/src/tools/read.ts +3762 -0
  1392. package/src/tools/render-utils.ts +926 -0
  1393. package/src/tools/renderers.ts +133 -0
  1394. package/src/tools/report-tool-issue.ts +568 -0
  1395. package/src/tools/resolve.ts +423 -0
  1396. package/src/tools/review.ts +103 -0
  1397. package/src/tools/run-scope.ts +417 -0
  1398. package/src/tools/security-scan.ts +287 -0
  1399. package/src/tools/shell-tokenize.ts +213 -0
  1400. package/src/tools/sqlite-reader.ts +884 -0
  1401. package/src/tools/terminal-output.ts +141 -0
  1402. package/src/tools/todo.ts +1226 -0
  1403. package/src/tools/tool-errors.ts +62 -0
  1404. package/src/tools/tool-result.ts +102 -0
  1405. package/src/tools/tool-timeouts.ts +38 -0
  1406. package/src/tools/tts.ts +266 -0
  1407. package/src/tools/vibe.ts +608 -0
  1408. package/src/tools/write.ts +1646 -0
  1409. package/src/tools/xdev.ts +560 -0
  1410. package/src/tools/yield.ts +486 -0
  1411. package/src/tts/downloader.ts +64 -0
  1412. package/src/tts/index.ts +10 -0
  1413. package/src/tts/models.ts +137 -0
  1414. package/src/tts/runtime.ts +21 -0
  1415. package/src/tts/speakable.ts +392 -0
  1416. package/src/tts/speech-enhancer.ts +206 -0
  1417. package/src/tts/streaming-player.ts +120 -0
  1418. package/src/tts/tts-client.ts +475 -0
  1419. package/src/tts/tts-protocol.ts +69 -0
  1420. package/src/tts/tts-worker.ts +434 -0
  1421. package/src/tts/vocalizer.ts +419 -0
  1422. package/src/tts/wav.ts +58 -0
  1423. package/src/tui/code-cell.ts +268 -0
  1424. package/src/tui/file-list.ts +55 -0
  1425. package/src/tui/hyperlink.ts +178 -0
  1426. package/src/tui/index.ts +13 -0
  1427. package/src/tui/output-block.ts +268 -0
  1428. package/src/tui/status-line.ts +54 -0
  1429. package/src/tui/tree-list.ts +172 -0
  1430. package/src/tui/types.ts +15 -0
  1431. package/src/tui/utils.ts +103 -0
  1432. package/src/tui/width-aware-text.ts +58 -0
  1433. package/src/utils/active-repo-context.ts +143 -0
  1434. package/src/utils/block-context.ts +312 -0
  1435. package/src/utils/changelog.ts +373 -0
  1436. package/src/utils/clipboard.ts +361 -0
  1437. package/src/utils/command-args.ts +74 -0
  1438. package/src/utils/commit-message-generator.ts +148 -0
  1439. package/src/utils/cpuprofile.ts +235 -0
  1440. package/src/utils/edit-mode.ts +61 -0
  1441. package/src/utils/enhanced-paste.ts +230 -0
  1442. package/src/utils/event-bus.ts +33 -0
  1443. package/src/utils/external-editor.ts +80 -0
  1444. package/src/utils/fetch-timeout.ts +10 -0
  1445. package/src/utils/file-display-mode.ts +44 -0
  1446. package/src/utils/file-mentions.ts +293 -0
  1447. package/src/utils/git.ts +2472 -0
  1448. package/src/utils/image-loading.ts +236 -0
  1449. package/src/utils/image-resize.ts +420 -0
  1450. package/src/utils/image-vision-fallback.ts +196 -0
  1451. package/src/utils/inspect-image-mode.ts +39 -0
  1452. package/src/utils/ipc.ts +38 -0
  1453. package/src/utils/jj.ts +422 -0
  1454. package/src/utils/lang-from-path.ts +251 -0
  1455. package/src/utils/late-cleanup.ts +17 -0
  1456. package/src/utils/local-date.ts +7 -0
  1457. package/src/utils/mac-file-urls.applescript +37 -0
  1458. package/src/utils/markit-cache.ts +166 -0
  1459. package/src/utils/markit.ts +223 -0
  1460. package/src/utils/mupdf-wasm-embed.ts +12 -0
  1461. package/src/utils/open.ts +126 -0
  1462. package/src/utils/profile-tree.ts +111 -0
  1463. package/src/utils/prompt-path.ts +3 -0
  1464. package/src/utils/qrcode.ts +535 -0
  1465. package/src/utils/sample-profile.ts +437 -0
  1466. package/src/utils/session-color.ts +142 -0
  1467. package/src/utils/shell-snapshot-fn-env.sh +63 -0
  1468. package/src/utils/shell-snapshot.ts +326 -0
  1469. package/src/utils/sixel.ts +69 -0
  1470. package/src/utils/thinking-display.ts +163 -0
  1471. package/src/utils/title-generator.ts +597 -0
  1472. package/src/utils/token-rate.ts +72 -0
  1473. package/src/utils/tool-choice.ts +50 -0
  1474. package/src/utils/tools-manager.ts +411 -0
  1475. package/src/utils/turndown.ts +82 -0
  1476. package/src/utils/zip.ts +1106 -0
  1477. package/src/vibe/runtime.ts +1684 -0
  1478. package/src/vibe/state.ts +4 -0
  1479. package/src/web/kagi.ts +305 -0
  1480. package/src/web/parallel.ts +354 -0
  1481. package/src/web/scrapers/artifacthub.ts +207 -0
  1482. package/src/web/scrapers/arxiv.ts +83 -0
  1483. package/src/web/scrapers/aur.ts +162 -0
  1484. package/src/web/scrapers/biorxiv.ts +133 -0
  1485. package/src/web/scrapers/bluesky.ts +262 -0
  1486. package/src/web/scrapers/brew.ts +172 -0
  1487. package/src/web/scrapers/cheatsh.ts +68 -0
  1488. package/src/web/scrapers/chocolatey.ts +196 -0
  1489. package/src/web/scrapers/choosealicense.ts +95 -0
  1490. package/src/web/scrapers/cisa-kev.ts +87 -0
  1491. package/src/web/scrapers/clojars.ts +154 -0
  1492. package/src/web/scrapers/coingecko.ts +177 -0
  1493. package/src/web/scrapers/crates-io.ts +97 -0
  1494. package/src/web/scrapers/crossref.ts +136 -0
  1495. package/src/web/scrapers/devto.ts +147 -0
  1496. package/src/web/scrapers/discogs.ts +306 -0
  1497. package/src/web/scrapers/discourse.ts +197 -0
  1498. package/src/web/scrapers/dockerhub.ts +138 -0
  1499. package/src/web/scrapers/docs-rs.ts +663 -0
  1500. package/src/web/scrapers/fdroid.ts +134 -0
  1501. package/src/web/scrapers/firefox-addons.ts +191 -0
  1502. package/src/web/scrapers/flathub.ts +223 -0
  1503. package/src/web/scrapers/github-gist.ts +58 -0
  1504. package/src/web/scrapers/github.ts +800 -0
  1505. package/src/web/scrapers/gitlab.ts +401 -0
  1506. package/src/web/scrapers/go-pkg.ts +266 -0
  1507. package/src/web/scrapers/hackage.ts +129 -0
  1508. package/src/web/scrapers/hackernews.ts +189 -0
  1509. package/src/web/scrapers/hex.ts +105 -0
  1510. package/src/web/scrapers/huggingface.ts +321 -0
  1511. package/src/web/scrapers/iacr.ts +89 -0
  1512. package/src/web/scrapers/index.ts +252 -0
  1513. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1514. package/src/web/scrapers/lemmy.ts +203 -0
  1515. package/src/web/scrapers/lobsters.ts +175 -0
  1516. package/src/web/scrapers/mastodon.ts +292 -0
  1517. package/src/web/scrapers/maven.ts +138 -0
  1518. package/src/web/scrapers/mdn.ts +173 -0
  1519. package/src/web/scrapers/metacpan.ts +222 -0
  1520. package/src/web/scrapers/musicbrainz.ts +250 -0
  1521. package/src/web/scrapers/npm.ts +98 -0
  1522. package/src/web/scrapers/nuget.ts +183 -0
  1523. package/src/web/scrapers/nvd.ts +222 -0
  1524. package/src/web/scrapers/ollama.ts +239 -0
  1525. package/src/web/scrapers/open-vsx.ts +106 -0
  1526. package/src/web/scrapers/opencorporates.ts +292 -0
  1527. package/src/web/scrapers/openlibrary.ts +336 -0
  1528. package/src/web/scrapers/orcid.ts +286 -0
  1529. package/src/web/scrapers/osv.ts +176 -0
  1530. package/src/web/scrapers/packagist.ts +160 -0
  1531. package/src/web/scrapers/pub-dev.ts +143 -0
  1532. package/src/web/scrapers/pubmed.ts +211 -0
  1533. package/src/web/scrapers/pypi.ts +112 -0
  1534. package/src/web/scrapers/rawg.ts +110 -0
  1535. package/src/web/scrapers/readthedocs.ts +121 -0
  1536. package/src/web/scrapers/reddit.ts +95 -0
  1537. package/src/web/scrapers/repology.ts +251 -0
  1538. package/src/web/scrapers/rfc.ts +201 -0
  1539. package/src/web/scrapers/rubygems.ts +103 -0
  1540. package/src/web/scrapers/searchcode.ts +189 -0
  1541. package/src/web/scrapers/sec-edgar.ts +261 -0
  1542. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1543. package/src/web/scrapers/snapcraft.ts +187 -0
  1544. package/src/web/scrapers/sourcegraph.ts +336 -0
  1545. package/src/web/scrapers/spdx.ts +108 -0
  1546. package/src/web/scrapers/spotify.ts +198 -0
  1547. package/src/web/scrapers/stackoverflow.ts +120 -0
  1548. package/src/web/scrapers/terraform.ts +277 -0
  1549. package/src/web/scrapers/tldr.ts +47 -0
  1550. package/src/web/scrapers/twitter.ts +94 -0
  1551. package/src/web/scrapers/types.ts +354 -0
  1552. package/src/web/scrapers/utils.ts +109 -0
  1553. package/src/web/scrapers/vimeo.ts +133 -0
  1554. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1555. package/src/web/scrapers/w3c.ts +156 -0
  1556. package/src/web/scrapers/wikidata.ts +344 -0
  1557. package/src/web/scrapers/wikipedia.ts +84 -0
  1558. package/src/web/scrapers/youtube.ts +325 -0
  1559. package/src/web/search/index.ts +393 -0
  1560. package/src/web/search/provider.ts +272 -0
  1561. package/src/web/search/providers/anthropic.ts +406 -0
  1562. package/src/web/search/providers/base.ts +112 -0
  1563. package/src/web/search/providers/brave.ts +181 -0
  1564. package/src/web/search/providers/browser-headers.ts +82 -0
  1565. package/src/web/search/providers/browser-page.ts +125 -0
  1566. package/src/web/search/providers/codex.ts +788 -0
  1567. package/src/web/search/providers/duckduckgo.ts +382 -0
  1568. package/src/web/search/providers/ecosia.ts +183 -0
  1569. package/src/web/search/providers/exa.ts +513 -0
  1570. package/src/web/search/providers/firecrawl.ts +213 -0
  1571. package/src/web/search/providers/gemini.ts +634 -0
  1572. package/src/web/search/providers/google.ts +195 -0
  1573. package/src/web/search/providers/jina.ts +144 -0
  1574. package/src/web/search/providers/kagi.ts +98 -0
  1575. package/src/web/search/providers/kimi.ts +221 -0
  1576. package/src/web/search/providers/mojeek.ts +220 -0
  1577. package/src/web/search/providers/parallel.ts +186 -0
  1578. package/src/web/search/providers/perplexity-auth.ts +142 -0
  1579. package/src/web/search/providers/perplexity.ts +997 -0
  1580. package/src/web/search/providers/public.ts +199 -0
  1581. package/src/web/search/providers/searxng.ts +467 -0
  1582. package/src/web/search/providers/startpage.ts +225 -0
  1583. package/src/web/search/providers/synthetic.ts +126 -0
  1584. package/src/web/search/providers/tavily.ts +244 -0
  1585. package/src/web/search/providers/tinyfish.ts +166 -0
  1586. package/src/web/search/providers/utils.ts +130 -0
  1587. package/src/web/search/providers/xai.ts +372 -0
  1588. package/src/web/search/providers/zai.ts +452 -0
  1589. package/src/web/search/query.ts +850 -0
  1590. package/src/web/search/render.ts +262 -0
  1591. package/src/web/search/types.ts +518 -0
  1592. package/src/web/search/utils.ts +17 -0
  1593. package/src/workspace-tree.ts +326 -0
@@ -0,0 +1,1646 @@
1
+ import { Database } from "bun:sqlite";
2
+ import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
4
+
5
+ import { formatHashlineHeader, stripHashlinePrefixes } from "@oh-my-pi/hashline";
6
+ import { type } from "@oh-my-pi/omptype";
7
+ import type {
8
+ AgentTool,
9
+ AgentToolContext,
10
+ AgentToolResult,
11
+ AgentToolUpdateCallback,
12
+ ToolTier,
13
+ } from "@oh-my-pi/pi-agent-core";
14
+ import { type Component, Text } from "@oh-my-pi/pi-tui";
15
+ import { isEnoent, isRecord, prompt, untilAborted } from "@oh-my-pi/pi-utils";
16
+
17
+ import { canonicalSnapshotKey, getFileSnapshotStore } from "../edit/file-snapshot-store";
18
+ import { normalizeToLF } from "../edit/normalize";
19
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
20
+ import { InternalUrlRouter } from "../internal-urls";
21
+ import { parseInternalUrl } from "../internal-urls/parse";
22
+ import { couldBecomeXdUrl, parseXdUrl } from "../internal-urls/xd-protocol";
23
+ import { createLspWritethrough, type FileDiagnosticsResult, type WritethroughCallback, writethroughNoop } from "../lsp";
24
+ import { DeferredDiagnostics } from "../lsp/deferred-diagnostics";
25
+ import { getDiagnosticsLedger } from "../lsp/diagnostics-ledger";
26
+ import { getLanguageFromPath, highlightCode, type Theme } from "../modes/theme/theme";
27
+ import writeDescription from "../prompts/tools/write.md" with { type: "text" };
28
+ import type { ToolSession } from "../sdk";
29
+ import { fileHyperlink, framedBlock, renderStatusLine } from "../tui";
30
+ import { resolveFileDisplayMode } from "../utils/file-display-mode";
31
+ import {
32
+ type ArchiveMemberContent,
33
+ archiveFormatFromPath,
34
+ parseArchivePathCandidates,
35
+ readArchiveEntries,
36
+ writeArchive,
37
+ } from "../utils/zip";
38
+ import { routeWriteThroughBridge } from "./acp-bridge";
39
+ import { resolveToolTier, truncateForPrompt } from "./approval";
40
+ import { assertEditableFile } from "./auto-generated-guard";
41
+ import {
42
+ type ConflictEntry,
43
+ conflictRegionPresent,
44
+ conflictRegionsEqual,
45
+ expandContentTokens,
46
+ getConflictHistory,
47
+ parseConflictUri,
48
+ spliceConflict,
49
+ } from "./conflict-detect";
50
+ import { invalidateFsScanAfterWrite } from "./fs-cache-invalidation";
51
+ import { type OutputMeta, outputMeta } from "./output-meta";
52
+ import {
53
+ formatPathRelativeToCwd,
54
+ isInternalUrlPath,
55
+ pathTargetsSsh,
56
+ peelWriteUrlSelector,
57
+ probeLiteralPathExists,
58
+ splitPathAndSel,
59
+ } from "./path-utils";
60
+ import { enforcePlanModeWrite, resolvePlanPath, unwrapHashlineHeaderPath } from "./plan-mode-guard";
61
+ import {
62
+ cachedRenderedString,
63
+ createRenderedStringCache,
64
+ Ellipsis,
65
+ formatDiagnostics,
66
+ formatErrorDetail,
67
+ formatExpandHint,
68
+ formatMoreItems,
69
+ formatStatusIcon,
70
+ getLspBatchRequest,
71
+ type RenderedStringCache,
72
+ replaceTabs,
73
+ shortenPath,
74
+ TRUNCATE_LENGTHS,
75
+ truncateToWidth,
76
+ } from "./render-utils";
77
+ import { dispatchReportIssueDevice, REPORT_ISSUE_DEVICE_NAME, renderReportIssueDeviceCall } from "./report-tool-issue";
78
+ import { dispatchResolutionDevice, isResolutionDeviceName, renderResolutionDeviceCall } from "./resolve";
79
+ import {
80
+ deleteRowByKey,
81
+ deleteRowByRowId,
82
+ insertRow,
83
+ isSqliteFile,
84
+ parseSqlitePathCandidates,
85
+ resolveTableRowLookup,
86
+ updateRowByKey,
87
+ updateRowByRowId,
88
+ } from "./sqlite-reader";
89
+ import { ToolError } from "./tool-errors";
90
+ import { toolResult } from "./tool-result";
91
+ import {
92
+ dispatchXdevTool,
93
+ renderXdevCall,
94
+ renderXdevResult,
95
+ resolveXdevTool,
96
+ type XdevDispatch,
97
+ xdevListing,
98
+ } from "./xdev";
99
+
100
+ const LOOSE_HASHLINE_HEADER_RE = /^\s*\[[^#\r\n]+#[^ \t\r\n]*\]\s*$/;
101
+ const EXECUTABLE_NOTICE = "[Notice: Made executable via chmod +x]";
102
+ const URI_LIKE_WRITE_PATH_RE = /^([a-z][a-z0-9+.-]*):\/{1,2}(.*)$/i;
103
+ const XD_MISSING_DELIMITER_RE = /^xd\/+(.*)$/i;
104
+ const XD_SCHEME_NEAR_MISSES: Record<string, true> = { dx: true, xdd: true, xdt: true };
105
+
106
+ function assertWriteTargetAddressable(target: string, router: InternalUrlRouter): void {
107
+ const trimmed = target.trim();
108
+ if (path.win32.isAbsolute(trimmed) || router.canHandle(trimmed)) return;
109
+
110
+ const missingDelimiter = trimmed.match(XD_MISSING_DELIMITER_RE);
111
+ if (missingDelimiter) {
112
+ throw new ToolError(
113
+ `Unknown URI-like write target '${trimmed}'. Did you mean 'xd://${missingDelimiter[1]}'? Prefix the path with './' to write it as a filesystem path.`,
114
+ );
115
+ }
116
+
117
+ const uriLike = trimmed.match(URI_LIKE_WRITE_PATH_RE);
118
+ if (!uriLike) return;
119
+
120
+ const scheme = uriLike[1]!.toLowerCase();
121
+ // conflict:// has no router handler but is spliced downstream by
122
+ // parseConflictUri (which emits its own precise id/scope errors); let it pass.
123
+ if (scheme === "conflict") return;
124
+ const canonicalScheme = router.getHandler(scheme) ? scheme : XD_SCHEME_NEAR_MISSES[scheme] ? "xd" : undefined;
125
+ const suggestion = canonicalScheme
126
+ ? ` Did you mean '${canonicalScheme}://${uriLike[2]}'?`
127
+ : " Tool devices use 'xd://<tool>'.";
128
+ throw new ToolError(
129
+ `Unknown URI-like write target '${trimmed}'.${suggestion} Prefix the path with './' to write it as a filesystem path.`,
130
+ );
131
+ }
132
+
133
+ /**
134
+ * Fail closed when a local write target looks like a mis-dispatched read.
135
+ *
136
+ * A read-only step that selects `write` instead of `read` passes the full read
137
+ * expression (`src/foo.tsx:1-260:raw`) as the target. Because a literal colon
138
+ * filename is legal on POSIX (issue #4618), that request otherwise resolves to
139
+ * filesystem creation and reports success, leaving a stray zero-byte file the
140
+ * model cannot recover from — the local analogue of the `xd://` near-miss guard
141
+ * ({@link assertWriteTargetAddressable}, issue #6123).
142
+ *
143
+ * Fires only on the high-confidence combination the report identifies: the tail
144
+ * parses as a read-tool selector, the literal target is missing, and no content
145
+ * was supplied. Non-empty content is the escape hatch — it is never blocked, so
146
+ * a deliberate write to a selector-shaped filename still succeeds. An existing
147
+ * literal path or an ambiguous stat (`"unknown"`: EACCES, transient I/O) also
148
+ * passes through so a real file is never shadowed by the guard.
149
+ */
150
+ function readSelectorForEmptyWrite(target: string, content: string): string | undefined {
151
+ if (content.length > 0) return undefined;
152
+ return splitPathAndSel(target).sel;
153
+ }
154
+
155
+ function throwReadSelectorMisfire(target: string, sel: string): never {
156
+ throw new ToolError(
157
+ `write target '${target}' ends with a read-tool selector ':${sel}' and no such file exists — refusing to create a literal file by that name. ` +
158
+ `If you meant to read it, use read({ path: "${target}" }). ` +
159
+ `If you truly intend to create this file, pass its contents in \`content\` (a non-empty write is never blocked).`,
160
+ );
161
+ }
162
+
163
+ /**
164
+ * Recognize a semicolon-joined list of read-tool selectors mis-dispatched as a
165
+ * single write target — the multi-file read expression the scout emitted in
166
+ * issue #6809 (`a.txt:1-2;b/c.txt:3-4`). Every `;`-segment must be non-empty and
167
+ * carry its own read selector ({@link splitPathAndSel} peels a `:N-M`, `:raw`,
168
+ * or `:conflicts` tail). No real call targets such a list: `read` accepts one
169
+ * path, `write` writes one file. Unlike {@link readSelectorForEmptyWrite} this
170
+ * fires regardless of `content` — the non-empty-content escape hatch exists for
171
+ * a lone selector-shaped *filename*, never a `;`-list, and honoring it here
172
+ * silently creates a nested directory tree (`a.txt:1-2;b/`) in the workspace.
173
+ * The caller still probes the literal target first, so an existing POSIX file
174
+ * by that exact name stays writable (same escape as the single-selector guard).
175
+ */
176
+ function readSelectorListMisfire(target: string): number | undefined {
177
+ if (!target.includes(";")) return undefined;
178
+ const segments = target.split(";");
179
+ if (segments.length < 2) return undefined;
180
+ for (const segment of segments) {
181
+ const trimmed = segment.trim();
182
+ if (trimmed.length === 0 || splitPathAndSel(trimmed).sel === undefined) return undefined;
183
+ }
184
+ return segments.length;
185
+ }
186
+
187
+ function throwReadSelectorListMisfire(target: string, count: number): never {
188
+ throw new ToolError(
189
+ `write target '${target}' is a semicolon-joined list of ${count} read-tool selectors, not a filesystem path — refusing to create it. ` +
190
+ `write creates a single file; issue one read() per path to read these ranges (e.g. read({ path: "<one path>:<range>" })).`,
191
+ );
192
+ }
193
+
194
+ async function assertNotReadSelectorMisfire(target: string, content: string, cwd: string): Promise<void> {
195
+ const listCount = readSelectorListMisfire(target);
196
+ if (listCount !== undefined && (await probeLiteralPathExists(target, cwd)) === "missing") {
197
+ throwReadSelectorListMisfire(target, listCount);
198
+ }
199
+ const sel = readSelectorForEmptyWrite(target, content);
200
+ if (sel === undefined) return;
201
+ if ((await probeLiteralPathExists(target, cwd)) !== "missing") return;
202
+ throwReadSelectorMisfire(target, sel);
203
+ }
204
+
205
+ const BULK_DIRECTIVE_RE = /^#?(\d+)\s*[:=]\s*(@ours|@theirs|@base|@both)$/;
206
+ /**
207
+ * The head of a per-id directive line — `<id>:` / `<id>=` (optionally `#`-prefixed),
208
+ * regardless of whether its value is a valid `@side` token. Used only to sharpen the
209
+ * error message when a directive block is malformed (e.g. `15: some literal text`).
210
+ */
211
+ const BULK_DIRECTIVE_HEAD_RE = /^#?\d+\s*[:=]/;
212
+
213
+ function truncateDirectiveLine(line: string): string {
214
+ return line.length > 60 ? `${line.slice(0, 57)}…` : line;
215
+ }
216
+
217
+ /**
218
+ * Parse `conflict://*` per-id directive content: every non-empty line must be
219
+ * `<id>: @side` (also accepted: `#<id> = @side`), where `@side` is one of
220
+ * `@ours` / `@theirs` / `@base` / `@both`.
221
+ *
222
+ * Returns `null` only when NO line is directive-shaped (→ uniform bulk mode).
223
+ * Throws on duplicate ids, and — critically — on a *partial* directive block:
224
+ * content that mixes valid `<id>: @side` lines with lines that aren't. Without
225
+ * that guard a per-id write carrying any non-token value (a literal or
226
+ * multi-line replacement, e.g. `15: <multi-line content>`) fell through to
227
+ * uniform bulk mode, which pasted the raw directive text verbatim into every
228
+ * block and still reported success. Per-id bulk is token-only; literal or
229
+ * multi-line replacements must go through individual `conflict://<N>` writes.
230
+ */
231
+ function parseBulkDirectives(content: string): Map<number, string> | null {
232
+ const map = new Map<number, string>();
233
+ const stray: string[] = [];
234
+ let sawDirective = false;
235
+ for (const raw of content.split("\n")) {
236
+ const line = raw.trim();
237
+ if (line.length === 0) continue;
238
+ const match = line.match(BULK_DIRECTIVE_RE);
239
+ if (!match) {
240
+ stray.push(line);
241
+ continue;
242
+ }
243
+ sawDirective = true;
244
+ const id = Number.parseInt(match[1], 10);
245
+ if (map.has(id)) {
246
+ throw new ToolError(`Bulk directive lists conflict #${id} twice — each id may appear once.`);
247
+ }
248
+ map.set(id, match[2]);
249
+ }
250
+ // No directive lines at all → not a per-id block; caller uses uniform mode.
251
+ if (!sawDirective) return null;
252
+ if (stray.length > 0) {
253
+ const sample = stray[0]!;
254
+ const tokenHint = BULK_DIRECTIVE_HEAD_RE.test(sample)
255
+ ? `Per-id bulk only accepts the tokens @ours/@theirs/@base/@both — one side per id, single line. `
256
+ : "";
257
+ throw new ToolError(
258
+ `Malformed \`conflict://*\` per-id block: ${stray.length} line(s) are not \`<id>: @side\` directives (first: \`${truncateDirectiveLine(sample)}\`). ` +
259
+ tokenHint +
260
+ `Literal or multi-line replacement content isn't supported in a per-id block — resolve those blocks with individual \`write({ path: "conflict://<N>", content })\` calls (you can issue several at once). ` +
261
+ `For a pure pick-a-side pass, make every non-empty line \`<id>: @ours\` (or @theirs/@base/@both).`,
262
+ );
263
+ }
264
+ return map;
265
+ }
266
+
267
+ /**
268
+ * Resolve per-id directives, preferring the pre-strip `raw` content and falling
269
+ * back to the hashline-stripped `stripped` content.
270
+ *
271
+ * Raw is preferred because the `<id>:` directive heads look exactly like
272
+ * hashline `LINE:` prefixes and would be eaten by stripping. When the two
273
+ * contents are identical (hashline mode off) a single parse decides everything,
274
+ * so a malformed-block error propagates straight through — the previous
275
+ * `?? parseBulkDirectives(...)` chain would have swallowed it and silently
276
+ * degraded to uniform bulk mode, pasting the raw directive text into every
277
+ * block. When they differ, a malformed raw block still defers to a *clean*
278
+ * stripped block, but otherwise surfaces its error rather than degrading.
279
+ */
280
+ function resolveBulkDirectives(raw: string, stripped: string): Map<number, string> | null {
281
+ if (raw === stripped) return parseBulkDirectives(raw);
282
+ let rawResult: Map<number, string> | null;
283
+ try {
284
+ rawResult = parseBulkDirectives(raw);
285
+ } catch (rawError) {
286
+ let fallback: Map<number, string> | null = null;
287
+ try {
288
+ fallback = parseBulkDirectives(stripped);
289
+ } catch {
290
+ fallback = null;
291
+ }
292
+ if (fallback) return fallback;
293
+ throw rawError;
294
+ }
295
+ return rawResult ?? parseBulkDirectives(stripped);
296
+ }
297
+
298
+ const writeSchema = type({
299
+ path: type("string").describe("file path"),
300
+ content: type("string").describe("file content"),
301
+ });
302
+
303
+ export type WriteToolInput = typeof writeSchema.infer;
304
+
305
+ /** Details returned by the write tool for TUI rendering */
306
+ export interface WriteToolDetails {
307
+ diagnostics?: FileDiagnosticsResult;
308
+ meta?: OutputMeta;
309
+ /** Set when the file was auto-chmod'd because content begins with a `#!` shebang. */
310
+ madeExecutable?: boolean;
311
+ /** Absolute filesystem path the write resolved to. Used by the renderer to wrap
312
+ * the (possibly cwd-relative) header path in an OSC 8 `file://` hyperlink. */
313
+ resolvedPath?: string;
314
+ /** Set when the write dispatched an `xd://` tool device; drives renderer delegation. */
315
+ xdev?: XdevDispatch;
316
+ }
317
+
318
+ /**
319
+ * Strip hashline display prefixes from write content.
320
+ *
321
+ * Includes a fallback for loosely-formed section headers that still carry
322
+ * line-number prefixes (for example legacy or malformed hashline echoes).
323
+ */
324
+ function stripWriteContentWithPotentialLooseHeader(lines: string[]): { text: string; stripped: boolean } {
325
+ const cleaned = stripHashlinePrefixes(lines);
326
+ if (cleaned !== lines) {
327
+ return { text: cleaned.join("\n"), stripped: true };
328
+ }
329
+
330
+ const headerIndex = lines.findIndex(line => line.trim().length > 0);
331
+ if (headerIndex === -1 || !LOOSE_HASHLINE_HEADER_RE.test(lines[headerIndex])) {
332
+ return { text: lines.join("\n"), stripped: false };
333
+ }
334
+
335
+ const linesWithoutHeader = lines.slice(0, headerIndex).concat(lines.slice(headerIndex + 1));
336
+ const cleanedWithoutHeader = stripHashlinePrefixes(linesWithoutHeader);
337
+ if (cleanedWithoutHeader === linesWithoutHeader) {
338
+ return { text: lines.join("\n"), stripped: false };
339
+ }
340
+ return { text: cleanedWithoutHeader.join("\n"), stripped: true };
341
+ }
342
+
343
+ /**
344
+ * Strip hashline display prefixes from write content.
345
+ *
346
+ * Only active when hashline edit mode is enabled — the model sees `[PATH#HASH]`
347
+ * headers plus `LINE:` prefixes in read output and sometimes copies them into write content.
348
+ */
349
+ function stripWriteContent(session: ToolSession, content: string): { text: string; stripped: boolean } {
350
+ if (!resolveFileDisplayMode(session).hashLines) {
351
+ return { text: content, stripped: false };
352
+ }
353
+ return stripWriteContentWithPotentialLooseHeader(content.split("\n"));
354
+ }
355
+
356
+ /**
357
+ * Record a snapshot of the freshly-written `content` for `absolutePath`
358
+ * so subsequent hashline edits address the new file with a current tag,
359
+ * and return the matching `[displayPath#TAG]` header. Returns `undefined`
360
+ * when the session is not in hashline mode so callers can no-op cheaply.
361
+ *
362
+ * Mirrors the post-commit snapshot recording the hashline patcher performs
363
+ * after a successful edit: the model gets a tag without an extra `read`.
364
+ */
365
+ function maybeWriteSnapshotHeader(session: ToolSession, absolutePath: string, content: string): string | undefined {
366
+ if (!resolveFileDisplayMode(session).hashLines) return undefined;
367
+ const normalized = normalizeToLF(content);
368
+ const tag = getFileSnapshotStore(session).record(canonicalSnapshotKey(absolutePath), normalized);
369
+ return formatHashlineHeader(formatPathRelativeToCwd(absolutePath, session.cwd), tag);
370
+ }
371
+
372
+ /**
373
+ * Append a trailing note line to the first text block of a tool result.
374
+ * Mutates `result` in place (the result object is owned by this call).
375
+ */
376
+ function appendNoteToResult(result: AgentToolResult<WriteToolDetails>, note: string): void {
377
+ const firstText = result.content.find(
378
+ (block): block is { type: "text"; text: string } => block.type === "text" && typeof block.text === "string",
379
+ );
380
+ if (firstText) {
381
+ firstText.text = firstText.text.length > 0 ? `${firstText.text}\n${note}` : note;
382
+ } else {
383
+ result.content.push({ type: "text", text: note });
384
+ }
385
+ }
386
+
387
+ function emitWriteProgress(
388
+ onUpdate: AgentToolUpdateCallback<WriteToolDetails> | undefined,
389
+ content: string,
390
+ displayPath: string,
391
+ resolvedPath?: string,
392
+ ): void {
393
+ onUpdate?.({
394
+ content: [{ type: "text", text: `Writing ${content.length} bytes to ${shortenPath(displayPath)}...` }],
395
+ details: resolvedPath ? { resolvedPath } : {},
396
+ });
397
+ }
398
+
399
+ /**
400
+ * If `content` begins with a `#!` shebang, ensure the file is executable.
401
+ *
402
+ * Mirrors `chmod a+x` (adds user/group/other execute bits to existing mode).
403
+ * Errors are swallowed: chmod failure (e.g. Windows ACL, read-only mount)
404
+ * MUST NOT fail an otherwise successful write. Returns whether the mode
405
+ * actually changed so the caller can surface a note.
406
+ */
407
+ async function maybeMarkExecutableForShebang(absolutePath: string, content: string): Promise<boolean> {
408
+ if (!content.startsWith("#!")) return false;
409
+ try {
410
+ const stat = await fs.stat(absolutePath);
411
+ const currentMode = stat.mode & 0o7777;
412
+ const newMode = currentMode | 0o111;
413
+ if (newMode === currentMode) return false;
414
+ await fs.chmod(absolutePath, newMode);
415
+ return true;
416
+ } catch {
417
+ return false;
418
+ }
419
+ }
420
+
421
+ // ═══════════════════════════════════════════════════════════════════════════
422
+ // Tool Class
423
+ // ═══════════════════════════════════════════════════════════════════════════
424
+
425
+ type WriteParams = WriteToolInput;
426
+
427
+ interface ResolvedArchiveWritePath {
428
+ absolutePath: string;
429
+ archivePath: string;
430
+ archiveSubPath: string;
431
+ exists: boolean;
432
+ }
433
+
434
+ interface ResolvedSqliteWritePath {
435
+ absolutePath: string;
436
+ sqlitePath: string;
437
+ table: string;
438
+ key?: string;
439
+ exists: boolean;
440
+ }
441
+
442
+ function isArchivePathNotFound(error: unknown): boolean {
443
+ if (isEnoent(error)) return true;
444
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOTDIR";
445
+ }
446
+
447
+ function normalizeArchiveWriteSubPath(rawPath: string): string {
448
+ const normalized = rawPath.replace(/\\/g, "/");
449
+ if (normalized.length === 0) {
450
+ throw new ToolError("Archive write path must target a file inside the archive");
451
+ }
452
+ if (normalized.endsWith("/")) {
453
+ throw new ToolError("Archive write path must target a file, not a directory");
454
+ }
455
+
456
+ const parts = normalized.split("/");
457
+ const normalizedParts: string[] = [];
458
+ for (const part of parts) {
459
+ if (!part || part === ".") continue;
460
+ if (part === "..") {
461
+ throw new ToolError("Archive path cannot contain '..'");
462
+ }
463
+ normalizedParts.push(part);
464
+ }
465
+
466
+ if (normalizedParts.length === 0) {
467
+ throw new ToolError("Archive write path must target a file inside the archive");
468
+ }
469
+
470
+ return normalizedParts.join("/");
471
+ }
472
+
473
+ function parseSqliteWriteTarget(subPath: string, queryString: string): { table: string; key?: string } {
474
+ if (queryString.trim().length > 0) {
475
+ throw new ToolError("SQLite write paths do not support query parameters");
476
+ }
477
+
478
+ const normalized = subPath.replace(/^:+/, "").trim();
479
+ if (!normalized) {
480
+ throw new ToolError("SQLite write path must target a table");
481
+ }
482
+
483
+ const separatorIndex = normalized.indexOf(":");
484
+ const table = separatorIndex === -1 ? normalized : normalized.slice(0, separatorIndex);
485
+ const key = separatorIndex === -1 ? undefined : normalized.slice(separatorIndex + 1);
486
+ if (!table) {
487
+ throw new ToolError("SQLite write path must target a table");
488
+ }
489
+ if (key !== undefined && key.length === 0) {
490
+ throw new ToolError("SQLite row writes require a non-empty row key");
491
+ }
492
+
493
+ return { table, key };
494
+ }
495
+
496
+ /**
497
+ * Write tool implementation.
498
+ *
499
+ * Creates or overwrites files with optional LSP formatting and diagnostics.
500
+ */
501
+ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails> {
502
+ readonly name = "write";
503
+ readonly approval = (args: unknown): ToolTier => {
504
+ const rawPath = (args as Partial<WriteParams>).path;
505
+ if (typeof rawPath !== "string") return "write";
506
+ // Unwrap a hashline `[path#TAG]` wrapper first (parity with execute) so a
507
+ // wrapped `[ssh://h/x#ABCD]` can't dodge scheme detection and the tier checks below.
508
+ const path = unwrapHashlineHeaderPath(rawPath);
509
+ // xd:// device writes execute the mounted tool — take its approval tier.
510
+ // The resolution devices (xd://resolve, xd://reject, xd://propose)
511
+ // finalize a staged, already-previewed action, so they stay at read tier.
512
+ const xdevTarget = parseXdUrl(path);
513
+ if (xdevTarget) {
514
+ if (xdevTarget.name === REPORT_ISSUE_DEVICE_NAME) return "write";
515
+ if (xdevTarget.name && isResolutionDeviceName(xdevTarget.name)) return "read";
516
+ const inst =
517
+ xdevTarget.name && this.session.xdev ? resolveXdevTool(this.session.xdev, xdevTarget.name) : undefined;
518
+ if (!inst) return "exec";
519
+ // Decode the device JSON payload and evaluate the mounted tool's own
520
+ // approval (which may be argument-dependent, e.g. ast_edit is read-tier
521
+ // for internal-URL paths, debug is read-tier for inspection actions).
522
+ // Malformed JSON, non-object payloads, missing content, and approval
523
+ // functions that reject schema-invalid objects stay exec so the gate
524
+ // fails closed — the dispatch itself rejects invalid arguments too.
525
+ const rawContent = (args as Partial<WriteParams>).content;
526
+ if (typeof rawContent !== "string") return "exec";
527
+ let parsed: unknown;
528
+ try {
529
+ parsed = JSON.parse(rawContent);
530
+ } catch {
531
+ return "exec";
532
+ }
533
+ if (!isRecord(parsed)) return "exec";
534
+ try {
535
+ return resolveToolTier(inst, parsed);
536
+ } catch {
537
+ return "exec";
538
+ }
539
+ }
540
+ // Remote SSH writes open an outbound connection and run a remote shell —
541
+ // gate them like the exec-tier `ssh` tool, ahead of the handler-write
542
+ // logic. Substring match also covers selector-suffixed targets.
543
+ if (pathTargetsSsh(path)) return "exec";
544
+ if (!isInternalUrlPath(path)) return "write";
545
+ // Internal URLs are usually session-local artifacts (read tier), but a
546
+ // scheme whose handler exposes a `write` hook mutates handler-owned user
547
+ // data (e.g. vault:// notes) and must take the write tier so always-ask
548
+ // mode actually prompts.
549
+ const match = /^([a-z][a-z0-9+.-]*):\/\//i.exec(path.trim());
550
+ const handler = match ? InternalUrlRouter.instance().getHandler(match[1]!.toLowerCase()) : undefined;
551
+ return handler?.write ? "write" : "read";
552
+ };
553
+ readonly formatApprovalDetails = (args: unknown): string[] => {
554
+ const params = args as Partial<WriteParams>;
555
+ const targetPath = typeof params.path === "string" ? params.path : "(missing)";
556
+ const content = typeof params.content === "string" ? params.content : "";
557
+ return [`Path: ${truncateForPrompt(targetPath)}`, `Content:\n${truncateForPrompt(content)}`];
558
+ };
559
+ readonly label = "Write";
560
+ readonly description: string;
561
+ readonly parameters = writeSchema;
562
+ readonly strict = true;
563
+ readonly concurrency = "exclusive";
564
+ readonly loadMode = "essential";
565
+
566
+ /** Stream matchers should see the real file content, not its JSON-escaped argument encoding. */
567
+ matcherDigest(args: unknown): string | undefined {
568
+ const content = (args as Partial<WriteParams>).content;
569
+ return typeof content === "string" ? content : undefined;
570
+ }
571
+
572
+ readonly #writethrough: WritethroughCallback;
573
+ readonly #deferredDiagnostics: DeferredDiagnostics | undefined;
574
+
575
+ constructor(private readonly session: ToolSession) {
576
+ const enableLsp = session.enableLsp ?? true;
577
+ const enableFormat = enableLsp && session.settings.get("lsp.formatOnWrite");
578
+ const enableDiagnostics = enableLsp && session.settings.get("lsp.diagnosticsOnWrite");
579
+ const dedup = enableDiagnostics && session.settings.get("lsp.diagnosticsDeduplicate");
580
+ this.#deferredDiagnostics =
581
+ enableDiagnostics && session.queueDeferredDiagnostics ? new DeferredDiagnostics(session, dedup) : undefined;
582
+ this.#writethrough = enableLsp
583
+ ? createLspWritethrough(session.cwd, {
584
+ enableFormat,
585
+ enableDiagnostics,
586
+ transformDiagnostics: dedup
587
+ ? (path, result) => getDiagnosticsLedger(session).reduce(path, result)
588
+ : undefined,
589
+ })
590
+ : writethroughNoop;
591
+ this.description = prompt.render(writeDescription);
592
+ }
593
+
594
+ async #resolveArchiveWritePath(writePath: string): Promise<ResolvedArchiveWritePath | null> {
595
+ const candidates = parseArchivePathCandidates(writePath).filter(candidate => candidate.archivePath !== writePath);
596
+ if (candidates.length === 0) {
597
+ return null;
598
+ }
599
+
600
+ const fallbackCandidate = candidates[candidates.length - 1]!;
601
+ const fallback: ResolvedArchiveWritePath = {
602
+ absolutePath: resolvePlanPath(this.session, fallbackCandidate.archivePath),
603
+ archivePath: fallbackCandidate.archivePath,
604
+ archiveSubPath: normalizeArchiveWriteSubPath(fallbackCandidate.subPath),
605
+ exists: false,
606
+ };
607
+
608
+ for (const candidate of candidates) {
609
+ const absolutePath = resolvePlanPath(this.session, candidate.archivePath);
610
+ try {
611
+ const stat = await Bun.file(absolutePath).stat();
612
+ if (stat.isDirectory()) {
613
+ continue;
614
+ }
615
+
616
+ return {
617
+ absolutePath,
618
+ archivePath: candidate.archivePath,
619
+ archiveSubPath: normalizeArchiveWriteSubPath(candidate.subPath),
620
+ exists: true,
621
+ };
622
+ } catch (error) {
623
+ if (!isArchivePathNotFound(error)) {
624
+ throw error;
625
+ }
626
+ }
627
+ }
628
+
629
+ return fallback;
630
+ }
631
+
632
+ async #writeArchiveEntry(
633
+ content: string,
634
+ resolvedArchivePath: ResolvedArchiveWritePath,
635
+ ): Promise<AgentToolResult<WriteToolDetails>> {
636
+ // Resolve symlinks before the tmp+rename swap: renaming over a symlink
637
+ // replaces the link itself with a regular file instead of writing
638
+ // through to its target.
639
+ const finalPath = resolvedArchivePath.exists
640
+ ? await fs.realpath(resolvedArchivePath.absolutePath).catch(() => resolvedArchivePath.absolutePath)
641
+ : resolvedArchivePath.absolutePath;
642
+ // A realpath swap can land on a name without an archive extension; a
643
+ // whole-archive rewrite then defaults to an uncompressed tar, matching the
644
+ // previous `isZip`/`isGzip`/else fallthrough.
645
+ const format = archiveFormatFromPath(finalPath) ?? "tar";
646
+ // Rewrites are whole-archive: write to a temp file and rename so a
647
+ // crash/disk-full mid-write can't destroy the original archive.
648
+ const tmpPath = `${finalPath}.tmp-${process.pid}`;
649
+
650
+ const parentDir = path.dirname(resolvedArchivePath.absolutePath);
651
+ if (parentDir && parentDir !== ".") {
652
+ await fs.mkdir(parentDir, { recursive: true });
653
+ }
654
+
655
+ const entries = new Map<string, ArchiveMemberContent>();
656
+ if (resolvedArchivePath.exists) {
657
+ try {
658
+ const existing = await readArchiveEntries({ bytes: await Bun.file(finalPath).bytes(), format });
659
+ for (const [entryPath, data] of existing) {
660
+ entries.set(entryPath, data);
661
+ }
662
+ } catch (error) {
663
+ throw new ToolError(error instanceof Error ? error.message : String(error));
664
+ }
665
+ }
666
+ const writeTarget = `${resolvedArchivePath.archivePath}:${resolvedArchivePath.archiveSubPath}`;
667
+ const sel = readSelectorForEmptyWrite(writeTarget, content);
668
+ if (sel !== undefined && !entries.has(resolvedArchivePath.archiveSubPath)) {
669
+ throwReadSelectorMisfire(writeTarget, sel);
670
+ }
671
+ entries.set(resolvedArchivePath.archiveSubPath, content);
672
+
673
+ try {
674
+ await writeArchive(tmpPath, format, entries);
675
+ await fs.rename(tmpPath, finalPath);
676
+ } catch (error) {
677
+ await fs.rm(tmpPath, { force: true }).catch(() => {});
678
+ throw new ToolError(error instanceof Error ? error.message : String(error));
679
+ }
680
+
681
+ invalidateFsScanAfterWrite(resolvedArchivePath.absolutePath);
682
+ const outputPath = `${formatPathRelativeToCwd(resolvedArchivePath.absolutePath, this.session.cwd)}:${
683
+ resolvedArchivePath.archiveSubPath
684
+ }`;
685
+ return {
686
+ content: [{ type: "text", text: `Successfully wrote ${content.length} bytes to ${outputPath}` }],
687
+ details: { resolvedPath: resolvedArchivePath.absolutePath },
688
+ };
689
+ }
690
+
691
+ async #resolveSqliteWritePath(writePath: string): Promise<ResolvedSqliteWritePath | null> {
692
+ const candidates = parseSqlitePathCandidates(writePath).filter(candidate => candidate.sqlitePath !== writePath);
693
+ if (candidates.length === 0) {
694
+ return null;
695
+ }
696
+
697
+ const fallbackCandidate = candidates[candidates.length - 1]!;
698
+ const fallbackTarget = parseSqliteWriteTarget(fallbackCandidate.subPath, fallbackCandidate.queryString);
699
+ const fallback: ResolvedSqliteWritePath = {
700
+ absolutePath: resolvePlanPath(this.session, fallbackCandidate.sqlitePath),
701
+ sqlitePath: fallbackCandidate.sqlitePath,
702
+ table: fallbackTarget.table,
703
+ key: fallbackTarget.key,
704
+ exists: false,
705
+ };
706
+
707
+ let sawExistingNonSqlite = false;
708
+ for (const candidate of candidates) {
709
+ const target = parseSqliteWriteTarget(candidate.subPath, candidate.queryString);
710
+ const absolutePath = resolvePlanPath(this.session, candidate.sqlitePath);
711
+ try {
712
+ const stat = await Bun.file(absolutePath).stat();
713
+ if (stat.isDirectory()) {
714
+ continue;
715
+ }
716
+ if (!(await isSqliteFile(absolutePath))) {
717
+ sawExistingNonSqlite = true;
718
+ continue;
719
+ }
720
+
721
+ return {
722
+ absolutePath,
723
+ sqlitePath: candidate.sqlitePath,
724
+ table: target.table,
725
+ key: target.key,
726
+ exists: true,
727
+ };
728
+ } catch (error) {
729
+ if (!isArchivePathNotFound(error)) {
730
+ throw error;
731
+ }
732
+ }
733
+ }
734
+
735
+ if (sawExistingNonSqlite) {
736
+ return null;
737
+ }
738
+
739
+ return fallback;
740
+ }
741
+
742
+ async #writeSqliteRow(
743
+ displayPath: string,
744
+ content: string,
745
+ resolvedSqlitePath: ResolvedSqliteWritePath,
746
+ ): Promise<AgentToolResult<WriteToolDetails>> {
747
+ let db: Database | null = null;
748
+ try {
749
+ if (!resolvedSqlitePath.exists) {
750
+ throw new ToolError(`SQLite database '${displayPath}' not found`);
751
+ }
752
+
753
+ db = new Database(resolvedSqlitePath.absolutePath, { create: false, strict: true });
754
+ db.run("PRAGMA busy_timeout = 3000");
755
+
756
+ const trimmedContent = content.trim();
757
+ let resultText: string;
758
+ if (trimmedContent.length === 0) {
759
+ if (!resolvedSqlitePath.key) {
760
+ throw new ToolError("SQLite deletes require a row key in the path");
761
+ }
762
+
763
+ const lookup = resolveTableRowLookup(db, resolvedSqlitePath.table);
764
+ const deleted =
765
+ lookup.kind === "pk"
766
+ ? deleteRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key)
767
+ : deleteRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key);
768
+ resultText =
769
+ deleted > 0
770
+ ? `Deleted row '${resolvedSqlitePath.key}' from ${resolvedSqlitePath.table}`
771
+ : `No row deleted from ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;
772
+ } else {
773
+ let parsedContent: unknown;
774
+ try {
775
+ parsedContent = Bun.JSON5.parse(content);
776
+ } catch (error) {
777
+ throw new ToolError(
778
+ `SQLite write content must be valid JSON5: ${error instanceof Error ? error.message : String(error)}`,
779
+ );
780
+ }
781
+
782
+ if (!isRecord(parsedContent)) {
783
+ throw new ToolError("SQLite write content must be a JSON object");
784
+ }
785
+
786
+ if (resolvedSqlitePath.key) {
787
+ const lookup = resolveTableRowLookup(db, resolvedSqlitePath.table);
788
+ const updated =
789
+ lookup.kind === "pk"
790
+ ? updateRowByKey(db, resolvedSqlitePath.table, lookup, resolvedSqlitePath.key, parsedContent)
791
+ : updateRowByRowId(db, resolvedSqlitePath.table, resolvedSqlitePath.key, parsedContent);
792
+ resultText =
793
+ updated > 0
794
+ ? `Updated row '${resolvedSqlitePath.key}' in ${resolvedSqlitePath.table}`
795
+ : `No row updated in ${resolvedSqlitePath.table} for key '${resolvedSqlitePath.key}'`;
796
+ } else {
797
+ insertRow(db, resolvedSqlitePath.table, parsedContent);
798
+ resultText = `Inserted row into ${resolvedSqlitePath.table}`;
799
+ }
800
+ }
801
+
802
+ invalidateFsScanAfterWrite(resolvedSqlitePath.absolutePath);
803
+ return toolResult<WriteToolDetails>({ resolvedPath: resolvedSqlitePath.absolutePath })
804
+ .text(resultText)
805
+ .sourcePath(resolvedSqlitePath.absolutePath)
806
+ .done();
807
+ } catch (error) {
808
+ if (isEnoent(error)) {
809
+ throw new ToolError(`SQLite database '${displayPath}' not found`);
810
+ }
811
+ if (error instanceof ToolError) {
812
+ throw error;
813
+ }
814
+ throw new ToolError(error instanceof Error ? error.message : String(error));
815
+ } finally {
816
+ db?.close();
817
+ }
818
+ }
819
+
820
+ /**
821
+ * Resolve a single `conflict://<N>` write by splicing the recorded
822
+ * marker region in the registered file with `replacementContent`.
823
+ * The write deliberately bypasses the LSP writethrough: the file may
824
+ * still hold other unresolved marker blocks, so formatting could
825
+ * corrupt them and diagnostics would be marker-noise anyway.
826
+ *
827
+ * Entry ids are session-stable: they keep working even after later
828
+ * writes resolve other blocks in the same file. The recorded range
829
+ * is re-validated on disk before splicing so an out-of-band edit
830
+ * surfaces as a clear error instead of corrupting the file.
831
+ */
832
+ async #resolveConflict(
833
+ entry: ConflictEntry,
834
+ replacementContent: string,
835
+ stripped: boolean,
836
+ signal: AbortSignal | undefined,
837
+ ): Promise<AgentToolResult<WriteToolDetails>> {
838
+ const absolutePath = entry.absolutePath;
839
+ if (!(await fs.exists(absolutePath))) {
840
+ throw new ToolError(`Conflict #${entry.id} target '${entry.displayPath}' no longer exists.`);
841
+ }
842
+
843
+ const expanded = expandContentTokens(replacementContent, entry);
844
+ const originalText = await Bun.file(absolutePath).text();
845
+ const splice = spliceConflict(originalText, entry, expanded);
846
+ const newContent = splice.text;
847
+
848
+ await writethroughNoop(absolutePath, newContent, signal);
849
+ invalidateFsScanAfterWrite(absolutePath);
850
+ this.session.bumpFileMutationVersion?.(absolutePath);
851
+ this.session.fileSnapshotStore?.invalidate(absolutePath);
852
+ const history = this.session.conflictHistory;
853
+ history?.invalidate(entry.id);
854
+ if (history) {
855
+ // Drop stale duplicate registrations of the same region: a re-read
856
+ // after an out-of-band shift registers a fresh id at the new
857
+ // startLine while the stale twin persists at the old one. A DISTINCT
858
+ // conflict block that is merely byte-identical still occurs in the
859
+ // post-splice content and must stay addressable.
860
+ for (const other of history.entries()) {
861
+ if (
862
+ other.absolutePath === absolutePath &&
863
+ conflictRegionsEqual(other, entry) &&
864
+ !conflictRegionPresent(newContent, other)
865
+ ) {
866
+ history.invalidate(other.id);
867
+ }
868
+ }
869
+ }
870
+
871
+ const header = maybeWriteSnapshotHeader(this.session, absolutePath, newContent);
872
+ const range =
873
+ entry.startLine === entry.endLine
874
+ ? `line ${entry.startLine}`
875
+ : `lines ${entry.startLine}\u2013${entry.endLine}`;
876
+ const summary = `Resolved conflict #${entry.id} at ${range} in ${entry.displayPath}.`;
877
+ let resultText = header ? `${header}\n${summary}` : summary;
878
+ if (stripped) {
879
+ resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
880
+ }
881
+ const echoTrimmed = splice.trimmedLeading + splice.trimmedTrailing;
882
+ if (echoTrimmed > 0) {
883
+ resultText += `\nNote: dropped ${echoTrimmed} content line(s) that duplicated the code adjacent to the conflict region — writes replace only the marker block; surrounding lines stay in place.`;
884
+ }
885
+
886
+ return {
887
+ content: [{ type: "text", text: resultText }],
888
+ details: { resolvedPath: absolutePath },
889
+ };
890
+ }
891
+
892
+ /**
893
+ * Look up a single conflict entry by id and dispatch to {@link #resolveConflict}.
894
+ * Throws a clear `not found` error when the id has been invalidated.
895
+ */
896
+ async #resolveSingleConflictById(
897
+ id: number,
898
+ replacementContent: string,
899
+ stripped: boolean,
900
+ signal: AbortSignal | undefined,
901
+ ): Promise<AgentToolResult<WriteToolDetails>> {
902
+ const entry = getConflictHistory(this.session).get(id);
903
+ if (!entry) {
904
+ throw new ToolError(
905
+ `Conflict #${id} not found. Conflict ids are registered when \`read\` surfaces a marker block; re-read the file to get a current id.`,
906
+ );
907
+ }
908
+ return this.#resolveConflict(entry, replacementContent, stripped, signal);
909
+ }
910
+
911
+ /**
912
+ * Bulk-resolve every registered conflict via `conflict://*`.
913
+ *
914
+ * Entries are grouped by file and applied bottom-up by recorded start
915
+ * line so each splice keeps later anchors valid. `content` tokens are
916
+ * expanded *per entry*, so `content: "@ours"` keeps each block's own
917
+ * ours side rather than collapsing every conflict to the first
918
+ * block's ours.
919
+ *
920
+ * All-or-nothing semantics within a file: if any splice for a file
921
+ * fails (stale anchors, missing base for `@base`, etc.), that file is
922
+ * left untouched and the error is surfaced. Files that succeed are
923
+ * still written. The result text reports per-file counts so the agent
924
+ * can re-read the failed files and retry.
925
+ */
926
+ async #resolveAllConflicts(
927
+ replacementContent: string,
928
+ stripped: boolean,
929
+ signal: AbortSignal | undefined,
930
+ rawContent: string = replacementContent,
931
+ ): Promise<AgentToolResult<WriteToolDetails>> {
932
+ const history = getConflictHistory(this.session);
933
+ const allEntries = history.entries();
934
+ if (allEntries.length === 0) {
935
+ throw new ToolError(
936
+ "`conflict://*` has nothing to resolve — no conflicts are currently registered. Re-read the file(s) with conflicts first.",
937
+ );
938
+ }
939
+
940
+ // Per-id directive mode: content made solely of `<id>: @side` lines
941
+ // resolves each listed conflict with that side in one call. Ideal for
942
+ // merge-hell files where dozens of pick-one blocks each need their own
943
+ // winner — one call instead of one write per conflict. Parsed from the
944
+ // PRE-strip content: hashline prefix stripping would otherwise eat the
945
+ // `<id>: ` heads as echoed line numbers.
946
+ const directives = resolveBulkDirectives(rawContent, replacementContent);
947
+ if (directives) {
948
+ const known = new Set(allEntries.map(entry => entry.id));
949
+ const unknown = [...directives.keys()].filter(id => !known.has(id));
950
+ if (unknown.length > 0) {
951
+ throw new ToolError(
952
+ `Bulk directive references unknown conflict id(s) ${unknown.map(id => `#${id}`).join(", ")}. Currently registered: ${allEntries.map(e => `#${e.id}`).join(", ")}.`,
953
+ );
954
+ }
955
+ }
956
+ const selectedEntries = directives ? allEntries.filter(entry => directives.has(entry.id)) : allEntries;
957
+ const contentFor = (entry: ConflictEntry): string =>
958
+ directives ? (directives.get(entry.id) as string) : replacementContent;
959
+
960
+ const byFile = new Map<string, ConflictEntry[]>();
961
+ for (const entry of selectedEntries) {
962
+ const bucket = byFile.get(entry.absolutePath) ?? [];
963
+ bucket.push(entry);
964
+ byFile.set(entry.absolutePath, bucket);
965
+ }
966
+
967
+ const succeededFiles: { displayPath: string; count: number; header?: string }[] = [];
968
+ const failedFiles: { displayPath: string; count: number; error: string }[] = [];
969
+ let totalResolvedIds = 0;
970
+ let totalEchoTrimmed = 0;
971
+
972
+ for (const [absolutePath, fileEntries] of byFile) {
973
+ const sample = fileEntries[0]!;
974
+ if (!(await fs.exists(absolutePath))) {
975
+ failedFiles.push({
976
+ displayPath: sample.displayPath,
977
+ count: fileEntries.length,
978
+ error: "file no longer exists",
979
+ });
980
+ continue;
981
+ }
982
+
983
+ fileEntries.sort((a, b) => b.startLine - a.startLine);
984
+
985
+ let text: string;
986
+ const resolvedEntries: ConflictEntry[] = [];
987
+ const staleEntries: ConflictEntry[] = [];
988
+ let failure: string | undefined;
989
+ try {
990
+ text = await Bun.file(absolutePath).text();
991
+ } catch (error) {
992
+ failedFiles.push({
993
+ displayPath: sample.displayPath,
994
+ count: fileEntries.length,
995
+ error: error instanceof Error ? error.message : String(error),
996
+ });
997
+ continue;
998
+ }
999
+ for (const entry of fileEntries) {
1000
+ try {
1001
+ const expanded = expandContentTokens(contentFor(entry), entry);
1002
+ const splice = spliceConflict(text, entry, expanded);
1003
+ text = splice.text;
1004
+ totalEchoTrimmed += splice.trimmedLeading + splice.trimmedTrailing;
1005
+ resolvedEntries.push(entry);
1006
+ } catch (error) {
1007
+ // A locate-miss for a region an earlier entry already spliced
1008
+ // in this pass is a stale duplicate registration (re-read after
1009
+ // an out-of-band shift) — treat it as already resolved.
1010
+ if (resolvedEntries.some(done => conflictRegionsEqual(done, entry))) {
1011
+ staleEntries.push(entry);
1012
+ continue;
1013
+ }
1014
+ failure = error instanceof Error ? error.message : String(error);
1015
+ break;
1016
+ }
1017
+ }
1018
+ if (failure !== undefined) {
1019
+ failedFiles.push({
1020
+ displayPath: sample.displayPath,
1021
+ count: fileEntries.length,
1022
+ error: failure,
1023
+ });
1024
+ continue;
1025
+ }
1026
+
1027
+ await writethroughNoop(absolutePath, text, signal);
1028
+ invalidateFsScanAfterWrite(absolutePath);
1029
+ this.session.bumpFileMutationVersion?.(absolutePath);
1030
+ this.session.fileSnapshotStore?.invalidate(absolutePath);
1031
+ for (const entry of resolvedEntries) history.invalidate(entry.id);
1032
+ for (const entry of staleEntries) history.invalidate(entry.id);
1033
+ const header = maybeWriteSnapshotHeader(this.session, absolutePath, text);
1034
+ succeededFiles.push({ displayPath: sample.displayPath, count: resolvedEntries.length, header });
1035
+ totalResolvedIds += resolvedEntries.length;
1036
+ }
1037
+
1038
+ const summaryLines: string[] = [];
1039
+ const fileWord = (n: number) => (n === 1 ? "file" : "files");
1040
+ const conflictWord = (n: number) => (n === 1 ? "conflict" : "conflicts");
1041
+ if (succeededFiles.length > 0) {
1042
+ summaryLines.push(
1043
+ `Resolved ${totalResolvedIds} ${conflictWord(totalResolvedIds)} across ${succeededFiles.length} ${fileWord(succeededFiles.length)}:`,
1044
+ );
1045
+ for (const file of succeededFiles) {
1046
+ summaryLines.push(` ${file.displayPath}: ${file.count} ${conflictWord(file.count)}`);
1047
+ }
1048
+ }
1049
+ if (directives && selectedEntries.length < allEntries.length) {
1050
+ const remaining = allEntries.filter(entry => !directives.has(entry.id)).map(entry => `#${entry.id}`);
1051
+ summaryLines.push(
1052
+ `Directive mode: ${remaining.length} unlisted ${conflictWord(remaining.length)} still registered (${remaining.join(", ")}).`,
1053
+ );
1054
+ }
1055
+ if (totalEchoTrimmed > 0) {
1056
+ summaryLines.push(
1057
+ `Note: dropped ${totalEchoTrimmed} content line(s) that duplicated code adjacent to conflict regions — writes replace only the marker block; surrounding lines stay in place.`,
1058
+ );
1059
+ }
1060
+ if (failedFiles.length > 0) {
1061
+ summaryLines.push(
1062
+ `Failed to resolve ${failedFiles.length} ${fileWord(failedFiles.length)} — registered entries left intact for retry:`,
1063
+ );
1064
+ for (const file of failedFiles) {
1065
+ summaryLines.push(` ${file.displayPath}: ${file.count} ${conflictWord(file.count)} (${file.error})`);
1066
+ }
1067
+ }
1068
+ const headerLines = succeededFiles
1069
+ .map(file => file.header)
1070
+ .filter((header): header is string => header !== undefined);
1071
+ if (headerLines.length > 0) {
1072
+ summaryLines.push("Snapshots:");
1073
+ for (const header of headerLines) summaryLines.push(` ${header}`);
1074
+ }
1075
+ if (stripped && !directives) {
1076
+ summaryLines.push("Note: auto-stripped hashline display prefixes from content before writing.");
1077
+ }
1078
+ const resultText = summaryLines.join("\n");
1079
+
1080
+ if (failedFiles.length > 0 && succeededFiles.length === 0) {
1081
+ throw new ToolError(resultText);
1082
+ }
1083
+ return {
1084
+ content: [{ type: "text", text: resultText }],
1085
+ details: {},
1086
+ isError: failedFiles.length > 0 ? true : undefined,
1087
+ };
1088
+ }
1089
+
1090
+ async execute(
1091
+ _toolCallId: string,
1092
+ { path: rawPath, content }: WriteParams,
1093
+ signal?: AbortSignal,
1094
+ onUpdate?: AgentToolUpdateCallback<WriteToolDetails>,
1095
+ context?: AgentToolContext,
1096
+ ): Promise<AgentToolResult<WriteToolDetails>> {
1097
+ // Strip a hashline `[path#TAG]` wrapper up front so every downstream
1098
+ // decision (scheme routing, internal-URL handler dispatch, plan-mode
1099
+ // guard, plan path resolution, ACP bridge routing) sees the same
1100
+ // filesystem target. Without this, a model that pastes a `read`
1101
+ // header as the `path` arg would slip past `isInternalUrlPath`
1102
+ // (which fails on a leading `[`) and the bridge router would send a
1103
+ // `[local://scratch.md#ABCD]` write to the editor instead of the
1104
+ // session-local sandbox.
1105
+ // Peel a read-tool selector (`:raw`, `:1-20`, …) so the write target matches
1106
+ // what `read` resolves for the same URL; line-range/malformed selectors throw.
1107
+ const path = peelWriteUrlSelector(unwrapHashlineHeaderPath(rawPath));
1108
+ return untilAborted(signal, async () => {
1109
+ // Strip hashline display prefixes ([PATH#HASH] + LINE:) if the model copied them from read output
1110
+ const { text: cleanContent, stripped } = stripWriteContent(this.session, content);
1111
+ const internalRouter = InternalUrlRouter.instance();
1112
+ assertWriteTargetAddressable(path, internalRouter);
1113
+ if (internalRouter.canHandle(path)) {
1114
+ const parsed = parseInternalUrl(path);
1115
+ const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
1116
+ const handler = internalRouter.getHandler(scheme);
1117
+ if (handler?.write) {
1118
+ // Handler-owned writes mutate user data outside the local
1119
+ // sandbox. xd:// dispatches retain each wrapped tool's tier.
1120
+ if (scheme !== "xd") {
1121
+ enforcePlanModeWrite(this.session, path, { op: "update" });
1122
+ emitWriteProgress(onUpdate, cleanContent, path);
1123
+ }
1124
+ let xdResult: AgentToolResult<WriteToolDetails> | undefined;
1125
+ await internalRouter.write(path, cleanContent, {
1126
+ cwd: this.session.cwd,
1127
+ signal,
1128
+ xd: {
1129
+ write: async (name, deviceContent) => {
1130
+ if (name === REPORT_ISSUE_DEVICE_NAME) {
1131
+ const { result, xdev } = await dispatchReportIssueDevice(this.session, deviceContent);
1132
+ xdResult = {
1133
+ content: result.content,
1134
+ details: { xdev },
1135
+ isError: result.isError,
1136
+ useless: result.useless,
1137
+ };
1138
+ return;
1139
+ }
1140
+ if (name && isResolutionDeviceName(name)) {
1141
+ const { result, xdev } = await dispatchResolutionDevice(this.session, name, deviceContent);
1142
+ xdResult = {
1143
+ content: result.content,
1144
+ details: { xdev },
1145
+ isError: result.isError,
1146
+ useless: result.useless,
1147
+ };
1148
+ return;
1149
+ }
1150
+ const xdev = this.session.xdev;
1151
+ if (!xdev) {
1152
+ throw new ToolError("xd:// is not mounted in this session.");
1153
+ }
1154
+ if (!name) {
1155
+ throw new ToolError(`Cannot write to xd:// itself — pick a device:\n${xdevListing(xdev)}`);
1156
+ }
1157
+ const { result, xdev: dispatch } = await dispatchXdevTool(
1158
+ xdev,
1159
+ name,
1160
+ deviceContent,
1161
+ _toolCallId,
1162
+ signal,
1163
+ onUpdate as AgentToolUpdateCallback,
1164
+ // The write tool's own gate just resolved approval at this
1165
+ // device's tier (see #approval above) — mark it so a wrapped
1166
+ // inner tool does not prompt a second time.
1167
+ context ? { ...context, xdevApproved: true } : undefined,
1168
+ );
1169
+ xdResult = {
1170
+ content: result.content,
1171
+ details: { xdev: dispatch },
1172
+ isError: result.isError,
1173
+ useless: result.useless,
1174
+ };
1175
+ },
1176
+ },
1177
+ });
1178
+ if (xdResult) return xdResult;
1179
+ let resultText = `Successfully wrote ${cleanContent.length} bytes to ${path}`;
1180
+ if (stripped) {
1181
+ resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
1182
+ }
1183
+ return { content: [{ type: "text", text: resultText }], details: {} };
1184
+ }
1185
+ if (scheme !== "local") await internalRouter.write(path, cleanContent);
1186
+ // local:// is backed by the session-local artifact sandbox and is
1187
+ // resolved by resolvePlanPath below so write/read share the same root.
1188
+ }
1189
+
1190
+ const conflictUri = parseConflictUri(path);
1191
+ if (conflictUri) {
1192
+ if (conflictUri.scope) {
1193
+ throw new ToolError(
1194
+ `Conflict URI scope '/${conflictUri.scope}' is read-only — read \`conflict://${conflictUri.id}/${conflictUri.scope}\` to inspect that side. To write, drop the scope (\`conflict://${conflictUri.id}\`) and put the chosen content (or shorthand like \`@${conflictUri.scope}\`) in \`content\`.`,
1195
+ );
1196
+ }
1197
+ emitWriteProgress(onUpdate, cleanContent, path);
1198
+ const result =
1199
+ conflictUri.id === "*"
1200
+ ? await this.#resolveAllConflicts(cleanContent, stripped, signal, content)
1201
+ : await this.#resolveSingleConflictById(conflictUri.id, cleanContent, stripped, signal);
1202
+ if (conflictUri.recoveredPrefix !== undefined) {
1203
+ appendNoteToResult(
1204
+ result,
1205
+ `Note: stripped erroneous '${conflictUri.recoveredPrefix}:' prefix from path; conflict URIs are global (use \`conflict://${conflictUri.id}\`, not \`<file>:conflict://${conflictUri.id}\`).`,
1206
+ );
1207
+ }
1208
+ return result;
1209
+ }
1210
+ const resolvedArchivePath = await this.#resolveArchiveWritePath(path);
1211
+ if (resolvedArchivePath) {
1212
+ enforcePlanModeWrite(this.session, resolvedArchivePath.archivePath, {
1213
+ op: resolvedArchivePath.exists ? "update" : "create",
1214
+ });
1215
+
1216
+ emitWriteProgress(
1217
+ onUpdate,
1218
+ cleanContent,
1219
+ `${formatPathRelativeToCwd(resolvedArchivePath.absolutePath, this.session.cwd)}:${
1220
+ resolvedArchivePath.archiveSubPath
1221
+ }`,
1222
+ resolvedArchivePath.absolutePath,
1223
+ );
1224
+ const archiveResult = await this.#writeArchiveEntry(cleanContent, resolvedArchivePath);
1225
+ if (stripped) {
1226
+ const firstText = archiveResult.content.find(
1227
+ (block): block is { type: "text"; text: string } =>
1228
+ block.type === "text" && typeof block.text === "string",
1229
+ );
1230
+ if (firstText) {
1231
+ firstText.text += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
1232
+ }
1233
+ }
1234
+ return archiveResult;
1235
+ }
1236
+
1237
+ const resolvedSqlitePath = await this.#resolveSqliteWritePath(path);
1238
+ if (resolvedSqlitePath) {
1239
+ enforcePlanModeWrite(this.session, resolvedSqlitePath.sqlitePath, { op: "update" });
1240
+
1241
+ emitWriteProgress(onUpdate, cleanContent, path, resolvedSqlitePath.absolutePath);
1242
+ const sqliteResult = await this.#writeSqliteRow(path, cleanContent, resolvedSqlitePath);
1243
+ if (stripped) {
1244
+ const firstText = sqliteResult.content.find(
1245
+ (block): block is { type: "text"; text: string } =>
1246
+ block.type === "text" && typeof block.text === "string",
1247
+ );
1248
+ if (firstText) {
1249
+ firstText.text += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
1250
+ }
1251
+ }
1252
+ return sqliteResult;
1253
+ }
1254
+
1255
+ await assertNotReadSelectorMisfire(path, cleanContent, this.session.cwd);
1256
+ enforcePlanModeWrite(this.session, path, { op: "create" });
1257
+ const absolutePath = resolvePlanPath(this.session, path);
1258
+ const batchRequest = getLspBatchRequest(context?.toolCall);
1259
+
1260
+ // Check if file exists and is auto-generated before overwriting
1261
+ if (await fs.exists(absolutePath)) {
1262
+ await assertEditableFile(absolutePath, path, this.session.settings);
1263
+ }
1264
+
1265
+ const displayPath = formatPathRelativeToCwd(absolutePath, this.session.cwd);
1266
+ emitWriteProgress(onUpdate, cleanContent, displayPath, absolutePath);
1267
+
1268
+ // Try ACP bridge first for editor-visible filesystem paths. Internal
1269
+ // artifacts such as local:// plans are owned by OMP, not the editor.
1270
+ const bridgeWrite = await routeWriteThroughBridge(this.session, path, absolutePath, cleanContent, signal);
1271
+ if (bridgeWrite) {
1272
+ // `write` always replaces the whole file, so (unlike hashline's
1273
+ // hunk-scoped diff) there's no size cost to keying the header/
1274
+ // executable-bit check on the verified post-write content —
1275
+ // use it so a drifted write (e.g. client format-on-save) still
1276
+ // hands back a tag that matches what's actually on disk.
1277
+ const madeExecutable = await maybeMarkExecutableForShebang(absolutePath, bridgeWrite.text);
1278
+ const header = maybeWriteSnapshotHeader(this.session, absolutePath, bridgeWrite.text);
1279
+ const writeLine = `Successfully wrote ${cleanContent.length} bytes to ${displayPath}`;
1280
+ let resultText = header ? `${header}\n${writeLine}` : writeLine;
1281
+ if (stripped) {
1282
+ resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
1283
+ }
1284
+ if (madeExecutable) {
1285
+ resultText += `\n${EXECUTABLE_NOTICE}`;
1286
+ }
1287
+ return {
1288
+ content: [{ type: "text", text: resultText }],
1289
+ details: { resolvedPath: absolutePath, madeExecutable: madeExecutable || undefined },
1290
+ };
1291
+ }
1292
+
1293
+ const diagnostics = await this.#writethrough(
1294
+ absolutePath,
1295
+ cleanContent,
1296
+ signal,
1297
+ undefined,
1298
+ batchRequest,
1299
+ dst => this.#deferredDiagnostics?.begin(dst),
1300
+ );
1301
+ invalidateFsScanAfterWrite(absolutePath);
1302
+ if (!this.#deferredDiagnostics || batchRequest?.flush === false) {
1303
+ this.session.bumpFileMutationVersion?.(absolutePath);
1304
+ }
1305
+ const madeExecutable = await maybeMarkExecutableForShebang(absolutePath, cleanContent);
1306
+
1307
+ const header = maybeWriteSnapshotHeader(this.session, absolutePath, cleanContent);
1308
+ const writeLine = `Successfully wrote ${cleanContent.length} bytes to ${displayPath}`;
1309
+ let resultText = header ? `${header}\n${writeLine}` : writeLine;
1310
+ if (stripped) {
1311
+ resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
1312
+ }
1313
+ if (madeExecutable) {
1314
+ resultText += `\n${EXECUTABLE_NOTICE}`;
1315
+ }
1316
+ if (!diagnostics) {
1317
+ return {
1318
+ content: [{ type: "text", text: resultText }],
1319
+ details: { resolvedPath: absolutePath, madeExecutable: madeExecutable || undefined },
1320
+ };
1321
+ }
1322
+
1323
+ return {
1324
+ content: [{ type: "text", text: resultText }],
1325
+ details: {
1326
+ resolvedPath: absolutePath,
1327
+ diagnostics,
1328
+ madeExecutable: madeExecutable || undefined,
1329
+ meta: outputMeta()
1330
+ .diagnostics(diagnostics.summary, diagnostics.messages ?? [])
1331
+ .get(),
1332
+ },
1333
+ };
1334
+ });
1335
+ }
1336
+ }
1337
+
1338
+ // =============================================================================
1339
+ // TUI Renderer
1340
+ // =============================================================================
1341
+
1342
+ interface WriteRenderArgs {
1343
+ path?: unknown;
1344
+ file_path?: unknown;
1345
+ content?: unknown;
1346
+ }
1347
+
1348
+ const WRITE_PREVIEW_LINES = 6;
1349
+ const WRITE_STREAMING_PREVIEW_LINES = 12;
1350
+
1351
+ function countLines(text: string): number {
1352
+ if (!text) return 0;
1353
+ return text.split("\n").length;
1354
+ }
1355
+
1356
+ /** Bounded newline scan: whether `text` spans more than `maxLines` lines.
1357
+ * Runs on every live compose (the repaint predicate below), so it must not
1358
+ * materialize the split the way `countLines` does. */
1359
+ function exceedsLineCount(text: string, maxLines: number): boolean {
1360
+ if (!text) return false;
1361
+ let lines = 1;
1362
+ for (let index = text.indexOf("\n"); index !== -1; index = text.indexOf("\n", index + 1)) {
1363
+ if (++lines > maxLines) return true;
1364
+ }
1365
+ return false;
1366
+ }
1367
+
1368
+ function writeContentOf(args: unknown): string {
1369
+ if (args == null || typeof args !== "object" || !("content" in args)) return "";
1370
+ const content = args.content;
1371
+ return typeof content === "string" ? content : "";
1372
+ }
1373
+
1374
+ function formatLineCountSuffix(lineCount: number, uiTheme: Theme): string {
1375
+ if (lineCount <= 0) return "";
1376
+ return uiTheme.fg("dim", ` · ${lineCount} line${lineCount === 1 ? "" : "s"}`);
1377
+ }
1378
+
1379
+ function normalizeDisplayText(text: unknown): string {
1380
+ let displayText = "";
1381
+ if (typeof text === "string") {
1382
+ displayText = text;
1383
+ } else if (text !== undefined && text !== null) {
1384
+ displayText = String(text);
1385
+ }
1386
+ return displayText.replace(/\r/g, "");
1387
+ }
1388
+
1389
+ /**
1390
+ * Minimum line-number gutter width for write previews. The streaming preview's
1391
+ * gutter must stay byte-stable as the line count grows: a width derived purely
1392
+ * from `String(totalLines).length` widens at the 10/100/1000-line crossings,
1393
+ * rewriting every already-rendered row — which forces the transcript's commit
1394
+ * audit to recommit the block's committed prefix (a full duplicate in native
1395
+ * scrollback). Reserving 3 digits keeps the gutter constant through 999 lines
1396
+ * and keeps the streamed rows byte-identical to the final result render.
1397
+ */
1398
+ const WRITE_GUTTER_MIN_WIDTH = 3;
1399
+
1400
+ function formatStreamingContent(
1401
+ content: string,
1402
+ expanded: boolean,
1403
+ language: string | undefined,
1404
+ uiTheme: Theme,
1405
+ spinnerFrame?: number,
1406
+ cache?: RenderedStringCache,
1407
+ ): string {
1408
+ if (!content) return "";
1409
+ const bodyText = cachedRenderedString(cache, uiTheme, expanded, language ?? "", content, () => {
1410
+ const lines = normalizeDisplayText(content).split("\n");
1411
+ const totalLines = lines.length;
1412
+ // Collapsed: follow the streaming edge with a bounded tail window so the box
1413
+ // stays short enough not to strand its scrolled-off head above the viewport
1414
+ // while the block is volatile. `Ctrl+O` (expanded) lifts the cap for a
1415
+ // deliberate full view — matching the eval streaming preview.
1416
+ const startIndex = expanded ? 0 : Math.max(0, totalLines - WRITE_STREAMING_PREVIEW_LINES);
1417
+ const visibleLines = lines.slice(startIndex);
1418
+ const hidden = startIndex;
1419
+ const highlighted = highlightCode(visibleLines.join("\n"), language);
1420
+ const lineNumberWidth = Math.max(WRITE_GUTTER_MIN_WIDTH, String(totalLines).length);
1421
+
1422
+ let text = "\n\n";
1423
+ if (hidden > 0) {
1424
+ text += `${uiTheme.fg("dim", `… (${hidden} earlier line${hidden === 1 ? "" : "s"})`)}\n`;
1425
+ }
1426
+ for (let i = 0; i < highlighted.length; i++) {
1427
+ const lineNum = startIndex + i + 1;
1428
+ const gutter = uiTheme.fg("dim", `${String(lineNum).padStart(lineNumberWidth, " ")} `);
1429
+ const body = replaceTabs(highlighted[i] ?? "");
1430
+ text += `${gutter}${body}\n`;
1431
+ }
1432
+ return text;
1433
+ });
1434
+ // The animated glyph lives on this trailing line — inside the transcript's
1435
+ // volatile-tail holdback — never in the header: an animating head row pins
1436
+ // the native-scrollback commit boundary at the top of the block, so a long
1437
+ // expanded preview could never scroll-append mid-stream.
1438
+ const spinner = spinnerFrame !== undefined ? `${formatStatusIcon("running", uiTheme, spinnerFrame)} ` : "";
1439
+ return `${bodyText}${spinner}${uiTheme.fg("dim", `… (streaming)`)}`;
1440
+ }
1441
+
1442
+ function renderContentPreview(
1443
+ content: string,
1444
+ expanded: boolean,
1445
+ language: string | undefined,
1446
+ uiTheme: Theme,
1447
+ cache?: RenderedStringCache,
1448
+ ): string {
1449
+ if (!content) return "";
1450
+ return cachedRenderedString(cache, uiTheme, expanded, language ?? "", content, () => {
1451
+ const rawLines = normalizeDisplayText(content).split("\n");
1452
+ const totalLines = rawLines.length;
1453
+ const maxLines = expanded ? totalLines : Math.min(totalLines, WRITE_PREVIEW_LINES);
1454
+ const visibleLines = rawLines.slice(0, maxLines);
1455
+ const highlighted = highlightCode(visibleLines.join("\n"), language);
1456
+ const lineNumberWidth = Math.max(WRITE_GUTTER_MIN_WIDTH, String(totalLines).length);
1457
+ const hidden = totalLines - maxLines;
1458
+
1459
+ let text = "\n\n";
1460
+ for (let i = 0; i < highlighted.length; i++) {
1461
+ const lineNum = i + 1;
1462
+ const gutter = uiTheme.fg("dim", `${String(lineNum).padStart(lineNumberWidth, " ")} `);
1463
+ const body = replaceTabs(highlighted[i] ?? "");
1464
+ text += `${gutter}${body}\n`;
1465
+ }
1466
+ if (!expanded && hidden > 0) {
1467
+ const hint = formatExpandHint(uiTheme, expanded, hidden > 0);
1468
+ const moreLine = `${formatMoreItems(hidden, "line")}${hint ? ` ${hint}` : ""}`;
1469
+ text += uiTheme.fg("dim", moreLine);
1470
+ }
1471
+ return text.trimEnd();
1472
+ });
1473
+ }
1474
+
1475
+ /** Render context for the write tool: resolves an `xd://`-mounted tool so its live renderer drives device dispatch previews. */
1476
+ export interface WriteRenderContext {
1477
+ resolveXdevMounted?: (name: string) => AgentTool | undefined;
1478
+ }
1479
+
1480
+ export const writeToolRenderer = {
1481
+ renderCall(
1482
+ args: WriteRenderArgs,
1483
+ options: RenderResultOptions & { renderContext?: WriteRenderContext },
1484
+ uiTheme: Theme,
1485
+ ): Component | undefined {
1486
+ const rawPath =
1487
+ typeof args.file_path === "string" ? args.file_path : typeof args.path === "string" ? args.path : "";
1488
+ // Render NOTHING until the streamed path arrives and provably is not an
1489
+ // xd:// device; xd:// writes then delegate to the mounted tool's renderer.
1490
+ // A present-but-malformed path (array/object from a bad provider parse)
1491
+ // is definitively not xd:// — fall through to the legacy frame.
1492
+ if (args.path === undefined && args.file_path === undefined) return undefined;
1493
+ if (rawPath && couldBecomeXdUrl(rawPath)) {
1494
+ const xdev = parseXdUrl(rawPath);
1495
+ // The path string is settled once the content field started streaming.
1496
+ const pathSettled = args.content !== undefined;
1497
+ if (!xdev?.name || !pathSettled) return undefined;
1498
+ if (isResolutionDeviceName(xdev.name)) return renderResolutionDeviceCall(xdev.name, args.content, uiTheme);
1499
+ if (xdev.name === REPORT_ISSUE_DEVICE_NAME) return renderReportIssueDeviceCall(args.content, uiTheme);
1500
+ return renderXdevCall(xdev.name, args.content, options, uiTheme, options.renderContext?.resolveXdevMounted);
1501
+ }
1502
+ const filePath = shortenPath(rawPath);
1503
+ const lang = rawPath ? (getLanguageFromPath(rawPath) ?? "text") : "text";
1504
+ const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
1505
+ const pathDisplay = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
1506
+ // No status icon on the head row: it's the head of the framed block, and
1507
+ // native-scrollback commits are prefix-only — an animated glyph would pin
1508
+ // the commit boundary at the top, and the pending hourglass just adds
1509
+ // noise. The liveness cue rides the trailing "(streaming)" line instead.
1510
+ const header = renderStatusLine(
1511
+ {
1512
+ title: "Write",
1513
+ description: `${langIcon} ${pathDisplay}`,
1514
+ },
1515
+ uiTheme,
1516
+ );
1517
+ const content = normalizeDisplayText(args.content);
1518
+ const streamingCache = createRenderedStringCache();
1519
+ return framedBlock(uiTheme, width => {
1520
+ const body = content
1521
+ ? formatStreamingContent(
1522
+ content,
1523
+ Boolean(options?.expanded),
1524
+ lang,
1525
+ uiTheme,
1526
+ options?.spinnerFrame,
1527
+ streamingCache,
1528
+ )
1529
+ : "";
1530
+ const bodyLines = body ? body.split("\n") : [];
1531
+ while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
1532
+ return {
1533
+ header,
1534
+ sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
1535
+ state: "pending",
1536
+ borderColor: "borderMuted",
1537
+ width,
1538
+ };
1539
+ });
1540
+ },
1541
+
1542
+ renderResult(
1543
+ result: { content: Array<{ type: string; text?: string }>; details?: WriteToolDetails; isError?: boolean },
1544
+ options: RenderResultOptions & { renderContext?: WriteRenderContext },
1545
+ uiTheme: Theme,
1546
+ args?: WriteRenderArgs,
1547
+ ): Component {
1548
+ // xd:// dispatch results render as the mounted tool's own result.
1549
+ const xdev = result.details?.xdev;
1550
+ if (xdev) {
1551
+ const delegated = renderXdevResult(xdev, result, options, uiTheme, options.renderContext?.resolveXdevMounted);
1552
+ if (delegated) return delegated;
1553
+ const text = result.content?.find(c => c.type === "text")?.text ?? "";
1554
+ return new Text(uiTheme.fg("toolOutput", replaceTabs(text)), 0, 0);
1555
+ }
1556
+ const rawPath =
1557
+ typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
1558
+ const filePath = shortenPath(rawPath);
1559
+ const fileContent = normalizeDisplayText(args?.content);
1560
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
1561
+ const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
1562
+ // The header shows the cwd-relative path but links to the absolute path the
1563
+ // write resolved to (args.path may be relative, which would yield a broken
1564
+ // `file://` URI). Falls back to plain text when the result lacks a path.
1565
+ const linkTarget = result.details?.resolvedPath;
1566
+ const styledPath = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
1567
+ const pathDisplay = filePath && linkTarget ? fileHyperlink(linkTarget, styledPath) : styledPath;
1568
+
1569
+ if (result.isError) {
1570
+ const errorText = result.content?.find(c => c.type === "text")?.text ?? "";
1571
+ const header = renderStatusLine(
1572
+ { icon: "error", title: "Write", description: `${langIcon} ${pathDisplay}` },
1573
+ uiTheme,
1574
+ );
1575
+ return framedBlock(uiTheme, width => ({
1576
+ header,
1577
+ sections: [{ lines: formatErrorDetail(errorText, uiTheme).split("\n") }],
1578
+ state: "error",
1579
+ borderColor: "error",
1580
+ width,
1581
+ }));
1582
+ }
1583
+
1584
+ const isPartial = options.isPartial === true;
1585
+ const progressText = result.content?.find(c => c.type === "text")?.text ?? "";
1586
+ const lineCount = countLines(fileContent);
1587
+ const lineSuffix = formatLineCountSuffix(lineCount, uiTheme);
1588
+ const execSuffix =
1589
+ !isPartial && result.details?.madeExecutable
1590
+ ? `${uiTheme.fg("dim", " · ")}${uiTheme.fg("success", "made executable!")}`
1591
+ : "";
1592
+ const header = renderStatusLine(
1593
+ {
1594
+ icon: isPartial ? "running" : undefined,
1595
+ iconOverride: isPartial ? undefined : uiTheme.styledSymbol("tool.write", "accent"),
1596
+ spinnerFrame: options.spinnerFrame,
1597
+ title: "Write",
1598
+ description: `${langIcon} ${pathDisplay}${lineSuffix}${execSuffix}`,
1599
+ },
1600
+ uiTheme,
1601
+ );
1602
+ const diagnostics = result.details?.diagnostics;
1603
+
1604
+ const previewCache = createRenderedStringCache();
1605
+ return framedBlock(uiTheme, width => {
1606
+ const { expanded } = options;
1607
+ let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
1608
+ if (isPartial && progressText) {
1609
+ const safeProgressText = truncateToWidth(
1610
+ replaceTabs(progressText),
1611
+ TRUNCATE_LENGTHS.LINE,
1612
+ Ellipsis.Unicode,
1613
+ );
1614
+ body = `${uiTheme.fg("muted", safeProgressText)}${body ? `\n${body}` : ""}`;
1615
+ }
1616
+ if (!isPartial && diagnostics) {
1617
+ const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
1618
+ uiTheme.getLangIcon(getLanguageFromPath(fp)),
1619
+ );
1620
+ if (diagText.trim()) {
1621
+ const diagLines = diagText.split("\n");
1622
+ const firstNonEmpty = diagLines.findIndex(line => line.trim());
1623
+ if (firstNonEmpty >= 0) body += `\n${diagLines.slice(firstNonEmpty).join("\n")}`;
1624
+ }
1625
+ }
1626
+ const bodyLines = body.split("\n");
1627
+ while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
1628
+ return {
1629
+ header,
1630
+ sections: bodyLines.length > 0 ? [{ lines: bodyLines }] : [],
1631
+ state: isPartial ? "pending" : "success",
1632
+ borderColor: "borderMuted",
1633
+ width,
1634
+ };
1635
+ });
1636
+ },
1637
+ mergeCallAndResult: true,
1638
+ // The collapsed pending preview follows the streaming edge with a tail
1639
+ // window once the content outgrows it (`… (N earlier lines)` + last rows);
1640
+ // the first partial result re-anchors the frame to the top of the file, so
1641
+ // tail rows already committed to viewport/native scrollback would survive
1642
+ // as stale content above the new frame without a full replay. Expanded and
1643
+ // short previews stay top-anchored and skip the (scrollback-wiping) reset.
1644
+ forceFirstResultViewportRepaint: (args: unknown, options: RenderResultOptions) =>
1645
+ !options.expanded && exceedsLineCount(writeContentOf(args), WRITE_STREAMING_PREVIEW_LINES),
1646
+ };