@nghyane/arcane 0.1.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 (738) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +12 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +109 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/custom-tools/todo/index.ts +206 -0
  7. package/examples/extensions/README.md +143 -0
  8. package/examples/extensions/api-demo.ts +89 -0
  9. package/examples/extensions/chalk-logger.ts +25 -0
  10. package/examples/extensions/hello.ts +32 -0
  11. package/examples/extensions/pirate.ts +43 -0
  12. package/examples/extensions/plan-mode.ts +550 -0
  13. package/examples/extensions/reload-runtime.ts +37 -0
  14. package/examples/extensions/todo.ts +296 -0
  15. package/examples/extensions/tools.ts +144 -0
  16. package/examples/extensions/with-deps/index.ts +35 -0
  17. package/examples/extensions/with-deps/package-lock.json +31 -0
  18. package/examples/extensions/with-deps/package.json +16 -0
  19. package/examples/hooks/README.md +56 -0
  20. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  21. package/examples/hooks/confirm-destructive.ts +58 -0
  22. package/examples/hooks/custom-compaction.ts +116 -0
  23. package/examples/hooks/dirty-repo-guard.ts +51 -0
  24. package/examples/hooks/file-trigger.ts +40 -0
  25. package/examples/hooks/git-checkpoint.ts +52 -0
  26. package/examples/hooks/handoff.ts +150 -0
  27. package/examples/hooks/permission-gate.ts +33 -0
  28. package/examples/hooks/protected-paths.ts +29 -0
  29. package/examples/hooks/qna.ts +119 -0
  30. package/examples/hooks/status-line.ts +39 -0
  31. package/examples/sdk/01-minimal.ts +21 -0
  32. package/examples/sdk/02-custom-model.ts +49 -0
  33. package/examples/sdk/03-custom-prompt.ts +43 -0
  34. package/examples/sdk/04-skills.ts +43 -0
  35. package/examples/sdk/06-extensions.ts +80 -0
  36. package/examples/sdk/06-hooks.ts +61 -0
  37. package/examples/sdk/07-context-files.ts +35 -0
  38. package/examples/sdk/08-prompt-templates.ts +36 -0
  39. package/examples/sdk/08-slash-commands.ts +41 -0
  40. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  41. package/examples/sdk/11-sessions.ts +47 -0
  42. package/examples/sdk/README.md +150 -0
  43. package/package.json +464 -0
  44. package/scripts/format-prompts.ts +184 -0
  45. package/scripts/generate-docs-index.ts +40 -0
  46. package/scripts/generate-template.ts +32 -0
  47. package/src/bun-imports.d.ts +22 -0
  48. package/src/capability/context-file.ts +39 -0
  49. package/src/capability/extension-module.ts +33 -0
  50. package/src/capability/extension.ts +47 -0
  51. package/src/capability/fs.ts +89 -0
  52. package/src/capability/hook.ts +39 -0
  53. package/src/capability/index.ts +432 -0
  54. package/src/capability/instruction.ts +36 -0
  55. package/src/capability/mcp.ts +60 -0
  56. package/src/capability/prompt.ts +34 -0
  57. package/src/capability/rule.ts +223 -0
  58. package/src/capability/settings.ts +34 -0
  59. package/src/capability/skill.ts +48 -0
  60. package/src/capability/slash-command.ts +39 -0
  61. package/src/capability/ssh.ts +41 -0
  62. package/src/capability/system-prompt.ts +34 -0
  63. package/src/capability/tool.ts +37 -0
  64. package/src/capability/types.ts +156 -0
  65. package/src/cli/args.ts +259 -0
  66. package/src/cli/config-cli.ts +357 -0
  67. package/src/cli/file-processor.ts +124 -0
  68. package/src/cli/grep-cli.ts +152 -0
  69. package/src/cli/jupyter-cli.ts +106 -0
  70. package/src/cli/list-models.ts +103 -0
  71. package/src/cli/plugin-cli.ts +661 -0
  72. package/src/cli/session-picker.ts +42 -0
  73. package/src/cli/setup-cli.ts +376 -0
  74. package/src/cli/shell-cli.ts +174 -0
  75. package/src/cli/ssh-cli.ts +179 -0
  76. package/src/cli/stats-cli.ts +197 -0
  77. package/src/cli/update-cli.ts +286 -0
  78. package/src/cli/web-search-cli.ts +143 -0
  79. package/src/cli.ts +65 -0
  80. package/src/commands/commit.ts +36 -0
  81. package/src/commands/config.ts +51 -0
  82. package/src/commands/grep.ts +41 -0
  83. package/src/commands/jupyter.ts +32 -0
  84. package/src/commands/launch.ts +139 -0
  85. package/src/commands/plugin.ts +70 -0
  86. package/src/commands/setup.ts +42 -0
  87. package/src/commands/shell.ts +29 -0
  88. package/src/commands/ssh.ts +60 -0
  89. package/src/commands/stats.ts +29 -0
  90. package/src/commands/update.ts +21 -0
  91. package/src/commands/web-search.ts +42 -0
  92. package/src/commit/agentic/agent.ts +311 -0
  93. package/src/commit/agentic/fallback.ts +96 -0
  94. package/src/commit/agentic/index.ts +359 -0
  95. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  96. package/src/commit/agentic/prompts/session-user.md +25 -0
  97. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  98. package/src/commit/agentic/prompts/system.md +38 -0
  99. package/src/commit/agentic/state.ts +69 -0
  100. package/src/commit/agentic/tools/analyze-file.ts +118 -0
  101. package/src/commit/agentic/tools/git-file-diff.ts +194 -0
  102. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  103. package/src/commit/agentic/tools/git-overview.ts +84 -0
  104. package/src/commit/agentic/tools/index.ts +56 -0
  105. package/src/commit/agentic/tools/propose-changelog.ts +128 -0
  106. package/src/commit/agentic/tools/propose-commit.ts +154 -0
  107. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  108. package/src/commit/agentic/tools/split-commit.ts +280 -0
  109. package/src/commit/agentic/topo-sort.ts +44 -0
  110. package/src/commit/agentic/trivial.ts +51 -0
  111. package/src/commit/agentic/validation.ts +200 -0
  112. package/src/commit/analysis/conventional.ts +165 -0
  113. package/src/commit/analysis/index.ts +4 -0
  114. package/src/commit/analysis/scope.ts +242 -0
  115. package/src/commit/analysis/summary.ts +112 -0
  116. package/src/commit/analysis/validation.ts +66 -0
  117. package/src/commit/changelog/detect.ts +37 -0
  118. package/src/commit/changelog/generate.ts +110 -0
  119. package/src/commit/changelog/index.ts +234 -0
  120. package/src/commit/changelog/parse.ts +44 -0
  121. package/src/commit/cli.ts +93 -0
  122. package/src/commit/git/diff.ts +148 -0
  123. package/src/commit/git/errors.ts +9 -0
  124. package/src/commit/git/index.ts +211 -0
  125. package/src/commit/git/operations.ts +54 -0
  126. package/src/commit/index.ts +5 -0
  127. package/src/commit/map-reduce/index.ts +64 -0
  128. package/src/commit/map-reduce/map-phase.ts +178 -0
  129. package/src/commit/map-reduce/reduce-phase.ts +145 -0
  130. package/src/commit/map-reduce/utils.ts +9 -0
  131. package/src/commit/message.ts +11 -0
  132. package/src/commit/model-selection.ts +69 -0
  133. package/src/commit/pipeline.ts +243 -0
  134. package/src/commit/prompts/analysis-system.md +148 -0
  135. package/src/commit/prompts/analysis-user.md +38 -0
  136. package/src/commit/prompts/changelog-system.md +50 -0
  137. package/src/commit/prompts/changelog-user.md +18 -0
  138. package/src/commit/prompts/file-observer-system.md +24 -0
  139. package/src/commit/prompts/file-observer-user.md +8 -0
  140. package/src/commit/prompts/reduce-system.md +50 -0
  141. package/src/commit/prompts/reduce-user.md +17 -0
  142. package/src/commit/prompts/summary-retry.md +3 -0
  143. package/src/commit/prompts/summary-system.md +38 -0
  144. package/src/commit/prompts/summary-user.md +13 -0
  145. package/src/commit/prompts/types-description.md +2 -0
  146. package/src/commit/types.ts +109 -0
  147. package/src/commit/utils/exclusions.ts +42 -0
  148. package/src/config/file-lock.ts +121 -0
  149. package/src/config/keybindings.ts +280 -0
  150. package/src/config/model-registry.ts +1140 -0
  151. package/src/config/model-resolver.ts +812 -0
  152. package/src/config/prompt-templates.ts +526 -0
  153. package/src/config/resolve-config-value.ts +92 -0
  154. package/src/config/settings-schema.ts +1236 -0
  155. package/src/config/settings.ts +706 -0
  156. package/src/config.ts +414 -0
  157. package/src/cursor.ts +239 -0
  158. package/src/debug/index.ts +431 -0
  159. package/src/debug/log-formatting.ts +60 -0
  160. package/src/debug/log-viewer.ts +903 -0
  161. package/src/debug/profiler.ts +158 -0
  162. package/src/debug/report-bundle.ts +366 -0
  163. package/src/debug/system-info.ts +112 -0
  164. package/src/discovery/agents-md.ts +68 -0
  165. package/src/discovery/agents.ts +199 -0
  166. package/src/discovery/builtin.ts +815 -0
  167. package/src/discovery/claude-plugins.ts +205 -0
  168. package/src/discovery/claude.ts +506 -0
  169. package/src/discovery/cline.ts +83 -0
  170. package/src/discovery/codex.ts +532 -0
  171. package/src/discovery/cursor.ts +218 -0
  172. package/src/discovery/gemini.ts +395 -0
  173. package/src/discovery/github.ts +117 -0
  174. package/src/discovery/helpers.ts +698 -0
  175. package/src/discovery/index.ts +89 -0
  176. package/src/discovery/mcp-json.ts +156 -0
  177. package/src/discovery/opencode.ts +394 -0
  178. package/src/discovery/ssh.ts +160 -0
  179. package/src/discovery/vscode.ts +103 -0
  180. package/src/discovery/windsurf.ts +145 -0
  181. package/src/exa/company.ts +57 -0
  182. package/src/exa/index.ts +62 -0
  183. package/src/exa/linkedin.ts +57 -0
  184. package/src/exa/mcp-client.ts +289 -0
  185. package/src/exa/render.ts +244 -0
  186. package/src/exa/researcher.ts +89 -0
  187. package/src/exa/search.ts +330 -0
  188. package/src/exa/types.ts +166 -0
  189. package/src/exa/websets.ts +247 -0
  190. package/src/exec/bash-executor.ts +184 -0
  191. package/src/exec/exec.ts +53 -0
  192. package/src/export/custom-share.ts +65 -0
  193. package/src/export/html/index.ts +162 -0
  194. package/src/export/html/template.css +889 -0
  195. package/src/export/html/template.generated.ts +2 -0
  196. package/src/export/html/template.html +45 -0
  197. package/src/export/html/template.js +1329 -0
  198. package/src/export/html/template.macro.ts +24 -0
  199. package/src/export/html/vendor/highlight.min.js +1213 -0
  200. package/src/export/html/vendor/marked.min.js +6 -0
  201. package/src/export/ttsr.ts +434 -0
  202. package/src/extensibility/custom-commands/bundled/review/index.ts +433 -0
  203. package/src/extensibility/custom-commands/index.ts +15 -0
  204. package/src/extensibility/custom-commands/loader.ts +231 -0
  205. package/src/extensibility/custom-commands/types.ts +111 -0
  206. package/src/extensibility/custom-tools/index.ts +22 -0
  207. package/src/extensibility/custom-tools/loader.ts +235 -0
  208. package/src/extensibility/custom-tools/types.ts +226 -0
  209. package/src/extensibility/custom-tools/wrapper.ts +45 -0
  210. package/src/extensibility/extensions/index.ts +136 -0
  211. package/src/extensibility/extensions/loader.ts +520 -0
  212. package/src/extensibility/extensions/runner.ts +774 -0
  213. package/src/extensibility/extensions/types.ts +1293 -0
  214. package/src/extensibility/extensions/wrapper.ts +188 -0
  215. package/src/extensibility/hooks/index.ts +16 -0
  216. package/src/extensibility/hooks/loader.ts +273 -0
  217. package/src/extensibility/hooks/runner.ts +441 -0
  218. package/src/extensibility/hooks/tool-wrapper.ts +106 -0
  219. package/src/extensibility/hooks/types.ts +817 -0
  220. package/src/extensibility/plugins/doctor.ts +65 -0
  221. package/src/extensibility/plugins/git-url.ts +281 -0
  222. package/src/extensibility/plugins/index.ts +33 -0
  223. package/src/extensibility/plugins/installer.ts +192 -0
  224. package/src/extensibility/plugins/loader.ts +338 -0
  225. package/src/extensibility/plugins/manager.ts +716 -0
  226. package/src/extensibility/plugins/parser.ts +105 -0
  227. package/src/extensibility/plugins/types.ts +190 -0
  228. package/src/extensibility/skills.ts +385 -0
  229. package/src/extensibility/slash-commands.ts +287 -0
  230. package/src/extensibility/tool-proxy.ts +25 -0
  231. package/src/index.ts +275 -0
  232. package/src/internal-urls/agent-protocol.ts +136 -0
  233. package/src/internal-urls/artifact-protocol.ts +97 -0
  234. package/src/internal-urls/docs-index.generated.ts +54 -0
  235. package/src/internal-urls/docs-protocol.ts +84 -0
  236. package/src/internal-urls/index.ts +31 -0
  237. package/src/internal-urls/json-query.ts +126 -0
  238. package/src/internal-urls/memory-protocol.ts +133 -0
  239. package/src/internal-urls/router.ts +70 -0
  240. package/src/internal-urls/rule-protocol.ts +55 -0
  241. package/src/internal-urls/skill-protocol.ts +111 -0
  242. package/src/internal-urls/types.ts +52 -0
  243. package/src/ipy/executor.ts +556 -0
  244. package/src/ipy/gateway-coordinator.ts +426 -0
  245. package/src/ipy/kernel.ts +892 -0
  246. package/src/ipy/modules.ts +109 -0
  247. package/src/ipy/prelude.py +831 -0
  248. package/src/ipy/prelude.ts +3 -0
  249. package/src/ipy/runtime.ts +222 -0
  250. package/src/lsp/client.ts +867 -0
  251. package/src/lsp/clients/biome-client.ts +202 -0
  252. package/src/lsp/clients/index.ts +50 -0
  253. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  254. package/src/lsp/clients/swiftlint-client.ts +120 -0
  255. package/src/lsp/config.ts +397 -0
  256. package/src/lsp/defaults.json +464 -0
  257. package/src/lsp/edits.ts +109 -0
  258. package/src/lsp/index.ts +1268 -0
  259. package/src/lsp/lspmux.ts +250 -0
  260. package/src/lsp/render.ts +689 -0
  261. package/src/lsp/types.ts +414 -0
  262. package/src/lsp/utils.ts +549 -0
  263. package/src/main.ts +773 -0
  264. package/src/mcp/client.ts +239 -0
  265. package/src/mcp/config-writer.ts +215 -0
  266. package/src/mcp/config.ts +363 -0
  267. package/src/mcp/index.ts +55 -0
  268. package/src/mcp/json-rpc.ts +84 -0
  269. package/src/mcp/loader.ts +124 -0
  270. package/src/mcp/manager.ts +490 -0
  271. package/src/mcp/oauth-discovery.ts +274 -0
  272. package/src/mcp/oauth-flow.ts +229 -0
  273. package/src/mcp/render.ts +123 -0
  274. package/src/mcp/tool-bridge.ts +372 -0
  275. package/src/mcp/tool-cache.ts +121 -0
  276. package/src/mcp/transports/http.ts +332 -0
  277. package/src/mcp/transports/index.ts +6 -0
  278. package/src/mcp/transports/stdio.ts +281 -0
  279. package/src/mcp/types.ts +248 -0
  280. package/src/memories/index.ts +1099 -0
  281. package/src/memories/storage.ts +563 -0
  282. package/src/modes/components/agent-dashboard.ts +1130 -0
  283. package/src/modes/components/assistant-message.ts +144 -0
  284. package/src/modes/components/bash-execution.ts +218 -0
  285. package/src/modes/components/bordered-loader.ts +41 -0
  286. package/src/modes/components/branch-summary-message.ts +45 -0
  287. package/src/modes/components/codemode-group.ts +369 -0
  288. package/src/modes/components/compaction-summary-message.ts +51 -0
  289. package/src/modes/components/countdown-timer.ts +46 -0
  290. package/src/modes/components/custom-editor.ts +181 -0
  291. package/src/modes/components/custom-message.ts +91 -0
  292. package/src/modes/components/diff.ts +186 -0
  293. package/src/modes/components/dynamic-border.ts +25 -0
  294. package/src/modes/components/extensions/extension-dashboard.ts +325 -0
  295. package/src/modes/components/extensions/extension-list.ts +484 -0
  296. package/src/modes/components/extensions/index.ts +9 -0
  297. package/src/modes/components/extensions/inspector-panel.ts +321 -0
  298. package/src/modes/components/extensions/state-manager.ts +586 -0
  299. package/src/modes/components/extensions/types.ts +191 -0
  300. package/src/modes/components/footer.ts +315 -0
  301. package/src/modes/components/history-search.ts +157 -0
  302. package/src/modes/components/hook-editor.ts +101 -0
  303. package/src/modes/components/hook-input.ts +72 -0
  304. package/src/modes/components/hook-message.ts +100 -0
  305. package/src/modes/components/hook-selector.ts +155 -0
  306. package/src/modes/components/index.ts +41 -0
  307. package/src/modes/components/keybinding-hints.ts +65 -0
  308. package/src/modes/components/login-dialog.ts +164 -0
  309. package/src/modes/components/mcp-add-wizard.ts +1295 -0
  310. package/src/modes/components/model-selector.ts +625 -0
  311. package/src/modes/components/oauth-selector.ts +210 -0
  312. package/src/modes/components/plugin-settings.ts +477 -0
  313. package/src/modes/components/python-execution.ts +196 -0
  314. package/src/modes/components/queue-mode-selector.ts +56 -0
  315. package/src/modes/components/read-tool-group.ts +119 -0
  316. package/src/modes/components/session-selector.ts +242 -0
  317. package/src/modes/components/settings-defs.ts +340 -0
  318. package/src/modes/components/settings-selector.ts +529 -0
  319. package/src/modes/components/show-images-selector.ts +45 -0
  320. package/src/modes/components/skill-message.ts +90 -0
  321. package/src/modes/components/status-line/index.ts +4 -0
  322. package/src/modes/components/status-line/presets.ts +94 -0
  323. package/src/modes/components/status-line/segments.ts +352 -0
  324. package/src/modes/components/status-line/separators.ts +55 -0
  325. package/src/modes/components/status-line/types.ts +75 -0
  326. package/src/modes/components/status-line-segment-editor.ts +354 -0
  327. package/src/modes/components/status-line.ts +421 -0
  328. package/src/modes/components/theme-selector.ts +63 -0
  329. package/src/modes/components/thinking-selector.ts +64 -0
  330. package/src/modes/components/todo-display.ts +115 -0
  331. package/src/modes/components/todo-reminder.ts +40 -0
  332. package/src/modes/components/tool-execution.ts +703 -0
  333. package/src/modes/components/tree-selector.ts +904 -0
  334. package/src/modes/components/ttsr-notification.ts +80 -0
  335. package/src/modes/components/user-message-selector.ts +146 -0
  336. package/src/modes/components/user-message.ts +22 -0
  337. package/src/modes/components/visual-truncate.ts +63 -0
  338. package/src/modes/components/welcome.ts +247 -0
  339. package/src/modes/controllers/command-controller.ts +1120 -0
  340. package/src/modes/controllers/event-controller.ts +479 -0
  341. package/src/modes/controllers/extension-ui-controller.ts +778 -0
  342. package/src/modes/controllers/input-controller.ts +671 -0
  343. package/src/modes/controllers/mcp-command-controller.ts +1315 -0
  344. package/src/modes/controllers/selector-controller.ts +712 -0
  345. package/src/modes/controllers/ssh-command-controller.ts +452 -0
  346. package/src/modes/index.ts +15 -0
  347. package/src/modes/interactive-mode.ts +1027 -0
  348. package/src/modes/print-mode.ts +191 -0
  349. package/src/modes/rpc/rpc-client.ts +583 -0
  350. package/src/modes/rpc/rpc-mode.ts +700 -0
  351. package/src/modes/rpc/rpc-types.ts +236 -0
  352. package/src/modes/theme/dark.json +95 -0
  353. package/src/modes/theme/defaults/alabaster.json +93 -0
  354. package/src/modes/theme/defaults/amethyst.json +96 -0
  355. package/src/modes/theme/defaults/anthracite.json +93 -0
  356. package/src/modes/theme/defaults/basalt.json +91 -0
  357. package/src/modes/theme/defaults/birch.json +95 -0
  358. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  359. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  360. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  361. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  362. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  363. package/src/modes/theme/defaults/dark-copper.json +95 -0
  364. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  365. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  366. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  367. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  368. package/src/modes/theme/defaults/dark-ember.json +95 -0
  369. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  370. package/src/modes/theme/defaults/dark-forest.json +96 -0
  371. package/src/modes/theme/defaults/dark-github.json +105 -0
  372. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  373. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  374. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  375. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  376. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  377. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  378. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  379. package/src/modes/theme/defaults/dark-nord.json +97 -0
  380. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  381. package/src/modes/theme/defaults/dark-one.json +100 -0
  382. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  383. package/src/modes/theme/defaults/dark-reef.json +91 -0
  384. package/src/modes/theme/defaults/dark-retro.json +92 -0
  385. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  386. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  387. package/src/modes/theme/defaults/dark-slate.json +95 -0
  388. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  389. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  390. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  391. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  392. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  393. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  394. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  395. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  396. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  397. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  398. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  399. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  400. package/src/modes/theme/defaults/graphite.json +92 -0
  401. package/src/modes/theme/defaults/index.ts +195 -0
  402. package/src/modes/theme/defaults/light-arctic.json +107 -0
  403. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  404. package/src/modes/theme/defaults/light-canyon.json +91 -0
  405. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  406. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  407. package/src/modes/theme/defaults/light-coral.json +95 -0
  408. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  409. package/src/modes/theme/defaults/light-dawn.json +90 -0
  410. package/src/modes/theme/defaults/light-dunes.json +91 -0
  411. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  412. package/src/modes/theme/defaults/light-forest.json +100 -0
  413. package/src/modes/theme/defaults/light-frost.json +95 -0
  414. package/src/modes/theme/defaults/light-github.json +115 -0
  415. package/src/modes/theme/defaults/light-glacier.json +91 -0
  416. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  417. package/src/modes/theme/defaults/light-haze.json +90 -0
  418. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  419. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  420. package/src/modes/theme/defaults/light-lavender.json +95 -0
  421. package/src/modes/theme/defaults/light-meadow.json +91 -0
  422. package/src/modes/theme/defaults/light-mint.json +95 -0
  423. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  424. package/src/modes/theme/defaults/light-ocean.json +99 -0
  425. package/src/modes/theme/defaults/light-one.json +99 -0
  426. package/src/modes/theme/defaults/light-opal.json +91 -0
  427. package/src/modes/theme/defaults/light-orchard.json +91 -0
  428. package/src/modes/theme/defaults/light-paper.json +95 -0
  429. package/src/modes/theme/defaults/light-prism.json +90 -0
  430. package/src/modes/theme/defaults/light-retro.json +98 -0
  431. package/src/modes/theme/defaults/light-sand.json +95 -0
  432. package/src/modes/theme/defaults/light-savanna.json +91 -0
  433. package/src/modes/theme/defaults/light-solarized.json +102 -0
  434. package/src/modes/theme/defaults/light-soleil.json +90 -0
  435. package/src/modes/theme/defaults/light-sunset.json +99 -0
  436. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  437. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  438. package/src/modes/theme/defaults/light-wetland.json +91 -0
  439. package/src/modes/theme/defaults/light-zenith.json +89 -0
  440. package/src/modes/theme/defaults/limestone.json +94 -0
  441. package/src/modes/theme/defaults/mahogany.json +97 -0
  442. package/src/modes/theme/defaults/marble.json +93 -0
  443. package/src/modes/theme/defaults/obsidian.json +91 -0
  444. package/src/modes/theme/defaults/onyx.json +91 -0
  445. package/src/modes/theme/defaults/pearl.json +93 -0
  446. package/src/modes/theme/defaults/porcelain.json +91 -0
  447. package/src/modes/theme/defaults/quartz.json +96 -0
  448. package/src/modes/theme/defaults/sandstone.json +95 -0
  449. package/src/modes/theme/defaults/titanium.json +90 -0
  450. package/src/modes/theme/light.json +93 -0
  451. package/src/modes/theme/mermaid-cache.ts +111 -0
  452. package/src/modes/theme/theme-schema.json +429 -0
  453. package/src/modes/theme/theme.ts +2333 -0
  454. package/src/modes/types.ts +216 -0
  455. package/src/modes/utils/ui-helpers.ts +529 -0
  456. package/src/patch/applicator.ts +1482 -0
  457. package/src/patch/diff.ts +425 -0
  458. package/src/patch/fuzzy.ts +784 -0
  459. package/src/patch/hashline.ts +972 -0
  460. package/src/patch/index.ts +964 -0
  461. package/src/patch/normalize.ts +397 -0
  462. package/src/patch/normative.ts +72 -0
  463. package/src/patch/parser.ts +532 -0
  464. package/src/patch/shared.ts +400 -0
  465. package/src/patch/types.ts +292 -0
  466. package/src/priority.json +35 -0
  467. package/src/prompts/agents/explore.md +48 -0
  468. package/src/prompts/agents/frontmatter.md +9 -0
  469. package/src/prompts/agents/init.md +36 -0
  470. package/src/prompts/agents/librarian.md +53 -0
  471. package/src/prompts/agents/oracle.md +51 -0
  472. package/src/prompts/agents/reviewer.md +70 -0
  473. package/src/prompts/agents/task.md +14 -0
  474. package/src/prompts/compaction/branch-summary-context.md +5 -0
  475. package/src/prompts/compaction/branch-summary-preamble.md +2 -0
  476. package/src/prompts/compaction/branch-summary.md +30 -0
  477. package/src/prompts/compaction/compaction-short-summary.md +9 -0
  478. package/src/prompts/compaction/compaction-summary-context.md +5 -0
  479. package/src/prompts/compaction/compaction-summary.md +38 -0
  480. package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
  481. package/src/prompts/compaction/compaction-update-summary.md +45 -0
  482. package/src/prompts/memories/consolidation.md +30 -0
  483. package/src/prompts/memories/read_path.md +11 -0
  484. package/src/prompts/memories/stage_one_input.md +6 -0
  485. package/src/prompts/memories/stage_one_system.md +21 -0
  486. package/src/prompts/review-request.md +64 -0
  487. package/src/prompts/system/agent-creation-architect.md +65 -0
  488. package/src/prompts/system/agent-creation-user.md +6 -0
  489. package/src/prompts/system/custom-system-prompt.md +68 -0
  490. package/src/prompts/system/file-operations.md +10 -0
  491. package/src/prompts/system/subagent-submit-reminder.md +11 -0
  492. package/src/prompts/system/subagent-system-prompt.md +31 -0
  493. package/src/prompts/system/subagent-user-prompt.md +8 -0
  494. package/src/prompts/system/summarization-system.md +3 -0
  495. package/src/prompts/system/system-prompt.md +300 -0
  496. package/src/prompts/system/title-system.md +2 -0
  497. package/src/prompts/system/ttsr-interrupt.md +7 -0
  498. package/src/prompts/system/web-search.md +28 -0
  499. package/src/prompts/tools/ask.md +44 -0
  500. package/src/prompts/tools/bash.md +24 -0
  501. package/src/prompts/tools/browser.md +33 -0
  502. package/src/prompts/tools/calculator.md +12 -0
  503. package/src/prompts/tools/explore.md +29 -0
  504. package/src/prompts/tools/fetch.md +16 -0
  505. package/src/prompts/tools/find.md +18 -0
  506. package/src/prompts/tools/gemini-image.md +23 -0
  507. package/src/prompts/tools/grep.md +28 -0
  508. package/src/prompts/tools/hashline.md +232 -0
  509. package/src/prompts/tools/librarian.md +24 -0
  510. package/src/prompts/tools/lsp.md +28 -0
  511. package/src/prompts/tools/oracle.md +26 -0
  512. package/src/prompts/tools/patch.md +74 -0
  513. package/src/prompts/tools/python.md +66 -0
  514. package/src/prompts/tools/read.md +36 -0
  515. package/src/prompts/tools/replace.md +38 -0
  516. package/src/prompts/tools/reviewer.md +41 -0
  517. package/src/prompts/tools/ssh.md +51 -0
  518. package/src/prompts/tools/task-summary.md +28 -0
  519. package/src/prompts/tools/task.md +275 -0
  520. package/src/prompts/tools/todo-write.md +65 -0
  521. package/src/prompts/tools/undo-edit.md +7 -0
  522. package/src/prompts/tools/web-search.md +19 -0
  523. package/src/prompts/tools/write.md +18 -0
  524. package/src/sdk.ts +1287 -0
  525. package/src/secrets/index.ts +116 -0
  526. package/src/secrets/obfuscator.ts +269 -0
  527. package/src/secrets/regex.ts +21 -0
  528. package/src/session/agent-session.ts +4669 -0
  529. package/src/session/agent-storage.ts +621 -0
  530. package/src/session/artifacts.ts +132 -0
  531. package/src/session/auth-storage.ts +1433 -0
  532. package/src/session/blob-store.ts +103 -0
  533. package/src/session/compaction/branch-summarization.ts +315 -0
  534. package/src/session/compaction/compaction.ts +864 -0
  535. package/src/session/compaction/index.ts +7 -0
  536. package/src/session/compaction/pruning.ts +91 -0
  537. package/src/session/compaction/utils.ts +171 -0
  538. package/src/session/history-storage.ts +170 -0
  539. package/src/session/messages.ts +317 -0
  540. package/src/session/session-manager.ts +2276 -0
  541. package/src/session/session-storage.ts +342 -0
  542. package/src/session/streaming-output.ts +565 -0
  543. package/src/slash-commands/builtin-registry.ts +439 -0
  544. package/src/ssh/config-writer.ts +183 -0
  545. package/src/ssh/connection-manager.ts +444 -0
  546. package/src/ssh/ssh-executor.ts +127 -0
  547. package/src/ssh/sshfs-mount.ts +135 -0
  548. package/src/stt/downloader.ts +71 -0
  549. package/src/stt/index.ts +3 -0
  550. package/src/stt/recorder.ts +351 -0
  551. package/src/stt/setup.ts +52 -0
  552. package/src/stt/stt-controller.ts +160 -0
  553. package/src/stt/transcribe.py +70 -0
  554. package/src/stt/transcriber.ts +91 -0
  555. package/src/system-prompt.ts +685 -0
  556. package/src/task/agents.ts +155 -0
  557. package/src/task/batch.ts +102 -0
  558. package/src/task/commands.ts +134 -0
  559. package/src/task/discovery.ts +126 -0
  560. package/src/task/executor.ts +908 -0
  561. package/src/task/index.ts +223 -0
  562. package/src/task/output-manager.ts +107 -0
  563. package/src/task/parallel.ts +84 -0
  564. package/src/task/render.ts +326 -0
  565. package/src/task/subprocess-tool-registry.ts +88 -0
  566. package/src/task/template.ts +32 -0
  567. package/src/task/types.ts +144 -0
  568. package/src/tools/ask.ts +523 -0
  569. package/src/tools/bash-interactive.ts +419 -0
  570. package/src/tools/bash-interceptor.ts +105 -0
  571. package/src/tools/bash-normalize.ts +107 -0
  572. package/src/tools/bash-skill-urls.ts +177 -0
  573. package/src/tools/bash.ts +347 -0
  574. package/src/tools/browser.ts +1374 -0
  575. package/src/tools/calculator.ts +537 -0
  576. package/src/tools/context.ts +39 -0
  577. package/src/tools/explore.ts +23 -0
  578. package/src/tools/fetch.ts +1091 -0
  579. package/src/tools/find.ts +540 -0
  580. package/src/tools/fs-cache-invalidation.ts +28 -0
  581. package/src/tools/gemini-image.ts +907 -0
  582. package/src/tools/grep.ts +489 -0
  583. package/src/tools/index.ts +337 -0
  584. package/src/tools/json-tree.ts +231 -0
  585. package/src/tools/jtd-to-json-schema.ts +247 -0
  586. package/src/tools/jtd-to-typescript.ts +198 -0
  587. package/src/tools/librarian.ts +33 -0
  588. package/src/tools/list-limit.ts +40 -0
  589. package/src/tools/notebook.ts +287 -0
  590. package/src/tools/oracle.ts +40 -0
  591. package/src/tools/output-meta.ts +459 -0
  592. package/src/tools/output-utils.ts +63 -0
  593. package/src/tools/path-utils.ts +116 -0
  594. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  595. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  596. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  597. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  598. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  599. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  600. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  601. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  602. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  603. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  604. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  605. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  606. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  607. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  608. package/src/tools/python.ts +1118 -0
  609. package/src/tools/read.ts +1193 -0
  610. package/src/tools/render-utils.ts +680 -0
  611. package/src/tools/renderers.ts +60 -0
  612. package/src/tools/reviewer-tool.ts +41 -0
  613. package/src/tools/ssh.ts +326 -0
  614. package/src/tools/subagent-tool.ts +169 -0
  615. package/src/tools/submit-result.ts +152 -0
  616. package/src/tools/todo-write.ts +255 -0
  617. package/src/tools/tool-errors.ts +92 -0
  618. package/src/tools/tool-result.ts +86 -0
  619. package/src/tools/undo-edit.ts +145 -0
  620. package/src/tools/undo-history.ts +22 -0
  621. package/src/tools/write.ts +274 -0
  622. package/src/tui/code-cell.ts +108 -0
  623. package/src/tui/file-list.ts +47 -0
  624. package/src/tui/index.ts +11 -0
  625. package/src/tui/output-block.ts +144 -0
  626. package/src/tui/status-line.ts +39 -0
  627. package/src/tui/tree-list.ts +53 -0
  628. package/src/tui/types.ts +16 -0
  629. package/src/tui/utils.ts +116 -0
  630. package/src/utils/changelog.ts +98 -0
  631. package/src/utils/event-bus.ts +33 -0
  632. package/src/utils/external-editor.ts +59 -0
  633. package/src/utils/file-display-mode.ts +36 -0
  634. package/src/utils/file-mentions.ts +384 -0
  635. package/src/utils/frontmatter.ts +101 -0
  636. package/src/utils/fuzzy.ts +108 -0
  637. package/src/utils/ignore-files.ts +119 -0
  638. package/src/utils/image-convert.ts +27 -0
  639. package/src/utils/image-resize.ts +236 -0
  640. package/src/utils/mime.ts +30 -0
  641. package/src/utils/open.ts +20 -0
  642. package/src/utils/shell-snapshot.ts +199 -0
  643. package/src/utils/timings.ts +26 -0
  644. package/src/utils/title-generator.ts +167 -0
  645. package/src/utils/tools-manager.ts +362 -0
  646. package/src/web/scrapers/artifacthub.ts +215 -0
  647. package/src/web/scrapers/arxiv.ts +88 -0
  648. package/src/web/scrapers/aur.ts +175 -0
  649. package/src/web/scrapers/biorxiv.ts +141 -0
  650. package/src/web/scrapers/bluesky.ts +284 -0
  651. package/src/web/scrapers/brew.ts +177 -0
  652. package/src/web/scrapers/cheatsh.ts +78 -0
  653. package/src/web/scrapers/chocolatey.ts +158 -0
  654. package/src/web/scrapers/choosealicense.ts +110 -0
  655. package/src/web/scrapers/cisa-kev.ts +100 -0
  656. package/src/web/scrapers/clojars.ts +180 -0
  657. package/src/web/scrapers/coingecko.ts +184 -0
  658. package/src/web/scrapers/crates-io.ts +128 -0
  659. package/src/web/scrapers/crossref.ts +149 -0
  660. package/src/web/scrapers/devto.ts +177 -0
  661. package/src/web/scrapers/discogs.ts +307 -0
  662. package/src/web/scrapers/discourse.ts +221 -0
  663. package/src/web/scrapers/dockerhub.ts +160 -0
  664. package/src/web/scrapers/fdroid.ts +158 -0
  665. package/src/web/scrapers/firefox-addons.ts +214 -0
  666. package/src/web/scrapers/flathub.ts +239 -0
  667. package/src/web/scrapers/github-gist.ts +68 -0
  668. package/src/web/scrapers/github.ts +490 -0
  669. package/src/web/scrapers/gitlab.ts +456 -0
  670. package/src/web/scrapers/go-pkg.ts +275 -0
  671. package/src/web/scrapers/hackage.ts +94 -0
  672. package/src/web/scrapers/hackernews.ts +208 -0
  673. package/src/web/scrapers/hex.ts +121 -0
  674. package/src/web/scrapers/huggingface.ts +385 -0
  675. package/src/web/scrapers/iacr.ts +86 -0
  676. package/src/web/scrapers/index.ts +249 -0
  677. package/src/web/scrapers/jetbrains-marketplace.ts +169 -0
  678. package/src/web/scrapers/lemmy.ts +220 -0
  679. package/src/web/scrapers/lobsters.ts +186 -0
  680. package/src/web/scrapers/mastodon.ts +310 -0
  681. package/src/web/scrapers/maven.ts +152 -0
  682. package/src/web/scrapers/mdn.ts +172 -0
  683. package/src/web/scrapers/metacpan.ts +253 -0
  684. package/src/web/scrapers/musicbrainz.ts +272 -0
  685. package/src/web/scrapers/npm.ts +114 -0
  686. package/src/web/scrapers/nuget.ts +205 -0
  687. package/src/web/scrapers/nvd.ts +243 -0
  688. package/src/web/scrapers/ollama.ts +265 -0
  689. package/src/web/scrapers/open-vsx.ts +119 -0
  690. package/src/web/scrapers/opencorporates.ts +275 -0
  691. package/src/web/scrapers/openlibrary.ts +319 -0
  692. package/src/web/scrapers/orcid.ts +298 -0
  693. package/src/web/scrapers/osv.ts +192 -0
  694. package/src/web/scrapers/packagist.ts +174 -0
  695. package/src/web/scrapers/pub-dev.ts +185 -0
  696. package/src/web/scrapers/pubmed.ts +177 -0
  697. package/src/web/scrapers/pypi.ts +129 -0
  698. package/src/web/scrapers/rawg.ts +124 -0
  699. package/src/web/scrapers/readthedocs.ts +125 -0
  700. package/src/web/scrapers/reddit.ts +104 -0
  701. package/src/web/scrapers/repology.ts +262 -0
  702. package/src/web/scrapers/rfc.ts +209 -0
  703. package/src/web/scrapers/rubygems.ts +117 -0
  704. package/src/web/scrapers/searchcode.ts +217 -0
  705. package/src/web/scrapers/sec-edgar.ts +274 -0
  706. package/src/web/scrapers/semantic-scholar.ts +190 -0
  707. package/src/web/scrapers/snapcraft.ts +200 -0
  708. package/src/web/scrapers/sourcegraph.ts +373 -0
  709. package/src/web/scrapers/spdx.ts +121 -0
  710. package/src/web/scrapers/spotify.ts +217 -0
  711. package/src/web/scrapers/stackoverflow.ts +124 -0
  712. package/src/web/scrapers/terraform.ts +304 -0
  713. package/src/web/scrapers/tldr.ts +51 -0
  714. package/src/web/scrapers/twitter.ts +97 -0
  715. package/src/web/scrapers/types.ts +200 -0
  716. package/src/web/scrapers/utils.ts +142 -0
  717. package/src/web/scrapers/vimeo.ts +152 -0
  718. package/src/web/scrapers/vscode-marketplace.ts +195 -0
  719. package/src/web/scrapers/w3c.ts +163 -0
  720. package/src/web/scrapers/wikidata.ts +357 -0
  721. package/src/web/scrapers/wikipedia.ts +95 -0
  722. package/src/web/scrapers/youtube.ts +312 -0
  723. package/src/web/search/auth.ts +178 -0
  724. package/src/web/search/index.ts +598 -0
  725. package/src/web/search/provider.ts +77 -0
  726. package/src/web/search/providers/anthropic.ts +284 -0
  727. package/src/web/search/providers/base.ts +22 -0
  728. package/src/web/search/providers/brave.ts +165 -0
  729. package/src/web/search/providers/codex.ts +377 -0
  730. package/src/web/search/providers/exa.ts +158 -0
  731. package/src/web/search/providers/gemini.ts +437 -0
  732. package/src/web/search/providers/jina.ts +99 -0
  733. package/src/web/search/providers/kimi.ts +196 -0
  734. package/src/web/search/providers/perplexity.ts +546 -0
  735. package/src/web/search/providers/synthetic.ts +136 -0
  736. package/src/web/search/providers/zai.ts +352 -0
  737. package/src/web/search/render.ts +299 -0
  738. package/src/web/search/types.ts +437 -0
@@ -0,0 +1,907 @@
1
+ import * as os from "node:os";
2
+ import * as path from "node:path";
3
+ import { getAntigravityHeaders, getEnvApiKey, StringEnum } from "@nghyane/arcane-ai";
4
+ import { $env, ptree, readSseJson, Snowflake, untilAborted } from "@nghyane/arcane-utils";
5
+ import { type Static, Type } from "@sinclair/typebox";
6
+ import type { ModelRegistry } from "../config/model-registry";
7
+ import { renderPromptTemplate } from "../config/prompt-templates";
8
+ import type { CustomTool } from "../extensibility/custom-tools/types";
9
+ import geminiImageDescription from "../prompts/tools/gemini-image.md" with { type: "text" };
10
+ import { detectSupportedImageMimeTypeFromFile } from "../utils/mime";
11
+ import { resolveReadPath } from "./path-utils";
12
+
13
+ const DEFAULT_MODEL = "gemini-3-pro-image-preview";
14
+ const DEFAULT_OPENROUTER_MODEL = "google/gemini-3-pro-image-preview";
15
+ const DEFAULT_ANTIGRAVITY_MODEL = "gemini-3-pro-image";
16
+ const DEFAULT_TIMEOUT_SECONDS = 120;
17
+ const MAX_IMAGE_SIZE = 20 * 1024 * 1024;
18
+
19
+ const ANTIGRAVITY_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com";
20
+ const IMAGE_SYSTEM_INSTRUCTION =
21
+ "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.";
22
+
23
+ type ImageProvider = "antigravity" | "gemini" | "openrouter";
24
+ interface ImageApiKey {
25
+ provider: ImageProvider;
26
+ apiKey: string;
27
+ projectId?: string;
28
+ }
29
+
30
+ const responseModalitySchema = StringEnum(["Image", "Text"]);
31
+ const aspectRatioSchema = StringEnum(["1:1", "3:4", "4:3", "9:16", "16:9"], {
32
+ description: "Aspect ratio (1:1, 3:4, 4:3, 9:16, 16:9).",
33
+ });
34
+ const imageSizeSchema = StringEnum(["1024x1024", "1536x1024", "1024x1536"], {
35
+ description: "Image size, mainly for gemini-3-pro-image-preview.",
36
+ });
37
+
38
+ const inputImageSchema = Type.Object(
39
+ {
40
+ path: Type.Optional(Type.String({ description: "Path to an input image file." })),
41
+ data: Type.Optional(Type.String({ description: "Base64 image data or a data: URL." })),
42
+ mime_type: Type.Optional(Type.String({ description: "Required for raw base64 data." })),
43
+ },
44
+ { additionalProperties: false },
45
+ );
46
+
47
+ const baseImageSchema = Type.Object(
48
+ {
49
+ subject: Type.String({
50
+ description:
51
+ "Main subject with key descriptors (e.g., 'A stoic robot barista with glowing blue optics', 'A weathered lighthouse on a rocky cliff').",
52
+ }),
53
+ action: Type.Optional(
54
+ Type.String({
55
+ description: "What the subject is doing (e.g., 'pouring latte art', 'standing against crashing waves').",
56
+ }),
57
+ ),
58
+ scene: Type.Optional(
59
+ Type.String({
60
+ description:
61
+ "Location or environment (e.g., 'in a futuristic café on Mars', 'during a violent thunderstorm at dusk').",
62
+ }),
63
+ ),
64
+ composition: Type.Optional(
65
+ Type.String({
66
+ description:
67
+ "Camera angle, framing, depth of field (e.g., 'low-angle close-up, shallow depth of field', 'wide establishing shot').",
68
+ }),
69
+ ),
70
+ lighting: Type.Optional(
71
+ Type.String({
72
+ description:
73
+ "Lighting setup and mood (e.g., 'warm rim lighting', 'golden hour backlight', 'hard noon shadows').",
74
+ }),
75
+ ),
76
+ style: Type.Optional(
77
+ Type.String({
78
+ description:
79
+ "Artistic style, mood, color grading (e.g., 'film noir mood, cinematic color grading', 'Studio Ghibli watercolor', 'photorealistic').",
80
+ }),
81
+ ),
82
+ camera: Type.Optional(
83
+ Type.String({
84
+ description:
85
+ "Lens and camera specs (e.g., 'Shot on 35mm, f/1.8', 'macro lens, extreme close-up', '85mm portrait lens').",
86
+ }),
87
+ ),
88
+ text: Type.Optional(
89
+ Type.String({
90
+ description:
91
+ "Text to render in image with specs: exact wording in quotes, font style, color, placement (e.g., 'Headline \"URBAN EXPLORER\" in bold white sans-serif at top center').",
92
+ }),
93
+ ),
94
+ changes: Type.Optional(
95
+ Type.Array(Type.String(), {
96
+ description:
97
+ "For edits: specific changes to make (e.g., ['Change the tie to green', 'Remove the car in background']). Use with input_images.",
98
+ }),
99
+ ),
100
+ preserve: Type.Optional(
101
+ Type.String({
102
+ description:
103
+ "For edits: what to keep unchanged (e.g., 'identity, face, hairstyle, lighting'). Use with input_images and changes.",
104
+ }),
105
+ ),
106
+ aspect_ratio: Type.Optional(aspectRatioSchema),
107
+ image_size: Type.Optional(imageSizeSchema),
108
+ input_images: Type.Optional(
109
+ Type.Array(inputImageSchema, {
110
+ description: "Optional input images for edits or variations.",
111
+ }),
112
+ ),
113
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 120)" })),
114
+ },
115
+ { additionalProperties: false },
116
+ );
117
+
118
+ export const geminiImageSchema = baseImageSchema;
119
+ export type GeminiImageParams = Static<typeof geminiImageSchema>;
120
+ export type GeminiResponseModality = Static<typeof responseModalitySchema>;
121
+
122
+ /**
123
+ * Assembles a structured prompt from the provided parameters.
124
+ * For generation: builds "subject, action, scene. composition. lighting. camera. style."
125
+ * For edits: appends change instructions and preserve directives.
126
+ */
127
+ function assemblePrompt(params: GeminiImageParams): string {
128
+ const parts: string[] = [];
129
+
130
+ // Core subject line: subject + action + scene
131
+ const subjectParts = [params.subject];
132
+ if (params.action) subjectParts.push(params.action);
133
+ if (params.scene) subjectParts.push(params.scene);
134
+ parts.push(subjectParts.join(", "));
135
+
136
+ // Technical details as separate sentences
137
+ if (params.composition) parts.push(params.composition);
138
+ if (params.lighting) parts.push(params.lighting);
139
+ if (params.camera) parts.push(params.camera);
140
+ if (params.style) parts.push(params.style);
141
+
142
+ // Join with periods for sentence structure
143
+ let prompt = `${parts.map(p => p.replace(/[.!,;:]+$/, "")).join(". ")}.`;
144
+
145
+ // Text rendering specs
146
+ if (params.text) {
147
+ prompt += `\n\nText: ${params.text}`;
148
+ }
149
+
150
+ // Edit mode: changes and preserve directives
151
+ if (params.changes?.length) {
152
+ prompt += `\n\nChanges:\n${params.changes.map(c => `- ${c}`).join("\n")}`;
153
+ if (params.preserve) {
154
+ prompt += `\n\nPreserve: ${params.preserve}`;
155
+ }
156
+ }
157
+
158
+ return prompt;
159
+ }
160
+
161
+ interface GeminiInlineData {
162
+ data?: string;
163
+ mimeType?: string;
164
+ }
165
+
166
+ interface GeminiPart {
167
+ text?: string;
168
+ inlineData?: GeminiInlineData;
169
+ }
170
+
171
+ interface GeminiCandidate {
172
+ content?: { parts?: GeminiPart[] };
173
+ }
174
+
175
+ interface GeminiSafetyRating {
176
+ category?: string;
177
+ probability?: string;
178
+ }
179
+
180
+ interface GeminiPromptFeedback {
181
+ blockReason?: string;
182
+ safetyRatings?: GeminiSafetyRating[];
183
+ }
184
+
185
+ interface GeminiUsageMetadata {
186
+ promptTokenCount?: number;
187
+ candidatesTokenCount?: number;
188
+ totalTokenCount?: number;
189
+ }
190
+
191
+ interface GeminiGenerateContentResponse {
192
+ candidates?: GeminiCandidate[];
193
+ promptFeedback?: GeminiPromptFeedback;
194
+ usageMetadata?: GeminiUsageMetadata;
195
+ }
196
+
197
+ interface OpenRouterImageUrl {
198
+ url: string;
199
+ }
200
+
201
+ interface OpenRouterContentPart {
202
+ type: "text" | "image_url";
203
+ text?: string;
204
+ image_url?: OpenRouterImageUrl;
205
+ }
206
+
207
+ interface OpenRouterMessage {
208
+ content?: string | OpenRouterContentPart[];
209
+ images?: Array<string | { image_url?: OpenRouterImageUrl }>;
210
+ }
211
+
212
+ interface OpenRouterChoice {
213
+ message?: OpenRouterMessage;
214
+ }
215
+
216
+ interface OpenRouterResponse {
217
+ choices?: OpenRouterChoice[];
218
+ }
219
+
220
+ interface AntigravityRequest {
221
+ project: string;
222
+ model: string;
223
+ request: {
224
+ contents: Array<{ role: "user"; parts: Array<{ text?: string; inlineData?: InlineImageData }> }>;
225
+ systemInstruction?: { parts: Array<{ text: string }> };
226
+ generationConfig?: {
227
+ responseModalities?: GeminiResponseModality[];
228
+ imageConfig?: { aspectRatio?: string; imageSize?: string };
229
+ candidateCount?: number;
230
+ };
231
+ safetySettings?: Array<{ category: string; threshold: string }>;
232
+ };
233
+ requestType?: string;
234
+ userAgent?: string;
235
+ requestId?: string;
236
+ }
237
+
238
+ interface AntigravityResponseChunk {
239
+ response?: {
240
+ candidates?: Array<{
241
+ content?: {
242
+ role: string;
243
+ parts?: Array<{
244
+ text?: string;
245
+ inlineData?: { mimeType?: string; data?: string };
246
+ }>;
247
+ };
248
+ }>;
249
+ usageMetadata?: GeminiUsageMetadata;
250
+ };
251
+ }
252
+
253
+ interface GeminiImageToolDetails {
254
+ provider: ImageProvider;
255
+ model: string;
256
+ imageCount: number;
257
+ imagePaths: string[];
258
+ images: InlineImageData[];
259
+ responseText?: string;
260
+ promptFeedback?: GeminiPromptFeedback;
261
+ usage?: GeminiUsageMetadata;
262
+ }
263
+
264
+ interface ImageInput {
265
+ path?: string;
266
+ data?: string;
267
+ mime_type?: string;
268
+ }
269
+
270
+ interface InlineImageData {
271
+ data: string;
272
+ mimeType: string;
273
+ }
274
+
275
+ function normalizeDataUrl(data: string): { data: string; mimeType?: string } {
276
+ const match = data.match(/^data:([^;]+);base64,(.+)$/);
277
+ if (!match) return { data };
278
+ return { data: match[2] ?? "", mimeType: match[1] };
279
+ }
280
+
281
+ function resolveOpenRouterModel(model: string): string {
282
+ return model.includes("/") ? model : `google/${model}`;
283
+ }
284
+
285
+ function toDataUrl(image: InlineImageData): string {
286
+ return `data:${image.mimeType};base64,${image.data}`;
287
+ }
288
+
289
+ async function loadImageFromUrl(imageUrl: string, signal?: AbortSignal): Promise<InlineImageData> {
290
+ if (imageUrl.startsWith("data:")) {
291
+ const normalized = normalizeDataUrl(imageUrl.trim());
292
+ if (!normalized.mimeType) {
293
+ throw new Error("mime_type is required when providing raw base64 data.");
294
+ }
295
+ if (!normalized.data) {
296
+ throw new Error("Image data is empty.");
297
+ }
298
+ return { data: normalized.data, mimeType: normalized.mimeType };
299
+ }
300
+
301
+ const response = await fetch(imageUrl, { signal });
302
+ if (!response.ok) {
303
+ const rawText = await response.text();
304
+ throw new Error(`Image download failed (${response.status}): ${rawText}`);
305
+ }
306
+ const contentType = response.headers.get("content-type")?.split(";")[0];
307
+ if (!contentType || !contentType.startsWith("image/")) {
308
+ throw new Error(`Unsupported image type from URL: ${imageUrl}`);
309
+ }
310
+ const buffer = await response.bytes();
311
+ return { data: buffer.toBase64(), mimeType: contentType };
312
+ }
313
+
314
+ function collectOpenRouterResponseText(message: OpenRouterMessage | undefined): string | undefined {
315
+ if (!message) return undefined;
316
+ if (typeof message.content === "string") {
317
+ const trimmed = message.content.trim();
318
+ return trimmed.length > 0 ? trimmed : undefined;
319
+ }
320
+ if (Array.isArray(message.content)) {
321
+ const texts = message.content
322
+ .filter(part => part.type === "text")
323
+ .map(part => part.text)
324
+ .filter((text): text is string => Boolean(text));
325
+ const combined = texts.join("\n").trim();
326
+ return combined.length > 0 ? combined : undefined;
327
+ }
328
+ return undefined;
329
+ }
330
+
331
+ function extractOpenRouterImageUrls(message: OpenRouterMessage | undefined): string[] {
332
+ const urls: string[] = [];
333
+ if (!message) return urls;
334
+ for (const image of message.images ?? []) {
335
+ if (typeof image === "string") {
336
+ urls.push(image);
337
+ continue;
338
+ }
339
+ if (image.image_url?.url) {
340
+ urls.push(image.image_url.url);
341
+ }
342
+ }
343
+ if (Array.isArray(message.content)) {
344
+ for (const part of message.content) {
345
+ if (part.type === "image_url" && part.image_url?.url) {
346
+ urls.push(part.image_url.url);
347
+ }
348
+ }
349
+ }
350
+ return urls;
351
+ }
352
+
353
+ /** Preferred provider set via settings (default: auto) */
354
+ let preferredImageProvider: ImageProvider | "auto" = "auto";
355
+
356
+ /** Set the preferred image provider from settings */
357
+ export function setPreferredImageProvider(provider: ImageProvider | "auto"): void {
358
+ preferredImageProvider = provider;
359
+ }
360
+
361
+ interface ParsedAntigravityCredentials {
362
+ accessToken: string;
363
+ projectId: string;
364
+ }
365
+
366
+ function parseAntigravityCredentials(raw: string): ParsedAntigravityCredentials | null {
367
+ try {
368
+ const parsed = JSON.parse(raw) as { token?: string; projectId?: string };
369
+ if (parsed.token && parsed.projectId) {
370
+ return { accessToken: parsed.token, projectId: parsed.projectId };
371
+ }
372
+ } catch {
373
+ // Invalid JSON
374
+ }
375
+ return null;
376
+ }
377
+
378
+ async function findAntigravityCredentials(modelRegistry: ModelRegistry): Promise<ImageApiKey | null> {
379
+ const apiKey = await modelRegistry.getApiKeyForProvider("google-antigravity");
380
+ if (!apiKey) return null;
381
+
382
+ const parsed = parseAntigravityCredentials(apiKey);
383
+ if (!parsed) return null;
384
+
385
+ return {
386
+ provider: "antigravity",
387
+ apiKey: parsed.accessToken,
388
+ projectId: parsed.projectId,
389
+ };
390
+ }
391
+
392
+ async function findImageApiKey(modelRegistry?: ModelRegistry): Promise<ImageApiKey | null> {
393
+ // If a specific provider is preferred, try it first
394
+ if (preferredImageProvider === "antigravity" && modelRegistry) {
395
+ const antigravity = await findAntigravityCredentials(modelRegistry);
396
+ if (antigravity) return antigravity;
397
+ // Fall through to auto-detect if preferred provider key not found
398
+ }
399
+ if (preferredImageProvider === "gemini") {
400
+ const geminiKey = getEnvApiKey("google");
401
+ if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
402
+ const googleKey = $env.GOOGLE_API_KEY;
403
+ if (googleKey) return { provider: "gemini", apiKey: googleKey };
404
+ // Fall through to auto-detect if preferred provider key not found
405
+ } else if (preferredImageProvider === "openrouter") {
406
+ const openRouterKey = getEnvApiKey("openrouter");
407
+ if (openRouterKey) return { provider: "openrouter", apiKey: openRouterKey };
408
+ // Fall through to auto-detect if preferred provider key not found
409
+ }
410
+
411
+ // Auto-detect: Antigravity takes priority, then OpenRouter, then Gemini
412
+ if (modelRegistry) {
413
+ const antigravity = await findAntigravityCredentials(modelRegistry);
414
+ if (antigravity) return antigravity;
415
+ }
416
+
417
+ const openRouterKey = getEnvApiKey("openrouter");
418
+ if (openRouterKey) return { provider: "openrouter", apiKey: openRouterKey };
419
+
420
+ const geminiKey = getEnvApiKey("google");
421
+ if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
422
+
423
+ const googleKey = $env.GOOGLE_API_KEY;
424
+ if (googleKey) return { provider: "gemini", apiKey: googleKey };
425
+
426
+ return null;
427
+ }
428
+
429
+ async function loadImageFromPath(imagePath: string, cwd: string): Promise<InlineImageData> {
430
+ const resolved = resolveReadPath(imagePath, cwd);
431
+ const file = Bun.file(resolved);
432
+ if (!(await file.exists())) {
433
+ throw new Error(`Image file not found: ${imagePath}`);
434
+ }
435
+ if (file.size > MAX_IMAGE_SIZE) {
436
+ throw new Error(`Image file too large: ${imagePath}`);
437
+ }
438
+
439
+ const mimeType = await detectSupportedImageMimeTypeFromFile(resolved);
440
+ if (!mimeType) {
441
+ throw new Error(`Unsupported image type: ${imagePath}`);
442
+ }
443
+
444
+ const buffer = await file.bytes();
445
+ return { data: buffer.toBase64(), mimeType };
446
+ }
447
+
448
+ async function resolveInputImage(input: ImageInput, cwd: string): Promise<InlineImageData> {
449
+ if (input.path) {
450
+ return loadImageFromPath(input.path, cwd);
451
+ }
452
+
453
+ if (input.data) {
454
+ const normalized = normalizeDataUrl(input.data.trim());
455
+ const mimeType = normalized.mimeType ?? input.mime_type;
456
+ if (!mimeType) {
457
+ throw new Error("mime_type is required when providing raw base64 data.");
458
+ }
459
+ if (!normalized.data) {
460
+ throw new Error("Image data is empty.");
461
+ }
462
+ return { data: normalized.data, mimeType };
463
+ }
464
+
465
+ throw new Error("input_images entries must include either path or data.");
466
+ }
467
+
468
+ function getExtensionForMime(mimeType: string): string {
469
+ const map: Record<string, string> = {
470
+ "image/png": "png",
471
+ "image/jpeg": "jpg",
472
+ "image/gif": "gif",
473
+ "image/webp": "webp",
474
+ };
475
+ return map[mimeType] ?? "png";
476
+ }
477
+
478
+ async function saveImageToTemp(image: InlineImageData): Promise<string> {
479
+ const ext = getExtensionForMime(image.mimeType);
480
+ const filename = `arc-image-${Snowflake.next()}.${ext}`;
481
+ const filepath = path.join(os.tmpdir(), filename);
482
+ await Bun.write(filepath, Buffer.from(image.data, "base64"));
483
+ return filepath;
484
+ }
485
+
486
+ async function saveImagesToTemp(images: InlineImageData[]): Promise<string[]> {
487
+ return Promise.all(images.map(saveImageToTemp));
488
+ }
489
+
490
+ function buildResponseSummary(
491
+ provider: ImageProvider,
492
+ model: string,
493
+ imagePaths: string[],
494
+ responseText: string | undefined,
495
+ ): string {
496
+ const lines = [`Provider: ${provider}`, `Model: ${model}`, `Generated ${imagePaths.length} image(s):`];
497
+ for (const p of imagePaths) {
498
+ lines.push(` ${p}`);
499
+ }
500
+ if (responseText) {
501
+ lines.push("", responseText.trim());
502
+ }
503
+ return lines.join("\n");
504
+ }
505
+
506
+ function collectResponseText(parts: GeminiPart[]): string | undefined {
507
+ const texts = parts.map(part => part.text).filter((text): text is string => Boolean(text));
508
+ const combined = texts.join("\n").trim();
509
+ return combined.length > 0 ? combined : undefined;
510
+ }
511
+
512
+ function collectInlineImages(parts: GeminiPart[]): InlineImageData[] {
513
+ const images: InlineImageData[] = [];
514
+ for (const part of parts) {
515
+ const data = part.inlineData?.data;
516
+ const mimeType = part.inlineData?.mimeType;
517
+ if (!data || !mimeType) continue;
518
+ images.push({ data, mimeType });
519
+ }
520
+ return images;
521
+ }
522
+
523
+ function combineParts(response: GeminiGenerateContentResponse): GeminiPart[] {
524
+ const parts: GeminiPart[] = [];
525
+ for (const candidate of response.candidates ?? []) {
526
+ const candidateParts = candidate.content?.parts ?? [];
527
+ parts.push(...candidateParts);
528
+ }
529
+ return parts;
530
+ }
531
+
532
+ function buildAntigravityRequest(
533
+ prompt: string,
534
+ model: string,
535
+ projectId: string,
536
+ aspectRatio: string | undefined,
537
+ imageSize: string | undefined,
538
+ inputImages: InlineImageData[],
539
+ ): AntigravityRequest {
540
+ const parts: Array<{ text?: string; inlineData?: InlineImageData }> = [];
541
+ for (const image of inputImages) {
542
+ parts.push({ inlineData: image });
543
+ }
544
+ parts.push({ text: prompt });
545
+
546
+ const imageConfig = aspectRatio || imageSize ? { aspectRatio: aspectRatio, imageSize: imageSize } : undefined;
547
+
548
+ return {
549
+ project: projectId,
550
+ model,
551
+ request: {
552
+ contents: [{ role: "user", parts }],
553
+ systemInstruction: { parts: [{ text: IMAGE_SYSTEM_INSTRUCTION }] },
554
+ generationConfig: {
555
+ responseModalities: ["Image"],
556
+ imageConfig,
557
+ candidateCount: 1,
558
+ },
559
+ safetySettings: [
560
+ { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_ONLY_HIGH" },
561
+ { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_ONLY_HIGH" },
562
+ { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_ONLY_HIGH" },
563
+ { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_ONLY_HIGH" },
564
+ { category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_ONLY_HIGH" },
565
+ ],
566
+ },
567
+ requestType: "agent",
568
+ requestId: `agent-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
569
+ userAgent: "antigravity",
570
+ };
571
+ }
572
+
573
+ interface AntigravitySseResult {
574
+ images: InlineImageData[];
575
+ text: string[];
576
+ usage?: GeminiUsageMetadata;
577
+ }
578
+
579
+ const _prefix = Buffer.from("data: ", "utf-8");
580
+
581
+ async function parseAntigravitySseForImage(response: Response, signal?: AbortSignal): Promise<AntigravitySseResult> {
582
+ if (!response.body) {
583
+ throw new Error("No response body");
584
+ }
585
+
586
+ const textParts: string[] = [];
587
+ const images: InlineImageData[] = [];
588
+ let usage: GeminiUsageMetadata | undefined;
589
+
590
+ for await (const chunk of readSseJson<AntigravityResponseChunk>(response.body, signal)) {
591
+ const responseData = chunk.response;
592
+ if (!responseData) continue;
593
+ if (!responseData.candidates) continue;
594
+ for (const candidate of responseData.candidates) {
595
+ const parts = candidate.content?.parts;
596
+ if (!parts) continue;
597
+ for (const part of parts) {
598
+ if (part.text) {
599
+ textParts.push(part.text);
600
+ }
601
+ const inlineData = part.inlineData;
602
+ if (inlineData?.data && inlineData.mimeType) {
603
+ images.push({ data: inlineData.data, mimeType: inlineData.mimeType });
604
+ }
605
+ }
606
+ }
607
+ if (responseData.usageMetadata) {
608
+ usage = responseData.usageMetadata;
609
+ }
610
+ }
611
+
612
+ return { images, text: textParts, usage };
613
+ }
614
+
615
+ export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageToolDetails> = {
616
+ name: "generate_image",
617
+ label: "GenerateImage",
618
+ description: renderPromptTemplate(geminiImageDescription),
619
+ parameters: geminiImageSchema,
620
+ async execute(_toolCallId, params, _onUpdate, ctx, signal) {
621
+ return untilAborted(signal, async () => {
622
+ const apiKey = await findImageApiKey(ctx.modelRegistry);
623
+ if (!apiKey) {
624
+ throw new Error(
625
+ "No image API credentials found. Login with google-antigravity, or set OPENROUTER_API_KEY, GEMINI_API_KEY, or GOOGLE_API_KEY.",
626
+ );
627
+ }
628
+
629
+ const provider = apiKey.provider;
630
+ const model =
631
+ provider === "antigravity"
632
+ ? DEFAULT_ANTIGRAVITY_MODEL
633
+ : provider === "openrouter"
634
+ ? DEFAULT_OPENROUTER_MODEL
635
+ : DEFAULT_MODEL;
636
+ const resolvedModel = provider === "openrouter" ? resolveOpenRouterModel(model) : model;
637
+ const cwd = ctx.sessionManager.getCwd();
638
+
639
+ const resolvedImages: InlineImageData[] = [];
640
+ if (params.input_images?.length) {
641
+ for (const input of params.input_images) {
642
+ resolvedImages.push(await resolveInputImage(input, cwd));
643
+ }
644
+ }
645
+
646
+ const { timeout: rawTimeout = DEFAULT_TIMEOUT_SECONDS } = params;
647
+ // Clamp to reasonable range: 1s - 600s (10 min)
648
+ const timeoutSeconds = Math.max(1, Math.min(600, rawTimeout));
649
+ const requestSignal = ptree.combineSignals(signal, timeoutSeconds * 1000);
650
+
651
+ if (provider === "antigravity") {
652
+ if (!apiKey.projectId) {
653
+ throw new Error("Missing projectId in antigravity credentials");
654
+ }
655
+
656
+ const prompt = assemblePrompt(params);
657
+ const requestBody = buildAntigravityRequest(
658
+ prompt,
659
+ model,
660
+ apiKey.projectId,
661
+ params.aspect_ratio,
662
+ params.image_size,
663
+ resolvedImages,
664
+ );
665
+
666
+ const response = await fetch(`${ANTIGRAVITY_ENDPOINT}/v1internal:streamGenerateContent?alt=sse`, {
667
+ method: "POST",
668
+ headers: {
669
+ Authorization: `Bearer ${apiKey.apiKey}`,
670
+ "Content-Type": "application/json",
671
+ Accept: "text/event-stream",
672
+ ...getAntigravityHeaders(),
673
+ },
674
+ body: JSON.stringify(requestBody),
675
+ signal: requestSignal,
676
+ });
677
+
678
+ if (!response.ok) {
679
+ const errorText = await response.text();
680
+ let message = errorText;
681
+ try {
682
+ const parsed = JSON.parse(errorText) as { error?: { message?: string } };
683
+ message = parsed.error?.message ?? message;
684
+ } catch {
685
+ // Keep raw text.
686
+ }
687
+ throw new Error(`Antigravity image request failed (${response.status}): ${message}`);
688
+ }
689
+
690
+ const parsed = await parseAntigravitySseForImage(response, requestSignal);
691
+ const responseText = parsed.text.length > 0 ? parsed.text.join(" ") : undefined;
692
+
693
+ if (parsed.images.length === 0) {
694
+ const messageText = responseText ? `\n\n${responseText}` : "";
695
+ return {
696
+ content: [{ type: "text", text: `No image data returned.${messageText}` }],
697
+ details: {
698
+ provider,
699
+ model,
700
+ imageCount: 0,
701
+ imagePaths: [],
702
+ images: [],
703
+ responseText,
704
+ usage: parsed.usage,
705
+ },
706
+ };
707
+ }
708
+
709
+ const imagePaths = await saveImagesToTemp(parsed.images);
710
+
711
+ return {
712
+ content: [{ type: "text", text: buildResponseSummary(provider, model, imagePaths, responseText) }],
713
+ details: {
714
+ provider,
715
+ model,
716
+ imageCount: parsed.images.length,
717
+ imagePaths,
718
+ images: parsed.images,
719
+ responseText,
720
+ usage: parsed.usage,
721
+ },
722
+ };
723
+ }
724
+
725
+ if (provider === "openrouter") {
726
+ const prompt = assemblePrompt(params);
727
+ const contentParts: OpenRouterContentPart[] = [{ type: "text", text: prompt }];
728
+ for (const image of resolvedImages) {
729
+ contentParts.push({ type: "image_url", image_url: { url: toDataUrl(image) } });
730
+ }
731
+
732
+ const requestBody = {
733
+ model: resolvedModel,
734
+ messages: [{ role: "user" as const, content: contentParts }],
735
+ };
736
+
737
+ const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
738
+ method: "POST",
739
+ headers: {
740
+ "Content-Type": "application/json",
741
+ Authorization: `Bearer ${apiKey.apiKey}`,
742
+ },
743
+ body: JSON.stringify(requestBody),
744
+ signal: requestSignal,
745
+ });
746
+
747
+ const rawText = await response.text();
748
+ if (!response.ok) {
749
+ let message = rawText;
750
+ try {
751
+ const parsed = JSON.parse(rawText) as { error?: { message?: string } };
752
+ message = parsed.error?.message ?? message;
753
+ } catch {
754
+ // Keep raw text.
755
+ }
756
+ throw new Error(`OpenRouter image request failed (${response.status}): ${message}`);
757
+ }
758
+
759
+ const data = JSON.parse(rawText) as OpenRouterResponse;
760
+ const message = data.choices?.[0]?.message;
761
+ const responseText = collectOpenRouterResponseText(message);
762
+ const imageUrls = extractOpenRouterImageUrls(message);
763
+ const inlineImages: InlineImageData[] = [];
764
+ for (const imageUrl of imageUrls) {
765
+ inlineImages.push(await loadImageFromUrl(imageUrl, requestSignal));
766
+ }
767
+
768
+ if (inlineImages.length === 0) {
769
+ const messageText = responseText ? `\n\n${responseText}` : "";
770
+ return {
771
+ content: [{ type: "text", text: `No image data returned.${messageText}` }],
772
+ details: {
773
+ provider,
774
+ model: resolvedModel,
775
+ imageCount: 0,
776
+ imagePaths: [],
777
+ images: [],
778
+ responseText,
779
+ },
780
+ };
781
+ }
782
+
783
+ const imagePaths = await saveImagesToTemp(inlineImages);
784
+
785
+ return {
786
+ content: [
787
+ { type: "text", text: buildResponseSummary(provider, resolvedModel, imagePaths, responseText) },
788
+ ],
789
+ details: {
790
+ provider,
791
+ model: resolvedModel,
792
+ imageCount: inlineImages.length,
793
+ imagePaths,
794
+ images: inlineImages,
795
+ responseText,
796
+ },
797
+ };
798
+ }
799
+
800
+ const parts = [] as Array<{ text?: string; inlineData?: InlineImageData }>;
801
+ for (const image of resolvedImages) {
802
+ parts.push({ inlineData: image });
803
+ }
804
+ parts.push({ text: assemblePrompt(params) });
805
+
806
+ const generationConfig: {
807
+ responseModalities: GeminiResponseModality[];
808
+ imageConfig?: { aspectRatio?: string; imageSize?: string };
809
+ } = {
810
+ responseModalities: ["Image"],
811
+ };
812
+
813
+ if (params.aspect_ratio || params.image_size) {
814
+ generationConfig.imageConfig = {
815
+ aspectRatio: params.aspect_ratio,
816
+ imageSize: params.image_size,
817
+ };
818
+ }
819
+
820
+ const requestBody = {
821
+ contents: [{ role: "user" as const, parts }],
822
+ generationConfig,
823
+ };
824
+
825
+ const response = await fetch(
826
+ `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:generateContent`,
827
+ {
828
+ method: "POST",
829
+ headers: {
830
+ "Content-Type": "application/json",
831
+ "x-goog-api-key": apiKey.apiKey,
832
+ },
833
+ body: JSON.stringify(requestBody),
834
+ signal: requestSignal,
835
+ },
836
+ );
837
+
838
+ const rawText = await response.text();
839
+ if (!response.ok) {
840
+ let message = rawText;
841
+ try {
842
+ const parsed = JSON.parse(rawText) as { error?: { message?: string } };
843
+ message = parsed.error?.message ?? message;
844
+ } catch {
845
+ // Keep raw text.
846
+ }
847
+ throw new Error(`Gemini image request failed (${response.status}): ${message}`);
848
+ }
849
+
850
+ const data = JSON.parse(rawText) as GeminiGenerateContentResponse;
851
+ const responseParts = combineParts(data);
852
+ const responseText = collectResponseText(responseParts);
853
+ const inlineImages = collectInlineImages(responseParts);
854
+
855
+ if (inlineImages.length === 0) {
856
+ const blocked = data.promptFeedback?.blockReason
857
+ ? `Blocked: ${data.promptFeedback.blockReason}`
858
+ : "No image data returned.";
859
+ return {
860
+ content: [{ type: "text", text: `${blocked}${responseText ? `\n\n${responseText}` : ""}` }],
861
+ details: {
862
+ provider,
863
+ model,
864
+ imageCount: 0,
865
+ imagePaths: [],
866
+ images: [],
867
+ responseText,
868
+ promptFeedback: data.promptFeedback,
869
+ usage: data.usageMetadata,
870
+ },
871
+ };
872
+ }
873
+
874
+ const imagePaths = await saveImagesToTemp(inlineImages);
875
+
876
+ return {
877
+ content: [{ type: "text", text: buildResponseSummary(provider, model, imagePaths, responseText) }],
878
+ details: {
879
+ provider,
880
+ model,
881
+ imageCount: inlineImages.length,
882
+ imagePaths,
883
+ images: inlineImages,
884
+ responseText,
885
+ promptFeedback: data.promptFeedback,
886
+ usage: data.usageMetadata,
887
+ },
888
+ };
889
+ });
890
+ },
891
+ };
892
+
893
+ export async function getGeminiImageTools(): Promise<
894
+ Array<CustomTool<typeof geminiImageSchema, GeminiImageToolDetails>>
895
+ > {
896
+ const apiKey = await findImageApiKey();
897
+ if (!apiKey) return [];
898
+ return [geminiImageTool];
899
+ }
900
+
901
+ export async function getGeminiImageToolsWithRegistry(
902
+ modelRegistry: ModelRegistry,
903
+ ): Promise<Array<CustomTool<typeof geminiImageSchema, GeminiImageToolDetails>>> {
904
+ const apiKey = await findImageApiKey(modelRegistry);
905
+ if (!apiKey) return [];
906
+ return [geminiImageTool];
907
+ }