@oh-my-pi/pi-coding-agent 7.0.0 → 8.0.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 (501) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +1 -1
  3. package/docs/hooks.md +2 -2
  4. package/docs/sdk.md +1 -1
  5. package/package.json +10 -10
  6. package/scripts/format-prompts.ts +143 -0
  7. package/scripts/generate-template.ts +1 -1
  8. package/src/cli/args.ts +3 -3
  9. package/src/cli/config-cli.ts +4 -4
  10. package/src/cli/file-processor.ts +3 -3
  11. package/src/cli/list-models.ts +2 -2
  12. package/src/cli/plugin-cli.ts +3 -3
  13. package/src/cli/session-picker.ts +2 -2
  14. package/src/cli/setup-cli.ts +2 -2
  15. package/src/cli/stats-cli.ts +1 -1
  16. package/src/cli/update-cli.ts +2 -2
  17. package/src/{core → config}/keybindings.ts +1 -1
  18. package/src/{core → config}/model-registry.ts +1 -1
  19. package/src/{core → config}/model-resolver.ts +3 -3
  20. package/src/{core → config}/prompt-templates.ts +2 -2
  21. package/src/{core → config}/settings-manager.ts +6 -6
  22. package/src/{core/cursor/exec-bridge.ts → cursor.ts} +4 -4
  23. package/src/discovery/agents-md.ts +4 -4
  24. package/src/discovery/builtin.ts +17 -17
  25. package/src/discovery/claude.ts +12 -12
  26. package/src/discovery/cline.ts +6 -6
  27. package/src/discovery/codex.ts +21 -21
  28. package/src/discovery/cursor.ts +9 -9
  29. package/src/discovery/gemini.ts +9 -9
  30. package/src/discovery/github.ts +6 -6
  31. package/src/discovery/helpers.ts +4 -4
  32. package/src/discovery/index.ts +16 -16
  33. package/src/discovery/mcp-json.ts +4 -4
  34. package/src/discovery/ssh.ts +4 -4
  35. package/src/discovery/vscode.ts +4 -4
  36. package/src/discovery/windsurf.ts +6 -6
  37. package/src/{core/tools/exa → exa}/company.ts +2 -3
  38. package/src/{core/tools/exa → exa}/index.ts +2 -2
  39. package/src/{core/tools/exa → exa}/linkedin.ts +2 -3
  40. package/src/{core/tools/exa → exa}/mcp-client.ts +2 -2
  41. package/src/{core/tools/exa → exa}/render.ts +3 -3
  42. package/src/{core/tools/exa → exa}/researcher.ts +1 -1
  43. package/src/{core/tools/exa → exa}/search.ts +2 -10
  44. package/src/{core/tools/exa → exa}/websets.ts +1 -1
  45. package/src/{core → exec}/bash-executor.ts +22 -6
  46. package/src/{core → export}/custom-share.ts +1 -1
  47. package/src/{core/export-html → export/html}/index.ts +3 -3
  48. package/src/{core → export}/ttsr.ts +2 -2
  49. package/src/{core → extensibility}/custom-commands/bundled/review/index.ts +4 -4
  50. package/src/{core → extensibility}/custom-commands/loader.ts +3 -3
  51. package/src/{core → extensibility}/custom-commands/types.ts +1 -1
  52. package/src/{core → extensibility}/custom-tools/loader.ts +9 -9
  53. package/src/{core → extensibility}/custom-tools/types.ts +6 -6
  54. package/src/{core → extensibility}/custom-tools/wrapper.ts +1 -1
  55. package/src/{core → extensibility}/extensions/loader.ts +8 -8
  56. package/src/{core → extensibility}/extensions/runner.ts +3 -3
  57. package/src/{core → extensibility}/extensions/types.ts +15 -15
  58. package/src/{core → extensibility}/extensions/wrapper.ts +1 -1
  59. package/src/{core → extensibility}/hooks/index.ts +1 -1
  60. package/src/{core → extensibility}/hooks/loader.ts +7 -7
  61. package/src/{core → extensibility}/hooks/runner.ts +4 -4
  62. package/src/{core → extensibility}/hooks/types.ts +9 -9
  63. package/src/{core → extensibility}/plugins/doctor.ts +1 -1
  64. package/src/{core → extensibility}/plugins/installer.ts +2 -3
  65. package/src/{core → extensibility}/plugins/paths.ts +1 -1
  66. package/src/{core → extensibility}/skills.ts +8 -48
  67. package/src/{core → extensibility}/slash-commands.ts +6 -6
  68. package/src/index.ts +127 -128
  69. package/src/internal-urls/agent-protocol.ts +126 -0
  70. package/src/internal-urls/artifact-protocol.ts +93 -0
  71. package/src/internal-urls/index.ts +28 -0
  72. package/src/internal-urls/json-query.ts +126 -0
  73. package/src/internal-urls/router.ts +69 -0
  74. package/src/internal-urls/rule-protocol.ts +56 -0
  75. package/src/internal-urls/skill-protocol.ts +112 -0
  76. package/src/internal-urls/types.ts +48 -0
  77. package/src/{core/python-executor.ts → ipy/executor.ts} +51 -11
  78. package/src/{core/python-gateway-coordinator.ts → ipy/gateway-coordinator.ts} +41 -325
  79. package/src/{core/python-kernel.ts → ipy/kernel.ts} +38 -10
  80. package/src/ipy/prelude.ts +3 -0
  81. package/src/{core/tools/lsp → lsp}/client.ts +7 -6
  82. package/src/{core/tools/lsp → lsp}/clients/biome-client.ts +1 -1
  83. package/src/{core/tools/lsp → lsp}/clients/index.ts +1 -1
  84. package/src/{core/tools/lsp → lsp}/clients/lsp-linter-client.ts +4 -4
  85. package/src/{core/tools/lsp → lsp}/config.ts +1 -1
  86. package/src/{core/tools/lsp → lsp}/index.ts +16 -15
  87. package/src/{core/tools/lsp → lsp}/render.ts +2 -2
  88. package/src/{core/tools/lsp → lsp}/types.ts +14 -16
  89. package/src/{core/tools/lsp → lsp}/utils.ts +1 -1
  90. package/src/main.ts +12 -12
  91. package/src/{core/mcp → mcp}/config.ts +8 -8
  92. package/src/{core/mcp → mcp}/loader.ts +5 -6
  93. package/src/{core/mcp → mcp}/manager.ts +2 -2
  94. package/src/{core/mcp → mcp}/tool-bridge.ts +35 -6
  95. package/src/{core/mcp → mcp}/tool-cache.ts +1 -1
  96. package/src/{core/mcp → mcp}/transports/http.ts +7 -1
  97. package/src/{core/mcp → mcp}/transports/stdio.ts +1 -1
  98. package/src/{core/mcp → mcp}/types.ts +1 -1
  99. package/src/migrations.ts +2 -2
  100. package/src/modes/{interactive/components → components}/armin.ts +1 -1
  101. package/src/modes/{interactive/components → components}/assistant-message.ts +1 -1
  102. package/src/modes/{interactive/components → components}/bash-execution.ts +37 -29
  103. package/src/modes/{interactive/components → components}/bordered-loader.ts +1 -1
  104. package/src/modes/{interactive/components → components}/branch-summary-message.ts +2 -2
  105. package/src/modes/{interactive/components → components}/compaction-summary-message.ts +2 -2
  106. package/src/modes/{interactive/components → components}/custom-message.ts +3 -3
  107. package/src/modes/{interactive/components → components}/diff.ts +1 -1
  108. package/src/modes/{interactive/components → components}/dynamic-border.ts +1 -1
  109. package/src/modes/{interactive/components → components}/extensions/extension-dashboard.ts +3 -3
  110. package/src/modes/{interactive/components → components}/extensions/extension-list.ts +2 -2
  111. package/src/modes/{interactive/components → components}/extensions/inspector-panel.ts +1 -1
  112. package/src/modes/{interactive/components → components}/extensions/state-manager.ts +11 -17
  113. package/src/modes/{interactive/components → components}/extensions/types.ts +1 -1
  114. package/src/modes/{interactive/components → components}/footer.ts +3 -3
  115. package/src/modes/{interactive/components → components}/history-search.ts +2 -2
  116. package/src/modes/{interactive/components → components}/hook-editor.ts +1 -1
  117. package/src/modes/{interactive/components → components}/hook-input.ts +1 -1
  118. package/src/modes/{interactive/components → components}/hook-message.ts +3 -3
  119. package/src/modes/{interactive/components → components}/hook-selector.ts +1 -1
  120. package/src/modes/{interactive/components → components}/keybinding-hints.ts +2 -2
  121. package/src/modes/{interactive/components → components}/login-dialog.ts +1 -1
  122. package/src/modes/{interactive/components → components}/model-selector.ts +5 -5
  123. package/src/modes/{interactive/components → components}/oauth-selector.ts +2 -2
  124. package/src/modes/{interactive/components → components}/plugin-settings.ts +3 -3
  125. package/src/modes/{interactive/components → components}/python-execution.ts +35 -24
  126. package/src/modes/{interactive/components → components}/queue-mode-selector.ts +1 -1
  127. package/src/modes/{interactive/components → components}/read-tool-group.ts +2 -2
  128. package/src/modes/{interactive/components → components}/session-selector.ts +3 -3
  129. package/src/modes/{interactive/components → components}/settings-defs.ts +2 -2
  130. package/src/modes/{interactive/components → components}/settings-selector.ts +2 -2
  131. package/src/modes/{interactive/components → components}/show-images-selector.ts +1 -1
  132. package/src/modes/{interactive/components → components}/status-line/segments.ts +2 -2
  133. package/src/modes/{interactive/components → components}/status-line/separators.ts +1 -1
  134. package/src/modes/{interactive/components → components}/status-line/types.ts +2 -2
  135. package/src/modes/{interactive/components → components}/status-line-segment-editor.ts +2 -2
  136. package/src/modes/{interactive/components → components}/status-line.ts +3 -3
  137. package/src/modes/{interactive/components → components}/theme-selector.ts +1 -1
  138. package/src/modes/{interactive/components → components}/thinking-selector.ts +1 -1
  139. package/src/modes/{interactive/components → components}/todo-display.ts +3 -4
  140. package/src/modes/{interactive/components → components}/todo-reminder.ts +2 -2
  141. package/src/modes/{interactive/components → components}/tool-execution.ts +8 -8
  142. package/src/modes/{interactive/components → components}/tree-selector.ts +3 -3
  143. package/src/modes/{interactive/components → components}/ttsr-notification.ts +2 -2
  144. package/src/modes/{interactive/components → components}/user-message-selector.ts +1 -1
  145. package/src/modes/{interactive/components → components}/user-message.ts +1 -1
  146. package/src/modes/{interactive/components → components}/welcome.ts +2 -2
  147. package/src/modes/{interactive/controllers → controllers}/command-controller.ts +32 -30
  148. package/src/modes/{interactive/controllers → controllers}/event-controller.ts +9 -9
  149. package/src/modes/{interactive/controllers → controllers}/extension-ui-controller.ts +8 -8
  150. package/src/modes/{interactive/controllers → controllers}/input-controller.ts +6 -6
  151. package/src/modes/{interactive/controllers → controllers}/selector-controller.ts +16 -16
  152. package/src/modes/index.ts +1 -1
  153. package/src/modes/{interactive/interactive-mode.ts → interactive-mode.ts} +14 -14
  154. package/src/modes/print-mode.ts +1 -1
  155. package/src/modes/rpc/rpc-client.ts +3 -3
  156. package/src/modes/rpc/rpc-mode.ts +3 -3
  157. package/src/modes/rpc/rpc-types.ts +3 -3
  158. package/src/modes/{interactive/theme → theme}/theme.ts +1 -1
  159. package/src/modes/{interactive/types.ts → types.ts} +8 -9
  160. package/src/modes/{interactive/utils → utils}/ui-helpers.ts +20 -27
  161. package/src/{core/tools/patch → patch}/applicator.ts +1 -1
  162. package/src/{core/tools/patch → patch}/diff.ts +1 -1
  163. package/src/{core/tools/patch → patch}/index.ts +31 -36
  164. package/src/{core/tools/patch → patch}/shared.ts +9 -6
  165. package/src/prompts/agents/explore.md +83 -46
  166. package/src/prompts/agents/init.md +9 -4
  167. package/src/prompts/agents/plan.md +8 -7
  168. package/src/prompts/agents/reviewer.md +36 -18
  169. package/src/prompts/agents/task.md +4 -4
  170. package/src/prompts/compaction/branch-summary-preamble.md +0 -1
  171. package/src/prompts/review-request.md +0 -1
  172. package/src/prompts/system/custom-system-prompt.md +2 -14
  173. package/src/prompts/system/file-operations.md +0 -2
  174. package/src/prompts/system/system-prompt.md +147 -138
  175. package/src/prompts/system/web-search.md +26 -0
  176. package/src/prompts/tools/ask.md +31 -24
  177. package/src/prompts/tools/bash.md +20 -17
  178. package/src/prompts/tools/calculator.md +9 -5
  179. package/src/prompts/tools/fetch.md +16 -0
  180. package/src/prompts/tools/find.md +15 -5
  181. package/src/prompts/tools/gemini-image.md +21 -6
  182. package/src/prompts/tools/grep.md +28 -12
  183. package/src/prompts/tools/lsp.md +35 -14
  184. package/src/prompts/tools/patch.md +39 -41
  185. package/src/prompts/tools/python.md +59 -76
  186. package/src/prompts/tools/read.md +23 -22
  187. package/src/prompts/tools/replace.md +19 -12
  188. package/src/prompts/tools/ssh.md +21 -28
  189. package/src/prompts/tools/task.md +54 -44
  190. package/src/prompts/tools/todo-write.md +52 -163
  191. package/src/prompts/tools/web-search.md +16 -9
  192. package/src/prompts/tools/write.md +13 -2
  193. package/src/{core/sdk.ts → sdk.ts} +65 -34
  194. package/src/{core → session}/agent-session.ts +45 -37
  195. package/src/{core → session}/agent-storage.ts +2 -2
  196. package/src/session/artifacts.ts +110 -0
  197. package/src/{core → session}/auth-storage.ts +1 -1
  198. package/src/{core → session}/compaction/branch-summarization.ts +5 -5
  199. package/src/{core → session}/compaction/compaction.ts +6 -6
  200. package/src/{core → session}/compaction/utils.ts +3 -3
  201. package/src/{core → session}/history-storage.ts +1 -1
  202. package/src/{core → session}/messages.ts +6 -8
  203. package/src/{core → session}/session-manager.ts +2 -2
  204. package/src/{core → session}/storage-migration.ts +2 -2
  205. package/src/session/streaming-output.ts +177 -0
  206. package/src/{core/ssh → ssh}/connection-manager.ts +1 -1
  207. package/src/{core/ssh → ssh}/ssh-executor.ts +19 -4
  208. package/src/{core/ssh → ssh}/sshfs-mount.ts +1 -1
  209. package/src/{core/system-prompt.ts → system-prompt.ts} +8 -37
  210. package/src/{core/tools/task → task}/agents.ts +8 -8
  211. package/src/{core/tools/task → task}/commands.ts +5 -6
  212. package/src/{core/tools/task → task}/discovery.ts +3 -3
  213. package/src/{core/tools/task → task}/executor.ts +34 -44
  214. package/src/{core/tools/task → task}/index.ts +206 -50
  215. package/src/{core/tools/task → task}/render.ts +80 -23
  216. package/src/{core/tools/task → task}/subprocess-tool-registry.ts +1 -1
  217. package/src/task/template.ts +47 -0
  218. package/src/{core/tools/task → task}/types.ts +19 -27
  219. package/src/{core/tools/task → task}/worker-protocol.ts +8 -4
  220. package/src/{core/tools/task → task}/worker.ts +34 -29
  221. package/src/task/worktree.ts +166 -0
  222. package/src/{core/tools → tools}/ask.ts +13 -21
  223. package/src/{core/tools → tools}/bash-interceptor.ts +1 -1
  224. package/src/{core/tools → tools}/bash.ts +61 -63
  225. package/src/{core/tools → tools}/calculator.ts +4 -4
  226. package/src/{core/tools → tools}/complete.ts +1 -1
  227. package/src/{core/tools → tools}/context.ts +2 -2
  228. package/src/{core/tools/web-fetch.ts → tools/fetch.ts} +97 -76
  229. package/src/{core/tools → tools}/find.ts +80 -104
  230. package/src/{core/tools → tools}/gemini-image.ts +420 -29
  231. package/src/{core/tools → tools}/grep.ts +155 -164
  232. package/src/{core/tools → tools}/index.ts +63 -56
  233. package/src/tools/list-limit.ts +40 -0
  234. package/src/{core/tools → tools}/ls.ts +44 -35
  235. package/src/{core/tools → tools}/notebook.ts +3 -3
  236. package/src/tools/output-meta.ts +443 -0
  237. package/src/tools/output-utils.ts +63 -0
  238. package/src/{core/tools → tools}/python.ts +105 -89
  239. package/src/tools/read.ts +882 -0
  240. package/src/{core/tools → tools}/render-utils.ts +1 -1
  241. package/src/{core/tools → tools}/renderers.ts +8 -10
  242. package/src/{core/tools → tools}/review.ts +2 -2
  243. package/src/{core/tools → tools}/ssh.ts +56 -59
  244. package/src/{core/tools → tools}/todo-write.ts +12 -23
  245. package/src/tools/tool-errors.ts +95 -0
  246. package/src/tools/tool-result.ts +92 -0
  247. package/src/{core/tools → tools}/truncate.ts +2 -2
  248. package/src/{core/tools → tools}/write.ts +15 -13
  249. package/src/utils/changelog.ts +1 -1
  250. package/src/{core → utils}/file-mentions.ts +4 -4
  251. package/src/utils/image-convert.ts +1 -1
  252. package/src/utils/image-resize.ts +1 -1
  253. package/src/utils/shell.ts +1 -1
  254. package/src/{core → utils}/title-generator.ts +4 -4
  255. package/src/utils/tools-manager.ts +1 -1
  256. package/src/{core/tools/web-scrapers → web/scrapers}/choosealicense.ts +1 -1
  257. package/src/{core/tools/web-scrapers → web/scrapers}/twitter.ts +3 -2
  258. package/src/{core/tools/web-scrapers → web/scrapers}/types.ts +4 -2
  259. package/src/{core/tools/web-scrapers → web/scrapers}/utils.ts +1 -1
  260. package/src/{core/tools/web-scrapers → web/scrapers}/youtube.ts +14 -13
  261. package/src/{core/tools/web-search → web/search}/auth.ts +4 -4
  262. package/src/{core/tools/web-search → web/search}/index.ts +22 -71
  263. package/src/{core/tools/web-search → web/search}/providers/anthropic.ts +7 -10
  264. package/src/{core/tools/web-search → web/search}/providers/exa.ts +2 -2
  265. package/src/{core/tools/web-search → web/search}/providers/perplexity.ts +4 -16
  266. package/src/{core/tools/web-search → web/search}/render.ts +3 -3
  267. package/scripts/migrate-sessions.sh +0 -93
  268. package/src/core/index.ts +0 -56
  269. package/src/core/python-prelude.ts +0 -3
  270. package/src/core/ssh-executor.ts +0 -5
  271. package/src/core/streaming-output.ts +0 -115
  272. package/src/core/tools/output.ts +0 -519
  273. package/src/core/tools/read.ts +0 -717
  274. package/src/core/tools/task/template.ts +0 -37
  275. package/src/prompts/tools/output.md +0 -47
  276. package/src/prompts/tools/web-fetch.md +0 -9
  277. /package/src/{core/tools/exa → exa}/types.ts +0 -0
  278. /package/src/{core → exec}/exec.ts +0 -0
  279. /package/src/{core/export-html → export/html}/template.css +0 -0
  280. /package/src/{core/export-html → export/html}/template.generated.ts +0 -0
  281. /package/src/{core/export-html → export/html}/template.html +0 -0
  282. /package/src/{core/export-html → export/html}/template.js +0 -0
  283. /package/src/{core/export-html → export/html}/template.macro.ts +0 -0
  284. /package/src/{core/export-html → export/html}/vendor/highlight.min.js +0 -0
  285. /package/src/{core/export-html → export/html}/vendor/marked.min.js +0 -0
  286. /package/src/{core → extensibility}/custom-commands/index.ts +0 -0
  287. /package/src/{core → extensibility}/custom-tools/index.ts +0 -0
  288. /package/src/{core → extensibility}/extensions/index.ts +0 -0
  289. /package/src/{core → extensibility}/hooks/tool-wrapper.ts +0 -0
  290. /package/src/{core → extensibility}/plugins/index.ts +0 -0
  291. /package/src/{core → extensibility}/plugins/loader.ts +0 -0
  292. /package/src/{core → extensibility}/plugins/manager.ts +0 -0
  293. /package/src/{core → extensibility}/plugins/parser.ts +0 -0
  294. /package/src/{core → extensibility}/plugins/types.ts +0 -0
  295. /package/src/{core/python-modules.ts → ipy/modules.ts} +0 -0
  296. /package/src/{core/python-prelude.py → ipy/prelude.py} +0 -0
  297. /package/src/{core/tools/lsp → lsp}/defaults.json +0 -0
  298. /package/src/{core/tools/lsp → lsp}/edits.ts +0 -0
  299. /package/src/{core/tools/lsp → lsp}/lspmux.ts +0 -0
  300. /package/src/{core/tools/lsp → lsp}/rust-analyzer.ts +0 -0
  301. /package/src/{core/mcp → mcp}/client.ts +0 -0
  302. /package/src/{core/mcp → mcp}/index.ts +0 -0
  303. /package/src/{core/mcp → mcp}/json-rpc.ts +0 -0
  304. /package/src/{core/mcp → mcp}/transports/index.ts +0 -0
  305. /package/src/modes/{interactive/components → components}/countdown-timer.ts +0 -0
  306. /package/src/modes/{interactive/components → components}/custom-editor.ts +0 -0
  307. /package/src/modes/{interactive/components → components}/extensions/index.ts +0 -0
  308. /package/src/modes/{interactive/components → components}/index.ts +0 -0
  309. /package/src/modes/{interactive/components → components}/status-line/index.ts +0 -0
  310. /package/src/modes/{interactive/components → components}/status-line/presets.ts +0 -0
  311. /package/src/modes/{interactive/components → components}/visual-truncate.ts +0 -0
  312. /package/src/modes/{interactive/theme → theme}/dark.json +0 -0
  313. /package/src/modes/{interactive/theme → theme}/defaults/alabaster.json +0 -0
  314. /package/src/modes/{interactive/theme → theme}/defaults/amethyst.json +0 -0
  315. /package/src/modes/{interactive/theme → theme}/defaults/anthracite.json +0 -0
  316. /package/src/modes/{interactive/theme → theme}/defaults/basalt.json +0 -0
  317. /package/src/modes/{interactive/theme → theme}/defaults/birch.json +0 -0
  318. /package/src/modes/{interactive/theme → theme}/defaults/dark-abyss.json +0 -0
  319. /package/src/modes/{interactive/theme → theme}/defaults/dark-arctic.json +0 -0
  320. /package/src/modes/{interactive/theme → theme}/defaults/dark-aurora.json +0 -0
  321. /package/src/modes/{interactive/theme → theme}/defaults/dark-catppuccin.json +0 -0
  322. /package/src/modes/{interactive/theme → theme}/defaults/dark-cavern.json +0 -0
  323. /package/src/modes/{interactive/theme → theme}/defaults/dark-copper.json +0 -0
  324. /package/src/modes/{interactive/theme → theme}/defaults/dark-cosmos.json +0 -0
  325. /package/src/modes/{interactive/theme → theme}/defaults/dark-cyberpunk.json +0 -0
  326. /package/src/modes/{interactive/theme → theme}/defaults/dark-dracula.json +0 -0
  327. /package/src/modes/{interactive/theme → theme}/defaults/dark-eclipse.json +0 -0
  328. /package/src/modes/{interactive/theme → theme}/defaults/dark-ember.json +0 -0
  329. /package/src/modes/{interactive/theme → theme}/defaults/dark-equinox.json +0 -0
  330. /package/src/modes/{interactive/theme → theme}/defaults/dark-forest.json +0 -0
  331. /package/src/modes/{interactive/theme → theme}/defaults/dark-github.json +0 -0
  332. /package/src/modes/{interactive/theme → theme}/defaults/dark-gruvbox.json +0 -0
  333. /package/src/modes/{interactive/theme → theme}/defaults/dark-lavender.json +0 -0
  334. /package/src/modes/{interactive/theme → theme}/defaults/dark-lunar.json +0 -0
  335. /package/src/modes/{interactive/theme → theme}/defaults/dark-midnight.json +0 -0
  336. /package/src/modes/{interactive/theme → theme}/defaults/dark-monochrome.json +0 -0
  337. /package/src/modes/{interactive/theme → theme}/defaults/dark-monokai.json +0 -0
  338. /package/src/modes/{interactive/theme → theme}/defaults/dark-nebula.json +0 -0
  339. /package/src/modes/{interactive/theme → theme}/defaults/dark-nord.json +0 -0
  340. /package/src/modes/{interactive/theme → theme}/defaults/dark-ocean.json +0 -0
  341. /package/src/modes/{interactive/theme → theme}/defaults/dark-one.json +0 -0
  342. /package/src/modes/{interactive/theme → theme}/defaults/dark-rainforest.json +0 -0
  343. /package/src/modes/{interactive/theme → theme}/defaults/dark-reef.json +0 -0
  344. /package/src/modes/{interactive/theme → theme}/defaults/dark-retro.json +0 -0
  345. /package/src/modes/{interactive/theme → theme}/defaults/dark-rose-pine.json +0 -0
  346. /package/src/modes/{interactive/theme → theme}/defaults/dark-sakura.json +0 -0
  347. /package/src/modes/{interactive/theme → theme}/defaults/dark-slate.json +0 -0
  348. /package/src/modes/{interactive/theme → theme}/defaults/dark-solarized.json +0 -0
  349. /package/src/modes/{interactive/theme → theme}/defaults/dark-solstice.json +0 -0
  350. /package/src/modes/{interactive/theme → theme}/defaults/dark-starfall.json +0 -0
  351. /package/src/modes/{interactive/theme → theme}/defaults/dark-sunset.json +0 -0
  352. /package/src/modes/{interactive/theme → theme}/defaults/dark-swamp.json +0 -0
  353. /package/src/modes/{interactive/theme → theme}/defaults/dark-synthwave.json +0 -0
  354. /package/src/modes/{interactive/theme → theme}/defaults/dark-taiga.json +0 -0
  355. /package/src/modes/{interactive/theme → theme}/defaults/dark-terminal.json +0 -0
  356. /package/src/modes/{interactive/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  357. /package/src/modes/{interactive/theme → theme}/defaults/dark-tundra.json +0 -0
  358. /package/src/modes/{interactive/theme → theme}/defaults/dark-twilight.json +0 -0
  359. /package/src/modes/{interactive/theme → theme}/defaults/dark-volcanic.json +0 -0
  360. /package/src/modes/{interactive/theme → theme}/defaults/graphite.json +0 -0
  361. /package/src/modes/{interactive/theme → theme}/defaults/index.ts +0 -0
  362. /package/src/modes/{interactive/theme → theme}/defaults/light-arctic.json +0 -0
  363. /package/src/modes/{interactive/theme → theme}/defaults/light-aurora-day.json +0 -0
  364. /package/src/modes/{interactive/theme → theme}/defaults/light-canyon.json +0 -0
  365. /package/src/modes/{interactive/theme → theme}/defaults/light-catppuccin.json +0 -0
  366. /package/src/modes/{interactive/theme → theme}/defaults/light-cirrus.json +0 -0
  367. /package/src/modes/{interactive/theme → theme}/defaults/light-coral.json +0 -0
  368. /package/src/modes/{interactive/theme → theme}/defaults/light-cyberpunk.json +0 -0
  369. /package/src/modes/{interactive/theme → theme}/defaults/light-dawn.json +0 -0
  370. /package/src/modes/{interactive/theme → theme}/defaults/light-dunes.json +0 -0
  371. /package/src/modes/{interactive/theme → theme}/defaults/light-eucalyptus.json +0 -0
  372. /package/src/modes/{interactive/theme → theme}/defaults/light-forest.json +0 -0
  373. /package/src/modes/{interactive/theme → theme}/defaults/light-frost.json +0 -0
  374. /package/src/modes/{interactive/theme → theme}/defaults/light-github.json +0 -0
  375. /package/src/modes/{interactive/theme → theme}/defaults/light-glacier.json +0 -0
  376. /package/src/modes/{interactive/theme → theme}/defaults/light-gruvbox.json +0 -0
  377. /package/src/modes/{interactive/theme → theme}/defaults/light-haze.json +0 -0
  378. /package/src/modes/{interactive/theme → theme}/defaults/light-honeycomb.json +0 -0
  379. /package/src/modes/{interactive/theme → theme}/defaults/light-lagoon.json +0 -0
  380. /package/src/modes/{interactive/theme → theme}/defaults/light-lavender.json +0 -0
  381. /package/src/modes/{interactive/theme → theme}/defaults/light-meadow.json +0 -0
  382. /package/src/modes/{interactive/theme → theme}/defaults/light-mint.json +0 -0
  383. /package/src/modes/{interactive/theme → theme}/defaults/light-monochrome.json +0 -0
  384. /package/src/modes/{interactive/theme → theme}/defaults/light-ocean.json +0 -0
  385. /package/src/modes/{interactive/theme → theme}/defaults/light-one.json +0 -0
  386. /package/src/modes/{interactive/theme → theme}/defaults/light-opal.json +0 -0
  387. /package/src/modes/{interactive/theme → theme}/defaults/light-orchard.json +0 -0
  388. /package/src/modes/{interactive/theme → theme}/defaults/light-paper.json +0 -0
  389. /package/src/modes/{interactive/theme → theme}/defaults/light-prism.json +0 -0
  390. /package/src/modes/{interactive/theme → theme}/defaults/light-retro.json +0 -0
  391. /package/src/modes/{interactive/theme → theme}/defaults/light-sand.json +0 -0
  392. /package/src/modes/{interactive/theme → theme}/defaults/light-savanna.json +0 -0
  393. /package/src/modes/{interactive/theme → theme}/defaults/light-solarized.json +0 -0
  394. /package/src/modes/{interactive/theme → theme}/defaults/light-soleil.json +0 -0
  395. /package/src/modes/{interactive/theme → theme}/defaults/light-sunset.json +0 -0
  396. /package/src/modes/{interactive/theme → theme}/defaults/light-synthwave.json +0 -0
  397. /package/src/modes/{interactive/theme → theme}/defaults/light-tokyo-night.json +0 -0
  398. /package/src/modes/{interactive/theme → theme}/defaults/light-wetland.json +0 -0
  399. /package/src/modes/{interactive/theme → theme}/defaults/light-zenith.json +0 -0
  400. /package/src/modes/{interactive/theme → theme}/defaults/limestone.json +0 -0
  401. /package/src/modes/{interactive/theme → theme}/defaults/mahogany.json +0 -0
  402. /package/src/modes/{interactive/theme → theme}/defaults/marble.json +0 -0
  403. /package/src/modes/{interactive/theme → theme}/defaults/obsidian.json +0 -0
  404. /package/src/modes/{interactive/theme → theme}/defaults/onyx.json +0 -0
  405. /package/src/modes/{interactive/theme → theme}/defaults/pearl.json +0 -0
  406. /package/src/modes/{interactive/theme → theme}/defaults/porcelain.json +0 -0
  407. /package/src/modes/{interactive/theme → theme}/defaults/quartz.json +0 -0
  408. /package/src/modes/{interactive/theme → theme}/defaults/sandstone.json +0 -0
  409. /package/src/modes/{interactive/theme → theme}/defaults/titanium.json +0 -0
  410. /package/src/modes/{interactive/theme → theme}/light.json +0 -0
  411. /package/src/modes/{interactive/theme → theme}/theme-schema.json +0 -0
  412. /package/src/{core/tools/patch → patch}/fuzzy.ts +0 -0
  413. /package/src/{core/tools/patch → patch}/normalize.ts +0 -0
  414. /package/src/{core/tools/patch → patch}/normative.ts +0 -0
  415. /package/src/{core/tools/patch → patch}/parser.ts +0 -0
  416. /package/src/{core/tools/patch → patch}/types.ts +0 -0
  417. /package/src/{core → session}/compaction/index.ts +0 -0
  418. /package/src/{core → session}/session-storage.ts +0 -0
  419. /package/src/{core/tools/task → task}/name-generator.ts +0 -0
  420. /package/src/{core/tools/task → task}/omp-command.ts +0 -0
  421. /package/src/{core/tools/task → task}/parallel.ts +0 -0
  422. /package/src/{core/tools → tools}/jtd-to-json-schema.ts +0 -0
  423. /package/src/{core/tools → tools}/path-utils.ts +0 -0
  424. /package/src/{core → utils}/event-bus.ts +0 -0
  425. /package/src/{core → utils}/frontmatter.ts +0 -0
  426. /package/src/{core → utils}/terminal-notify.ts +0 -0
  427. /package/src/{core → utils}/timings.ts +0 -0
  428. /package/src/{core → utils}/utils.ts +0 -0
  429. /package/src/{core/tools/web-scrapers → web/scrapers}/artifacthub.ts +0 -0
  430. /package/src/{core/tools/web-scrapers → web/scrapers}/arxiv.ts +0 -0
  431. /package/src/{core/tools/web-scrapers → web/scrapers}/aur.ts +0 -0
  432. /package/src/{core/tools/web-scrapers → web/scrapers}/biorxiv.ts +0 -0
  433. /package/src/{core/tools/web-scrapers → web/scrapers}/bluesky.ts +0 -0
  434. /package/src/{core/tools/web-scrapers → web/scrapers}/brew.ts +0 -0
  435. /package/src/{core/tools/web-scrapers → web/scrapers}/cheatsh.ts +0 -0
  436. /package/src/{core/tools/web-scrapers → web/scrapers}/chocolatey.ts +0 -0
  437. /package/src/{core/tools/web-scrapers → web/scrapers}/cisa-kev.ts +0 -0
  438. /package/src/{core/tools/web-scrapers → web/scrapers}/clojars.ts +0 -0
  439. /package/src/{core/tools/web-scrapers → web/scrapers}/coingecko.ts +0 -0
  440. /package/src/{core/tools/web-scrapers → web/scrapers}/crates-io.ts +0 -0
  441. /package/src/{core/tools/web-scrapers → web/scrapers}/crossref.ts +0 -0
  442. /package/src/{core/tools/web-scrapers → web/scrapers}/devto.ts +0 -0
  443. /package/src/{core/tools/web-scrapers → web/scrapers}/discogs.ts +0 -0
  444. /package/src/{core/tools/web-scrapers → web/scrapers}/discourse.ts +0 -0
  445. /package/src/{core/tools/web-scrapers → web/scrapers}/dockerhub.ts +0 -0
  446. /package/src/{core/tools/web-scrapers → web/scrapers}/fdroid.ts +0 -0
  447. /package/src/{core/tools/web-scrapers → web/scrapers}/firefox-addons.ts +0 -0
  448. /package/src/{core/tools/web-scrapers → web/scrapers}/flathub.ts +0 -0
  449. /package/src/{core/tools/web-scrapers → web/scrapers}/github-gist.ts +0 -0
  450. /package/src/{core/tools/web-scrapers → web/scrapers}/github.ts +0 -0
  451. /package/src/{core/tools/web-scrapers → web/scrapers}/gitlab.ts +0 -0
  452. /package/src/{core/tools/web-scrapers → web/scrapers}/go-pkg.ts +0 -0
  453. /package/src/{core/tools/web-scrapers → web/scrapers}/hackage.ts +0 -0
  454. /package/src/{core/tools/web-scrapers → web/scrapers}/hackernews.ts +0 -0
  455. /package/src/{core/tools/web-scrapers → web/scrapers}/hex.ts +0 -0
  456. /package/src/{core/tools/web-scrapers → web/scrapers}/huggingface.ts +0 -0
  457. /package/src/{core/tools/web-scrapers → web/scrapers}/iacr.ts +0 -0
  458. /package/src/{core/tools/web-scrapers → web/scrapers}/index.ts +0 -0
  459. /package/src/{core/tools/web-scrapers → web/scrapers}/jetbrains-marketplace.ts +0 -0
  460. /package/src/{core/tools/web-scrapers → web/scrapers}/lemmy.ts +0 -0
  461. /package/src/{core/tools/web-scrapers → web/scrapers}/lobsters.ts +0 -0
  462. /package/src/{core/tools/web-scrapers → web/scrapers}/mastodon.ts +0 -0
  463. /package/src/{core/tools/web-scrapers → web/scrapers}/maven.ts +0 -0
  464. /package/src/{core/tools/web-scrapers → web/scrapers}/mdn.ts +0 -0
  465. /package/src/{core/tools/web-scrapers → web/scrapers}/metacpan.ts +0 -0
  466. /package/src/{core/tools/web-scrapers → web/scrapers}/musicbrainz.ts +0 -0
  467. /package/src/{core/tools/web-scrapers → web/scrapers}/npm.ts +0 -0
  468. /package/src/{core/tools/web-scrapers → web/scrapers}/nuget.ts +0 -0
  469. /package/src/{core/tools/web-scrapers → web/scrapers}/nvd.ts +0 -0
  470. /package/src/{core/tools/web-scrapers → web/scrapers}/ollama.ts +0 -0
  471. /package/src/{core/tools/web-scrapers → web/scrapers}/open-vsx.ts +0 -0
  472. /package/src/{core/tools/web-scrapers → web/scrapers}/opencorporates.ts +0 -0
  473. /package/src/{core/tools/web-scrapers → web/scrapers}/openlibrary.ts +0 -0
  474. /package/src/{core/tools/web-scrapers → web/scrapers}/orcid.ts +0 -0
  475. /package/src/{core/tools/web-scrapers → web/scrapers}/osv.ts +0 -0
  476. /package/src/{core/tools/web-scrapers → web/scrapers}/packagist.ts +0 -0
  477. /package/src/{core/tools/web-scrapers → web/scrapers}/pub-dev.ts +0 -0
  478. /package/src/{core/tools/web-scrapers → web/scrapers}/pubmed.ts +0 -0
  479. /package/src/{core/tools/web-scrapers → web/scrapers}/pypi.ts +0 -0
  480. /package/src/{core/tools/web-scrapers → web/scrapers}/rawg.ts +0 -0
  481. /package/src/{core/tools/web-scrapers → web/scrapers}/readthedocs.ts +0 -0
  482. /package/src/{core/tools/web-scrapers → web/scrapers}/reddit.ts +0 -0
  483. /package/src/{core/tools/web-scrapers → web/scrapers}/repology.ts +0 -0
  484. /package/src/{core/tools/web-scrapers → web/scrapers}/rfc.ts +0 -0
  485. /package/src/{core/tools/web-scrapers → web/scrapers}/rubygems.ts +0 -0
  486. /package/src/{core/tools/web-scrapers → web/scrapers}/searchcode.ts +0 -0
  487. /package/src/{core/tools/web-scrapers → web/scrapers}/sec-edgar.ts +0 -0
  488. /package/src/{core/tools/web-scrapers → web/scrapers}/semantic-scholar.ts +0 -0
  489. /package/src/{core/tools/web-scrapers → web/scrapers}/snapcraft.ts +0 -0
  490. /package/src/{core/tools/web-scrapers → web/scrapers}/sourcegraph.ts +0 -0
  491. /package/src/{core/tools/web-scrapers → web/scrapers}/spdx.ts +0 -0
  492. /package/src/{core/tools/web-scrapers → web/scrapers}/spotify.ts +0 -0
  493. /package/src/{core/tools/web-scrapers → web/scrapers}/stackoverflow.ts +0 -0
  494. /package/src/{core/tools/web-scrapers → web/scrapers}/terraform.ts +0 -0
  495. /package/src/{core/tools/web-scrapers → web/scrapers}/tldr.ts +0 -0
  496. /package/src/{core/tools/web-scrapers → web/scrapers}/vimeo.ts +0 -0
  497. /package/src/{core/tools/web-scrapers → web/scrapers}/vscode-marketplace.ts +0 -0
  498. /package/src/{core/tools/web-scrapers → web/scrapers}/w3c.ts +0 -0
  499. /package/src/{core/tools/web-scrapers → web/scrapers}/wikidata.ts +0 -0
  500. /package/src/{core/tools/web-scrapers → web/scrapers}/wikipedia.ts +0 -0
  501. /package/src/{core/tools/web-search → web/search}/types.ts +0 -0
@@ -4,22 +4,38 @@ import { StringEnum } from "@oh-my-pi/pi-ai";
4
4
  import { untilAborted } from "@oh-my-pi/pi-utils";
5
5
  import { type Static, Type } from "@sinclair/typebox";
6
6
  import { nanoid } from "nanoid";
7
- import geminiImageDescription from "../../prompts/tools/gemini-image.md" with { type: "text" };
8
- import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime";
9
- import type { CustomTool } from "../custom-tools/types";
10
- import { renderPromptTemplate } from "../prompt-templates";
7
+ import type { ModelRegistry } from "$c/config/model-registry";
8
+ import { renderPromptTemplate } from "$c/config/prompt-templates";
9
+ import type { CustomTool } from "$c/extensibility/custom-tools/types";
10
+ import geminiImageDescription from "$c/prompts/tools/gemini-image.md" with { type: "text" };
11
+ import { detectSupportedImageMimeTypeFromFile } from "$c/utils/mime";
12
+ import { getEnv } from "$c/web/search/auth";
11
13
  import { resolveReadPath } from "./path-utils";
12
- import { getEnv } from "./web-search/auth";
13
14
 
14
15
  const DEFAULT_MODEL = "gemini-3-pro-image-preview";
15
16
  const DEFAULT_OPENROUTER_MODEL = "google/gemini-3-pro-image-preview";
17
+ const DEFAULT_ANTIGRAVITY_MODEL = "gemini-3-pro-image";
16
18
  const DEFAULT_TIMEOUT_SECONDS = 120;
17
19
  const MAX_IMAGE_SIZE = 20 * 1024 * 1024;
18
20
 
19
- type ImageProvider = "gemini" | "openrouter";
21
+ const ANTIGRAVITY_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com";
22
+ const ANTIGRAVITY_HEADERS = {
23
+ "User-Agent": "antigravity/1.11.5 darwin/arm64",
24
+ "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
25
+ "Client-Metadata": JSON.stringify({
26
+ ideType: "IDE_UNSPECIFIED",
27
+ platform: "PLATFORM_UNSPECIFIED",
28
+ pluginType: "GEMINI",
29
+ }),
30
+ };
31
+ const IMAGE_SYSTEM_INSTRUCTION =
32
+ "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.";
33
+
34
+ type ImageProvider = "antigravity" | "gemini" | "openrouter";
20
35
  interface ImageApiKey {
21
36
  provider: ImageProvider;
22
37
  apiKey: string;
38
+ projectId?: string;
23
39
  }
24
40
 
25
41
  const responseModalitySchema = StringEnum(["Image", "Text"]);
@@ -39,12 +55,63 @@ const inputImageSchema = Type.Object(
39
55
  { additionalProperties: false },
40
56
  );
41
57
 
42
- export const geminiImageSchema = Type.Object(
58
+ const baseImageSchema = Type.Object(
43
59
  {
44
- prompt: Type.String({ description: "Text prompt for image generation or editing." }),
45
- model: Type.Optional(
60
+ subject: Type.String({
61
+ description:
62
+ "Main subject with key descriptors (e.g., 'A stoic robot barista with glowing blue optics', 'A weathered lighthouse on a rocky cliff').",
63
+ }),
64
+ action: Type.Optional(
65
+ Type.String({
66
+ description: "What the subject is doing (e.g., 'pouring latte art', 'standing against crashing waves').",
67
+ }),
68
+ ),
69
+ scene: Type.Optional(
70
+ Type.String({
71
+ description:
72
+ "Location or environment (e.g., 'in a futuristic café on Mars', 'during a violent thunderstorm at dusk').",
73
+ }),
74
+ ),
75
+ composition: Type.Optional(
76
+ Type.String({
77
+ description:
78
+ "Camera angle, framing, depth of field (e.g., 'low-angle close-up, shallow depth of field', 'wide establishing shot').",
79
+ }),
80
+ ),
81
+ lighting: Type.Optional(
82
+ Type.String({
83
+ description:
84
+ "Lighting setup and mood (e.g., 'warm rim lighting', 'golden hour backlight', 'hard noon shadows').",
85
+ }),
86
+ ),
87
+ style: Type.Optional(
88
+ Type.String({
89
+ description:
90
+ "Artistic style, mood, color grading (e.g., 'film noir mood, cinematic color grading', 'Studio Ghibli watercolor', 'photorealistic').",
91
+ }),
92
+ ),
93
+ camera: Type.Optional(
94
+ Type.String({
95
+ description:
96
+ "Lens and camera specs (e.g., 'Shot on 35mm, f/1.8', 'macro lens, extreme close-up', '85mm portrait lens').",
97
+ }),
98
+ ),
99
+ text: Type.Optional(
46
100
  Type.String({
47
- description: `Image model. Default: ${DEFAULT_MODEL} (direct Gemini) or ${DEFAULT_OPENROUTER_MODEL} (OpenRouter).`,
101
+ description:
102
+ "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').",
103
+ }),
104
+ ),
105
+ changes: Type.Optional(
106
+ Type.Array(Type.String(), {
107
+ description:
108
+ "For edits: specific changes to make (e.g., ['Change the tie to green', 'Remove the car in background']). Use with input_images.",
109
+ }),
110
+ ),
111
+ preserve: Type.Optional(
112
+ Type.String({
113
+ description:
114
+ "For edits: what to keep unchanged (e.g., 'identity, face, hairstyle, lighting'). Use with input_images and changes.",
48
115
  }),
49
116
  ),
50
117
  aspect_ratio: Type.Optional(aspectRatioSchema),
@@ -54,20 +121,54 @@ export const geminiImageSchema = Type.Object(
54
121
  description: "Optional input images for edits or variations.",
55
122
  }),
56
123
  ),
57
- timeout_seconds: Type.Optional(
58
- Type.Number({
59
- description: `Request timeout in seconds (default: ${DEFAULT_TIMEOUT_SECONDS}).`,
60
- minimum: 1,
61
- maximum: 600,
62
- }),
63
- ),
124
+ timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 120)" })),
64
125
  },
65
126
  { additionalProperties: false },
66
127
  );
67
128
 
129
+ export const geminiImageSchema = baseImageSchema;
68
130
  export type GeminiImageParams = Static<typeof geminiImageSchema>;
69
131
  export type GeminiResponseModality = Static<typeof responseModalitySchema>;
70
132
 
133
+ /**
134
+ * Assembles a structured prompt from the provided parameters.
135
+ * For generation: builds "subject, action, scene. composition. lighting. camera. style."
136
+ * For edits: appends change instructions and preserve directives.
137
+ */
138
+ function assemblePrompt(params: GeminiImageParams): string {
139
+ const parts: string[] = [];
140
+
141
+ // Core subject line: subject + action + scene
142
+ const subjectParts = [params.subject];
143
+ if (params.action) subjectParts.push(params.action);
144
+ if (params.scene) subjectParts.push(params.scene);
145
+ parts.push(subjectParts.join(", "));
146
+
147
+ // Technical details as separate sentences
148
+ if (params.composition) parts.push(params.composition);
149
+ if (params.lighting) parts.push(params.lighting);
150
+ if (params.camera) parts.push(params.camera);
151
+ if (params.style) parts.push(params.style);
152
+
153
+ // Join with periods for sentence structure
154
+ let prompt = `${parts.map((p) => p.replace(/[.!,;:]+$/, "")).join(". ")}.`;
155
+
156
+ // Text rendering specs
157
+ if (params.text) {
158
+ prompt += `\n\nText: ${params.text}`;
159
+ }
160
+
161
+ // Edit mode: changes and preserve directives
162
+ if (params.changes?.length) {
163
+ prompt += `\n\nChanges:\n${params.changes.map((c) => `- ${c}`).join("\n")}`;
164
+ if (params.preserve) {
165
+ prompt += `\n\nPreserve: ${params.preserve}`;
166
+ }
167
+ }
168
+
169
+ return prompt;
170
+ }
171
+
71
172
  interface GeminiInlineData {
72
173
  data?: string;
73
174
  mimeType?: string;
@@ -127,6 +228,39 @@ interface OpenRouterResponse {
127
228
  choices?: OpenRouterChoice[];
128
229
  }
129
230
 
231
+ interface AntigravityRequest {
232
+ project: string;
233
+ model: string;
234
+ request: {
235
+ contents: Array<{ role: "user"; parts: Array<{ text?: string; inlineData?: InlineImageData }> }>;
236
+ systemInstruction?: { parts: Array<{ text: string }> };
237
+ generationConfig?: {
238
+ responseModalities?: GeminiResponseModality[];
239
+ imageConfig?: { aspectRatio?: string; imageSize?: string };
240
+ candidateCount?: number;
241
+ };
242
+ safetySettings?: Array<{ category: string; threshold: string }>;
243
+ };
244
+ requestType?: string;
245
+ userAgent?: string;
246
+ requestId?: string;
247
+ }
248
+
249
+ interface AntigravityResponseChunk {
250
+ response?: {
251
+ candidates?: Array<{
252
+ content?: {
253
+ role: string;
254
+ parts?: Array<{
255
+ text?: string;
256
+ inlineData?: { mimeType?: string; data?: string };
257
+ }>;
258
+ };
259
+ }>;
260
+ usageMetadata?: GeminiUsageMetadata;
261
+ };
262
+ }
263
+
130
264
  interface GeminiImageToolDetails {
131
265
  provider: ImageProvider;
132
266
  model: string;
@@ -235,8 +369,44 @@ export function setPreferredImageProvider(provider: ImageProvider | "auto"): voi
235
369
  preferredImageProvider = provider;
236
370
  }
237
371
 
238
- async function findImageApiKey(): Promise<ImageApiKey | null> {
372
+ interface ParsedAntigravityCredentials {
373
+ accessToken: string;
374
+ projectId: string;
375
+ }
376
+
377
+ function parseAntigravityCredentials(raw: string): ParsedAntigravityCredentials | null {
378
+ try {
379
+ const parsed = JSON.parse(raw) as { token?: string; projectId?: string };
380
+ if (parsed.token && parsed.projectId) {
381
+ return { accessToken: parsed.token, projectId: parsed.projectId };
382
+ }
383
+ } catch {
384
+ // Invalid JSON
385
+ }
386
+ return null;
387
+ }
388
+
389
+ async function findAntigravityCredentials(modelRegistry: ModelRegistry): Promise<ImageApiKey | null> {
390
+ const apiKey = await modelRegistry.getApiKeyForProvider("google-antigravity");
391
+ if (!apiKey) return null;
392
+
393
+ const parsed = parseAntigravityCredentials(apiKey);
394
+ if (!parsed) return null;
395
+
396
+ return {
397
+ provider: "antigravity",
398
+ apiKey: parsed.accessToken,
399
+ projectId: parsed.projectId,
400
+ };
401
+ }
402
+
403
+ async function findImageApiKey(modelRegistry?: ModelRegistry): Promise<ImageApiKey | null> {
239
404
  // If a specific provider is preferred, try it first
405
+ if (preferredImageProvider === "antigravity" && modelRegistry) {
406
+ const antigravity = await findAntigravityCredentials(modelRegistry);
407
+ if (antigravity) return antigravity;
408
+ // Fall through to auto-detect if preferred provider key not found
409
+ }
240
410
  if (preferredImageProvider === "gemini") {
241
411
  const geminiKey = await getEnv("GEMINI_API_KEY");
242
412
  if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
@@ -249,7 +419,12 @@ async function findImageApiKey(): Promise<ImageApiKey | null> {
249
419
  // Fall through to auto-detect if preferred provider key not found
250
420
  }
251
421
 
252
- // Auto-detect: OpenRouter takes priority
422
+ // Auto-detect: Antigravity takes priority, then OpenRouter, then Gemini
423
+ if (modelRegistry) {
424
+ const antigravity = await findAntigravityCredentials(modelRegistry);
425
+ if (antigravity) return antigravity;
426
+ }
427
+
253
428
  const openRouterKey = await getEnv("OPENROUTER_API_KEY");
254
429
  if (openRouterKey) return { provider: "openrouter", apiKey: openRouterKey };
255
430
 
@@ -323,8 +498,13 @@ async function saveImagesToTemp(images: InlineImageData[]): Promise<string[]> {
323
498
  return Promise.all(images.map(saveImageToTemp));
324
499
  }
325
500
 
326
- function buildResponseSummary(model: string, imagePaths: string[], responseText: string | undefined): string {
327
- const lines = [`Model: ${model}`, `Generated ${imagePaths.length} image(s):`];
501
+ function buildResponseSummary(
502
+ provider: ImageProvider,
503
+ model: string,
504
+ imagePaths: string[],
505
+ responseText: string | undefined,
506
+ ): string {
507
+ const lines = [`Provider: ${provider}`, `Model: ${model}`, `Generated ${imagePaths.length} image(s):`];
328
508
  for (const p of imagePaths) {
329
509
  lines.push(` ${p}`);
330
510
  }
@@ -365,6 +545,121 @@ function createRequestSignal(signal: AbortSignal | undefined, timeoutSeconds: nu
365
545
  return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
366
546
  }
367
547
 
548
+ function buildAntigravityRequest(
549
+ prompt: string,
550
+ model: string,
551
+ projectId: string,
552
+ aspectRatio: string | undefined,
553
+ imageSize: string | undefined,
554
+ inputImages: InlineImageData[],
555
+ ): AntigravityRequest {
556
+ const parts: Array<{ text?: string; inlineData?: InlineImageData }> = [];
557
+ for (const image of inputImages) {
558
+ parts.push({ inlineData: image });
559
+ }
560
+ parts.push({ text: prompt });
561
+
562
+ const imageConfig = aspectRatio || imageSize ? { aspectRatio: aspectRatio, imageSize: imageSize } : undefined;
563
+
564
+ return {
565
+ project: projectId,
566
+ model,
567
+ request: {
568
+ contents: [{ role: "user", parts }],
569
+ systemInstruction: { parts: [{ text: IMAGE_SYSTEM_INSTRUCTION }] },
570
+ generationConfig: {
571
+ responseModalities: ["Image"],
572
+ imageConfig,
573
+ candidateCount: 1,
574
+ },
575
+ safetySettings: [
576
+ { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_ONLY_HIGH" },
577
+ { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_ONLY_HIGH" },
578
+ { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_ONLY_HIGH" },
579
+ { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_ONLY_HIGH" },
580
+ { category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_ONLY_HIGH" },
581
+ ],
582
+ },
583
+ requestType: "agent",
584
+ requestId: `agent-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
585
+ userAgent: "antigravity",
586
+ };
587
+ }
588
+
589
+ interface AntigravitySseResult {
590
+ images: InlineImageData[];
591
+ text: string[];
592
+ usage?: GeminiUsageMetadata;
593
+ }
594
+
595
+ async function parseAntigravitySseForImage(response: Response, signal?: AbortSignal): Promise<AntigravitySseResult> {
596
+ if (!response.body) {
597
+ throw new Error("No response body");
598
+ }
599
+
600
+ const reader = response.body.getReader();
601
+ const decoder = new TextDecoder();
602
+ let buffer = "";
603
+ const textParts: string[] = [];
604
+ const images: InlineImageData[] = [];
605
+ let usage: GeminiUsageMetadata | undefined;
606
+
607
+ try {
608
+ while (true) {
609
+ if (signal?.aborted) {
610
+ throw new Error("Request was aborted");
611
+ }
612
+
613
+ const { done, value } = await reader.read();
614
+ if (done) break;
615
+
616
+ buffer += decoder.decode(value, { stream: true });
617
+ const lines = buffer.split("\n");
618
+ buffer = lines.pop() ?? "";
619
+
620
+ for (const line of lines) {
621
+ if (!line.startsWith("data:")) continue;
622
+ const jsonStr = line.slice(5).trim();
623
+ if (!jsonStr) continue;
624
+
625
+ let chunk: AntigravityResponseChunk;
626
+ try {
627
+ chunk = JSON.parse(jsonStr);
628
+ } catch {
629
+ continue;
630
+ }
631
+
632
+ const responseData = chunk.response;
633
+ if (!responseData?.candidates) continue;
634
+
635
+ if (responseData.usageMetadata) {
636
+ usage = responseData.usageMetadata;
637
+ }
638
+
639
+ for (const candidate of responseData.candidates) {
640
+ const parts = candidate.content?.parts;
641
+ if (!parts) continue;
642
+ for (const part of parts) {
643
+ if (part.text) {
644
+ textParts.push(part.text);
645
+ }
646
+ if (part.inlineData?.data && part.inlineData?.mimeType) {
647
+ images.push({
648
+ data: part.inlineData.data,
649
+ mimeType: part.inlineData.mimeType,
650
+ });
651
+ }
652
+ }
653
+ }
654
+ }
655
+ }
656
+ } finally {
657
+ reader.releaseLock();
658
+ }
659
+
660
+ return { images, text: textParts, usage };
661
+ }
662
+
368
663
  export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageToolDetails> = {
369
664
  name: "generate_image",
370
665
  label: "GenerateImage",
@@ -372,13 +667,20 @@ export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageTo
372
667
  parameters: geminiImageSchema,
373
668
  async execute(_toolCallId, params, _onUpdate, ctx, signal) {
374
669
  return untilAborted(signal, async () => {
375
- const apiKey = await findImageApiKey();
670
+ const apiKey = await findImageApiKey(ctx.modelRegistry);
376
671
  if (!apiKey) {
377
- throw new Error("OPENROUTER_API_KEY, GEMINI_API_KEY, or GOOGLE_API_KEY not found.");
672
+ throw new Error(
673
+ "No image API credentials found. Login with google-antigravity, or set OPENROUTER_API_KEY, GEMINI_API_KEY, or GOOGLE_API_KEY.",
674
+ );
378
675
  }
379
676
 
380
677
  const provider = apiKey.provider;
381
- const model = params.model ?? (provider === "openrouter" ? DEFAULT_OPENROUTER_MODEL : DEFAULT_MODEL);
678
+ const model =
679
+ provider === "antigravity"
680
+ ? DEFAULT_ANTIGRAVITY_MODEL
681
+ : provider === "openrouter"
682
+ ? DEFAULT_OPENROUTER_MODEL
683
+ : DEFAULT_MODEL;
382
684
  const resolvedModel = provider === "openrouter" ? resolveOpenRouterModel(model) : model;
383
685
  const cwd = ctx.sessionManager.getCwd();
384
686
 
@@ -389,11 +691,90 @@ export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageTo
389
691
  }
390
692
  }
391
693
 
392
- const timeoutSeconds = params.timeout_seconds ?? DEFAULT_TIMEOUT_SECONDS;
694
+ const { timeout: rawTimeout = DEFAULT_TIMEOUT_SECONDS } = params;
695
+ // Auto-convert milliseconds to seconds if value > 1000 (16+ min is unreasonable)
696
+ let timeoutSeconds = rawTimeout > 1000 ? rawTimeout / 1000 : rawTimeout;
697
+ // Clamp to reasonable range: 1s - 600s (10 min)
698
+ timeoutSeconds = Math.max(1, Math.min(600, timeoutSeconds));
393
699
  const requestSignal = createRequestSignal(signal, timeoutSeconds);
394
700
 
701
+ if (provider === "antigravity") {
702
+ if (!apiKey.projectId) {
703
+ throw new Error("Missing projectId in antigravity credentials");
704
+ }
705
+
706
+ const prompt = assemblePrompt(params);
707
+ const requestBody = buildAntigravityRequest(
708
+ prompt,
709
+ model,
710
+ apiKey.projectId,
711
+ params.aspect_ratio,
712
+ params.image_size,
713
+ resolvedImages,
714
+ );
715
+
716
+ const response = await fetch(`${ANTIGRAVITY_ENDPOINT}/v1internal:streamGenerateContent?alt=sse`, {
717
+ method: "POST",
718
+ headers: {
719
+ Authorization: `Bearer ${apiKey.apiKey}`,
720
+ "Content-Type": "application/json",
721
+ Accept: "text/event-stream",
722
+ ...ANTIGRAVITY_HEADERS,
723
+ },
724
+ body: JSON.stringify(requestBody),
725
+ signal: requestSignal,
726
+ });
727
+
728
+ if (!response.ok) {
729
+ const errorText = await response.text();
730
+ let message = errorText;
731
+ try {
732
+ const parsed = JSON.parse(errorText) as { error?: { message?: string } };
733
+ message = parsed.error?.message ?? message;
734
+ } catch {
735
+ // Keep raw text.
736
+ }
737
+ throw new Error(`Antigravity image request failed (${response.status}): ${message}`);
738
+ }
739
+
740
+ const parsed = await parseAntigravitySseForImage(response, requestSignal);
741
+ const responseText = parsed.text.length > 0 ? parsed.text.join(" ") : undefined;
742
+
743
+ if (parsed.images.length === 0) {
744
+ const messageText = responseText ? `\n\n${responseText}` : "";
745
+ return {
746
+ content: [{ type: "text", text: `No image data returned.${messageText}` }],
747
+ details: {
748
+ provider,
749
+ model,
750
+ imageCount: 0,
751
+ imagePaths: [],
752
+ images: [],
753
+ responseText,
754
+ usage: parsed.usage,
755
+ },
756
+ };
757
+ }
758
+
759
+ const imagePaths = await saveImagesToTemp(parsed.images);
760
+
761
+ return {
762
+ content: [{ type: "text", text: buildResponseSummary(provider, model, imagePaths, responseText) }],
763
+ details: {
764
+ provider,
765
+ model,
766
+ imageCount: parsed.images.length,
767
+ imagePaths,
768
+ images: parsed.images,
769
+ responseText,
770
+ usage: parsed.usage,
771
+ },
772
+ };
773
+ }
774
+
395
775
  if (provider === "openrouter") {
396
- const contentParts: OpenRouterContentPart[] = [{ type: "text", text: params.prompt }];
776
+ const prompt = assemblePrompt(params);
777
+ const contentParts: OpenRouterContentPart[] = [{ type: "text", text: prompt }];
397
778
  for (const image of resolvedImages) {
398
779
  contentParts.push({ type: "image_url", image_url: { url: toDataUrl(image) } });
399
780
  }
@@ -452,7 +833,9 @@ export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageTo
452
833
  const imagePaths = await saveImagesToTemp(inlineImages);
453
834
 
454
835
  return {
455
- content: [{ type: "text", text: buildResponseSummary(resolvedModel, imagePaths, responseText) }],
836
+ content: [
837
+ { type: "text", text: buildResponseSummary(provider, resolvedModel, imagePaths, responseText) },
838
+ ],
456
839
  details: {
457
840
  provider,
458
841
  model: resolvedModel,
@@ -468,7 +851,7 @@ export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageTo
468
851
  for (const image of resolvedImages) {
469
852
  parts.push({ inlineData: image });
470
853
  }
471
- parts.push({ text: params.prompt });
854
+ parts.push({ text: assemblePrompt(params) });
472
855
 
473
856
  const generationConfig: {
474
857
  responseModalities: GeminiResponseModality[];
@@ -541,7 +924,7 @@ export const geminiImageTool: CustomTool<typeof geminiImageSchema, GeminiImageTo
541
924
  const imagePaths = await saveImagesToTemp(inlineImages);
542
925
 
543
926
  return {
544
- content: [{ type: "text", text: buildResponseSummary(model, imagePaths, responseText) }],
927
+ content: [{ type: "text", text: buildResponseSummary(provider, model, imagePaths, responseText) }],
545
928
  details: {
546
929
  provider,
547
930
  model,
@@ -564,3 +947,11 @@ export async function getGeminiImageTools(): Promise<
564
947
  if (!apiKey) return [];
565
948
  return [geminiImageTool];
566
949
  }
950
+
951
+ export async function getGeminiImageToolsWithRegistry(
952
+ modelRegistry: ModelRegistry,
953
+ ): Promise<Array<CustomTool<typeof geminiImageSchema, GeminiImageToolDetails>>> {
954
+ const apiKey = await findImageApiKey(modelRegistry);
955
+ if (!apiKey) return [];
956
+ return [geminiImageTool];
957
+ }