@wahack/pi-coding-agent 15.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1152) hide show
  1. package/CHANGELOG.md +10031 -0
  2. package/README.md +36 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +104 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/extensions/README.md +142 -0
  7. package/examples/extensions/api-demo.ts +79 -0
  8. package/examples/extensions/chalk-logger.ts +25 -0
  9. package/examples/extensions/hello.ts +31 -0
  10. package/examples/extensions/pirate.ts +43 -0
  11. package/examples/extensions/plan-mode.ts +549 -0
  12. package/examples/extensions/reload-runtime.ts +38 -0
  13. package/examples/extensions/thinking-note.ts +13 -0
  14. package/examples/extensions/tools.ts +145 -0
  15. package/examples/extensions/with-deps/index.ts +36 -0
  16. package/examples/extensions/with-deps/package-lock.json +31 -0
  17. package/examples/extensions/with-deps/package.json +17 -0
  18. package/examples/hooks/README.md +56 -0
  19. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  20. package/examples/hooks/confirm-destructive.ts +58 -0
  21. package/examples/hooks/custom-compaction.ts +115 -0
  22. package/examples/hooks/dirty-repo-guard.ts +51 -0
  23. package/examples/hooks/file-trigger.ts +40 -0
  24. package/examples/hooks/git-checkpoint.ts +52 -0
  25. package/examples/hooks/handoff.ts +149 -0
  26. package/examples/hooks/permission-gate.ts +33 -0
  27. package/examples/hooks/protected-paths.ts +29 -0
  28. package/examples/hooks/qna.ts +118 -0
  29. package/examples/hooks/status-line.ts +39 -0
  30. package/examples/sdk/01-minimal.ts +21 -0
  31. package/examples/sdk/02-custom-model.ts +49 -0
  32. package/examples/sdk/03-custom-prompt.ts +46 -0
  33. package/examples/sdk/04-skills.ts +43 -0
  34. package/examples/sdk/06-extensions.ts +82 -0
  35. package/examples/sdk/06-hooks.ts +61 -0
  36. package/examples/sdk/07-context-files.ts +35 -0
  37. package/examples/sdk/08-prompt-templates.ts +41 -0
  38. package/examples/sdk/08-slash-commands.ts +46 -0
  39. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  40. package/examples/sdk/11-sessions.ts +47 -0
  41. package/examples/sdk/12-redis-sessions.ts +54 -0
  42. package/examples/sdk/13-sql-sessions.ts +61 -0
  43. package/examples/sdk/README.md +172 -0
  44. package/package.json +554 -0
  45. package/scripts/build-binary.ts +100 -0
  46. package/scripts/bundle-dist.ts +90 -0
  47. package/scripts/format-prompts.ts +68 -0
  48. package/scripts/generate-docs-index.ts +40 -0
  49. package/scripts/generate-template.ts +33 -0
  50. package/scripts/omp +42 -0
  51. package/scripts/omp.ts +19 -0
  52. package/src/async/index.ts +1 -0
  53. package/src/async/job-manager.ts +625 -0
  54. package/src/auto-thinking/classifier.ts +185 -0
  55. package/src/autoresearch/command-resume.md +14 -0
  56. package/src/autoresearch/dashboard.ts +436 -0
  57. package/src/autoresearch/git.ts +319 -0
  58. package/src/autoresearch/helpers.ts +218 -0
  59. package/src/autoresearch/index.ts +536 -0
  60. package/src/autoresearch/prompt-setup.md +43 -0
  61. package/src/autoresearch/prompt.md +103 -0
  62. package/src/autoresearch/resume-message.md +10 -0
  63. package/src/autoresearch/state.ts +273 -0
  64. package/src/autoresearch/storage.ts +699 -0
  65. package/src/autoresearch/tools/init-experiment.ts +272 -0
  66. package/src/autoresearch/tools/log-experiment.ts +524 -0
  67. package/src/autoresearch/tools/run-experiment.ts +407 -0
  68. package/src/autoresearch/tools/update-notes.ts +109 -0
  69. package/src/autoresearch/types.ts +168 -0
  70. package/src/bun-imports.d.ts +28 -0
  71. package/src/capability/context-file.ts +44 -0
  72. package/src/capability/extension-module.ts +34 -0
  73. package/src/capability/extension.ts +47 -0
  74. package/src/capability/fs.ts +117 -0
  75. package/src/capability/hook.ts +40 -0
  76. package/src/capability/index.ts +436 -0
  77. package/src/capability/instruction.ts +37 -0
  78. package/src/capability/mcp.ts +74 -0
  79. package/src/capability/prompt.ts +35 -0
  80. package/src/capability/rule-buckets.ts +66 -0
  81. package/src/capability/rule.ts +261 -0
  82. package/src/capability/settings.ts +34 -0
  83. package/src/capability/skill.ts +63 -0
  84. package/src/capability/slash-command.ts +40 -0
  85. package/src/capability/ssh.ts +41 -0
  86. package/src/capability/system-prompt.ts +34 -0
  87. package/src/capability/tool.ts +38 -0
  88. package/src/capability/types.ts +168 -0
  89. package/src/cli/agents-cli.ts +138 -0
  90. package/src/cli/args.ts +340 -0
  91. package/src/cli/auth-broker-cli.ts +895 -0
  92. package/src/cli/auth-gateway-cli.ts +611 -0
  93. package/src/cli/classify-install-target.ts +76 -0
  94. package/src/cli/claude-trace-cli.ts +795 -0
  95. package/src/cli/commands/init-xdg.ts +27 -0
  96. package/src/cli/completion-gen.ts +550 -0
  97. package/src/cli/config-cli.ts +418 -0
  98. package/src/cli/dry-balance-cli.ts +856 -0
  99. package/src/cli/extension-flags.ts +48 -0
  100. package/src/cli/file-processor.ts +133 -0
  101. package/src/cli/gallery-cli.ts +230 -0
  102. package/src/cli/gallery-fixtures/agentic.ts +407 -0
  103. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  104. package/src/cli/gallery-fixtures/edit.ts +194 -0
  105. package/src/cli/gallery-fixtures/fs.ts +220 -0
  106. package/src/cli/gallery-fixtures/index.ts +40 -0
  107. package/src/cli/gallery-fixtures/interaction.ts +49 -0
  108. package/src/cli/gallery-fixtures/memory.ts +81 -0
  109. package/src/cli/gallery-fixtures/misc.ts +250 -0
  110. package/src/cli/gallery-fixtures/search.ts +213 -0
  111. package/src/cli/gallery-fixtures/shell.ts +167 -0
  112. package/src/cli/gallery-fixtures/types.ts +57 -0
  113. package/src/cli/gallery-fixtures/web.ts +158 -0
  114. package/src/cli/gallery-screenshot.ts +279 -0
  115. package/src/cli/grep-cli.ts +160 -0
  116. package/src/cli/grievances-cli.ts +256 -0
  117. package/src/cli/initial-message.ts +58 -0
  118. package/src/cli/list-models.ts +194 -0
  119. package/src/cli/plugin-cli.ts +996 -0
  120. package/src/cli/read-cli.ts +57 -0
  121. package/src/cli/session-picker.ts +79 -0
  122. package/src/cli/setup-cli.ts +231 -0
  123. package/src/cli/shell-cli.ts +176 -0
  124. package/src/cli/ssh-cli.ts +179 -0
  125. package/src/cli/startup-cwd.ts +68 -0
  126. package/src/cli/stats-cli.ts +238 -0
  127. package/src/cli/tiny-models-cli.ts +127 -0
  128. package/src/cli/update-cli.ts +611 -0
  129. package/src/cli/usage-cli.ts +603 -0
  130. package/src/cli/web-search-cli.ts +132 -0
  131. package/src/cli/worktree-cli.ts +291 -0
  132. package/src/cli-commands.ts +79 -0
  133. package/src/cli.ts +200 -0
  134. package/src/commands/acp.ts +24 -0
  135. package/src/commands/agents.ts +57 -0
  136. package/src/commands/auth-broker.ts +99 -0
  137. package/src/commands/auth-gateway.ts +69 -0
  138. package/src/commands/commit.ts +46 -0
  139. package/src/commands/complete.ts +66 -0
  140. package/src/commands/completions.ts +60 -0
  141. package/src/commands/config.ts +51 -0
  142. package/src/commands/dry-balance.ts +43 -0
  143. package/src/commands/gallery.ts +52 -0
  144. package/src/commands/grep.ts +48 -0
  145. package/src/commands/grievances.ts +51 -0
  146. package/src/commands/install.ts +107 -0
  147. package/src/commands/launch.ts +169 -0
  148. package/src/commands/plugin.ts +78 -0
  149. package/src/commands/read.ts +38 -0
  150. package/src/commands/setup.ts +67 -0
  151. package/src/commands/shell.ts +29 -0
  152. package/src/commands/ssh.ts +60 -0
  153. package/src/commands/stats.ts +29 -0
  154. package/src/commands/tiny-models.ts +36 -0
  155. package/src/commands/update.ts +21 -0
  156. package/src/commands/usage.ts +35 -0
  157. package/src/commands/web-search.ts +42 -0
  158. package/src/commands/worktree.ts +56 -0
  159. package/src/commit/agentic/agent.ts +317 -0
  160. package/src/commit/agentic/fallback.ts +96 -0
  161. package/src/commit/agentic/index.ts +355 -0
  162. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  163. package/src/commit/agentic/prompts/session-user.md +25 -0
  164. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  165. package/src/commit/agentic/prompts/system.md +38 -0
  166. package/src/commit/agentic/state.ts +60 -0
  167. package/src/commit/agentic/tools/analyze-file.ts +146 -0
  168. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  169. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  170. package/src/commit/agentic/tools/git-overview.ts +81 -0
  171. package/src/commit/agentic/tools/index.ts +54 -0
  172. package/src/commit/agentic/tools/propose-changelog.ts +144 -0
  173. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  174. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  175. package/src/commit/agentic/tools/schemas.ts +23 -0
  176. package/src/commit/agentic/tools/split-commit.ts +245 -0
  177. package/src/commit/agentic/topo-sort.ts +44 -0
  178. package/src/commit/agentic/trivial.ts +51 -0
  179. package/src/commit/agentic/validation.ts +183 -0
  180. package/src/commit/analysis/conventional.ts +64 -0
  181. package/src/commit/analysis/index.ts +4 -0
  182. package/src/commit/analysis/scope.ts +242 -0
  183. package/src/commit/analysis/summary.ts +105 -0
  184. package/src/commit/analysis/validation.ts +66 -0
  185. package/src/commit/changelog/detect.ts +40 -0
  186. package/src/commit/changelog/generate.ts +97 -0
  187. package/src/commit/changelog/index.ts +234 -0
  188. package/src/commit/changelog/parse.ts +44 -0
  189. package/src/commit/cli.ts +85 -0
  190. package/src/commit/git/diff.ts +148 -0
  191. package/src/commit/index.ts +5 -0
  192. package/src/commit/map-reduce/index.ts +69 -0
  193. package/src/commit/map-reduce/map-phase.ts +193 -0
  194. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  195. package/src/commit/map-reduce/utils.ts +9 -0
  196. package/src/commit/message.ts +11 -0
  197. package/src/commit/model-selection.ts +92 -0
  198. package/src/commit/pipeline.ts +243 -0
  199. package/src/commit/prompts/analysis-system.md +148 -0
  200. package/src/commit/prompts/analysis-user.md +38 -0
  201. package/src/commit/prompts/changelog-system.md +50 -0
  202. package/src/commit/prompts/changelog-user.md +18 -0
  203. package/src/commit/prompts/file-observer-system.md +24 -0
  204. package/src/commit/prompts/file-observer-user.md +8 -0
  205. package/src/commit/prompts/reduce-system.md +50 -0
  206. package/src/commit/prompts/reduce-user.md +17 -0
  207. package/src/commit/prompts/summary-retry.md +3 -0
  208. package/src/commit/prompts/summary-system.md +38 -0
  209. package/src/commit/prompts/summary-user.md +13 -0
  210. package/src/commit/prompts/types-description.md +2 -0
  211. package/src/commit/shared-llm.ts +77 -0
  212. package/src/commit/types.ts +118 -0
  213. package/src/commit/utils/exclusions.ts +42 -0
  214. package/src/commit/utils.ts +58 -0
  215. package/src/config/api-key-resolver.ts +60 -0
  216. package/src/config/append-only-context-mode.ts +31 -0
  217. package/src/config/config-file.ts +317 -0
  218. package/src/config/file-lock.ts +164 -0
  219. package/src/config/keybindings.ts +628 -0
  220. package/src/config/mcp-schema.json +230 -0
  221. package/src/config/model-discovery.ts +554 -0
  222. package/src/config/model-registry.ts +2090 -0
  223. package/src/config/model-resolver.ts +1502 -0
  224. package/src/config/model-roles.ts +74 -0
  225. package/src/config/models-config-schema.ts +226 -0
  226. package/src/config/models-config.ts +129 -0
  227. package/src/config/prompt-templates.ts +185 -0
  228. package/src/config/resolve-config-value.ts +94 -0
  229. package/src/config/settings-schema.ts +3530 -0
  230. package/src/config/settings.ts +1178 -0
  231. package/src/config.ts +242 -0
  232. package/src/cursor.ts +340 -0
  233. package/src/dap/client.ts +760 -0
  234. package/src/dap/config.ts +189 -0
  235. package/src/dap/defaults.json +212 -0
  236. package/src/dap/index.ts +4 -0
  237. package/src/dap/session.ts +1441 -0
  238. package/src/dap/types.ts +610 -0
  239. package/src/debug/index.ts +515 -0
  240. package/src/debug/log-formatting.ts +58 -0
  241. package/src/debug/log-viewer.ts +908 -0
  242. package/src/debug/profiler.ts +162 -0
  243. package/src/debug/protocol-probe.ts +267 -0
  244. package/src/debug/raw-sse-buffer.ts +273 -0
  245. package/src/debug/raw-sse.ts +292 -0
  246. package/src/debug/report-bundle.ts +374 -0
  247. package/src/debug/system-info.ts +111 -0
  248. package/src/debug/terminal-info.ts +124 -0
  249. package/src/discovery/agents-md.ts +67 -0
  250. package/src/discovery/agents.ts +230 -0
  251. package/src/discovery/at-imports.ts +273 -0
  252. package/src/discovery/builtin-defaults.ts +39 -0
  253. package/src/discovery/builtin-rules/index.ts +54 -0
  254. package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
  255. package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
  256. package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
  257. package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
  258. package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
  259. package/src/discovery/builtin-rules/rs-result-type.md +19 -0
  260. package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
  261. package/src/discovery/builtin-rules/ts-import-type.md +42 -0
  262. package/src/discovery/builtin-rules/ts-no-any.md +56 -0
  263. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +44 -0
  264. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -0
  265. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  266. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  267. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  268. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +65 -0
  269. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  270. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  271. package/src/discovery/builtin.ts +906 -0
  272. package/src/discovery/claude-plugins.ts +386 -0
  273. package/src/discovery/claude.ts +584 -0
  274. package/src/discovery/cline.ts +83 -0
  275. package/src/discovery/codex.ts +522 -0
  276. package/src/discovery/cursor.ts +220 -0
  277. package/src/discovery/gemini.ts +383 -0
  278. package/src/discovery/github.ts +154 -0
  279. package/src/discovery/helpers.ts +1016 -0
  280. package/src/discovery/index.ts +81 -0
  281. package/src/discovery/mcp-json.ts +171 -0
  282. package/src/discovery/omp-extension-roots.ts +190 -0
  283. package/src/discovery/omp-plugins.ts +383 -0
  284. package/src/discovery/opencode.ts +398 -0
  285. package/src/discovery/plugin-dir-roots.ts +28 -0
  286. package/src/discovery/ssh.ts +153 -0
  287. package/src/discovery/substitute-plugin-root.ts +29 -0
  288. package/src/discovery/vscode.ts +105 -0
  289. package/src/discovery/windsurf.ts +147 -0
  290. package/src/edit/apply-patch/index.ts +87 -0
  291. package/src/edit/apply-patch/parser.ts +174 -0
  292. package/src/edit/diff.ts +999 -0
  293. package/src/edit/file-snapshot-store.ts +91 -0
  294. package/src/edit/hashline/block-resolver.ts +33 -0
  295. package/src/edit/hashline/diff.ts +290 -0
  296. package/src/edit/hashline/execute.ts +242 -0
  297. package/src/edit/hashline/filesystem.ts +130 -0
  298. package/src/edit/hashline/index.ts +5 -0
  299. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  300. package/src/edit/hashline/params.ts +18 -0
  301. package/src/edit/index.ts +571 -0
  302. package/src/edit/modes/apply-patch.lark +19 -0
  303. package/src/edit/modes/apply-patch.ts +53 -0
  304. package/src/edit/modes/patch.ts +1891 -0
  305. package/src/edit/modes/replace.ts +1137 -0
  306. package/src/edit/normalize.ts +345 -0
  307. package/src/edit/notebook.ts +242 -0
  308. package/src/edit/read-file.ts +25 -0
  309. package/src/edit/renderer.ts +769 -0
  310. package/src/edit/streaming.ts +517 -0
  311. package/src/eval/__tests__/agent-bridge.test.ts +708 -0
  312. package/src/eval/__tests__/bridge-timeout.test.ts +64 -0
  313. package/src/eval/__tests__/budget-bridge.test.ts +69 -0
  314. package/src/eval/__tests__/completion-bridge.test.ts +412 -0
  315. package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
  316. package/src/eval/__tests__/idle-timeout.test.ts +80 -0
  317. package/src/eval/__tests__/js-context-manager.test.ts +241 -0
  318. package/src/eval/__tests__/kernel-spawn.test.ts +103 -0
  319. package/src/eval/agent-bridge.ts +319 -0
  320. package/src/eval/backend.ts +71 -0
  321. package/src/eval/bridge-timeout.ts +44 -0
  322. package/src/eval/budget-bridge.ts +48 -0
  323. package/src/eval/completion-bridge.ts +207 -0
  324. package/src/eval/concurrency-bridge.ts +34 -0
  325. package/src/eval/idle-timeout.ts +91 -0
  326. package/src/eval/index.ts +4 -0
  327. package/src/eval/js/context-manager.ts +502 -0
  328. package/src/eval/js/executor.ts +173 -0
  329. package/src/eval/js/index.ts +51 -0
  330. package/src/eval/js/shared/helpers.ts +283 -0
  331. package/src/eval/js/shared/indirect-eval.ts +30 -0
  332. package/src/eval/js/shared/local-module-loader.ts +342 -0
  333. package/src/eval/js/shared/prelude.ts +2 -0
  334. package/src/eval/js/shared/prelude.txt +246 -0
  335. package/src/eval/js/shared/rewrite-imports.ts +532 -0
  336. package/src/eval/js/shared/runtime.ts +352 -0
  337. package/src/eval/js/shared/types.ts +18 -0
  338. package/src/eval/js/tool-bridge.ts +162 -0
  339. package/src/eval/js/worker-core.ts +132 -0
  340. package/src/eval/js/worker-entry.ts +30 -0
  341. package/src/eval/js/worker-protocol.ts +47 -0
  342. package/src/eval/py/__tests__/prelude.test.ts +19 -0
  343. package/src/eval/py/display.ts +71 -0
  344. package/src/eval/py/executor.ts +742 -0
  345. package/src/eval/py/index.ts +68 -0
  346. package/src/eval/py/kernel.ts +748 -0
  347. package/src/eval/py/prelude.py +658 -0
  348. package/src/eval/py/prelude.ts +3 -0
  349. package/src/eval/py/runner.py +1133 -0
  350. package/src/eval/py/runtime.ts +276 -0
  351. package/src/eval/py/spawn-options.ts +126 -0
  352. package/src/eval/py/tool-bridge.ts +182 -0
  353. package/src/eval/session-id.ts +8 -0
  354. package/src/eval/types.ts +48 -0
  355. package/src/exa/index.ts +2 -0
  356. package/src/exa/mcp-client.ts +370 -0
  357. package/src/exa/types.ts +69 -0
  358. package/src/exec/bash-executor.ts +419 -0
  359. package/src/exec/exec.ts +53 -0
  360. package/src/exec/non-interactive-env.ts +48 -0
  361. package/src/export/custom-share.ts +65 -0
  362. package/src/export/html/index.ts +164 -0
  363. package/src/export/html/template.css +1051 -0
  364. package/src/export/html/template.generated.ts +2 -0
  365. package/src/export/html/template.html +46 -0
  366. package/src/export/html/template.js +2271 -0
  367. package/src/export/html/template.macro.ts +25 -0
  368. package/src/export/html/vendor/highlight.min.js +1213 -0
  369. package/src/export/html/vendor/marked.min.js +6 -0
  370. package/src/export/ttsr.ts +583 -0
  371. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  372. package/src/extensibility/custom-commands/bundled/review/index.ts +489 -0
  373. package/src/extensibility/custom-commands/index.ts +2 -0
  374. package/src/extensibility/custom-commands/loader.ts +238 -0
  375. package/src/extensibility/custom-commands/types.ts +113 -0
  376. package/src/extensibility/custom-tools/index.ts +7 -0
  377. package/src/extensibility/custom-tools/loader.ts +269 -0
  378. package/src/extensibility/custom-tools/types.ts +270 -0
  379. package/src/extensibility/custom-tools/wrapper.ts +47 -0
  380. package/src/extensibility/extensions/compact-handler.ts +40 -0
  381. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  382. package/src/extensibility/extensions/index.ts +16 -0
  383. package/src/extensibility/extensions/loader.ts +572 -0
  384. package/src/extensibility/extensions/runner.ts +922 -0
  385. package/src/extensibility/extensions/types.ts +1322 -0
  386. package/src/extensibility/extensions/wrapper.ts +223 -0
  387. package/src/extensibility/hooks/index.ts +5 -0
  388. package/src/extensibility/hooks/loader.ts +257 -0
  389. package/src/extensibility/hooks/runner.ts +425 -0
  390. package/src/extensibility/hooks/tool-wrapper.ts +107 -0
  391. package/src/extensibility/hooks/types.ts +606 -0
  392. package/src/extensibility/legacy-pi-ai-shim.ts +24 -0
  393. package/src/extensibility/legacy-pi-coding-agent-shim.ts +15 -0
  394. package/src/extensibility/plugins/doctor.ts +65 -0
  395. package/src/extensibility/plugins/git-url.ts +367 -0
  396. package/src/extensibility/plugins/index.ts +9 -0
  397. package/src/extensibility/plugins/installer.ts +192 -0
  398. package/src/extensibility/plugins/legacy-pi-compat.ts +682 -0
  399. package/src/extensibility/plugins/loader.ts +313 -0
  400. package/src/extensibility/plugins/manager.ts +827 -0
  401. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  402. package/src/extensibility/plugins/marketplace/fetcher.ts +317 -0
  403. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  404. package/src/extensibility/plugins/marketplace/manager.ts +770 -0
  405. package/src/extensibility/plugins/marketplace/registry.ts +196 -0
  406. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  407. package/src/extensibility/plugins/marketplace/types.ts +191 -0
  408. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  409. package/src/extensibility/plugins/parser.ts +105 -0
  410. package/src/extensibility/plugins/types.ts +194 -0
  411. package/src/extensibility/shared-events.ts +343 -0
  412. package/src/extensibility/skills.ts +312 -0
  413. package/src/extensibility/slash-commands.ts +227 -0
  414. package/src/extensibility/tool-proxy.ts +25 -0
  415. package/src/extensibility/typebox.ts +418 -0
  416. package/src/extensibility/utils.ts +44 -0
  417. package/src/goals/index.ts +3 -0
  418. package/src/goals/runtime.ts +528 -0
  419. package/src/goals/state.ts +37 -0
  420. package/src/goals/tools/goal-tool.ts +251 -0
  421. package/src/hindsight/backend.ts +354 -0
  422. package/src/hindsight/bank.ts +156 -0
  423. package/src/hindsight/client.ts +598 -0
  424. package/src/hindsight/config.ts +175 -0
  425. package/src/hindsight/content.ts +210 -0
  426. package/src/hindsight/index.ts +8 -0
  427. package/src/hindsight/mental-models.ts +429 -0
  428. package/src/hindsight/seeds.json +32 -0
  429. package/src/hindsight/state.ts +488 -0
  430. package/src/hindsight/transcript.ts +71 -0
  431. package/src/index.ts +59 -0
  432. package/src/internal-urls/agent-protocol.ts +146 -0
  433. package/src/internal-urls/artifact-protocol.ts +107 -0
  434. package/src/internal-urls/docs-index.generated.ts +106 -0
  435. package/src/internal-urls/history-protocol.ts +113 -0
  436. package/src/internal-urls/index.ts +25 -0
  437. package/src/internal-urls/issue-pr-protocol.ts +584 -0
  438. package/src/internal-urls/json-query.ts +126 -0
  439. package/src/internal-urls/local-protocol.ts +287 -0
  440. package/src/internal-urls/mcp-protocol.ts +151 -0
  441. package/src/internal-urls/memory-protocol.ts +169 -0
  442. package/src/internal-urls/omp-protocol.ts +93 -0
  443. package/src/internal-urls/parse.ts +72 -0
  444. package/src/internal-urls/registry-helpers.ts +25 -0
  445. package/src/internal-urls/router.ts +105 -0
  446. package/src/internal-urls/rule-protocol.ts +45 -0
  447. package/src/internal-urls/skill-protocol.ts +96 -0
  448. package/src/internal-urls/types.ts +152 -0
  449. package/src/internal-urls/vault-protocol.ts +936 -0
  450. package/src/irc/bus.ts +292 -0
  451. package/src/lib/xai-http.ts +124 -0
  452. package/src/lsp/client.ts +1193 -0
  453. package/src/lsp/clients/biome-client.ts +264 -0
  454. package/src/lsp/clients/index.ts +50 -0
  455. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  456. package/src/lsp/clients/swiftlint-client.ts +120 -0
  457. package/src/lsp/config.ts +502 -0
  458. package/src/lsp/defaults.json +493 -0
  459. package/src/lsp/diagnostics-ledger.ts +51 -0
  460. package/src/lsp/edits.ts +267 -0
  461. package/src/lsp/index.ts +2477 -0
  462. package/src/lsp/lspmux.ts +233 -0
  463. package/src/lsp/render.ts +694 -0
  464. package/src/lsp/startup-events.ts +13 -0
  465. package/src/lsp/types.ts +455 -0
  466. package/src/lsp/utils.ts +718 -0
  467. package/src/main.ts +1325 -0
  468. package/src/mcp/client.ts +484 -0
  469. package/src/mcp/config-writer.ts +225 -0
  470. package/src/mcp/config.ts +365 -0
  471. package/src/mcp/index.ts +29 -0
  472. package/src/mcp/json-rpc.ts +122 -0
  473. package/src/mcp/loader.ts +124 -0
  474. package/src/mcp/manager.ts +1275 -0
  475. package/src/mcp/oauth-discovery.ts +442 -0
  476. package/src/mcp/oauth-flow.ts +442 -0
  477. package/src/mcp/render.ts +132 -0
  478. package/src/mcp/smithery-auth.ts +104 -0
  479. package/src/mcp/smithery-connect.ts +145 -0
  480. package/src/mcp/smithery-registry.ts +477 -0
  481. package/src/mcp/timeout.ts +59 -0
  482. package/src/mcp/tool-bridge.ts +426 -0
  483. package/src/mcp/tool-cache.ts +117 -0
  484. package/src/mcp/transports/http.ts +519 -0
  485. package/src/mcp/transports/index.ts +6 -0
  486. package/src/mcp/transports/stdio.ts +528 -0
  487. package/src/mcp/types.ts +423 -0
  488. package/src/memories/index.ts +1150 -0
  489. package/src/memories/storage.ts +577 -0
  490. package/src/memory-backend/index.ts +18 -0
  491. package/src/memory-backend/local-backend.ts +39 -0
  492. package/src/memory-backend/off-backend.ts +25 -0
  493. package/src/memory-backend/resolve.ts +25 -0
  494. package/src/memory-backend/runtime.ts +66 -0
  495. package/src/memory-backend/types.ts +166 -0
  496. package/src/mnemopi/backend.ts +547 -0
  497. package/src/mnemopi/config.ts +160 -0
  498. package/src/mnemopi/index.ts +3 -0
  499. package/src/mnemopi/state.ts +584 -0
  500. package/src/modes/acp/acp-agent.ts +2407 -0
  501. package/src/modes/acp/acp-client-bridge.ts +154 -0
  502. package/src/modes/acp/acp-event-mapper.ts +929 -0
  503. package/src/modes/acp/acp-mode.ts +23 -0
  504. package/src/modes/acp/index.ts +2 -0
  505. package/src/modes/acp/terminal-auth.ts +37 -0
  506. package/src/modes/components/agent-dashboard.ts +1206 -0
  507. package/src/modes/components/agent-hub.ts +1071 -0
  508. package/src/modes/components/assistant-message.ts +307 -0
  509. package/src/modes/components/bash-execution.ts +220 -0
  510. package/src/modes/components/bordered-loader.ts +41 -0
  511. package/src/modes/components/branch-summary-message.ts +45 -0
  512. package/src/modes/components/btw-panel.ts +104 -0
  513. package/src/modes/components/chat-block.ts +111 -0
  514. package/src/modes/components/compaction-summary-message.ts +87 -0
  515. package/src/modes/components/copy-selector.ts +206 -0
  516. package/src/modes/components/countdown-timer.ts +75 -0
  517. package/src/modes/components/custom-editor.ts +398 -0
  518. package/src/modes/components/custom-message.ts +63 -0
  519. package/src/modes/components/diff.ts +277 -0
  520. package/src/modes/components/dynamic-border.ts +34 -0
  521. package/src/modes/components/error-banner.ts +33 -0
  522. package/src/modes/components/eval-execution.ts +158 -0
  523. package/src/modes/components/execution-shared.ts +101 -0
  524. package/src/modes/components/extensions/extension-dashboard.ts +399 -0
  525. package/src/modes/components/extensions/extension-list.ts +502 -0
  526. package/src/modes/components/extensions/index.ts +9 -0
  527. package/src/modes/components/extensions/inspector-panel.ts +317 -0
  528. package/src/modes/components/extensions/state-manager.ts +627 -0
  529. package/src/modes/components/extensions/types.ts +186 -0
  530. package/src/modes/components/footer.ts +274 -0
  531. package/src/modes/components/history-search.ts +280 -0
  532. package/src/modes/components/hook-editor.ts +167 -0
  533. package/src/modes/components/hook-input.ts +87 -0
  534. package/src/modes/components/hook-message.ts +66 -0
  535. package/src/modes/components/hook-selector.ts +660 -0
  536. package/src/modes/components/index.ts +38 -0
  537. package/src/modes/components/keybinding-hints.ts +65 -0
  538. package/src/modes/components/late-diagnostics-message.ts +60 -0
  539. package/src/modes/components/login-dialog.ts +164 -0
  540. package/src/modes/components/mcp-add-wizard.ts +1340 -0
  541. package/src/modes/components/message-frame.ts +88 -0
  542. package/src/modes/components/model-selector.ts +1271 -0
  543. package/src/modes/components/oauth-selector.ts +368 -0
  544. package/src/modes/components/omfg-panel.ts +141 -0
  545. package/src/modes/components/overlay-box.ts +108 -0
  546. package/src/modes/components/plan-review-overlay.ts +820 -0
  547. package/src/modes/components/plan-toc.ts +138 -0
  548. package/src/modes/components/plugin-selector.ts +95 -0
  549. package/src/modes/components/plugin-settings.ts +722 -0
  550. package/src/modes/components/queue-mode-selector.ts +56 -0
  551. package/src/modes/components/read-tool-group.ts +670 -0
  552. package/src/modes/components/segment-track.ts +52 -0
  553. package/src/modes/components/session-selector.ts +625 -0
  554. package/src/modes/components/settings-defs.ts +189 -0
  555. package/src/modes/components/settings-selector.ts +651 -0
  556. package/src/modes/components/show-images-selector.ts +45 -0
  557. package/src/modes/components/skill-message.ts +89 -0
  558. package/src/modes/components/status-line/component.ts +869 -0
  559. package/src/modes/components/status-line/context-thresholds.ts +79 -0
  560. package/src/modes/components/status-line/git-utils.ts +42 -0
  561. package/src/modes/components/status-line/index.ts +5 -0
  562. package/src/modes/components/status-line/presets.ts +106 -0
  563. package/src/modes/components/status-line/segments.ts +584 -0
  564. package/src/modes/components/status-line/separators.ts +55 -0
  565. package/src/modes/components/status-line/token-rate.ts +66 -0
  566. package/src/modes/components/status-line/types.ts +108 -0
  567. package/src/modes/components/theme-selector.ts +63 -0
  568. package/src/modes/components/thinking-selector.ts +52 -0
  569. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  570. package/src/modes/components/tips.txt +19 -0
  571. package/src/modes/components/todo-reminder.ts +38 -0
  572. package/src/modes/components/tool-execution.ts +1024 -0
  573. package/src/modes/components/transcript-container.ts +608 -0
  574. package/src/modes/components/tree-selector.ts +978 -0
  575. package/src/modes/components/ttsr-notification.ts +122 -0
  576. package/src/modes/components/user-message-selector.ts +227 -0
  577. package/src/modes/components/user-message.ts +66 -0
  578. package/src/modes/components/visual-truncate.ts +63 -0
  579. package/src/modes/components/welcome.ts +493 -0
  580. package/src/modes/controllers/btw-controller.ts +105 -0
  581. package/src/modes/controllers/command-controller-shared.ts +109 -0
  582. package/src/modes/controllers/command-controller.ts +1566 -0
  583. package/src/modes/controllers/event-controller.ts +1054 -0
  584. package/src/modes/controllers/extension-ui-controller.ts +886 -0
  585. package/src/modes/controllers/input-controller.ts +1073 -0
  586. package/src/modes/controllers/mcp-command-controller.ts +2017 -0
  587. package/src/modes/controllers/omfg-controller.ts +283 -0
  588. package/src/modes/controllers/omfg-rule.ts +647 -0
  589. package/src/modes/controllers/selector-controller.ts +1108 -0
  590. package/src/modes/controllers/ssh-command-controller.ts +384 -0
  591. package/src/modes/controllers/streaming-reveal.ts +279 -0
  592. package/src/modes/controllers/tan-command-controller.ts +173 -0
  593. package/src/modes/controllers/todo-command-controller.ts +485 -0
  594. package/src/modes/data/emojis.json +1 -0
  595. package/src/modes/emoji-autocomplete.ts +285 -0
  596. package/src/modes/gradient-highlight.ts +87 -0
  597. package/src/modes/image-references.ts +117 -0
  598. package/src/modes/index.ts +17 -0
  599. package/src/modes/interactive-mode.ts +3370 -0
  600. package/src/modes/internal-url-autocomplete.ts +143 -0
  601. package/src/modes/loop-limit.ts +140 -0
  602. package/src/modes/magic-keywords.ts +20 -0
  603. package/src/modes/markdown-prose.ts +247 -0
  604. package/src/modes/oauth-manual-input.ts +69 -0
  605. package/src/modes/orchestrate.ts +42 -0
  606. package/src/modes/print-mode.ts +126 -0
  607. package/src/modes/prompt-action-autocomplete.ts +260 -0
  608. package/src/modes/rpc/host-tools.ts +186 -0
  609. package/src/modes/rpc/host-uris.ts +235 -0
  610. package/src/modes/rpc/rpc-client.ts +963 -0
  611. package/src/modes/rpc/rpc-mode.ts +947 -0
  612. package/src/modes/rpc/rpc-subagents.ts +265 -0
  613. package/src/modes/rpc/rpc-types.ts +458 -0
  614. package/src/modes/runtime-init.ts +116 -0
  615. package/src/modes/session-observer-registry.ts +146 -0
  616. package/src/modes/setup-version.ts +11 -0
  617. package/src/modes/setup-wizard/index.ts +99 -0
  618. package/src/modes/setup-wizard/lazy.ts +16 -0
  619. package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
  620. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  621. package/src/modes/setup-wizard/scenes/providers.ts +69 -0
  622. package/src/modes/setup-wizard/scenes/sign-in.ts +205 -0
  623. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  624. package/src/modes/setup-wizard/scenes/theme.ts +299 -0
  625. package/src/modes/setup-wizard/scenes/types.ts +48 -0
  626. package/src/modes/setup-wizard/scenes/web-search.ts +129 -0
  627. package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
  628. package/src/modes/shared.ts +47 -0
  629. package/src/modes/theme/dark.json +95 -0
  630. package/src/modes/theme/defaults/alabaster.json +93 -0
  631. package/src/modes/theme/defaults/amethyst.json +96 -0
  632. package/src/modes/theme/defaults/anthracite.json +93 -0
  633. package/src/modes/theme/defaults/basalt.json +91 -0
  634. package/src/modes/theme/defaults/birch.json +95 -0
  635. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  636. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  637. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  638. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  639. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  640. package/src/modes/theme/defaults/dark-copper.json +95 -0
  641. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  642. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  643. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  644. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  645. package/src/modes/theme/defaults/dark-ember.json +95 -0
  646. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  647. package/src/modes/theme/defaults/dark-forest.json +96 -0
  648. package/src/modes/theme/defaults/dark-github.json +105 -0
  649. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  650. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  651. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  652. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  653. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  654. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  655. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  656. package/src/modes/theme/defaults/dark-nord.json +97 -0
  657. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  658. package/src/modes/theme/defaults/dark-one.json +100 -0
  659. package/src/modes/theme/defaults/dark-poimandres.json +142 -0
  660. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  661. package/src/modes/theme/defaults/dark-reef.json +91 -0
  662. package/src/modes/theme/defaults/dark-retro.json +92 -0
  663. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  664. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  665. package/src/modes/theme/defaults/dark-slate.json +95 -0
  666. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  667. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  668. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  669. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  670. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  671. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  672. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  673. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  674. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  675. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  676. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  677. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  678. package/src/modes/theme/defaults/graphite.json +92 -0
  679. package/src/modes/theme/defaults/index.ts +199 -0
  680. package/src/modes/theme/defaults/light-arctic.json +107 -0
  681. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  682. package/src/modes/theme/defaults/light-canyon.json +91 -0
  683. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  684. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  685. package/src/modes/theme/defaults/light-coral.json +95 -0
  686. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  687. package/src/modes/theme/defaults/light-dawn.json +90 -0
  688. package/src/modes/theme/defaults/light-dunes.json +91 -0
  689. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  690. package/src/modes/theme/defaults/light-forest.json +100 -0
  691. package/src/modes/theme/defaults/light-frost.json +95 -0
  692. package/src/modes/theme/defaults/light-github.json +115 -0
  693. package/src/modes/theme/defaults/light-glacier.json +91 -0
  694. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  695. package/src/modes/theme/defaults/light-haze.json +90 -0
  696. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  697. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  698. package/src/modes/theme/defaults/light-lavender.json +95 -0
  699. package/src/modes/theme/defaults/light-meadow.json +91 -0
  700. package/src/modes/theme/defaults/light-mint.json +95 -0
  701. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  702. package/src/modes/theme/defaults/light-ocean.json +99 -0
  703. package/src/modes/theme/defaults/light-one.json +99 -0
  704. package/src/modes/theme/defaults/light-opal.json +91 -0
  705. package/src/modes/theme/defaults/light-orchard.json +91 -0
  706. package/src/modes/theme/defaults/light-paper.json +95 -0
  707. package/src/modes/theme/defaults/light-poimandres.json +142 -0
  708. package/src/modes/theme/defaults/light-prism.json +90 -0
  709. package/src/modes/theme/defaults/light-retro.json +98 -0
  710. package/src/modes/theme/defaults/light-sand.json +95 -0
  711. package/src/modes/theme/defaults/light-savanna.json +91 -0
  712. package/src/modes/theme/defaults/light-solarized.json +102 -0
  713. package/src/modes/theme/defaults/light-soleil.json +90 -0
  714. package/src/modes/theme/defaults/light-sunset.json +99 -0
  715. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  716. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  717. package/src/modes/theme/defaults/light-wetland.json +91 -0
  718. package/src/modes/theme/defaults/light-zenith.json +89 -0
  719. package/src/modes/theme/defaults/limestone.json +94 -0
  720. package/src/modes/theme/defaults/mahogany.json +97 -0
  721. package/src/modes/theme/defaults/marble.json +93 -0
  722. package/src/modes/theme/defaults/obsidian.json +91 -0
  723. package/src/modes/theme/defaults/onyx.json +91 -0
  724. package/src/modes/theme/defaults/pearl.json +93 -0
  725. package/src/modes/theme/defaults/porcelain.json +91 -0
  726. package/src/modes/theme/defaults/quartz.json +96 -0
  727. package/src/modes/theme/defaults/sandstone.json +95 -0
  728. package/src/modes/theme/defaults/titanium.json +90 -0
  729. package/src/modes/theme/light.json +93 -0
  730. package/src/modes/theme/mermaid-cache.ts +29 -0
  731. package/src/modes/theme/shimmer.ts +235 -0
  732. package/src/modes/theme/theme-schema.json +459 -0
  733. package/src/modes/theme/theme.ts +2676 -0
  734. package/src/modes/turn-budget.ts +31 -0
  735. package/src/modes/types.ts +359 -0
  736. package/src/modes/ultrathink.ts +41 -0
  737. package/src/modes/utils/context-usage.ts +339 -0
  738. package/src/modes/utils/copy-targets.ts +360 -0
  739. package/src/modes/utils/hotkeys-markdown.ts +61 -0
  740. package/src/modes/utils/keybinding-matchers.ts +51 -0
  741. package/src/modes/utils/tools-markdown.ts +27 -0
  742. package/src/modes/utils/ui-helpers.ts +801 -0
  743. package/src/modes/workflow.ts +42 -0
  744. package/src/plan-mode/approved-plan.ts +186 -0
  745. package/src/plan-mode/plan-handoff.ts +37 -0
  746. package/src/plan-mode/plan-protection.ts +31 -0
  747. package/src/plan-mode/state.ts +6 -0
  748. package/src/priority.json +41 -0
  749. package/src/prompts/agents/designer.md +66 -0
  750. package/src/prompts/agents/explore.md +58 -0
  751. package/src/prompts/agents/frontmatter.md +11 -0
  752. package/src/prompts/agents/init.md +33 -0
  753. package/src/prompts/agents/librarian.md +119 -0
  754. package/src/prompts/agents/oracle.md +55 -0
  755. package/src/prompts/agents/plan.md +48 -0
  756. package/src/prompts/agents/reviewer.md +140 -0
  757. package/src/prompts/agents/task.md +16 -0
  758. package/src/prompts/ci-green-request.md +36 -0
  759. package/src/prompts/dry-balance-bench.md +8 -0
  760. package/src/prompts/goals/goal-budget-limit.md +16 -0
  761. package/src/prompts/goals/goal-continuation.md +28 -0
  762. package/src/prompts/goals/goal-mode-active.md +23 -0
  763. package/src/prompts/memories/consolidation.md +30 -0
  764. package/src/prompts/memories/read-path.md +11 -0
  765. package/src/prompts/memories/stage_one_input.md +6 -0
  766. package/src/prompts/memories/stage_one_system.md +21 -0
  767. package/src/prompts/review-custom-request.md +22 -0
  768. package/src/prompts/review-headless-request.md +16 -0
  769. package/src/prompts/review-request.md +69 -0
  770. package/src/prompts/steering/user-interjection.md +10 -0
  771. package/src/prompts/system/agent-creation-architect.md +50 -0
  772. package/src/prompts/system/agent-creation-user.md +6 -0
  773. package/src/prompts/system/auto-continue.md +1 -0
  774. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  775. package/src/prompts/system/auto-thinking-difficulty.md +12 -0
  776. package/src/prompts/system/background-tan-dispatch.md +8 -0
  777. package/src/prompts/system/btw-user.md +8 -0
  778. package/src/prompts/system/commit-message-system.md +14 -0
  779. package/src/prompts/system/custom-system-prompt.md +64 -0
  780. package/src/prompts/system/eager-todo.md +13 -0
  781. package/src/prompts/system/empty-stop-retry.md +6 -0
  782. package/src/prompts/system/irc-incoming.md +7 -0
  783. package/src/prompts/system/manual-continue.md +7 -0
  784. package/src/prompts/system/memory-consolidation-system.md +8 -0
  785. package/src/prompts/system/memory-extraction-system.md +26 -0
  786. package/src/prompts/system/omfg-user.md +50 -0
  787. package/src/prompts/system/orchestrate-notice.md +40 -0
  788. package/src/prompts/system/plan-mode-active.md +109 -0
  789. package/src/prompts/system/plan-mode-approved.md +25 -0
  790. package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
  791. package/src/prompts/system/plan-mode-reference.md +11 -0
  792. package/src/prompts/system/plan-mode-subagent.md +33 -0
  793. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  794. package/src/prompts/system/project-prompt.md +52 -0
  795. package/src/prompts/system/subagent-system-prompt.md +64 -0
  796. package/src/prompts/system/subagent-user-prompt.md +3 -0
  797. package/src/prompts/system/subagent-yield-reminder.md +12 -0
  798. package/src/prompts/system/system-prompt.md +258 -0
  799. package/src/prompts/system/tiny-title-system.md +8 -0
  800. package/src/prompts/system/title-system.md +16 -0
  801. package/src/prompts/system/ttsr-interrupt.md +7 -0
  802. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  803. package/src/prompts/system/ultrathink-notice.md +3 -0
  804. package/src/prompts/system/web-search.md +25 -0
  805. package/src/prompts/system/workflow-notice.md +70 -0
  806. package/src/prompts/tools/apply-patch.md +65 -0
  807. package/src/prompts/tools/ask.md +30 -0
  808. package/src/prompts/tools/ast-edit.md +39 -0
  809. package/src/prompts/tools/ast-grep.md +42 -0
  810. package/src/prompts/tools/async-result.md +8 -0
  811. package/src/prompts/tools/bash.md +46 -0
  812. package/src/prompts/tools/browser.md +73 -0
  813. package/src/prompts/tools/checkpoint.md +16 -0
  814. package/src/prompts/tools/debug.md +34 -0
  815. package/src/prompts/tools/eval.md +92 -0
  816. package/src/prompts/tools/find.md +36 -0
  817. package/src/prompts/tools/github.md +21 -0
  818. package/src/prompts/tools/goal.md +18 -0
  819. package/src/prompts/tools/image-gen.md +7 -0
  820. package/src/prompts/tools/inspect-image-system.md +20 -0
  821. package/src/prompts/tools/inspect-image.md +32 -0
  822. package/src/prompts/tools/irc.md +59 -0
  823. package/src/prompts/tools/job.md +19 -0
  824. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  825. package/src/prompts/tools/lsp.md +42 -0
  826. package/src/prompts/tools/memory-edit.md +8 -0
  827. package/src/prompts/tools/patch.md +70 -0
  828. package/src/prompts/tools/read.md +84 -0
  829. package/src/prompts/tools/recall.md +5 -0
  830. package/src/prompts/tools/reflect.md +5 -0
  831. package/src/prompts/tools/render-mermaid.md +9 -0
  832. package/src/prompts/tools/replace.md +30 -0
  833. package/src/prompts/tools/resolve.md +9 -0
  834. package/src/prompts/tools/retain.md +6 -0
  835. package/src/prompts/tools/rewind.md +13 -0
  836. package/src/prompts/tools/search-tool-bm25.md +32 -0
  837. package/src/prompts/tools/search.md +24 -0
  838. package/src/prompts/tools/ssh.md +31 -0
  839. package/src/prompts/tools/task-summary.md +17 -0
  840. package/src/prompts/tools/task.md +88 -0
  841. package/src/prompts/tools/todo.md +62 -0
  842. package/src/prompts/tools/web-search.md +10 -0
  843. package/src/prompts/tools/write.md +14 -0
  844. package/src/registry/agent-lifecycle.ts +218 -0
  845. package/src/registry/agent-registry.ts +151 -0
  846. package/src/sdk.ts +2558 -0
  847. package/src/secrets/index.ts +123 -0
  848. package/src/secrets/obfuscator.ts +298 -0
  849. package/src/secrets/regex.ts +21 -0
  850. package/src/session/agent-session.ts +10121 -0
  851. package/src/session/agent-storage.ts +455 -0
  852. package/src/session/artifacts.ts +135 -0
  853. package/src/session/auth-broker-config.ts +131 -0
  854. package/src/session/auth-storage.ts +29 -0
  855. package/src/session/blob-store.ts +255 -0
  856. package/src/session/client-bridge.ts +85 -0
  857. package/src/session/history-storage.ts +348 -0
  858. package/src/session/indexed-session-storage.ts +430 -0
  859. package/src/session/messages.ts +541 -0
  860. package/src/session/redis-session-storage.ts +170 -0
  861. package/src/session/session-dump-format.ts +209 -0
  862. package/src/session/session-history-format.ts +246 -0
  863. package/src/session/session-manager.ts +3676 -0
  864. package/src/session/session-storage.ts +529 -0
  865. package/src/session/shake-types.ts +43 -0
  866. package/src/session/sql-session-storage.ts +314 -0
  867. package/src/session/streaming-output.ts +1330 -0
  868. package/src/session/tool-choice-queue.ts +213 -0
  869. package/src/session/yield-queue.ts +173 -0
  870. package/src/slash-commands/acp-builtins.ts +70 -0
  871. package/src/slash-commands/builtin-registry.ts +1798 -0
  872. package/src/slash-commands/helpers/context-report.ts +39 -0
  873. package/src/slash-commands/helpers/format.ts +46 -0
  874. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  875. package/src/slash-commands/helpers/mcp.ts +532 -0
  876. package/src/slash-commands/helpers/parse.ts +85 -0
  877. package/src/slash-commands/helpers/ssh.ts +195 -0
  878. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  879. package/src/slash-commands/helpers/todo.ts +279 -0
  880. package/src/slash-commands/helpers/usage-report.ts +95 -0
  881. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  882. package/src/slash-commands/types.ts +135 -0
  883. package/src/ssh/config-writer.ts +183 -0
  884. package/src/ssh/connection-manager.ts +509 -0
  885. package/src/ssh/ssh-executor.ts +189 -0
  886. package/src/ssh/sshfs-mount.ts +140 -0
  887. package/src/ssh/utils.ts +8 -0
  888. package/src/stt/downloader.ts +71 -0
  889. package/src/stt/index.ts +3 -0
  890. package/src/stt/recorder.ts +351 -0
  891. package/src/stt/setup.ts +52 -0
  892. package/src/stt/stt-controller.ts +160 -0
  893. package/src/stt/transcribe.py +70 -0
  894. package/src/stt/transcriber.ts +91 -0
  895. package/src/stubs/natives/index.ts +814 -0
  896. package/src/stubs/natives/package.json +7 -0
  897. package/src/stubs/tui/index.ts +282 -0
  898. package/src/stubs/tui/package.json +7 -0
  899. package/src/system-prompt.ts +611 -0
  900. package/src/task/agents.ts +167 -0
  901. package/src/task/commands.ts +132 -0
  902. package/src/task/discovery.ts +122 -0
  903. package/src/task/executor.ts +2133 -0
  904. package/src/task/index.ts +1419 -0
  905. package/src/task/name-generator.ts +1577 -0
  906. package/src/task/omp-command.ts +26 -0
  907. package/src/task/output-manager.ts +88 -0
  908. package/src/task/parallel.ts +116 -0
  909. package/src/task/render.ts +1381 -0
  910. package/src/task/repair-args.ts +129 -0
  911. package/src/task/subprocess-tool-registry.ts +88 -0
  912. package/src/task/types.ts +336 -0
  913. package/src/task/worktree.ts +514 -0
  914. package/src/telemetry-export.ts +144 -0
  915. package/src/thinking.ts +167 -0
  916. package/src/tiny/compiled-runtime.ts +179 -0
  917. package/src/tiny/device.ts +111 -0
  918. package/src/tiny/dtype.ts +101 -0
  919. package/src/tiny/models.ts +242 -0
  920. package/src/tiny/text.ts +165 -0
  921. package/src/tiny/title-client.ts +543 -0
  922. package/src/tiny/title-protocol.ts +56 -0
  923. package/src/tiny/worker.ts +568 -0
  924. package/src/tool-discovery/mode.ts +24 -0
  925. package/src/tool-discovery/tool-index.ts +256 -0
  926. package/src/tools/approval.ts +189 -0
  927. package/src/tools/archive-reader.ts +721 -0
  928. package/src/tools/ask.ts +928 -0
  929. package/src/tools/ast-edit.ts +642 -0
  930. package/src/tools/ast-grep.ts +452 -0
  931. package/src/tools/auto-generated-guard.ts +322 -0
  932. package/src/tools/bash-command-fixup.ts +37 -0
  933. package/src/tools/bash-interactive.ts +408 -0
  934. package/src/tools/bash-interceptor.ts +67 -0
  935. package/src/tools/bash-pty-selection.ts +14 -0
  936. package/src/tools/bash-skill-urls.ts +248 -0
  937. package/src/tools/bash.ts +1386 -0
  938. package/src/tools/browser/attach.ts +175 -0
  939. package/src/tools/browser/launch.ts +660 -0
  940. package/src/tools/browser/readable.ts +112 -0
  941. package/src/tools/browser/registry.ts +197 -0
  942. package/src/tools/browser/render.ts +216 -0
  943. package/src/tools/browser/tab-protocol.ts +105 -0
  944. package/src/tools/browser/tab-supervisor.ts +628 -0
  945. package/src/tools/browser/tab-worker-entry.ts +21 -0
  946. package/src/tools/browser/tab-worker.ts +1226 -0
  947. package/src/tools/browser.ts +343 -0
  948. package/src/tools/checkpoint.ts +136 -0
  949. package/src/tools/conflict-detect.ts +718 -0
  950. package/src/tools/context.ts +39 -0
  951. package/src/tools/debug.ts +1067 -0
  952. package/src/tools/eval-backends.ts +27 -0
  953. package/src/tools/eval-render.ts +752 -0
  954. package/src/tools/eval.ts +577 -0
  955. package/src/tools/fetch.ts +1926 -0
  956. package/src/tools/file-recorder.ts +35 -0
  957. package/src/tools/find.ts +609 -0
  958. package/src/tools/fs-cache-invalidation.ts +28 -0
  959. package/src/tools/gh-cache-invalidation.ts +255 -0
  960. package/src/tools/gh-format.ts +12 -0
  961. package/src/tools/gh-renderer.ts +481 -0
  962. package/src/tools/gh.ts +3720 -0
  963. package/src/tools/github-cache.ts +637 -0
  964. package/src/tools/grouped-file-output.ts +210 -0
  965. package/src/tools/image-gen.ts +1517 -0
  966. package/src/tools/index.ts +599 -0
  967. package/src/tools/inspect-image-renderer.ts +132 -0
  968. package/src/tools/inspect-image.ts +174 -0
  969. package/src/tools/irc.ts +723 -0
  970. package/src/tools/job.ts +557 -0
  971. package/src/tools/json-tree.ts +243 -0
  972. package/src/tools/jtd-to-json-schema.ts +219 -0
  973. package/src/tools/jtd-to-typescript.ts +136 -0
  974. package/src/tools/jtd-utils.ts +102 -0
  975. package/src/tools/list-limit.ts +40 -0
  976. package/src/tools/match-line-format.ts +20 -0
  977. package/src/tools/memory-edit.ts +59 -0
  978. package/src/tools/memory-recall.ts +100 -0
  979. package/src/tools/memory-reflect.ts +88 -0
  980. package/src/tools/memory-render.ts +202 -0
  981. package/src/tools/memory-retain.ts +91 -0
  982. package/src/tools/output-meta.ts +754 -0
  983. package/src/tools/output-schema-validator.ts +132 -0
  984. package/src/tools/path-utils.ts +1054 -0
  985. package/src/tools/plan-mode-guard.ts +108 -0
  986. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  987. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  988. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  989. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  990. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  991. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  992. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  993. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  994. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  995. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  996. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  997. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  998. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  999. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  1000. package/src/tools/read.ts +2929 -0
  1001. package/src/tools/render-mermaid.ts +69 -0
  1002. package/src/tools/render-utils.ts +838 -0
  1003. package/src/tools/renderers.ts +77 -0
  1004. package/src/tools/report-tool-issue.ts +534 -0
  1005. package/src/tools/resolve.ts +276 -0
  1006. package/src/tools/review.ts +253 -0
  1007. package/src/tools/search-tool-bm25.ts +351 -0
  1008. package/src/tools/search.ts +1580 -0
  1009. package/src/tools/sqlite-reader.ts +828 -0
  1010. package/src/tools/ssh.ts +349 -0
  1011. package/src/tools/todo.ts +982 -0
  1012. package/src/tools/tool-errors.ts +62 -0
  1013. package/src/tools/tool-result.ts +94 -0
  1014. package/src/tools/tool-timeouts.ts +30 -0
  1015. package/src/tools/tts.ts +133 -0
  1016. package/src/tools/write.ts +1217 -0
  1017. package/src/tools/yield.ts +269 -0
  1018. package/src/tui/code-cell.ts +216 -0
  1019. package/src/tui/file-list.ts +55 -0
  1020. package/src/tui/hyperlink.ts +175 -0
  1021. package/src/tui/index.ts +12 -0
  1022. package/src/tui/output-block.ts +240 -0
  1023. package/src/tui/status-line.ts +54 -0
  1024. package/src/tui/tree-list.ts +84 -0
  1025. package/src/tui/types.ts +15 -0
  1026. package/src/tui/utils.ts +103 -0
  1027. package/src/utils/block-context.ts +312 -0
  1028. package/src/utils/changelog.ts +132 -0
  1029. package/src/utils/clipboard.ts +193 -0
  1030. package/src/utils/command-args.ts +76 -0
  1031. package/src/utils/commit-message-generator.ts +151 -0
  1032. package/src/utils/edit-mode.ts +41 -0
  1033. package/src/utils/enhanced-paste.ts +230 -0
  1034. package/src/utils/event-bus.ts +33 -0
  1035. package/src/utils/external-editor.ts +65 -0
  1036. package/src/utils/file-display-mode.ts +45 -0
  1037. package/src/utils/file-mentions.ts +281 -0
  1038. package/src/utils/git.ts +1833 -0
  1039. package/src/utils/image-loading.ts +132 -0
  1040. package/src/utils/image-resize.ts +309 -0
  1041. package/src/utils/jj.ts +248 -0
  1042. package/src/utils/lang-from-path.ts +239 -0
  1043. package/src/utils/markit.ts +89 -0
  1044. package/src/utils/open.ts +55 -0
  1045. package/src/utils/session-color.ts +68 -0
  1046. package/src/utils/shell-snapshot.ts +187 -0
  1047. package/src/utils/sixel.ts +69 -0
  1048. package/src/utils/title-generator.ts +373 -0
  1049. package/src/utils/tool-choice.ts +33 -0
  1050. package/src/utils/tools-manager.ts +363 -0
  1051. package/src/web/kagi.ts +305 -0
  1052. package/src/web/parallel.ts +353 -0
  1053. package/src/web/scrapers/artifacthub.ts +207 -0
  1054. package/src/web/scrapers/arxiv.ts +83 -0
  1055. package/src/web/scrapers/aur.ts +162 -0
  1056. package/src/web/scrapers/biorxiv.ts +133 -0
  1057. package/src/web/scrapers/bluesky.ts +262 -0
  1058. package/src/web/scrapers/brew.ts +172 -0
  1059. package/src/web/scrapers/cheatsh.ts +68 -0
  1060. package/src/web/scrapers/chocolatey.ts +196 -0
  1061. package/src/web/scrapers/choosealicense.ts +95 -0
  1062. package/src/web/scrapers/cisa-kev.ts +87 -0
  1063. package/src/web/scrapers/clojars.ts +154 -0
  1064. package/src/web/scrapers/coingecko.ts +177 -0
  1065. package/src/web/scrapers/crates-io.ts +97 -0
  1066. package/src/web/scrapers/crossref.ts +136 -0
  1067. package/src/web/scrapers/devto.ts +147 -0
  1068. package/src/web/scrapers/discogs.ts +306 -0
  1069. package/src/web/scrapers/discourse.ts +197 -0
  1070. package/src/web/scrapers/dockerhub.ts +138 -0
  1071. package/src/web/scrapers/docs-rs.ts +653 -0
  1072. package/src/web/scrapers/fdroid.ts +134 -0
  1073. package/src/web/scrapers/firefox-addons.ts +191 -0
  1074. package/src/web/scrapers/flathub.ts +223 -0
  1075. package/src/web/scrapers/github-gist.ts +58 -0
  1076. package/src/web/scrapers/github.ts +704 -0
  1077. package/src/web/scrapers/gitlab.ts +401 -0
  1078. package/src/web/scrapers/go-pkg.ts +266 -0
  1079. package/src/web/scrapers/hackage.ts +140 -0
  1080. package/src/web/scrapers/hackernews.ts +189 -0
  1081. package/src/web/scrapers/hex.ts +105 -0
  1082. package/src/web/scrapers/huggingface.ts +321 -0
  1083. package/src/web/scrapers/iacr.ts +89 -0
  1084. package/src/web/scrapers/index.ts +252 -0
  1085. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1086. package/src/web/scrapers/lemmy.ts +203 -0
  1087. package/src/web/scrapers/lobsters.ts +175 -0
  1088. package/src/web/scrapers/mastodon.ts +292 -0
  1089. package/src/web/scrapers/maven.ts +138 -0
  1090. package/src/web/scrapers/mdn.ts +173 -0
  1091. package/src/web/scrapers/metacpan.ts +222 -0
  1092. package/src/web/scrapers/musicbrainz.ts +250 -0
  1093. package/src/web/scrapers/npm.ts +98 -0
  1094. package/src/web/scrapers/nuget.ts +183 -0
  1095. package/src/web/scrapers/nvd.ts +222 -0
  1096. package/src/web/scrapers/ollama.ts +239 -0
  1097. package/src/web/scrapers/open-vsx.ts +106 -0
  1098. package/src/web/scrapers/opencorporates.ts +292 -0
  1099. package/src/web/scrapers/openlibrary.ts +336 -0
  1100. package/src/web/scrapers/orcid.ts +286 -0
  1101. package/src/web/scrapers/osv.ts +176 -0
  1102. package/src/web/scrapers/packagist.ts +160 -0
  1103. package/src/web/scrapers/pub-dev.ts +143 -0
  1104. package/src/web/scrapers/pubmed.ts +211 -0
  1105. package/src/web/scrapers/pypi.ts +112 -0
  1106. package/src/web/scrapers/rawg.ts +110 -0
  1107. package/src/web/scrapers/readthedocs.ts +120 -0
  1108. package/src/web/scrapers/reddit.ts +95 -0
  1109. package/src/web/scrapers/repology.ts +251 -0
  1110. package/src/web/scrapers/rfc.ts +201 -0
  1111. package/src/web/scrapers/rubygems.ts +103 -0
  1112. package/src/web/scrapers/searchcode.ts +189 -0
  1113. package/src/web/scrapers/sec-edgar.ts +261 -0
  1114. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1115. package/src/web/scrapers/snapcraft.ts +187 -0
  1116. package/src/web/scrapers/sourcegraph.ts +336 -0
  1117. package/src/web/scrapers/spdx.ts +108 -0
  1118. package/src/web/scrapers/spotify.ts +198 -0
  1119. package/src/web/scrapers/stackoverflow.ts +120 -0
  1120. package/src/web/scrapers/terraform.ts +277 -0
  1121. package/src/web/scrapers/tldr.ts +47 -0
  1122. package/src/web/scrapers/twitter.ts +94 -0
  1123. package/src/web/scrapers/types.ts +397 -0
  1124. package/src/web/scrapers/utils.ts +109 -0
  1125. package/src/web/scrapers/vimeo.ts +133 -0
  1126. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1127. package/src/web/scrapers/w3c.ts +156 -0
  1128. package/src/web/scrapers/wikidata.ts +344 -0
  1129. package/src/web/scrapers/wikipedia.ts +84 -0
  1130. package/src/web/scrapers/youtube.ts +325 -0
  1131. package/src/web/search/index.ts +292 -0
  1132. package/src/web/search/provider.ts +157 -0
  1133. package/src/web/search/providers/anthropic.ts +318 -0
  1134. package/src/web/search/providers/base.ts +89 -0
  1135. package/src/web/search/providers/brave.ts +152 -0
  1136. package/src/web/search/providers/codex.ts +591 -0
  1137. package/src/web/search/providers/exa.ts +400 -0
  1138. package/src/web/search/providers/gemini.ts +460 -0
  1139. package/src/web/search/providers/jina.ts +111 -0
  1140. package/src/web/search/providers/kagi.ts +86 -0
  1141. package/src/web/search/providers/kimi.ts +196 -0
  1142. package/src/web/search/providers/parallel.ts +225 -0
  1143. package/src/web/search/providers/perplexity.ts +730 -0
  1144. package/src/web/search/providers/searxng.ts +313 -0
  1145. package/src/web/search/providers/synthetic.ts +114 -0
  1146. package/src/web/search/providers/tavily.ts +176 -0
  1147. package/src/web/search/providers/utils.ts +128 -0
  1148. package/src/web/search/providers/zai.ts +333 -0
  1149. package/src/web/search/render.ts +262 -0
  1150. package/src/web/search/types.ts +482 -0
  1151. package/src/web/search/utils.ts +17 -0
  1152. package/src/workspace-tree.ts +286 -0
@@ -0,0 +1,1517 @@
1
+ import * as os from "node:os";
2
+ import * as path from "node:path";
3
+ import { type ApiKey, type FetchImpl, getEnvApiKey, type Model, withAuth } from "@oh-my-pi/pi-ai";
4
+ import {
5
+ CODEX_BASE_URL,
6
+ getCodexAccountId,
7
+ OPENAI_HEADER_VALUES,
8
+ OPENAI_HEADERS,
9
+ URL_PATHS,
10
+ } from "@oh-my-pi/pi-catalog/wire/codex";
11
+ import { getAntigravityUserAgent } from "@oh-my-pi/pi-catalog/wire/gemini-headers";
12
+ import {
13
+ $env,
14
+ isEnoent,
15
+ parseImageMetadata,
16
+ prompt,
17
+ ptree,
18
+ readSseJson,
19
+ Snowflake,
20
+ untilAborted,
21
+ } from "@oh-my-pi/pi-utils";
22
+ import * as z from "zod/v4";
23
+ import packageJson from "../../package.json" with { type: "json" };
24
+
25
+ import { isAuthenticated, type ModelRegistry } from "../config/model-registry";
26
+ import type { CustomTool } from "../extensibility/custom-tools/types";
27
+ import { ohMyPiXAIUserAgent, resolveXAIHttpCredentials } from "../lib/xai-http";
28
+ import imageGenDescription from "../prompts/tools/image-gen.md" with { type: "text" };
29
+ import { resolveReadPath } from "./path-utils";
30
+
31
+ const DEFAULT_MODEL = "gemini-3-pro-image-preview";
32
+ const DEFAULT_OPENROUTER_MODEL = "google/gemini-3-pro-image-preview";
33
+ const DEFAULT_ANTIGRAVITY_MODEL = "gemini-3-pro-image";
34
+ const DEFAULT_XAI_IMAGE_MODEL = "grok-imagine-image";
35
+ const IMAGE_TIMEOUT = 3 * 60 * 1000; // 3 minutes
36
+ const MAX_IMAGE_SIZE = 35 * 1024 * 1024;
37
+ const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
38
+ const OPENAI_IMAGE_OUTPUT_FORMAT = "webp";
39
+ const OPENAI_IMAGE_MIME_TYPE = "image/webp";
40
+
41
+ const ANTIGRAVITY_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com";
42
+ const IMAGE_SYSTEM_INSTRUCTION =
43
+ "You are an AI image generator. Generate images based on user descriptions. Focus on creating high-quality, visually appealing images that match the user's request.";
44
+
45
+ export type ImageProvider = "antigravity" | "gemini" | "openai" | "openai-codex" | "openrouter" | "xai";
46
+ export type ImageProviderPreference = Exclude<ImageProvider, "openai-codex"> | "auto";
47
+
48
+ interface ImageApiKey {
49
+ provider: ImageProvider;
50
+ apiKey: string;
51
+ projectId?: string;
52
+ model?: Model;
53
+ }
54
+
55
+ const COMMON_IMAGE_ASPECT_RATIOS = ["1:1", "3:4", "4:3", "9:16", "16:9"] as const;
56
+ const XAI_IMAGE_ASPECT_RATIOS = [...COMMON_IMAGE_ASPECT_RATIOS, "3:2", "2:3"] as const;
57
+ const COMMON_IMAGE_ASPECT_RATIO_SET = new Set<string>(COMMON_IMAGE_ASPECT_RATIOS);
58
+ const IMAGE_PROVIDER_PREFERENCES = new Set<string>(["auto", "antigravity", "gemini", "openai", "openrouter", "xai"]);
59
+
60
+ const responseModalitySchema = z.enum(["IMAGE", "TEXT"] as const);
61
+ const aspectRatioSchema = z.enum(XAI_IMAGE_ASPECT_RATIOS).describe("aspect ratio");
62
+ const imageSizeSchema = z.enum(["1024x1024", "1536x1024", "1024x1536"] as const).describe("image size");
63
+
64
+ const inputImageSchema = z
65
+ .object({
66
+ path: z.string().describe("input image path").optional(),
67
+ data: z.string().describe("base64 image data").optional(),
68
+ mime_type: z.string().describe("mime type").optional(),
69
+ })
70
+ .strict();
71
+
72
+ const baseImageSchema = z
73
+ .object({
74
+ subject: z.string().describe("main subject"),
75
+ action: z.string().describe("what subject is doing").optional(),
76
+ scene: z.string().describe("location or environment").optional(),
77
+ composition: z.string().describe("camera angle and framing").optional(),
78
+ lighting: z.string().describe("lighting setup").optional(),
79
+ style: z.string().describe("artistic style").optional(),
80
+ text: z.string().describe("text to render").optional(),
81
+ changes: z.array(z.string()).describe("edits to make").optional(),
82
+ aspect_ratio: aspectRatioSchema.optional(),
83
+ image_size: imageSizeSchema.optional(),
84
+ input: z.array(inputImageSchema).describe("input images").optional(),
85
+ })
86
+ .strict();
87
+
88
+ export const imageGenSchema = baseImageSchema;
89
+ export type ImageGenParams = z.infer<typeof imageGenSchema>;
90
+ export type GeminiResponseModality = z.infer<typeof responseModalitySchema>;
91
+
92
+ /**
93
+ * Assembles a structured prompt from the provided parameters.
94
+ * For generation: builds "subject, action, scene. composition. lighting. camera. style."
95
+ * For edits: appends change instructions and preserve directives.
96
+ */
97
+ function assemblePrompt(params: ImageGenParams): string {
98
+ const parts: string[] = [];
99
+
100
+ // Core subject line: subject + action + scene
101
+ const subjectParts = [params.subject];
102
+ if (params.action) subjectParts.push(params.action);
103
+ if (params.scene) subjectParts.push(params.scene);
104
+ parts.push(subjectParts.join(", "));
105
+
106
+ // Technical details as separate sentences
107
+ if (params.composition) parts.push(params.composition);
108
+ if (params.lighting) parts.push(params.lighting);
109
+ if (params.style) parts.push(params.style);
110
+
111
+ // Join with periods for sentence structure
112
+ let prompt = `${parts.map(p => p.replace(/[.!,;:]+$/, "")).join(". ")}.`;
113
+
114
+ // Text rendering specs
115
+ if (params.text) {
116
+ prompt += `\n\nText: ${params.text}`;
117
+ }
118
+
119
+ // Edit mode: changes and preserve directives
120
+ if (params.changes?.length) {
121
+ prompt += `\n\nChanges:\n${params.changes.map(c => `- ${c}`).join("\n")}`;
122
+ }
123
+
124
+ return prompt;
125
+ }
126
+
127
+ interface GeminiInlineData {
128
+ data?: string;
129
+ mimeType?: string;
130
+ }
131
+
132
+ interface GeminiPart {
133
+ text?: string;
134
+ inlineData?: GeminiInlineData;
135
+ }
136
+
137
+ interface GeminiCandidate {
138
+ content?: { parts?: GeminiPart[] };
139
+ }
140
+
141
+ interface GeminiSafetyRating {
142
+ category?: string;
143
+ probability?: string;
144
+ }
145
+
146
+ interface GeminiPromptFeedback {
147
+ blockReason?: string;
148
+ safetyRatings?: GeminiSafetyRating[];
149
+ }
150
+
151
+ interface GeminiUsageMetadata {
152
+ promptTokenCount?: number;
153
+ candidatesTokenCount?: number;
154
+ totalTokenCount?: number;
155
+ }
156
+
157
+ interface GeminiGenerateContentResponse {
158
+ candidates?: GeminiCandidate[];
159
+ promptFeedback?: GeminiPromptFeedback;
160
+ usageMetadata?: GeminiUsageMetadata;
161
+ }
162
+
163
+ interface OpenAIResponsesUsage {
164
+ input_tokens?: number;
165
+ output_tokens?: number;
166
+ total_tokens?: number;
167
+ }
168
+
169
+ type ImageUsageMetadata = GeminiUsageMetadata | OpenAIResponsesUsage;
170
+
171
+ type OpenAIImageAction = "edit" | "generate";
172
+
173
+ interface OpenAIInputTextContent {
174
+ type: "input_text";
175
+ text: string;
176
+ }
177
+
178
+ interface OpenAIInputImageContent {
179
+ type: "input_image";
180
+ detail: "auto";
181
+ image_url: string;
182
+ }
183
+
184
+ type OpenAIInputContent = OpenAIInputTextContent | OpenAIInputImageContent;
185
+
186
+ interface OpenAIImageGenerationTool {
187
+ type: "image_generation";
188
+ action: OpenAIImageAction;
189
+ output_format: typeof OPENAI_IMAGE_OUTPUT_FORMAT;
190
+ size?: string;
191
+ }
192
+
193
+ interface OpenAIHostedImageRequest {
194
+ model: string;
195
+ instructions?: string;
196
+ input: Array<{ role: "user"; content: OpenAIInputContent[] }>;
197
+ tools: OpenAIImageGenerationTool[];
198
+ tool_choice: { type: "image_generation" };
199
+ store: false;
200
+ stream?: boolean;
201
+ }
202
+
203
+ interface OpenAIImageGenerationCall {
204
+ id?: string;
205
+ type: "image_generation_call";
206
+ result?: string;
207
+ revised_prompt?: string;
208
+ status?: string;
209
+ }
210
+
211
+ interface OpenAIOutputText {
212
+ type: "output_text" | "refusal";
213
+ text?: string;
214
+ refusal?: string;
215
+ }
216
+
217
+ interface OpenAIOutputMessage {
218
+ id?: string;
219
+ type: "message";
220
+ content?: OpenAIOutputText[];
221
+ }
222
+
223
+ type OpenAIResponseOutput = OpenAIImageGenerationCall | OpenAIOutputMessage;
224
+
225
+ interface OpenAIHostedImageResponse {
226
+ output?: OpenAIResponseOutput[];
227
+ usage?: OpenAIResponsesUsage;
228
+ error?: { code?: string; message?: string };
229
+ }
230
+
231
+ interface OpenAISseEvent {
232
+ type?: string;
233
+ item?: OpenAIResponseOutput;
234
+ response?: OpenAIHostedImageResponse;
235
+ code?: string;
236
+ message?: string;
237
+ error?: { code?: string; message?: string };
238
+ }
239
+
240
+ interface OpenAIHostedImageResult {
241
+ images: InlineImageData[];
242
+ responseText?: string;
243
+ revisedPrompt?: string;
244
+ usage?: OpenAIResponsesUsage;
245
+ }
246
+
247
+ interface OpenRouterImageUrl {
248
+ url: string;
249
+ }
250
+
251
+ interface OpenRouterContentPart {
252
+ type: "text" | "image_url";
253
+ text?: string;
254
+ image_url?: OpenRouterImageUrl;
255
+ }
256
+
257
+ interface OpenRouterMessage {
258
+ content?: string | OpenRouterContentPart[];
259
+ images?: Array<string | { image_url?: OpenRouterImageUrl }>;
260
+ }
261
+
262
+ interface OpenRouterChoice {
263
+ message?: OpenRouterMessage;
264
+ }
265
+
266
+ interface OpenRouterResponse {
267
+ choices?: OpenRouterChoice[];
268
+ }
269
+
270
+ interface AntigravityRequest {
271
+ project: string;
272
+ model: string;
273
+ request: {
274
+ contents: Array<{ role: "user"; parts: Array<{ text?: string; inlineData?: InlineImageData }> }>;
275
+ systemInstruction?: { parts: Array<{ text: string }> };
276
+ generationConfig?: {
277
+ responseModalities?: GeminiResponseModality[];
278
+ imageConfig?: { aspectRatio?: string; imageSize?: string };
279
+ candidateCount?: number;
280
+ };
281
+ safetySettings?: Array<{ category: string; threshold: string }>;
282
+ };
283
+ requestType?: string;
284
+ userAgent?: string;
285
+ requestId?: string;
286
+ }
287
+
288
+ interface XAIImageReference {
289
+ // OpenAI-compat discriminator. Every code example at
290
+ // docs.x.ai/developers/rest-api-reference/inference/images sends this
291
+ // alongside `url`; the schema text doesn't strictly require it, but
292
+ // matching the documented wire format avoids relying on schema-vs-example.
293
+ readonly type: "image_url";
294
+ readonly url: string;
295
+ }
296
+
297
+ interface XAIImageRequestBase {
298
+ readonly model: string;
299
+ readonly prompt: string;
300
+ readonly aspect_ratio: string;
301
+ readonly resolution: "1k" | "2k";
302
+ readonly n: number;
303
+ readonly response_format: "b64_json" | "url";
304
+ }
305
+
306
+ // xAI image request body. Three shapes:
307
+ // 1. text-only generation → POST /v1/images/generations
308
+ // 2. single-source edit (image field) → POST /v1/images/edits
309
+ // 3. multi-reference edit (images field) → POST /v1/images/edits
310
+ // `image` and `images` are mutually exclusive per docs.x.ai; the discriminated
311
+ // union enforces that statically. The runtime cap (XAI_MAX_EDIT_IMAGES) bounds
312
+ // the array length, which TypeScript cannot encode without lossy tuple unions.
313
+ type XAIImageRequestBody =
314
+ | (XAIImageRequestBase & { readonly image?: never; readonly images?: never })
315
+ | (XAIImageRequestBase & { readonly image: XAIImageReference; readonly images?: never })
316
+ | (XAIImageRequestBase & { readonly images: readonly XAIImageReference[]; readonly image?: never });
317
+
318
+ interface AntigravityResponseChunk {
319
+ response?: {
320
+ candidates?: Array<{
321
+ content?: {
322
+ role: string;
323
+ parts?: Array<{
324
+ text?: string;
325
+ inlineData?: { mimeType?: string; data?: string };
326
+ }>;
327
+ };
328
+ }>;
329
+ usageMetadata?: GeminiUsageMetadata;
330
+ };
331
+ }
332
+
333
+ interface ImageGenToolDetails {
334
+ provider: ImageProvider;
335
+ model: string;
336
+ imageCount: number;
337
+ imagePaths: string[];
338
+ images: InlineImageData[];
339
+ responseText?: string;
340
+ promptFeedback?: GeminiPromptFeedback;
341
+ revisedPrompt?: string;
342
+ usage?: ImageUsageMetadata;
343
+ }
344
+
345
+ interface ImageInput {
346
+ path?: string;
347
+ data?: string;
348
+ mime_type?: string;
349
+ }
350
+
351
+ interface InlineImageData {
352
+ data: string;
353
+ mimeType: string;
354
+ }
355
+
356
+ function normalizeDataUrl(data: string): { data: string; mimeType?: string } {
357
+ const match = data.match(/^data:([^;]+);base64,(.+)$/);
358
+ if (!match) return { data };
359
+ return { data: match[2] ?? "", mimeType: match[1] };
360
+ }
361
+
362
+ function resolveOpenRouterModel(model: string): string {
363
+ return model.includes("/") ? model : `google/${model}`;
364
+ }
365
+
366
+ function toDataUrl(image: InlineImageData): string {
367
+ return `data:${image.mimeType};base64,${image.data}`;
368
+ }
369
+
370
+ async function loadImageFromUrl(
371
+ imageUrl: string,
372
+ fetchImpl: FetchImpl,
373
+ signal?: AbortSignal,
374
+ ): Promise<InlineImageData> {
375
+ if (imageUrl.startsWith("data:")) {
376
+ const normalized = normalizeDataUrl(imageUrl.trim());
377
+ if (!normalized.mimeType) {
378
+ throw new Error("mime_type is required when providing raw base64 data.");
379
+ }
380
+ if (!normalized.data) {
381
+ throw new Error("Image data is empty.");
382
+ }
383
+ return { data: normalized.data, mimeType: normalized.mimeType };
384
+ }
385
+
386
+ const response = await fetchImpl(imageUrl, { signal });
387
+ if (!response.ok) {
388
+ const rawText = await response.text();
389
+ throw new Error(`Image download failed (${response.status}): ${rawText}`);
390
+ }
391
+ const contentType = response.headers.get("content-type")?.split(";")[0];
392
+ if (!contentType?.startsWith("image/")) {
393
+ throw new Error(`Unsupported image type from URL: ${imageUrl}`);
394
+ }
395
+ const buffer = await response.bytes();
396
+ return { data: buffer.toBase64(), mimeType: contentType };
397
+ }
398
+
399
+ function collectOpenRouterResponseText(message: OpenRouterMessage | undefined): string | undefined {
400
+ if (!message) return undefined;
401
+ if (typeof message.content === "string") {
402
+ const trimmed = message.content.trim();
403
+ return trimmed.length > 0 ? trimmed : undefined;
404
+ }
405
+ if (Array.isArray(message.content)) {
406
+ const texts = message.content
407
+ .filter(part => part.type === "text")
408
+ .map(part => part.text)
409
+ .filter((text): text is string => Boolean(text));
410
+ const combined = texts.join("\n").trim();
411
+ return combined.length > 0 ? combined : undefined;
412
+ }
413
+ return undefined;
414
+ }
415
+
416
+ function extractOpenRouterImageUrls(message: OpenRouterMessage | undefined): string[] {
417
+ const urls: string[] = [];
418
+ if (!message) return urls;
419
+ for (const image of message.images ?? []) {
420
+ if (typeof image === "string") {
421
+ urls.push(image);
422
+ continue;
423
+ }
424
+ if (image.image_url?.url) {
425
+ urls.push(image.image_url.url);
426
+ }
427
+ }
428
+ if (Array.isArray(message.content)) {
429
+ for (const part of message.content) {
430
+ if (part.type === "image_url" && part.image_url?.url) {
431
+ urls.push(part.image_url.url);
432
+ }
433
+ }
434
+ }
435
+ return urls;
436
+ }
437
+
438
+ /** Preferred provider set via settings (default: auto) */
439
+ let preferredImageProvider: ImageProviderPreference = "auto";
440
+
441
+ export function isImageProviderPreference(value: unknown): value is ImageProviderPreference {
442
+ return typeof value === "string" && IMAGE_PROVIDER_PREFERENCES.has(value);
443
+ }
444
+
445
+ /** Set the preferred image provider from settings */
446
+ export function setPreferredImageProvider(provider: ImageProviderPreference): void {
447
+ preferredImageProvider = provider;
448
+ }
449
+ function assertImageAspectRatioSupported(provider: ImageProvider, aspectRatio: ImageGenParams["aspect_ratio"]): void {
450
+ if (!aspectRatio || provider === "xai" || COMMON_IMAGE_ASPECT_RATIO_SET.has(aspectRatio)) {
451
+ return;
452
+ }
453
+ throw new Error(
454
+ `Aspect ratio ${aspectRatio} is only supported by xAI image generation. Set providers.image to xai or use one of ${COMMON_IMAGE_ASPECT_RATIOS.join(", ")}.`,
455
+ );
456
+ }
457
+
458
+ interface ParsedAntigravityCredentials {
459
+ accessToken: string;
460
+ projectId: string;
461
+ }
462
+
463
+ function parseAntigravityCredentials(raw: string): ParsedAntigravityCredentials | null {
464
+ try {
465
+ const parsed = JSON.parse(raw) as { token?: string; projectId?: string };
466
+ if (parsed.token && parsed.projectId) {
467
+ return { accessToken: parsed.token, projectId: parsed.projectId };
468
+ }
469
+ } catch {
470
+ // Invalid JSON
471
+ }
472
+ return null;
473
+ }
474
+
475
+ async function findAntigravityCredentials(
476
+ modelRegistry: ModelRegistry,
477
+ sessionId?: string,
478
+ ): Promise<ImageApiKey | null> {
479
+ const apiKey = await modelRegistry.getApiKeyForProvider("google-antigravity", sessionId, {
480
+ modelId: DEFAULT_ANTIGRAVITY_MODEL,
481
+ });
482
+ if (!apiKey) return null;
483
+
484
+ const parsed = parseAntigravityCredentials(apiKey);
485
+ if (!parsed) return null;
486
+
487
+ return {
488
+ provider: "antigravity",
489
+ apiKey: parsed.accessToken,
490
+ projectId: parsed.projectId,
491
+ };
492
+ }
493
+
494
+ async function findXAIImageCredentials(modelRegistry?: ModelRegistry): Promise<ImageApiKey | null> {
495
+ if (modelRegistry) {
496
+ const creds = await resolveXAIHttpCredentials(modelRegistry);
497
+ if (creds) return { provider: "xai", apiKey: creds.apiKey };
498
+ return null;
499
+ }
500
+ const apiKey = $env.XAI_API_KEY;
501
+ if (apiKey) return { provider: "xai", apiKey };
502
+ return null;
503
+ }
504
+
505
+ async function findOpenAIHostedImageCredentials(
506
+ modelRegistry: ModelRegistry | undefined,
507
+ activeModel: Model | undefined,
508
+ sessionId?: string,
509
+ ): Promise<ImageApiKey | null> {
510
+ if (!modelRegistry || !isOpenAIHostedImageModel(activeModel)) return null;
511
+ const apiKey = await modelRegistry.getApiKey(activeModel, sessionId);
512
+ if (!isAuthenticated(apiKey)) return null;
513
+ return {
514
+ provider: getOpenAIHostedImageProvider(activeModel),
515
+ apiKey,
516
+ model: activeModel,
517
+ };
518
+ }
519
+
520
+ async function findImageApiKey(
521
+ modelRegistry?: ModelRegistry,
522
+ activeModel?: Model,
523
+ sessionId?: string,
524
+ ): Promise<ImageApiKey | null> {
525
+ // If a specific provider is preferred, try it first.
526
+ if (preferredImageProvider === "openai") {
527
+ const openAI = await findOpenAIHostedImageCredentials(modelRegistry, activeModel, sessionId);
528
+ if (openAI) return openAI;
529
+ // Fall through to auto-detect if preferred provider key not found.
530
+ } else if (preferredImageProvider === "antigravity" && modelRegistry) {
531
+ const antigravity = await findAntigravityCredentials(modelRegistry, sessionId);
532
+ if (antigravity) return antigravity;
533
+ // Fall through to auto-detect if preferred provider key not found.
534
+ } else if (preferredImageProvider === "gemini") {
535
+ const geminiKey = getEnvApiKey("google");
536
+ if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
537
+ const googleKey = $env.GOOGLE_API_KEY;
538
+ if (googleKey) return { provider: "gemini", apiKey: googleKey };
539
+ // Fall through to auto-detect if preferred provider key not found.
540
+ } else if (preferredImageProvider === "openrouter") {
541
+ const openRouterKey = getEnvApiKey("openrouter");
542
+ if (openRouterKey) return { provider: "openrouter", apiKey: openRouterKey };
543
+ // Fall through to auto-detect if preferred provider key not found.
544
+ } else if (preferredImageProvider === "xai") {
545
+ const xai = await findXAIImageCredentials(modelRegistry);
546
+ if (xai) return xai;
547
+ // Fall through to auto-detect if preferred provider key not found.
548
+ }
549
+
550
+ // Auto-detect: GPT hosted image generation, then Antigravity, xAI, OpenRouter, Gemini.
551
+ const openAI = await findOpenAIHostedImageCredentials(modelRegistry, activeModel, sessionId);
552
+ if (openAI) return openAI;
553
+
554
+ if (modelRegistry) {
555
+ const antigravity = await findAntigravityCredentials(modelRegistry, sessionId);
556
+ if (antigravity) return antigravity;
557
+ }
558
+
559
+ const xai = await findXAIImageCredentials(modelRegistry);
560
+ if (xai) return xai;
561
+
562
+ const openRouterKey = getEnvApiKey("openrouter");
563
+ if (openRouterKey) return { provider: "openrouter", apiKey: openRouterKey };
564
+
565
+ const geminiKey = getEnvApiKey("google");
566
+ if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
567
+
568
+ const googleKey = $env.GOOGLE_API_KEY;
569
+ if (googleKey) return { provider: "gemini", apiKey: googleKey };
570
+
571
+ return null;
572
+ }
573
+
574
+ async function loadImageFromPath(imagePath: string, cwd: string): Promise<InlineImageData> {
575
+ const resolved = resolveReadPath(imagePath, cwd);
576
+ try {
577
+ const buffer = await Bun.file(resolved).bytes();
578
+ if (buffer.length > MAX_IMAGE_SIZE) {
579
+ throw new Error(`Image file too large: ${imagePath}`);
580
+ }
581
+
582
+ const metadata = parseImageMetadata(buffer);
583
+ const mimeType = metadata?.mimeType;
584
+ if (!mimeType) {
585
+ throw new Error(`Unsupported image type: ${imagePath}`);
586
+ }
587
+
588
+ return { data: buffer.toBase64(), mimeType };
589
+ } catch (err) {
590
+ if (isEnoent(err)) throw new Error(`Image file not found: ${imagePath}`);
591
+ throw err;
592
+ }
593
+ }
594
+
595
+ async function resolveInputImage(input: ImageInput, cwd: string): Promise<InlineImageData> {
596
+ if (input.path) {
597
+ return loadImageFromPath(input.path, cwd);
598
+ }
599
+
600
+ if (input.data) {
601
+ const normalized = normalizeDataUrl(input.data.trim());
602
+ const mimeType = normalized.mimeType ?? input.mime_type;
603
+ if (!mimeType) {
604
+ throw new Error("mime_type is required when providing raw base64 data.");
605
+ }
606
+ if (!normalized.data) {
607
+ throw new Error("Image data is empty.");
608
+ }
609
+ return { data: normalized.data, mimeType };
610
+ }
611
+
612
+ throw new Error("input_images entries must include either path or data.");
613
+ }
614
+
615
+ function getExtensionForMime(mimeType: string): string {
616
+ const map: Record<string, string> = {
617
+ "image/png": "png",
618
+ "image/jpeg": "jpg",
619
+ "image/gif": "gif",
620
+ "image/webp": "webp",
621
+ };
622
+ return map[mimeType] ?? "png";
623
+ }
624
+
625
+ async function saveImageToTemp(image: InlineImageData): Promise<string> {
626
+ const ext = getExtensionForMime(image.mimeType);
627
+ const filename = `omp-image-${Snowflake.next()}.${ext}`;
628
+ const filepath = path.join(os.tmpdir(), filename);
629
+ await Bun.write(filepath, Buffer.from(image.data, "base64"));
630
+ return filepath;
631
+ }
632
+
633
+ async function saveImagesToTemp(images: InlineImageData[]): Promise<string[]> {
634
+ return Promise.all(images.map(saveImageToTemp));
635
+ }
636
+
637
+ function buildResponseSummary(
638
+ provider: ImageProvider,
639
+ model: string,
640
+ imagePaths: string[],
641
+ responseText: string | undefined,
642
+ ): string {
643
+ const lines = [`Provider: ${provider}`, `Model: ${model}`, `Generated ${imagePaths.length} image(s):`];
644
+ for (const p of imagePaths) {
645
+ lines.push(` ${p}`);
646
+ }
647
+ if (responseText) {
648
+ lines.push("", responseText.trim());
649
+ }
650
+ return lines.join("\n");
651
+ }
652
+
653
+ function collectResponseText(parts: GeminiPart[]): string | undefined {
654
+ const texts = parts.map(part => part.text).filter((text): text is string => Boolean(text));
655
+ const combined = texts.join("\n").trim();
656
+ return combined.length > 0 ? combined : undefined;
657
+ }
658
+
659
+ function collectInlineImages(parts: GeminiPart[]): InlineImageData[] {
660
+ const images: InlineImageData[] = [];
661
+ for (const part of parts) {
662
+ const data = part.inlineData?.data;
663
+ const mimeType = part.inlineData?.mimeType;
664
+ if (!data || !mimeType) continue;
665
+ images.push({ data, mimeType });
666
+ }
667
+ return images;
668
+ }
669
+
670
+ function isOpenAIHostedImageModel(model: Model | undefined): model is Model {
671
+ if (!model) return false;
672
+ if (model.provider !== "openai" && model.provider !== "openai-codex") return false;
673
+ if (model.api !== "openai-responses" && model.api !== "openai-codex-responses") return false;
674
+ const modelId = model.id.toLowerCase();
675
+ return modelId.startsWith("gpt-") || modelId === "o3" || modelId.startsWith("o3-");
676
+ }
677
+
678
+ function getOpenAIHostedImageProvider(model: Model): ImageProvider {
679
+ return model.api === "openai-codex-responses" || model.provider === "openai-codex" ? "openai-codex" : "openai";
680
+ }
681
+
682
+ function resolveOpenAIImageSize(aspectRatio: string | undefined, imageSize: string | undefined): string | undefined {
683
+ if (imageSize) return imageSize;
684
+ switch (aspectRatio) {
685
+ case "1:1":
686
+ return "1024x1024";
687
+ case "3:4":
688
+ case "9:16":
689
+ return "1024x1536";
690
+ case "4:3":
691
+ case "16:9":
692
+ return "1536x1024";
693
+ default:
694
+ return undefined;
695
+ }
696
+ }
697
+
698
+ function buildOpenAIHostedImageRequest(
699
+ model: Model,
700
+ promptText: string,
701
+ params: ImageGenParams,
702
+ inputImages: InlineImageData[],
703
+ stream: boolean,
704
+ ): OpenAIHostedImageRequest {
705
+ const content: OpenAIInputContent[] = [{ type: "input_text", text: promptText }];
706
+ for (const image of inputImages) {
707
+ content.push({ type: "input_image", detail: "auto", image_url: toDataUrl(image) });
708
+ }
709
+
710
+ const size = resolveOpenAIImageSize(params.aspect_ratio, params.image_size);
711
+ const tool: OpenAIImageGenerationTool = {
712
+ type: "image_generation",
713
+ action: inputImages.length > 0 ? "edit" : "generate",
714
+ output_format: OPENAI_IMAGE_OUTPUT_FORMAT,
715
+ ...(size ? { size } : {}),
716
+ };
717
+
718
+ return {
719
+ model: model.id,
720
+ input: [{ role: "user", content }],
721
+ tools: [tool],
722
+ tool_choice: { type: "image_generation" },
723
+ store: false,
724
+ ...(stream
725
+ ? {
726
+ instructions:
727
+ "You are an AI image generator. Generate images based on user descriptions. Focus on creating high-quality, visually appealing images that match the user's request.",
728
+ }
729
+ : {}),
730
+ ...(stream ? { stream: true } : {}),
731
+ };
732
+ }
733
+
734
+ function createOpenAIInlineImage(data: string): InlineImageData {
735
+ const bytes = Buffer.from(data, "base64");
736
+ const mimeType = parseImageMetadata(bytes)?.mimeType ?? OPENAI_IMAGE_MIME_TYPE;
737
+ return { data, mimeType };
738
+ }
739
+
740
+ function collectOpenAIHostedImageResult(response: OpenAIHostedImageResponse): OpenAIHostedImageResult {
741
+ const images: InlineImageData[] = [];
742
+ const textParts: string[] = [];
743
+ let revisedPrompt: string | undefined;
744
+
745
+ for (const output of response.output ?? []) {
746
+ if (output.type === "image_generation_call") {
747
+ if (output.result) {
748
+ images.push(createOpenAIInlineImage(output.result));
749
+ }
750
+ if (output.revised_prompt) {
751
+ revisedPrompt = output.revised_prompt;
752
+ }
753
+ continue;
754
+ }
755
+
756
+ for (const part of output.content ?? []) {
757
+ if (part.type === "output_text" && part.text) {
758
+ textParts.push(part.text);
759
+ } else if (part.type === "refusal" && part.refusal) {
760
+ textParts.push(part.refusal);
761
+ }
762
+ }
763
+ }
764
+
765
+ const responseText = textParts.join("\n").trim();
766
+ return {
767
+ images,
768
+ revisedPrompt,
769
+ responseText: responseText.length > 0 ? responseText : undefined,
770
+ usage: response.usage,
771
+ };
772
+ }
773
+
774
+ function getOpenAIResponseErrorMessage(rawText: string): string {
775
+ try {
776
+ const parsed = JSON.parse(rawText) as { error?: { message?: string } };
777
+ return parsed.error?.message ?? rawText;
778
+ } catch {
779
+ return rawText;
780
+ }
781
+ }
782
+
783
+ function getOpenAIBaseUrl(model: Model): string {
784
+ const fallback =
785
+ model.api === "openai-codex-responses" || model.provider === "openai-codex"
786
+ ? CODEX_BASE_URL
787
+ : DEFAULT_OPENAI_BASE_URL;
788
+ return (model.baseUrl || fallback).replace(/\/+$/, "");
789
+ }
790
+
791
+ function getOpenAIResponsesUrl(model: Model): string {
792
+ const baseUrl = getOpenAIBaseUrl(model);
793
+ if (model.api !== "openai-codex-responses" && model.provider !== "openai-codex") {
794
+ return `${baseUrl}/responses`;
795
+ }
796
+ const baseWithSlash = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
797
+ return new URL(URL_PATHS.RESPONSES.slice(1), baseWithSlash)
798
+ .toString()
799
+ .replace(URL_PATHS.RESPONSES, URL_PATHS.CODEX_RESPONSES);
800
+ }
801
+
802
+ function buildOpenAIImageHeaders(model: Model, apiKey: string, sessionId: string | undefined): Headers {
803
+ const headers = new Headers(model.headers ?? {});
804
+ headers.set("Content-Type", "application/json");
805
+ headers.set("Authorization", `Bearer ${apiKey}`);
806
+
807
+ if (model.api === "openai-codex-responses" || model.provider === "openai-codex") {
808
+ const accountId = getCodexAccountId(apiKey);
809
+ if (!accountId) {
810
+ throw new Error("Failed to extract accountId from OpenAI Codex token");
811
+ }
812
+ headers.delete("x-api-key");
813
+ headers.set(OPENAI_HEADERS.ACCOUNT_ID, accountId);
814
+ headers.set(OPENAI_HEADERS.BETA, OPENAI_HEADER_VALUES.BETA_RESPONSES);
815
+ headers.set(OPENAI_HEADERS.ORIGINATOR, OPENAI_HEADER_VALUES.ORIGINATOR_CODEX);
816
+ headers.set("User-Agent", `pi/${packageJson.version} (${os.platform()} ${os.release()}; ${os.arch()})`);
817
+ if (sessionId) {
818
+ headers.set(OPENAI_HEADERS.CONVERSATION_ID, sessionId);
819
+ headers.set(OPENAI_HEADERS.SESSION_ID, sessionId);
820
+ }
821
+ }
822
+
823
+ return headers;
824
+ }
825
+
826
+ async function parseOpenAIHostedImageSse(response: Response, signal?: AbortSignal): Promise<OpenAIHostedImageResult> {
827
+ if (!response.body) {
828
+ throw new Error("No response body");
829
+ }
830
+
831
+ const fallbackOutput: OpenAIResponseOutput[] = [];
832
+ let completedResponse: OpenAIHostedImageResponse | undefined;
833
+
834
+ for await (const event of readSseJson<OpenAISseEvent>(response.body, signal)) {
835
+ if (event.type === "error") {
836
+ const message = event.error?.message ?? event.message ?? "OpenAI image request failed";
837
+ throw new Error(message);
838
+ }
839
+ if (event.type === "response.failed") {
840
+ const message = event.response?.error?.message ?? "OpenAI image request failed";
841
+ throw new Error(message);
842
+ }
843
+ if (event.type === "response.output_item.done" && event.item) {
844
+ fallbackOutput.push(event.item);
845
+ }
846
+ if ((event.type === "response.completed" || event.type === "response.done") && event.response) {
847
+ completedResponse = event.response;
848
+ }
849
+ }
850
+
851
+ return collectOpenAIHostedImageResult(
852
+ completedResponse?.output?.length
853
+ ? completedResponse
854
+ : { output: fallbackOutput, usage: completedResponse?.usage },
855
+ );
856
+ }
857
+
858
+ async function generateOpenAIHostedImage(
859
+ apiKey: string,
860
+ model: Model,
861
+ params: ImageGenParams,
862
+ inputImages: InlineImageData[],
863
+ fetchImpl: FetchImpl,
864
+ signal: AbortSignal | undefined,
865
+ sessionId: string | undefined,
866
+ ): Promise<OpenAIHostedImageResult> {
867
+ const promptText = assemblePrompt(params);
868
+ const stream = model.api === "openai-codex-responses" || model.provider === "openai-codex";
869
+ const requestBody = buildOpenAIHostedImageRequest(model, promptText, params, inputImages, stream);
870
+ const response = await fetchImpl(getOpenAIResponsesUrl(model), {
871
+ method: "POST",
872
+ headers: buildOpenAIImageHeaders(model, apiKey, sessionId),
873
+ body: JSON.stringify(requestBody),
874
+ signal,
875
+ });
876
+
877
+ if (!response.ok) {
878
+ const errorText = await response.text();
879
+ throw Object.assign(
880
+ new Error(`OpenAI image request failed (${response.status}): ${getOpenAIResponseErrorMessage(errorText)}`),
881
+ { status: response.status },
882
+ );
883
+ }
884
+
885
+ const contentType = response.headers.get("content-type") ?? "";
886
+ if (stream || contentType.includes("text/event-stream")) {
887
+ return parseOpenAIHostedImageSse(response, signal);
888
+ }
889
+
890
+ const data = (await response.json()) as OpenAIHostedImageResponse;
891
+ return collectOpenAIHostedImageResult(data);
892
+ }
893
+
894
+ function combineParts(response: GeminiGenerateContentResponse): GeminiPart[] {
895
+ const parts: GeminiPart[] = [];
896
+ for (const candidate of response.candidates ?? []) {
897
+ const candidateParts = candidate.content?.parts ?? [];
898
+ parts.push(...candidateParts);
899
+ }
900
+ return parts;
901
+ }
902
+
903
+ function buildAntigravityRequest(
904
+ prompt: string,
905
+ model: string,
906
+ projectId: string,
907
+ aspectRatio: string | undefined,
908
+ imageSize: string | undefined,
909
+ inputImages: InlineImageData[],
910
+ ): AntigravityRequest {
911
+ const parts: Array<{ text?: string; inlineData?: InlineImageData }> = [];
912
+ for (const image of inputImages) {
913
+ parts.push({ inlineData: image });
914
+ }
915
+ parts.push({ text: prompt });
916
+
917
+ const imageConfig = aspectRatio || imageSize ? { aspectRatio: aspectRatio, imageSize: imageSize } : undefined;
918
+
919
+ return {
920
+ project: projectId,
921
+ model,
922
+ request: {
923
+ contents: [{ role: "user", parts }],
924
+ systemInstruction: { parts: [{ text: IMAGE_SYSTEM_INSTRUCTION }] },
925
+ generationConfig: {
926
+ responseModalities: ["IMAGE"],
927
+ imageConfig,
928
+ candidateCount: 1,
929
+ },
930
+ safetySettings: [
931
+ { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_ONLY_HIGH" },
932
+ { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_ONLY_HIGH" },
933
+ { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_ONLY_HIGH" },
934
+ { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_ONLY_HIGH" },
935
+ { category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_ONLY_HIGH" },
936
+ ],
937
+ },
938
+ requestType: "agent",
939
+ requestId: `agent-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
940
+ userAgent: "antigravity",
941
+ };
942
+ }
943
+
944
+ // xAI image-edit cap per docs.x.ai (POST /v1/images/edits supports up to 3
945
+ // source images for multi-reference editing).
946
+ const XAI_MAX_EDIT_IMAGES = 3;
947
+
948
+ // Map the OpenAI-style pixel-size enum (image_size) to xAI's discrete tier.
949
+ // "1024x1024" → "1k"; anything wider (1536x... or ...x1536) → "2k". Absent
950
+ // image_size defaults to "1k", matching hermes-agent's DEFAULT_RESOLUTION
951
+ // (plugins/image_gen/xai/__init__.py:71).
952
+ function resolveXAIResolution(imageSize: string | undefined): "1k" | "2k" {
953
+ if (!imageSize || imageSize === "1024x1024") return "1k";
954
+ return "2k";
955
+ }
956
+
957
+ // Build the discriminated edit body. Caller must ensure images.length is in
958
+ // [1, XAI_MAX_EDIT_IMAGES]; the bound check fires earlier in execute().
959
+ function buildXAIEditPayload(base: XAIImageRequestBase, images: readonly InlineImageData[]): XAIImageRequestBody {
960
+ const refs: readonly XAIImageReference[] = images.map(img => ({
961
+ type: "image_url",
962
+ url: toDataUrl(img),
963
+ }));
964
+ const [first, ...rest] = refs;
965
+ if (first === undefined) return base; // unreachable: caller checked images.length > 0
966
+ return rest.length === 0 ? { ...base, image: first } : { ...base, images: refs };
967
+ }
968
+
969
+ interface AntigravitySseResult {
970
+ images: InlineImageData[];
971
+ text: string[];
972
+ usage?: GeminiUsageMetadata;
973
+ }
974
+
975
+ async function parseAntigravitySseForImage(response: Response, signal?: AbortSignal): Promise<AntigravitySseResult> {
976
+ if (!response.body) {
977
+ throw new Error("No response body");
978
+ }
979
+
980
+ const textParts: string[] = [];
981
+ const images: InlineImageData[] = [];
982
+ let usage: GeminiUsageMetadata | undefined;
983
+
984
+ for await (const chunk of readSseJson<AntigravityResponseChunk>(response.body, signal)) {
985
+ const responseData = chunk.response;
986
+ if (!responseData) continue;
987
+ if (!responseData.candidates) continue;
988
+ for (const candidate of responseData.candidates) {
989
+ const parts = candidate.content?.parts;
990
+ if (!parts) continue;
991
+ for (const part of parts) {
992
+ if (part.text) {
993
+ textParts.push(part.text);
994
+ }
995
+ const inlineData = part.inlineData;
996
+ if (inlineData?.data && inlineData.mimeType) {
997
+ images.push({ data: inlineData.data, mimeType: inlineData.mimeType });
998
+ }
999
+ }
1000
+ }
1001
+ if (responseData.usageMetadata) {
1002
+ usage = responseData.usageMetadata;
1003
+ }
1004
+ }
1005
+
1006
+ return { images, text: textParts, usage };
1007
+ }
1008
+
1009
+ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails> = {
1010
+ name: "generate_image",
1011
+ label: "GenerateImage",
1012
+ strict: false,
1013
+ approval: "write",
1014
+ description: prompt.render(imageGenDescription),
1015
+ parameters: imageGenSchema,
1016
+ async execute(_toolCallId, params, _onUpdate, ctx, signal) {
1017
+ return untilAborted(signal, async () => {
1018
+ const sessionId = ctx.sessionManager.getSessionId();
1019
+ const apiKey = await findImageApiKey(ctx.modelRegistry, ctx.model, sessionId);
1020
+ if (!apiKey) {
1021
+ throw new Error(
1022
+ "No image API credentials found. Use a GPT Responses/Codex model with OpenAI credentials, login with google-antigravity or xAI Grok OAuth, or set XAI_API_KEY, OPENROUTER_API_KEY, GEMINI_API_KEY, or GOOGLE_API_KEY.",
1023
+ );
1024
+ }
1025
+
1026
+ const provider = apiKey.provider;
1027
+ const model =
1028
+ provider === "openai" || provider === "openai-codex"
1029
+ ? (apiKey.model?.id ?? "gpt")
1030
+ : provider === "antigravity"
1031
+ ? DEFAULT_ANTIGRAVITY_MODEL
1032
+ : provider === "openrouter"
1033
+ ? DEFAULT_OPENROUTER_MODEL
1034
+ : provider === "xai"
1035
+ ? DEFAULT_XAI_IMAGE_MODEL
1036
+ : DEFAULT_MODEL;
1037
+ const resolvedModel = provider === "openrouter" ? resolveOpenRouterModel(model) : model;
1038
+ assertImageAspectRatioSupported(provider, params.aspect_ratio);
1039
+ const cwd = ctx.sessionManager.getCwd();
1040
+
1041
+ const resolvedImages: InlineImageData[] = [];
1042
+ if (params.input?.length) {
1043
+ for (const input of params.input) {
1044
+ resolvedImages.push(await resolveInputImage(input, cwd));
1045
+ }
1046
+ }
1047
+
1048
+ const requestSignal = ptree.combineSignals(signal, IMAGE_TIMEOUT);
1049
+ const fetchImpl = ctx.fetch ?? fetch;
1050
+
1051
+ if (provider === "openai" || provider === "openai-codex") {
1052
+ if (!apiKey.model) {
1053
+ throw new Error("Missing active GPT model for OpenAI image generation");
1054
+ }
1055
+
1056
+ const hostedModel = apiKey.model;
1057
+ const hostedKey: ApiKey = ctx.modelRegistry.resolver(hostedModel.provider, {
1058
+ sessionId,
1059
+ baseUrl: hostedModel.baseUrl,
1060
+ modelId: hostedModel.id,
1061
+ });
1062
+
1063
+ const parsed = await withAuth(
1064
+ hostedKey,
1065
+ key =>
1066
+ generateOpenAIHostedImage(
1067
+ key,
1068
+ hostedModel,
1069
+ params,
1070
+ resolvedImages,
1071
+ fetchImpl,
1072
+ requestSignal,
1073
+ sessionId,
1074
+ ),
1075
+ { signal: requestSignal },
1076
+ );
1077
+
1078
+ if (parsed.images.length === 0) {
1079
+ const messageText = parsed.responseText ? `\n\n${parsed.responseText}` : "";
1080
+ return {
1081
+ content: [{ type: "text", text: `No image data returned.${messageText}` }],
1082
+ details: {
1083
+ provider,
1084
+ model,
1085
+ imageCount: 0,
1086
+ imagePaths: [],
1087
+ images: [],
1088
+ responseText: parsed.responseText,
1089
+ revisedPrompt: parsed.revisedPrompt,
1090
+ usage: parsed.usage,
1091
+ },
1092
+ };
1093
+ }
1094
+
1095
+ const imagePaths = await saveImagesToTemp(parsed.images);
1096
+
1097
+ return {
1098
+ content: [
1099
+ { type: "text", text: buildResponseSummary(provider, model, imagePaths, parsed.responseText) },
1100
+ ],
1101
+ details: {
1102
+ provider,
1103
+ model,
1104
+ imageCount: parsed.images.length,
1105
+ imagePaths,
1106
+ images: parsed.images,
1107
+ responseText: parsed.responseText,
1108
+ revisedPrompt: parsed.revisedPrompt,
1109
+ usage: parsed.usage,
1110
+ },
1111
+ };
1112
+ }
1113
+
1114
+ if (provider === "antigravity") {
1115
+ if (!apiKey.projectId) {
1116
+ throw new Error("Missing projectId in antigravity credentials");
1117
+ }
1118
+
1119
+ const prompt = assemblePrompt(params);
1120
+ const antigravityKey: ApiKey = ctx.modelRegistry.resolver("google-antigravity", {
1121
+ sessionId,
1122
+ modelId: DEFAULT_ANTIGRAVITY_MODEL,
1123
+ });
1124
+
1125
+ const response = await withAuth(
1126
+ antigravityKey,
1127
+ async key => {
1128
+ // On a retry the resolver yields the raw stored credential JSON
1129
+ // ({ token, projectId }); the initial seed is the already-parsed
1130
+ // access token. Tolerate both, falling back to the seed projectId.
1131
+ const rotated = parseAntigravityCredentials(key);
1132
+ const bearer = rotated?.accessToken ?? key;
1133
+ const projectId = rotated?.projectId ?? apiKey.projectId!;
1134
+ const requestBody = buildAntigravityRequest(
1135
+ prompt,
1136
+ model,
1137
+ projectId,
1138
+ params.aspect_ratio,
1139
+ params.image_size,
1140
+ resolvedImages,
1141
+ );
1142
+
1143
+ const resp = await fetchImpl(`${ANTIGRAVITY_ENDPOINT}/v1internal:streamGenerateContent?alt=sse`, {
1144
+ method: "POST",
1145
+ headers: {
1146
+ Authorization: `Bearer ${bearer}`,
1147
+ "Content-Type": "application/json",
1148
+ Accept: "text/event-stream",
1149
+ "User-Agent": getAntigravityUserAgent(),
1150
+ },
1151
+ body: JSON.stringify(requestBody),
1152
+ signal: requestSignal,
1153
+ });
1154
+
1155
+ if (!resp.ok) {
1156
+ const errorText = await resp.text();
1157
+ let message = errorText;
1158
+ try {
1159
+ const parsedErr = JSON.parse(errorText) as { error?: { message?: string } };
1160
+ message = parsedErr.error?.message ?? message;
1161
+ } catch {
1162
+ // Keep raw text.
1163
+ }
1164
+ throw Object.assign(new Error(`Antigravity image request failed (${resp.status}): ${message}`), {
1165
+ status: resp.status,
1166
+ });
1167
+ }
1168
+ return resp;
1169
+ },
1170
+ { signal: requestSignal },
1171
+ );
1172
+
1173
+ const parsed = await parseAntigravitySseForImage(response, requestSignal);
1174
+ const responseText = parsed.text.length > 0 ? parsed.text.join(" ") : undefined;
1175
+
1176
+ if (parsed.images.length === 0) {
1177
+ const messageText = responseText ? `\n\n${responseText}` : "";
1178
+ return {
1179
+ content: [{ type: "text", text: `No image data returned.${messageText}` }],
1180
+ details: {
1181
+ provider,
1182
+ model,
1183
+ imageCount: 0,
1184
+ imagePaths: [],
1185
+ images: [],
1186
+ responseText,
1187
+ usage: parsed.usage,
1188
+ },
1189
+ };
1190
+ }
1191
+
1192
+ const imagePaths = await saveImagesToTemp(parsed.images);
1193
+
1194
+ return {
1195
+ content: [{ type: "text", text: buildResponseSummary(provider, model, imagePaths, responseText) }],
1196
+ details: {
1197
+ provider,
1198
+ model,
1199
+ imageCount: parsed.images.length,
1200
+ imagePaths,
1201
+ images: parsed.images,
1202
+ responseText,
1203
+ usage: parsed.usage,
1204
+ },
1205
+ };
1206
+ }
1207
+
1208
+ if (provider === "xai") {
1209
+ if (!ctx.modelRegistry) {
1210
+ throw new Error("Missing modelRegistry for xAI image generation");
1211
+ }
1212
+ const xaiCreds = await resolveXAIHttpCredentials(ctx.modelRegistry, resolvedModel);
1213
+ if (!xaiCreds) {
1214
+ throw new Error(
1215
+ "No xAI credentials. Run /login → xAI Grok OAuth (SuperGrok Subscription) or set XAI_API_KEY.",
1216
+ );
1217
+ }
1218
+
1219
+ const prompt = assemblePrompt(params);
1220
+ const aspectRatio = params.aspect_ratio ?? "1:1";
1221
+ const xaiResolution = resolveXAIResolution(params.image_size);
1222
+
1223
+ const isEdit = resolvedImages.length > 0;
1224
+ if (isEdit && resolvedImages.length > XAI_MAX_EDIT_IMAGES) {
1225
+ throw new Error(
1226
+ `xAI image edits accept up to ${XAI_MAX_EDIT_IMAGES} reference images; got ${resolvedImages.length}.`,
1227
+ );
1228
+ }
1229
+
1230
+ const xaiBaseBody: XAIImageRequestBase = {
1231
+ model: resolvedModel,
1232
+ prompt,
1233
+ aspect_ratio: aspectRatio,
1234
+ resolution: xaiResolution,
1235
+ n: 1,
1236
+ response_format: "b64_json",
1237
+ };
1238
+ const xaiBody: XAIImageRequestBody = isEdit
1239
+ ? buildXAIEditPayload(xaiBaseBody, resolvedImages)
1240
+ : xaiBaseBody;
1241
+ const xaiEndpoint = isEdit ? "/images/edits" : "/images/generations";
1242
+
1243
+ const xaiKey: ApiKey = ctx.modelRegistry.resolver(xaiCreds.provider, {
1244
+ sessionId,
1245
+ baseUrl: xaiCreds.baseURL,
1246
+ });
1247
+
1248
+ const xaiRawText = await withAuth(
1249
+ xaiKey,
1250
+ async key => {
1251
+ const resp = await fetchImpl(`${xaiCreds.baseURL}${xaiEndpoint}`, {
1252
+ method: "POST",
1253
+ headers: {
1254
+ Authorization: `Bearer ${key}`,
1255
+ "Content-Type": "application/json",
1256
+ "User-Agent": ohMyPiXAIUserAgent(),
1257
+ },
1258
+ body: JSON.stringify(xaiBody),
1259
+ signal: requestSignal,
1260
+ });
1261
+ const rawText = await resp.text();
1262
+ if (!resp.ok) {
1263
+ let message = rawText;
1264
+ try {
1265
+ const parsedErr = JSON.parse(rawText) as { error?: { message?: string } };
1266
+ message = parsedErr.error?.message ?? message;
1267
+ } catch {
1268
+ // Keep raw text.
1269
+ }
1270
+ throw Object.assign(new Error(`xAI image request failed (${resp.status}): ${message}`), {
1271
+ status: resp.status,
1272
+ });
1273
+ }
1274
+ return rawText;
1275
+ },
1276
+ { signal: requestSignal },
1277
+ );
1278
+
1279
+ const xaiData = JSON.parse(xaiRawText) as {
1280
+ data?: Array<{ b64_json?: string; url?: string }>;
1281
+ };
1282
+ const xaiInlineImages: InlineImageData[] = [];
1283
+ for (const entry of xaiData.data ?? []) {
1284
+ if (entry.b64_json) {
1285
+ const bytes = Buffer.from(entry.b64_json, "base64");
1286
+ const mimeType = parseImageMetadata(bytes)?.mimeType ?? "image/png";
1287
+ xaiInlineImages.push({ data: entry.b64_json, mimeType });
1288
+ } else if (entry.url) {
1289
+ xaiInlineImages.push(await loadImageFromUrl(entry.url, fetchImpl, requestSignal));
1290
+ }
1291
+ }
1292
+
1293
+ if (xaiInlineImages.length === 0) {
1294
+ return {
1295
+ content: [{ type: "text", text: "No image data returned." }],
1296
+ details: {
1297
+ provider,
1298
+ model: resolvedModel,
1299
+ imageCount: 0,
1300
+ imagePaths: [],
1301
+ images: [],
1302
+ },
1303
+ };
1304
+ }
1305
+
1306
+ const xaiImagePaths = await saveImagesToTemp(xaiInlineImages);
1307
+
1308
+ return {
1309
+ content: [
1310
+ { type: "text", text: buildResponseSummary(provider, resolvedModel, xaiImagePaths, undefined) },
1311
+ ],
1312
+ details: {
1313
+ provider,
1314
+ model: resolvedModel,
1315
+ imageCount: xaiInlineImages.length,
1316
+ imagePaths: xaiImagePaths,
1317
+ images: xaiInlineImages,
1318
+ },
1319
+ };
1320
+ }
1321
+
1322
+ if (provider === "openrouter") {
1323
+ const prompt = assemblePrompt(params);
1324
+ const contentParts: OpenRouterContentPart[] = [{ type: "text", text: prompt }];
1325
+ for (const image of resolvedImages) {
1326
+ contentParts.push({ type: "image_url", image_url: { url: toDataUrl(image) } });
1327
+ }
1328
+
1329
+ const requestBody = {
1330
+ model: resolvedModel,
1331
+ messages: [{ role: "user" as const, content: contentParts }],
1332
+ };
1333
+
1334
+ const rawText = await withAuth(apiKey.apiKey, async key => {
1335
+ const resp = await fetchImpl("https://openrouter.ai/api/v1/chat/completions", {
1336
+ method: "POST",
1337
+ headers: {
1338
+ "Content-Type": "application/json",
1339
+ Authorization: `Bearer ${key}`,
1340
+ "HTTP-Referer": "https://omp.sh/",
1341
+ "X-OpenRouter-Title": "Oh-My-Pi",
1342
+ "X-OpenRouter-Categories": "cli-agent",
1343
+ },
1344
+ body: JSON.stringify(requestBody),
1345
+ signal: requestSignal,
1346
+ });
1347
+ const text = await resp.text();
1348
+ if (!resp.ok) {
1349
+ let message = text;
1350
+ try {
1351
+ const parsed = JSON.parse(text) as { error?: { message?: string } };
1352
+ message = parsed.error?.message ?? message;
1353
+ } catch {
1354
+ // Keep raw text.
1355
+ }
1356
+ throw Object.assign(new Error(`OpenRouter image request failed (${resp.status}): ${message}`), {
1357
+ status: resp.status,
1358
+ });
1359
+ }
1360
+ return text;
1361
+ });
1362
+
1363
+ const data = JSON.parse(rawText) as OpenRouterResponse;
1364
+ const message = data.choices?.[0]?.message;
1365
+ const responseText = collectOpenRouterResponseText(message);
1366
+ const imageUrls = extractOpenRouterImageUrls(message);
1367
+ const inlineImages: InlineImageData[] = [];
1368
+ for (const imageUrl of imageUrls) {
1369
+ inlineImages.push(await loadImageFromUrl(imageUrl, fetchImpl, requestSignal));
1370
+ }
1371
+
1372
+ if (inlineImages.length === 0) {
1373
+ const messageText = responseText ? `\n\n${responseText}` : "";
1374
+ return {
1375
+ content: [{ type: "text", text: `No image data returned.${messageText}` }],
1376
+ details: {
1377
+ provider,
1378
+ model: resolvedModel,
1379
+ imageCount: 0,
1380
+ imagePaths: [],
1381
+ images: [],
1382
+ responseText,
1383
+ },
1384
+ };
1385
+ }
1386
+
1387
+ const imagePaths = await saveImagesToTemp(inlineImages);
1388
+
1389
+ return {
1390
+ content: [
1391
+ { type: "text", text: buildResponseSummary(provider, resolvedModel, imagePaths, responseText) },
1392
+ ],
1393
+ details: {
1394
+ provider,
1395
+ model: resolvedModel,
1396
+ imageCount: inlineImages.length,
1397
+ imagePaths,
1398
+ images: inlineImages,
1399
+ responseText,
1400
+ },
1401
+ };
1402
+ }
1403
+
1404
+ const parts = [] as Array<{ text?: string; inlineData?: InlineImageData }>;
1405
+ for (const image of resolvedImages) {
1406
+ parts.push({ inlineData: image });
1407
+ }
1408
+ parts.push({ text: assemblePrompt(params) });
1409
+
1410
+ const generationConfig: {
1411
+ responseModalities: GeminiResponseModality[];
1412
+ imageConfig?: { aspectRatio?: string; imageSize?: string };
1413
+ } = {
1414
+ responseModalities: ["IMAGE"],
1415
+ };
1416
+
1417
+ if (params.aspect_ratio || params.image_size) {
1418
+ generationConfig.imageConfig = {
1419
+ aspectRatio: params.aspect_ratio,
1420
+ imageSize: params.image_size,
1421
+ };
1422
+ }
1423
+
1424
+ const requestBody = {
1425
+ contents: [{ role: "user" as const, parts }],
1426
+ generationConfig,
1427
+ };
1428
+
1429
+ const rawText = await withAuth(apiKey.apiKey, async key => {
1430
+ const resp = await fetchImpl(
1431
+ `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:generateContent`,
1432
+ {
1433
+ method: "POST",
1434
+ headers: {
1435
+ "Content-Type": "application/json",
1436
+ "x-goog-api-key": key,
1437
+ },
1438
+ body: JSON.stringify(requestBody),
1439
+ signal: requestSignal,
1440
+ },
1441
+ );
1442
+ const text = await resp.text();
1443
+ if (!resp.ok) {
1444
+ let message = text;
1445
+ try {
1446
+ const parsed = JSON.parse(text) as { error?: { message?: string } };
1447
+ message = parsed.error?.message ?? message;
1448
+ } catch {
1449
+ // Keep raw text.
1450
+ }
1451
+ throw Object.assign(new Error(`Gemini image request failed (${resp.status}): ${message}`), {
1452
+ status: resp.status,
1453
+ });
1454
+ }
1455
+ return text;
1456
+ });
1457
+
1458
+ const data = JSON.parse(rawText) as GeminiGenerateContentResponse;
1459
+ const responseParts = combineParts(data);
1460
+ const responseText = collectResponseText(responseParts);
1461
+ const inlineImages = collectInlineImages(responseParts);
1462
+
1463
+ if (inlineImages.length === 0) {
1464
+ const blocked = data.promptFeedback?.blockReason
1465
+ ? `Blocked: ${data.promptFeedback.blockReason}`
1466
+ : "No image data returned.";
1467
+ return {
1468
+ content: [{ type: "text", text: `${blocked}${responseText ? `\n\n${responseText}` : ""}` }],
1469
+ details: {
1470
+ provider,
1471
+ model,
1472
+ imageCount: 0,
1473
+ imagePaths: [],
1474
+ images: [],
1475
+ responseText,
1476
+ promptFeedback: data.promptFeedback,
1477
+ usage: data.usageMetadata,
1478
+ },
1479
+ };
1480
+ }
1481
+
1482
+ const imagePaths = await saveImagesToTemp(inlineImages);
1483
+
1484
+ return {
1485
+ content: [{ type: "text", text: buildResponseSummary(provider, model, imagePaths, responseText) }],
1486
+ details: {
1487
+ provider,
1488
+ model,
1489
+ imageCount: inlineImages.length,
1490
+ imagePaths,
1491
+ images: inlineImages,
1492
+ responseText,
1493
+ promptFeedback: data.promptFeedback,
1494
+ usage: data.usageMetadata,
1495
+ },
1496
+ };
1497
+ });
1498
+ },
1499
+ };
1500
+
1501
+ export async function getImageGenTools(
1502
+ modelRegistry?: ModelRegistry,
1503
+ activeModel?: Model,
1504
+ ): Promise<Array<CustomTool<typeof imageGenSchema, ImageGenToolDetails>>> {
1505
+ const apiKey = await findImageApiKey(modelRegistry, activeModel);
1506
+ if (!apiKey) return [];
1507
+ return [imageGenTool];
1508
+ }
1509
+
1510
+ export async function getImageGenToolsWithRegistry(
1511
+ modelRegistry: ModelRegistry,
1512
+ activeModel?: Model,
1513
+ ): Promise<Array<CustomTool<typeof imageGenSchema, ImageGenToolDetails>>> {
1514
+ const apiKey = await findImageApiKey(modelRegistry, activeModel);
1515
+ if (!apiKey) return [];
1516
+ return [imageGenTool];
1517
+ }