@aryee337/aery-ai 0.1.148 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. package/CHANGELOG.md +2914 -0
  2. package/README.md +614 -813
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth-broker/client.d.ts +66 -0
  5. package/dist/types/auth-broker/index.d.ts +5 -0
  6. package/dist/types/auth-broker/refresher.d.ts +25 -0
  7. package/dist/types/auth-broker/remote-store.d.ts +96 -0
  8. package/dist/types/auth-broker/server.d.ts +32 -0
  9. package/dist/types/auth-broker/types.d.ts +105 -0
  10. package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
  11. package/dist/types/auth-gateway/http.d.ts +39 -0
  12. package/dist/types/auth-gateway/index.d.ts +3 -0
  13. package/dist/types/auth-gateway/server.d.ts +36 -0
  14. package/dist/types/auth-gateway/types.d.ts +117 -0
  15. package/dist/types/auth-storage.d.ts +739 -0
  16. package/dist/types/index.d.ts +49 -0
  17. package/dist/types/model-cache.d.ts +17 -0
  18. package/dist/types/model-manager.d.ts +64 -0
  19. package/dist/types/model-thinking.d.ts +100 -0
  20. package/dist/types/models.d.ts +12 -0
  21. package/dist/types/provider-details.d.ts +24 -0
  22. package/dist/types/provider-models/bundled-references.d.ts +4 -0
  23. package/dist/types/provider-models/descriptors.d.ts +50 -0
  24. package/dist/types/provider-models/google.d.ts +24 -0
  25. package/dist/types/provider-models/index.d.ts +5 -0
  26. package/dist/types/provider-models/ollama.d.ts +7 -0
  27. package/dist/types/provider-models/openai-compat.d.ts +296 -0
  28. package/dist/types/provider-models/special.d.ts +16 -0
  29. package/dist/types/providers/aery-native-client.d.ts +13 -0
  30. package/dist/types/providers/aery-native-server.d.ts +68 -0
  31. package/dist/types/providers/amazon-bedrock.d.ts +38 -0
  32. package/dist/types/providers/anthropic-client.d.ts +99 -0
  33. package/dist/types/providers/anthropic-messages-server-schema.d.ts +465 -0
  34. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  35. package/dist/types/providers/anthropic-wire.d.ts +262 -0
  36. package/dist/types/providers/anthropic.d.ts +206 -0
  37. package/dist/types/providers/aws-credentials.d.ts +43 -0
  38. package/dist/types/providers/aws-eventstream.d.ts +38 -0
  39. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  40. package/dist/types/providers/azure-openai-responses.d.ts +15 -0
  41. package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
  42. package/dist/types/providers/cursor.d.ts +43 -0
  43. package/dist/types/providers/error-message.d.ts +27 -0
  44. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  45. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  46. package/dist/types/providers/google-auth.d.ts +24 -0
  47. package/dist/types/providers/google-gemini-cli.d.ts +81 -0
  48. package/dist/types/providers/google-gemini-headers.d.ts +18 -0
  49. package/dist/types/providers/google-shared.d.ts +171 -0
  50. package/dist/types/providers/google-types.d.ts +138 -0
  51. package/dist/types/providers/google-vertex.d.ts +7 -0
  52. package/dist/types/providers/google.d.ts +4 -0
  53. package/dist/types/providers/grammar.d.ts +1 -0
  54. package/dist/types/providers/kimi.d.ts +27 -0
  55. package/dist/types/providers/mock.d.ts +173 -0
  56. package/dist/types/providers/ollama.d.ts +6 -0
  57. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  58. package/dist/types/providers/openai-chat-server-schema.d.ts +817 -0
  59. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  60. package/dist/types/providers/openai-codex/constants.d.ts +26 -0
  61. package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
  62. package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
  63. package/dist/types/providers/openai-codex-responses.d.ts +67 -0
  64. package/dist/types/providers/openai-completions-compat.d.ts +25 -0
  65. package/dist/types/providers/openai-completions.d.ts +54 -0
  66. package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
  67. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  68. package/dist/types/providers/openai-responses-shared.d.ts +100 -0
  69. package/dist/types/providers/openai-responses.d.ts +66 -0
  70. package/dist/types/providers/register-builtins.d.ts +31 -0
  71. package/dist/types/providers/synthetic.d.ts +26 -0
  72. package/dist/{providers → types/providers}/transform-messages.d.ts +6 -2
  73. package/dist/types/providers/vision-guard.d.ts +8 -0
  74. package/dist/types/providers/xai-responses.d.ts +23 -0
  75. package/dist/types/rate-limit-utils.d.ts +19 -0
  76. package/dist/types/stream.d.ts +28 -0
  77. package/dist/types/types.d.ts +801 -0
  78. package/dist/types/usage/claude.d.ts +4 -0
  79. package/dist/types/usage/gemini.d.ts +2 -0
  80. package/dist/types/usage/github-copilot.d.ts +7 -0
  81. package/dist/types/usage/google-antigravity.d.ts +2 -0
  82. package/dist/types/usage/kimi.d.ts +2 -0
  83. package/dist/types/usage/minimax-code.d.ts +2 -0
  84. package/dist/types/usage/openai-codex.d.ts +3 -0
  85. package/dist/types/usage/shared.d.ts +1 -0
  86. package/dist/types/usage/zai.d.ts +2 -0
  87. package/dist/types/usage.d.ts +260 -0
  88. package/dist/types/utils/abort.d.ts +19 -0
  89. package/dist/types/utils/abortable-iterator.d.ts +4 -0
  90. package/dist/types/utils/anthropic-auth.d.ts +35 -0
  91. package/dist/types/utils/discovery/antigravity.d.ts +61 -0
  92. package/dist/types/utils/discovery/codex.d.ts +38 -0
  93. package/dist/types/utils/discovery/cursor.d.ts +23 -0
  94. package/dist/types/utils/discovery/gemini.d.ts +25 -0
  95. package/dist/types/utils/discovery/index.d.ts +4 -0
  96. package/dist/types/utils/discovery/openai-compatible.d.ts +72 -0
  97. package/dist/types/utils/event-stream.d.ts +28 -0
  98. package/dist/types/utils/fireworks-model-id.d.ts +10 -0
  99. package/dist/types/utils/foundry.d.ts +1 -0
  100. package/dist/types/utils/http-inspector.d.ts +31 -0
  101. package/dist/types/utils/idle-iterator.d.ts +78 -0
  102. package/dist/types/utils/json-parse.d.ts +37 -0
  103. package/dist/types/utils/oauth/__tests__/xai-oauth.test.d.ts +1 -0
  104. package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
  105. package/dist/types/utils/oauth/anthropic.d.ts +22 -0
  106. package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
  107. package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
  108. package/dist/types/utils/oauth/callback-server.d.ts +57 -0
  109. package/dist/types/utils/oauth/cerebras.d.ts +1 -0
  110. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
  111. package/dist/types/utils/oauth/cursor.d.ts +15 -0
  112. package/dist/types/utils/oauth/deepseek.d.ts +10 -0
  113. package/dist/types/utils/oauth/firepass.d.ts +1 -0
  114. package/dist/types/utils/oauth/fireworks.d.ts +1 -0
  115. package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
  116. package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
  117. package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
  118. package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
  119. package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
  120. package/dist/types/utils/oauth/huggingface.d.ts +19 -0
  121. package/dist/types/utils/oauth/index.d.ts +38 -0
  122. package/dist/types/utils/oauth/kagi.d.ts +17 -0
  123. package/dist/types/utils/oauth/kilo.d.ts +5 -0
  124. package/dist/types/utils/oauth/kimi.d.ts +21 -0
  125. package/dist/types/utils/oauth/litellm.d.ts +18 -0
  126. package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
  127. package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
  128. package/dist/types/utils/oauth/moonshot.d.ts +1 -0
  129. package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
  130. package/dist/types/utils/oauth/nvidia.d.ts +18 -0
  131. package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
  132. package/dist/types/utils/oauth/ollama.d.ts +18 -0
  133. package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
  134. package/dist/types/utils/oauth/opencode.d.ts +18 -0
  135. package/dist/types/utils/oauth/openrouter.d.ts +1 -0
  136. package/dist/types/utils/oauth/parallel.d.ts +17 -0
  137. package/dist/types/utils/oauth/perplexity.d.ts +9 -0
  138. package/dist/{utils → types/utils}/oauth/pkce.d.ts +0 -5
  139. package/dist/types/utils/oauth/qianfan.d.ts +17 -0
  140. package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
  141. package/dist/types/utils/oauth/synthetic.d.ts +1 -0
  142. package/dist/types/utils/oauth/tavily.d.ts +17 -0
  143. package/dist/types/utils/oauth/together.d.ts +1 -0
  144. package/dist/types/utils/oauth/types.d.ts +44 -0
  145. package/dist/types/utils/oauth/venice.d.ts +18 -0
  146. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
  147. package/dist/types/utils/oauth/vllm.d.ts +16 -0
  148. package/dist/types/utils/oauth/wafer.d.ts +2 -0
  149. package/dist/types/utils/oauth/xai-oauth.d.ts +60 -0
  150. package/dist/types/utils/oauth/xiaomi.d.ts +19 -0
  151. package/dist/types/utils/oauth/zai.d.ts +18 -0
  152. package/dist/types/utils/oauth/zenmux.d.ts +1 -0
  153. package/dist/types/utils/oauth/zhipu.d.ts +18 -0
  154. package/dist/{utils → types/utils}/overflow.d.ts +9 -11
  155. package/dist/types/utils/parse-bind.d.ts +23 -0
  156. package/dist/types/utils/provider-response.d.ts +3 -0
  157. package/dist/types/utils/request-debug.d.ts +29 -0
  158. package/dist/types/utils/retry-after.d.ts +3 -0
  159. package/dist/types/utils/retry.d.ts +26 -0
  160. package/dist/types/utils/schema/adapt.d.ts +24 -0
  161. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  162. package/dist/types/utils/schema/dereference.d.ts +11 -0
  163. package/dist/types/utils/schema/draft.d.ts +10 -0
  164. package/dist/types/utils/schema/equality.d.ts +4 -0
  165. package/dist/types/utils/schema/fields.d.ts +49 -0
  166. package/dist/types/utils/schema/index.d.ts +13 -0
  167. package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
  168. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  169. package/dist/types/utils/schema/normalize.d.ts +93 -0
  170. package/dist/types/utils/schema/spill.d.ts +8 -0
  171. package/dist/types/utils/schema/stamps.d.ts +25 -0
  172. package/dist/types/utils/schema/types.d.ts +4 -0
  173. package/dist/types/utils/schema/wire.d.ts +53 -0
  174. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  175. package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
  176. package/dist/types/utils/sse-debug.d.ts +10 -0
  177. package/dist/types/utils/stream-markup-healing.d.ts +80 -0
  178. package/dist/types/utils/tool-choice.d.ts +50 -0
  179. package/dist/types/utils/validation.d.ts +17 -0
  180. package/dist/types/utils.d.ts +28 -0
  181. package/package.json +139 -105
  182. package/src/api-registry.ts +96 -0
  183. package/src/auth-broker/client.ts +358 -0
  184. package/src/auth-broker/index.ts +5 -0
  185. package/src/auth-broker/refresher.ts +117 -0
  186. package/src/auth-broker/remote-store.ts +623 -0
  187. package/src/auth-broker/server.ts +644 -0
  188. package/src/auth-broker/types.ts +127 -0
  189. package/src/auth-broker/wire-schemas.ts +200 -0
  190. package/src/auth-gateway/http.ts +194 -0
  191. package/src/auth-gateway/index.ts +3 -0
  192. package/src/auth-gateway/server.ts +818 -0
  193. package/src/auth-gateway/types.ts +143 -0
  194. package/src/auth-storage.ts +4422 -0
  195. package/src/index.ts +54 -0
  196. package/src/model-cache.ts +129 -0
  197. package/src/model-manager.ts +469 -0
  198. package/src/model-thinking.ts +782 -0
  199. package/src/models.json +83530 -0
  200. package/src/models.json.d.ts +9 -0
  201. package/src/models.ts +56 -0
  202. package/src/prompts/turn-aborted-guidance.md +4 -0
  203. package/src/provider-details.ts +90 -0
  204. package/src/provider-models/bundled-references.ts +38 -0
  205. package/src/provider-models/descriptors.ts +355 -0
  206. package/src/provider-models/google.ts +88 -0
  207. package/src/provider-models/index.ts +5 -0
  208. package/src/provider-models/ollama.ts +153 -0
  209. package/src/provider-models/openai-compat.ts +2817 -0
  210. package/src/provider-models/special.ts +67 -0
  211. package/src/providers/aery-native-client.ts +228 -0
  212. package/src/providers/aery-native-server.ts +212 -0
  213. package/src/providers/amazon-bedrock.ts +873 -0
  214. package/src/providers/anthropic-client.ts +318 -0
  215. package/src/providers/anthropic-messages-server-schema.ts +243 -0
  216. package/src/providers/anthropic-messages-server.ts +683 -0
  217. package/src/providers/anthropic-wire.ts +268 -0
  218. package/src/providers/anthropic.ts +3094 -0
  219. package/src/providers/aws-credentials.ts +501 -0
  220. package/src/providers/aws-eventstream.ts +185 -0
  221. package/src/providers/aws-sigv4.ts +218 -0
  222. package/src/providers/azure-openai-responses.ts +361 -0
  223. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  224. package/src/providers/cursor/proto/agent.proto +3526 -0
  225. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  226. package/src/providers/cursor/proto/buf.yaml +17 -0
  227. package/src/providers/cursor.ts +2621 -0
  228. package/src/providers/error-message.ts +21 -0
  229. package/src/providers/github-copilot-headers.ts +140 -0
  230. package/src/providers/gitlab-duo.ts +372 -0
  231. package/src/providers/google-auth.ts +252 -0
  232. package/src/providers/google-gemini-cli.ts +809 -0
  233. package/src/providers/google-gemini-headers.ts +41 -0
  234. package/src/providers/google-shared.ts +917 -0
  235. package/src/providers/google-types.ts +167 -0
  236. package/src/providers/google-vertex.ts +91 -0
  237. package/src/providers/google.ts +41 -0
  238. package/src/providers/grammar.ts +70 -0
  239. package/src/providers/kimi.ts +52 -0
  240. package/src/providers/mock.ts +496 -0
  241. package/src/providers/ollama.ts +644 -0
  242. package/src/providers/openai-anthropic-shim.ts +138 -0
  243. package/src/providers/openai-chat-server-schema.ts +252 -0
  244. package/src/providers/openai-chat-server.ts +647 -0
  245. package/src/providers/openai-codex/constants.ts +43 -0
  246. package/src/providers/openai-codex/request-transformer.ts +161 -0
  247. package/src/providers/openai-codex/response-handler.ts +81 -0
  248. package/src/providers/openai-codex-responses.ts +3018 -0
  249. package/src/providers/openai-completions-compat.ts +300 -0
  250. package/src/providers/openai-completions.ts +1979 -0
  251. package/src/providers/openai-responses-server-schema.ts +290 -0
  252. package/src/providers/openai-responses-server.ts +1183 -0
  253. package/src/providers/openai-responses-shared.ts +873 -0
  254. package/src/providers/openai-responses.ts +679 -0
  255. package/src/providers/register-builtins.ts +436 -0
  256. package/src/providers/synthetic.ts +50 -0
  257. package/src/providers/transform-messages.ts +382 -0
  258. package/src/providers/vision-guard.ts +31 -0
  259. package/src/providers/xai-responses.ts +82 -0
  260. package/src/rate-limit-utils.ts +84 -0
  261. package/src/stream.ts +1065 -0
  262. package/src/types.ts +944 -0
  263. package/src/usage/claude.ts +482 -0
  264. package/src/usage/gemini.ts +250 -0
  265. package/src/usage/github-copilot.ts +421 -0
  266. package/src/usage/google-antigravity.ts +201 -0
  267. package/src/usage/kimi.ts +271 -0
  268. package/src/usage/minimax-code.ts +31 -0
  269. package/src/usage/openai-codex.ts +503 -0
  270. package/src/usage/shared.ts +10 -0
  271. package/src/usage/zai.ts +247 -0
  272. package/src/usage.ts +185 -0
  273. package/src/utils/abort.ts +51 -0
  274. package/src/utils/abortable-iterator.ts +69 -0
  275. package/src/utils/anthropic-auth.ts +93 -0
  276. package/src/utils/discovery/antigravity.ts +261 -0
  277. package/src/utils/discovery/codex.ts +371 -0
  278. package/src/utils/discovery/cursor.ts +306 -0
  279. package/src/utils/discovery/gemini.ts +248 -0
  280. package/src/utils/discovery/index.ts +4 -0
  281. package/src/utils/discovery/openai-compatible.ts +224 -0
  282. package/src/utils/event-stream.ts +142 -0
  283. package/src/utils/fireworks-model-id.ts +30 -0
  284. package/src/utils/foundry.ts +8 -0
  285. package/src/utils/http-inspector.ts +176 -0
  286. package/src/utils/idle-iterator.ts +267 -0
  287. package/src/utils/json-parse.ts +182 -0
  288. package/src/utils/oauth/__tests__/xai-oauth.test.ts +107 -0
  289. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  290. package/src/utils/oauth/anthropic.ts +273 -0
  291. package/src/utils/oauth/api-key-login.ts +87 -0
  292. package/src/utils/oauth/api-key-validation.ts +92 -0
  293. package/src/utils/oauth/callback-server.ts +276 -0
  294. package/src/utils/oauth/cerebras.ts +16 -0
  295. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  296. package/src/utils/oauth/cursor.ts +157 -0
  297. package/src/utils/oauth/deepseek.ts +53 -0
  298. package/src/utils/oauth/firepass.ts +24 -0
  299. package/src/utils/oauth/fireworks.ts +15 -0
  300. package/src/utils/oauth/github-copilot.ts +362 -0
  301. package/src/utils/oauth/gitlab-duo.ts +123 -0
  302. package/src/utils/oauth/google-antigravity.ts +200 -0
  303. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  304. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  305. package/src/utils/oauth/huggingface.ts +62 -0
  306. package/src/utils/oauth/index.ts +484 -0
  307. package/src/utils/oauth/kagi.ts +47 -0
  308. package/src/utils/oauth/kilo.ts +87 -0
  309. package/src/utils/oauth/kimi.ts +254 -0
  310. package/src/utils/oauth/litellm.ts +47 -0
  311. package/src/utils/oauth/lm-studio.ts +38 -0
  312. package/src/utils/oauth/minimax-code.ts +78 -0
  313. package/src/utils/oauth/moonshot.ts +23 -0
  314. package/src/utils/oauth/nanogpt.ts +15 -0
  315. package/src/utils/oauth/nvidia.ts +70 -0
  316. package/src/utils/oauth/oauth.html +203 -0
  317. package/src/utils/oauth/ollama-cloud.ts +28 -0
  318. package/src/utils/oauth/ollama.ts +47 -0
  319. package/src/utils/oauth/openai-codex.ts +299 -0
  320. package/src/utils/oauth/opencode.ts +49 -0
  321. package/src/utils/oauth/openrouter.ts +20 -0
  322. package/src/utils/oauth/parallel.ts +46 -0
  323. package/src/utils/oauth/perplexity.ts +206 -0
  324. package/src/utils/oauth/pkce.ts +18 -0
  325. package/src/utils/oauth/qianfan.ts +58 -0
  326. package/src/utils/oauth/qwen-portal.ts +60 -0
  327. package/src/utils/oauth/synthetic.ts +15 -0
  328. package/src/utils/oauth/tavily.ts +46 -0
  329. package/src/utils/oauth/together.ts +16 -0
  330. package/src/utils/oauth/types.ts +99 -0
  331. package/src/utils/oauth/venice.ts +59 -0
  332. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  333. package/src/utils/oauth/vllm.ts +40 -0
  334. package/src/utils/oauth/wafer.ts +50 -0
  335. package/src/utils/oauth/xai-oauth.ts +342 -0
  336. package/src/utils/oauth/xiaomi.ts +139 -0
  337. package/src/utils/oauth/zai.ts +60 -0
  338. package/src/utils/oauth/zenmux.ts +15 -0
  339. package/src/utils/oauth/zhipu.ts +60 -0
  340. package/src/utils/overflow.ts +137 -0
  341. package/src/utils/parse-bind.ts +54 -0
  342. package/src/utils/provider-response.ts +30 -0
  343. package/src/utils/request-debug.ts +336 -0
  344. package/src/utils/retry-after.ts +110 -0
  345. package/src/utils/retry.ts +54 -0
  346. package/src/utils/schema/CONSTRAINTS.md +164 -0
  347. package/src/utils/schema/adapt.ts +36 -0
  348. package/src/utils/schema/compatibility.ts +435 -0
  349. package/src/utils/schema/dereference.ts +98 -0
  350. package/src/utils/schema/draft.ts +341 -0
  351. package/src/utils/schema/equality.ts +97 -0
  352. package/src/utils/schema/fields.ts +191 -0
  353. package/src/utils/schema/index.ts +13 -0
  354. package/src/utils/schema/json-schema-validator.ts +577 -0
  355. package/src/utils/schema/meta-validator.ts +167 -0
  356. package/src/utils/schema/normalize.ts +1588 -0
  357. package/src/utils/schema/spill.ts +43 -0
  358. package/src/utils/schema/stamps.ts +97 -0
  359. package/src/utils/schema/types.ts +10 -0
  360. package/src/utils/schema/wire.ts +293 -0
  361. package/src/utils/schema/zod-decontaminate.ts +331 -0
  362. package/src/utils/sdk-stream-timeout.ts +43 -0
  363. package/src/utils/sse-debug.ts +289 -0
  364. package/src/utils/stream-markup-healing.ts +612 -0
  365. package/src/utils/tool-choice.ts +99 -0
  366. package/src/utils/validation.ts +1024 -0
  367. package/src/utils.ts +166 -0
  368. package/dist/api-registry.d.ts +0 -20
  369. package/dist/api-registry.d.ts.map +0 -1
  370. package/dist/api-registry.js +0 -44
  371. package/dist/api-registry.js.map +0 -1
  372. package/dist/bedrock-provider.d.ts +0 -5
  373. package/dist/bedrock-provider.d.ts.map +0 -1
  374. package/dist/bedrock-provider.js +0 -6
  375. package/dist/bedrock-provider.js.map +0 -1
  376. package/dist/cli.d.ts +0 -3
  377. package/dist/cli.d.ts.map +0 -1
  378. package/dist/cli.js +0 -130
  379. package/dist/cli.js.map +0 -1
  380. package/dist/env-api-keys.d.ts +0 -18
  381. package/dist/env-api-keys.d.ts.map +0 -1
  382. package/dist/env-api-keys.js +0 -178
  383. package/dist/env-api-keys.js.map +0 -1
  384. package/dist/image-models.d.ts +0 -10
  385. package/dist/image-models.d.ts.map +0 -1
  386. package/dist/image-models.generated.d.ts +0 -440
  387. package/dist/image-models.generated.d.ts.map +0 -1
  388. package/dist/image-models.generated.js +0 -442
  389. package/dist/image-models.generated.js.map +0 -1
  390. package/dist/image-models.js +0 -23
  391. package/dist/image-models.js.map +0 -1
  392. package/dist/images-api-registry.d.ts +0 -14
  393. package/dist/images-api-registry.d.ts.map +0 -1
  394. package/dist/images-api-registry.js +0 -22
  395. package/dist/images-api-registry.js.map +0 -1
  396. package/dist/images.d.ts +0 -4
  397. package/dist/images.d.ts.map +0 -1
  398. package/dist/images.js +0 -14
  399. package/dist/images.js.map +0 -1
  400. package/dist/index.d.ts +0 -32
  401. package/dist/index.d.ts.map +0 -1
  402. package/dist/index.js +0 -20
  403. package/dist/index.js.map +0 -1
  404. package/dist/models.d.ts +0 -18
  405. package/dist/models.d.ts.map +0 -1
  406. package/dist/models.generated.d.ts +0 -17480
  407. package/dist/models.generated.d.ts.map +0 -1
  408. package/dist/models.generated.js +0 -16339
  409. package/dist/models.generated.js.map +0 -1
  410. package/dist/models.js +0 -71
  411. package/dist/models.js.map +0 -1
  412. package/dist/oauth.d.ts +0 -2
  413. package/dist/oauth.d.ts.map +0 -1
  414. package/dist/oauth.js +0 -2
  415. package/dist/oauth.js.map +0 -1
  416. package/dist/providers/aery-error-formatting.d.ts +0 -13
  417. package/dist/providers/aery-error-formatting.d.ts.map +0 -1
  418. package/dist/providers/aery-error-formatting.js +0 -112
  419. package/dist/providers/aery-error-formatting.js.map +0 -1
  420. package/dist/providers/amazon-bedrock.d.ts +0 -38
  421. package/dist/providers/amazon-bedrock.d.ts.map +0 -1
  422. package/dist/providers/amazon-bedrock.js +0 -763
  423. package/dist/providers/amazon-bedrock.js.map +0 -1
  424. package/dist/providers/anthropic.d.ts +0 -71
  425. package/dist/providers/anthropic.d.ts.map +0 -1
  426. package/dist/providers/anthropic.js +0 -949
  427. package/dist/providers/anthropic.js.map +0 -1
  428. package/dist/providers/azure-openai-responses.d.ts +0 -15
  429. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  430. package/dist/providers/azure-openai-responses.js +0 -225
  431. package/dist/providers/azure-openai-responses.js.map +0 -1
  432. package/dist/providers/cloudflare.d.ts +0 -13
  433. package/dist/providers/cloudflare.d.ts.map +0 -1
  434. package/dist/providers/cloudflare.js +0 -26
  435. package/dist/providers/cloudflare.js.map +0 -1
  436. package/dist/providers/faux.d.ts +0 -56
  437. package/dist/providers/faux.d.ts.map +0 -1
  438. package/dist/providers/faux.js +0 -368
  439. package/dist/providers/faux.js.map +0 -1
  440. package/dist/providers/github-copilot-headers.d.ts +0 -8
  441. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  442. package/dist/providers/github-copilot-headers.js +0 -29
  443. package/dist/providers/github-copilot-headers.js.map +0 -1
  444. package/dist/providers/google-gemini-cli.d.ts +0 -74
  445. package/dist/providers/google-gemini-cli.d.ts.map +0 -1
  446. package/dist/providers/google-gemini-cli.js +0 -779
  447. package/dist/providers/google-gemini-cli.js.map +0 -1
  448. package/dist/providers/google-shared.d.ts +0 -70
  449. package/dist/providers/google-shared.d.ts.map +0 -1
  450. package/dist/providers/google-shared.js +0 -329
  451. package/dist/providers/google-shared.js.map +0 -1
  452. package/dist/providers/google-vertex.d.ts +0 -15
  453. package/dist/providers/google-vertex.d.ts.map +0 -1
  454. package/dist/providers/google-vertex.js +0 -442
  455. package/dist/providers/google-vertex.js.map +0 -1
  456. package/dist/providers/google.d.ts +0 -13
  457. package/dist/providers/google.d.ts.map +0 -1
  458. package/dist/providers/google.js +0 -400
  459. package/dist/providers/google.js.map +0 -1
  460. package/dist/providers/images/openrouter.d.ts +0 -3
  461. package/dist/providers/images/openrouter.d.ts.map +0 -1
  462. package/dist/providers/images/openrouter.js +0 -129
  463. package/dist/providers/images/openrouter.js.map +0 -1
  464. package/dist/providers/images/register-builtins.d.ts +0 -4
  465. package/dist/providers/images/register-builtins.d.ts.map +0 -1
  466. package/dist/providers/images/register-builtins.js +0 -34
  467. package/dist/providers/images/register-builtins.js.map +0 -1
  468. package/dist/providers/mistral.d.ts +0 -25
  469. package/dist/providers/mistral.d.ts.map +0 -1
  470. package/dist/providers/mistral.js +0 -535
  471. package/dist/providers/mistral.js.map +0 -1
  472. package/dist/providers/openai-codex-responses.d.ts +0 -30
  473. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  474. package/dist/providers/openai-codex-responses.js +0 -1090
  475. package/dist/providers/openai-codex-responses.js.map +0 -1
  476. package/dist/providers/openai-completions.d.ts +0 -19
  477. package/dist/providers/openai-completions.d.ts.map +0 -1
  478. package/dist/providers/openai-completions.js +0 -950
  479. package/dist/providers/openai-completions.js.map +0 -1
  480. package/dist/providers/openai-prompt-cache.d.ts +0 -3
  481. package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
  482. package/dist/providers/openai-prompt-cache.js +0 -10
  483. package/dist/providers/openai-prompt-cache.js.map +0 -1
  484. package/dist/providers/openai-responses-shared.d.ts +0 -18
  485. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  486. package/dist/providers/openai-responses-shared.js +0 -492
  487. package/dist/providers/openai-responses-shared.js.map +0 -1
  488. package/dist/providers/openai-responses.d.ts +0 -13
  489. package/dist/providers/openai-responses.d.ts.map +0 -1
  490. package/dist/providers/openai-responses.js +0 -237
  491. package/dist/providers/openai-responses.js.map +0 -1
  492. package/dist/providers/register-builtins.d.ts +0 -38
  493. package/dist/providers/register-builtins.d.ts.map +0 -1
  494. package/dist/providers/register-builtins.js +0 -278
  495. package/dist/providers/register-builtins.js.map +0 -1
  496. package/dist/providers/simple-options.d.ts +0 -8
  497. package/dist/providers/simple-options.d.ts.map +0 -1
  498. package/dist/providers/simple-options.js +0 -41
  499. package/dist/providers/simple-options.js.map +0 -1
  500. package/dist/providers/transform-messages.d.ts.map +0 -1
  501. package/dist/providers/transform-messages.js +0 -184
  502. package/dist/providers/transform-messages.js.map +0 -1
  503. package/dist/session-resources.d.ts +0 -4
  504. package/dist/session-resources.d.ts.map +0 -1
  505. package/dist/session-resources.js +0 -22
  506. package/dist/session-resources.js.map +0 -1
  507. package/dist/stream.d.ts +0 -8
  508. package/dist/stream.d.ts.map +0 -1
  509. package/dist/stream.js +0 -27
  510. package/dist/stream.js.map +0 -1
  511. package/dist/types.d.ts +0 -498
  512. package/dist/types.d.ts.map +0 -1
  513. package/dist/types.js +0 -2
  514. package/dist/types.js.map +0 -1
  515. package/dist/utils/diagnostics.d.ts +0 -19
  516. package/dist/utils/diagnostics.d.ts.map +0 -1
  517. package/dist/utils/diagnostics.js +0 -25
  518. package/dist/utils/diagnostics.js.map +0 -1
  519. package/dist/utils/event-stream.d.ts +0 -21
  520. package/dist/utils/event-stream.d.ts.map +0 -1
  521. package/dist/utils/event-stream.js +0 -81
  522. package/dist/utils/event-stream.js.map +0 -1
  523. package/dist/utils/hash.d.ts +0 -3
  524. package/dist/utils/hash.d.ts.map +0 -1
  525. package/dist/utils/hash.js +0 -14
  526. package/dist/utils/hash.js.map +0 -1
  527. package/dist/utils/headers.d.ts +0 -2
  528. package/dist/utils/headers.d.ts.map +0 -1
  529. package/dist/utils/headers.js +0 -8
  530. package/dist/utils/headers.js.map +0 -1
  531. package/dist/utils/json-parse.d.ts +0 -16
  532. package/dist/utils/json-parse.d.ts.map +0 -1
  533. package/dist/utils/json-parse.js +0 -113
  534. package/dist/utils/json-parse.js.map +0 -1
  535. package/dist/utils/node-http-proxy.d.ts +0 -10
  536. package/dist/utils/node-http-proxy.d.ts.map +0 -1
  537. package/dist/utils/node-http-proxy.js +0 -97
  538. package/dist/utils/node-http-proxy.js.map +0 -1
  539. package/dist/utils/oauth/anthropic.d.ts +0 -25
  540. package/dist/utils/oauth/anthropic.d.ts.map +0 -1
  541. package/dist/utils/oauth/anthropic.js +0 -335
  542. package/dist/utils/oauth/anthropic.js.map +0 -1
  543. package/dist/utils/oauth/device-code.d.ts +0 -19
  544. package/dist/utils/oauth/device-code.d.ts.map +0 -1
  545. package/dist/utils/oauth/device-code.js +0 -55
  546. package/dist/utils/oauth/device-code.js.map +0 -1
  547. package/dist/utils/oauth/github-copilot.d.ts +0 -30
  548. package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
  549. package/dist/utils/oauth/github-copilot.js +0 -268
  550. package/dist/utils/oauth/github-copilot.js.map +0 -1
  551. package/dist/utils/oauth/google-antigravity.d.ts +0 -26
  552. package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
  553. package/dist/utils/oauth/google-antigravity.js +0 -377
  554. package/dist/utils/oauth/google-antigravity.js.map +0 -1
  555. package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
  556. package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
  557. package/dist/utils/oauth/google-gemini-cli.js +0 -482
  558. package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
  559. package/dist/utils/oauth/index.d.ts +0 -63
  560. package/dist/utils/oauth/index.d.ts.map +0 -1
  561. package/dist/utils/oauth/index.js +0 -131
  562. package/dist/utils/oauth/index.js.map +0 -1
  563. package/dist/utils/oauth/oauth-page.d.ts +0 -3
  564. package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
  565. package/dist/utils/oauth/oauth-page.js +0 -105
  566. package/dist/utils/oauth/oauth-page.js.map +0 -1
  567. package/dist/utils/oauth/openai-codex.d.ts +0 -34
  568. package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
  569. package/dist/utils/oauth/openai-codex.js +0 -385
  570. package/dist/utils/oauth/openai-codex.js.map +0 -1
  571. package/dist/utils/oauth/pkce.d.ts.map +0 -1
  572. package/dist/utils/oauth/pkce.js +0 -31
  573. package/dist/utils/oauth/pkce.js.map +0 -1
  574. package/dist/utils/oauth/types.d.ts +0 -64
  575. package/dist/utils/oauth/types.d.ts.map +0 -1
  576. package/dist/utils/oauth/types.js +0 -2
  577. package/dist/utils/oauth/types.js.map +0 -1
  578. package/dist/utils/overflow.d.ts.map +0 -1
  579. package/dist/utils/overflow.js +0 -151
  580. package/dist/utils/overflow.js.map +0 -1
  581. package/dist/utils/sanitize-unicode.d.ts +0 -22
  582. package/dist/utils/sanitize-unicode.d.ts.map +0 -1
  583. package/dist/utils/sanitize-unicode.js +0 -26
  584. package/dist/utils/sanitize-unicode.js.map +0 -1
  585. package/dist/utils/typebox-helpers.d.ts +0 -17
  586. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  587. package/dist/utils/typebox-helpers.js +0 -21
  588. package/dist/utils/typebox-helpers.js.map +0 -1
  589. package/dist/utils/validation.d.ts +0 -18
  590. package/dist/utils/validation.d.ts.map +0 -1
  591. package/dist/utils/validation.js +0 -281
  592. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,917 @@
1
+ /**
2
+ * Shared utilities for Google Generative AI and Google Cloud Code Assist providers.
3
+ */
4
+
5
+ import { extractHttpStatusFromError, readSseJson } from "@aryee337/aery-utils";
6
+ import { calculateCost } from "../models";
7
+ import type {
8
+ Api,
9
+ AssistantMessage,
10
+ Context,
11
+ FetchImpl,
12
+ ImageContent,
13
+ Model,
14
+ StopReason,
15
+ StreamOptions,
16
+ TextContent,
17
+ ThinkingContent,
18
+ Tool,
19
+ ToolCall,
20
+ } from "../types";
21
+ import { normalizeSystemPrompts } from "../utils";
22
+ import { AssistantMessageEventStream } from "../utils/event-stream";
23
+ import { finalizeErrorMessage, type RawHttpRequestDump, withHttpStatus } from "../utils/http-inspector";
24
+ import { normalizeSchemaForCCA, normalizeSchemaForGoogle, toolWireSchema } from "../utils/schema";
25
+ import type {
26
+ Content,
27
+ FinishReason,
28
+ FunctionCallingConfigMode,
29
+ GenerateContentConfig,
30
+ GenerateContentParameters,
31
+ GenerateContentResponse,
32
+ Part,
33
+ ThinkingConfig,
34
+ ThinkingLevel,
35
+ } from "./google-types";
36
+ import { transformMessages } from "./transform-messages";
37
+ import { NON_VISION_IMAGE_PLACEHOLDER } from "./vision-guard";
38
+
39
+ export type {
40
+ Content,
41
+ FunctionCallingConfigMode,
42
+ GenerateContentParameters,
43
+ GenerateContentResponse,
44
+ ThinkingConfig,
45
+ } from "./google-types";
46
+ export { normalizeSchemaForGoogle };
47
+
48
+ type GoogleApiType = "google-generative-ai" | "google-gemini-cli" | "google-vertex";
49
+
50
+ /**
51
+ * Thinking level for Gemini 3 models. Mirrors Google's `ThinkingLevel` enum values.
52
+ * Defined here (not in any specific provider) so all Google providers can reference it
53
+ * without inducing a circular dependency.
54
+ */
55
+ export type GoogleThinkingLevel = "THINKING_LEVEL_UNSPECIFIED" | "MINIMAL" | "LOW" | "MEDIUM" | "HIGH";
56
+
57
+ /**
58
+ * Sampling/thinking options shared by `streamGoogle` and `streamGoogleVertex`.
59
+ * `google-gemini-cli` uses a different transport and request shape — do not extend this for it.
60
+ */
61
+ export interface GoogleSharedStreamOptions extends StreamOptions {
62
+ /**
63
+ * Tool selection mode. String forms map directly to Gemini
64
+ * `FunctionCallingConfigMode`. The object form forces a single named tool
65
+ * — `mode: "ANY"` is wire-required when `allowedFunctionNames` is set.
66
+ */
67
+ toolChoice?: "auto" | "none" | "any" | { mode: "ANY"; allowedFunctionNames: [string, ...string[]] };
68
+ thinking?: {
69
+ enabled: boolean;
70
+ budgetTokens?: number;
71
+ level?: GoogleThinkingLevel;
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Determines whether a streamed Gemini `Part` should be treated as "thinking".
77
+ *
78
+ * Protocol note (Gemini / Vertex AI thought signatures):
79
+ * - `thought: true` is the definitive marker for thinking content (thought summaries).
80
+ * - `thoughtSignature` is an encrypted representation of the model's internal thought process
81
+ * used to preserve reasoning context across multi-turn interactions.
82
+ * - `thoughtSignature` can appear on ANY part type (text, functionCall, etc.) - it does NOT
83
+ * indicate the part itself is thinking content.
84
+ * - For non-functionCall responses, the signature appears on the last part for context replay.
85
+ * - When persisting/replaying model outputs, signature-bearing parts must be preserved as-is;
86
+ * do not merge/move signatures across parts.
87
+ *
88
+ * See: https://ai.google.dev/gemini-api/docs/thought-signatures
89
+ */
90
+ export function isThinkingPart(part: Pick<Part, "thought" | "thoughtSignature">): boolean {
91
+ return part.thought === true;
92
+ }
93
+
94
+ /**
95
+ * Retain thought signatures during streaming.
96
+ *
97
+ * Some backends only send `thoughtSignature` on the first delta for a given part/block; later deltas may omit it.
98
+ * This helper preserves the last non-empty signature for the current block.
99
+ *
100
+ * Note: this does NOT merge or move signatures across distinct response parts. It only prevents
101
+ * a signature from being overwritten with `undefined` within the same streamed block.
102
+ */
103
+ export function retainThoughtSignature(existing: string | undefined, incoming: string | undefined): string | undefined {
104
+ if (typeof incoming === "string" && incoming.length > 0) return incoming;
105
+ return existing;
106
+ }
107
+
108
+ // Thought signatures must be base64 for Google APIs (TYPE_BYTES).
109
+ const base64SignaturePattern = /^[A-Za-z0-9+/]+={0,2}$/;
110
+
111
+ const SKIP_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
112
+
113
+ function isValidThoughtSignature(signature: string | undefined): boolean {
114
+ if (!signature) return false;
115
+ if (signature.length % 4 !== 0) return false;
116
+ return base64SignaturePattern.test(signature);
117
+ }
118
+
119
+ /**
120
+ * Only keep signatures from the same provider/model and with valid base64.
121
+ */
122
+ function resolveThoughtSignature(isSameProviderAndModel: boolean, signature: string | undefined): string | undefined {
123
+ return isSameProviderAndModel && isValidThoughtSignature(signature) ? signature : undefined;
124
+ }
125
+
126
+ /**
127
+ * Claude models via Google APIs require explicit tool call IDs in function calls/responses.
128
+ */
129
+ export function requiresToolCallId(modelId: string): boolean {
130
+ return modelId.startsWith("claude-");
131
+ }
132
+
133
+ function getGeminiMajorVersion(modelId: string): number | undefined {
134
+ const match = modelId.toLowerCase().match(/^gemini(?:-live)?-(\d+)/);
135
+ if (!match) return undefined;
136
+ return Number.parseInt(match[1], 10);
137
+ }
138
+
139
+ function supportsMultimodalFunctionResponse(modelId: string): boolean {
140
+ const geminiMajorVersion = getGeminiMajorVersion(modelId);
141
+ if (geminiMajorVersion !== undefined) {
142
+ return geminiMajorVersion >= 3;
143
+ }
144
+ return true;
145
+ }
146
+
147
+ function isGemini3Model(modelId: string): boolean {
148
+ return modelId.includes("gemini-3");
149
+ }
150
+
151
+ /**
152
+ * Convert internal messages to Gemini Content[] format.
153
+ */
154
+ export function convertMessages<T extends GoogleApiType>(model: Model<T>, context: Context): Content[] {
155
+ const contents: Content[] = [];
156
+ const normalizeToolCallId = (id: string): string => {
157
+ if (!requiresToolCallId(model.id)) return id;
158
+ return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
159
+ };
160
+
161
+ const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
162
+
163
+ for (const msg of transformedMessages) {
164
+ if (msg.role === "user" || msg.role === "developer") {
165
+ if (typeof msg.content === "string") {
166
+ // Skip empty user messages
167
+ if (!msg.content || msg.content.trim() === "") continue;
168
+ contents.push({
169
+ role: "user",
170
+ parts: [{ text: msg.content.toWellFormed() }],
171
+ });
172
+ } else {
173
+ const supportsImages = model.input.includes("image");
174
+ const parts: Part[] = [];
175
+ let omittedImages = false;
176
+ for (const item of msg.content) {
177
+ if (item.type === "text") {
178
+ const text = item.text.toWellFormed();
179
+ if (text.trim().length === 0) continue;
180
+ parts.push({ text });
181
+ } else if (supportsImages) {
182
+ parts.push({
183
+ inlineData: {
184
+ mimeType: item.mimeType,
185
+ data: item.data,
186
+ },
187
+ });
188
+ } else {
189
+ omittedImages = true;
190
+ }
191
+ }
192
+ if (omittedImages) {
193
+ parts.push({ text: NON_VISION_IMAGE_PLACEHOLDER });
194
+ }
195
+ if (parts.length === 0) continue;
196
+ contents.push({
197
+ role: "user",
198
+ parts,
199
+ });
200
+ }
201
+ } else if (msg.role === "assistant") {
202
+ const parts: Part[] = [];
203
+ // Check if message is from same provider and model - only then keep thinking blocks
204
+ const isSameProviderAndModel = msg.provider === model.provider && msg.model === model.id;
205
+
206
+ for (const block of msg.content) {
207
+ if (block.type === "text") {
208
+ // Skip empty text blocks - they can cause issues with some models (e.g. Claude via Antigravity)
209
+ if (!block.text || block.text.trim() === "") continue;
210
+ const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.textSignature);
211
+ parts.push({
212
+ text: block.text.toWellFormed(),
213
+ ...(thoughtSignature && { thoughtSignature }),
214
+ });
215
+ } else if (block.type === "thinking") {
216
+ // Skip empty thinking blocks
217
+ if (!block.thinking || block.thinking.trim() === "") continue;
218
+ // Only keep as thinking block if same provider AND same model
219
+ // Otherwise convert to plain text (no tags to avoid model mimicking them)
220
+ if (isSameProviderAndModel) {
221
+ const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thinkingSignature);
222
+ parts.push({
223
+ thought: true,
224
+ text: block.thinking.toWellFormed(),
225
+ ...(thoughtSignature && { thoughtSignature }),
226
+ });
227
+ } else {
228
+ parts.push({
229
+ text: block.thinking.toWellFormed(),
230
+ });
231
+ }
232
+ } else if (block.type === "toolCall") {
233
+ const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thoughtSignature);
234
+ const effectiveSignature =
235
+ thoughtSignature || (isGemini3Model(model.id) ? SKIP_THOUGHT_SIGNATURE : undefined);
236
+
237
+ const part: Part = {
238
+ functionCall: {
239
+ name: block.name,
240
+ args: block.arguments ?? {},
241
+ ...(requiresToolCallId(model.id) ? { id: block.id } : {}),
242
+ },
243
+ };
244
+ if (model.provider === "google-vertex" && part?.functionCall?.id) {
245
+ delete part.functionCall.id; // Vertex AI does not support 'id' in functionCall
246
+ }
247
+ if (effectiveSignature) {
248
+ part.thoughtSignature = effectiveSignature;
249
+ }
250
+ parts.push(part);
251
+ }
252
+ }
253
+
254
+ if (parts.length === 0) continue;
255
+ contents.push({
256
+ role: "model",
257
+ parts,
258
+ });
259
+ } else if (msg.role === "toolResult") {
260
+ // Extract text and image content
261
+ const supportsImages = model.input.includes("image");
262
+ const textContent = msg.content.filter((c): c is TextContent => c.type === "text");
263
+ const textResult = textContent.map(c => c.text).join("\n");
264
+ const imageContent = supportsImages ? msg.content.filter((c): c is ImageContent => c.type === "image") : [];
265
+ const omittedImages = !supportsImages && msg.content.some((c): c is ImageContent => c.type === "image");
266
+
267
+ const hasText = textResult.length > 0;
268
+ const hasImages = imageContent.length > 0;
269
+
270
+ // Gemini 3+ models support multimodal function responses with images nested inside
271
+ // functionResponse.parts. Claude and other non-Gemini models behind Cloud Code Assist /
272
+ // Antigravity also accept this shape. Gemini < 3 still needs a separate user image turn.
273
+ const modelSupportsMultimodalFunctionResponse = supportsMultimodalFunctionResponse(model.id);
274
+
275
+ // Use "output" key for success, "error" key for errors as per SDK documentation
276
+ const responseValue = omittedImages
277
+ ? [hasText ? textResult.toWellFormed() : "", NON_VISION_IMAGE_PLACEHOLDER].filter(Boolean).join("\n")
278
+ : hasText
279
+ ? textResult.toWellFormed()
280
+ : hasImages
281
+ ? "(see attached image)"
282
+ : "";
283
+
284
+ const imageParts: Part[] = imageContent.map(imageBlock => ({
285
+ inlineData: {
286
+ mimeType: imageBlock.mimeType,
287
+ data: imageBlock.data,
288
+ },
289
+ }));
290
+
291
+ const includeId = requiresToolCallId(model.id);
292
+ const functionResponsePart: Part = {
293
+ functionResponse: {
294
+ name: msg.toolName,
295
+ response: msg.isError ? { error: responseValue } : { output: responseValue },
296
+ ...(hasImages && modelSupportsMultimodalFunctionResponse && { parts: imageParts }),
297
+ ...(includeId ? { id: msg.toolCallId } : {}),
298
+ },
299
+ };
300
+
301
+ if (model.provider === "google-vertex" && functionResponsePart.functionResponse?.id) {
302
+ delete functionResponsePart.functionResponse.id; // Vertex AI does not support 'id' in functionResponse
303
+ }
304
+
305
+ // Cloud Code Assist API requires all function responses to be in a single user turn.
306
+ // Check if the last content is already a user turn with function responses and merge.
307
+ const lastContent = contents[contents.length - 1];
308
+ if (lastContent?.role === "user" && lastContent.parts?.some(p => p.functionResponse)) {
309
+ lastContent.parts.push(functionResponsePart);
310
+ } else {
311
+ contents.push({
312
+ role: "user",
313
+ parts: [functionResponsePart],
314
+ });
315
+ }
316
+
317
+ // For Gemini < 3, add images in a separate user message
318
+ if (hasImages && !modelSupportsMultimodalFunctionResponse) {
319
+ contents.push({
320
+ role: "user",
321
+ parts: [{ text: "Tool result image:" }, ...imageParts],
322
+ });
323
+ }
324
+ }
325
+ }
326
+
327
+ return contents;
328
+ }
329
+
330
+ /**
331
+ * Convert tools to Gemini function declarations format.
332
+ *
333
+ * We prefer `parametersJsonSchema` (full JSON Schema: anyOf/oneOf/const/etc.).
334
+ *
335
+ * Claude models via Cloud Code Assist require the legacy `parameters` field; the API
336
+ * translates it into Anthropic's `input_schema`. When using that path, we sanitize the
337
+ * schema to remove Google-unsupported JSON Schema keywords.
338
+ */
339
+ export function convertTools(
340
+ tools: Tool[],
341
+ model: Model<"google-generative-ai" | "google-gemini-cli" | "google-vertex">,
342
+ ): { functionDeclarations: Record<string, unknown>[] }[] | undefined {
343
+ if (tools.length === 0) return undefined;
344
+
345
+ /**
346
+ * Claude models on Cloud Code Assist need the legacy `parameters` field;
347
+ * the API translates it into Anthropic's `input_schema`.
348
+ */
349
+ const useParameters = model.id.startsWith("claude-");
350
+
351
+ return [
352
+ {
353
+ functionDeclarations: tools.map(tool => ({
354
+ name: tool.name,
355
+ description: tool.description || "",
356
+ ...(useParameters
357
+ ? { parameters: normalizeSchemaForCCA(toolWireSchema(tool)) }
358
+ : { parametersJsonSchema: toolWireSchema(tool) }),
359
+ })),
360
+ },
361
+ ];
362
+ }
363
+
364
+ /**
365
+ * Map tool choice string to Gemini FunctionCallingConfigMode.
366
+ */
367
+ export function mapToolChoice(choice: string): FunctionCallingConfigMode {
368
+ switch (choice) {
369
+ case "auto":
370
+ return "AUTO";
371
+ case "none":
372
+ return "NONE";
373
+ case "any":
374
+ return "ANY";
375
+ default:
376
+ return "AUTO";
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Map Gemini FinishReason to our StopReason.
382
+ */
383
+ export function mapStopReason(reason: FinishReason): StopReason {
384
+ switch (reason) {
385
+ case "STOP":
386
+ return "stop";
387
+ case "MAX_TOKENS":
388
+ return "length";
389
+ case "BLOCKLIST":
390
+ case "PROHIBITED_CONTENT":
391
+ case "SPII":
392
+ case "SAFETY":
393
+ case "IMAGE_SAFETY":
394
+ case "IMAGE_PROHIBITED_CONTENT":
395
+ case "IMAGE_RECITATION":
396
+ case "IMAGE_OTHER":
397
+ case "RECITATION":
398
+ case "FINISH_REASON_UNSPECIFIED":
399
+ case "OTHER":
400
+ case "LANGUAGE":
401
+ case "MALFORMED_FUNCTION_CALL":
402
+ case "UNEXPECTED_TOOL_CALL":
403
+ case "NO_IMAGE":
404
+ return "error";
405
+ default: {
406
+ throw new Error(`Unhandled stop reason: ${reason satisfies never}`);
407
+ }
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Map string finish reason to our StopReason (for raw API responses).
413
+ */
414
+ export function mapStopReasonString(reason: string): StopReason {
415
+ switch (reason) {
416
+ case "STOP":
417
+ return "stop";
418
+ case "MAX_TOKENS":
419
+ return "length";
420
+ default:
421
+ return "error";
422
+ }
423
+ }
424
+
425
+ /**
426
+ * Module-local counter for generating unique tool call IDs across Google providers.
427
+ * Shared so that a single monotonically-increasing sequence is used regardless of which
428
+ * Google API surface produced the stream — purely for uniqueness, not ordering semantics.
429
+ */
430
+ let toolCallCounter = 0;
431
+
432
+ export function nextToolCallId(name: string): string {
433
+ return `${name}_${Date.now()}_${++toolCallCounter}`;
434
+ }
435
+
436
+ /**
437
+ * Push the appropriate `text_end` / `thinking_end` event for the given block.
438
+ * Shared between the SDK-backed stream consumer and the gemini-cli SSE consumer so
439
+ * the end-of-block event shape stays in lockstep.
440
+ */
441
+ export function pushBlockEndEvent(
442
+ block: TextContent | ThinkingContent,
443
+ contentIndex: number,
444
+ output: AssistantMessage,
445
+ stream: AssistantMessageEventStream,
446
+ ): void {
447
+ if (block.type === "text") {
448
+ stream.push({ type: "text_end", contentIndex, content: block.text, partial: output });
449
+ } else {
450
+ stream.push({ type: "thinking_end", contentIndex, content: block.thinking, partial: output });
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Push the three lifecycle events (`toolcall_start` / `toolcall_delta` / `toolcall_end`) for a
456
+ * fully-assembled `ToolCall`. Caller is responsible for appending the toolCall to `output.content`
457
+ * before invoking — this helper does not mutate `output.content`.
458
+ */
459
+ export function pushToolCallEvents(
460
+ toolCall: ToolCall,
461
+ contentIndex: number,
462
+ output: AssistantMessage,
463
+ stream: AssistantMessageEventStream,
464
+ ): void {
465
+ stream.push({ type: "toolcall_start", contentIndex, partial: output });
466
+ stream.push({
467
+ type: "toolcall_delta",
468
+ contentIndex,
469
+ delta: JSON.stringify(toolCall.arguments),
470
+ partial: output,
471
+ });
472
+ stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
473
+ }
474
+
475
+ /**
476
+ * Append a new text- or thinking-block to `output.content` and push the matching
477
+ * `text_start` / `thinking_start` event. `onBeforeStartEvent` lets the SSE consumer
478
+ * inject its `ensureStarted()` first-token side effect into the canonical event order.
479
+ */
480
+ export function startTextOrThinkingBlock(
481
+ isThinking: boolean,
482
+ output: AssistantMessage,
483
+ stream: AssistantMessageEventStream,
484
+ onBeforeStartEvent?: () => void,
485
+ ): TextContent | ThinkingContent {
486
+ const block: TextContent | ThinkingContent = isThinking
487
+ ? { type: "thinking", thinking: "", thinkingSignature: undefined }
488
+ : { type: "text", text: "" };
489
+ output.content.push(block);
490
+ onBeforeStartEvent?.();
491
+ const contentIndex = output.content.length - 1;
492
+ if (isThinking) {
493
+ stream.push({ type: "thinking_start", contentIndex, partial: output });
494
+ } else {
495
+ stream.push({ type: "text_start", contentIndex, partial: output });
496
+ }
497
+ return block;
498
+ }
499
+
500
+ /**
501
+ * Drives the chunked `generateContentStream` iterator into an `AssistantMessage` and
502
+ * the corresponding `AssistantMessageEventStream`. Shared between `streamGoogle` and
503
+ * `streamGoogleVertex` — every observable event order and stop-reason rule is preserved.
504
+ *
505
+ * The caller still owns: `output` construction, timing fields (`duration`/`ttft`),
506
+ * `rawRequestDump`, the `client.models.generateContentStream(params)` call itself,
507
+ * pushing `start`/`done`/`error` events, and the surrounding try/catch that translates
508
+ * thrown errors into `output.stopReason`/`errorMessage`.
509
+ *
510
+ * This helper handles: the chunk loop, currentBlock flush transitions, usage metadata
511
+ * decoding (`calculateCost` included), tool-call id collision avoidance, finish-reason
512
+ * mapping, and the abort/stop-reason post-checks that re-throw to bubble into the
513
+ * caller's catch.
514
+ */
515
+ export async function consumeGoogleStream<T extends GoogleApiType>(args: {
516
+ googleStream: AsyncIterable<GenerateContentResponse>;
517
+ output: AssistantMessage;
518
+ stream: AssistantMessageEventStream;
519
+ model: Model<T>;
520
+ options: { signal?: AbortSignal } | undefined;
521
+ /** Vertex preserves `textSignature` on streamed text deltas; google-generative-ai does not. */
522
+ retainTextSignature?: boolean;
523
+ onFirstToken?: () => void;
524
+ }): Promise<void> {
525
+ const { googleStream, output, stream, model, options, retainTextSignature, onFirstToken } = args;
526
+ const blocks = output.content;
527
+ const blockIndex = () => blocks.length - 1;
528
+ let currentBlock: TextContent | ThinkingContent | null = null;
529
+ let firstTokenSeen = false;
530
+
531
+ const flushCurrent = () => {
532
+ if (!currentBlock) return;
533
+ pushBlockEndEvent(currentBlock, blockIndex(), output, stream);
534
+ };
535
+
536
+ for await (const chunk of googleStream) {
537
+ const candidate = chunk.candidates?.[0];
538
+ if (candidate?.content?.parts) {
539
+ for (const part of candidate.content.parts) {
540
+ if (part.text !== undefined) {
541
+ if (!firstTokenSeen) {
542
+ firstTokenSeen = true;
543
+ onFirstToken?.();
544
+ }
545
+ const isThinking = isThinkingPart(part);
546
+ if (
547
+ !currentBlock ||
548
+ (isThinking && currentBlock.type !== "thinking") ||
549
+ (!isThinking && currentBlock.type !== "text")
550
+ ) {
551
+ flushCurrent();
552
+ currentBlock = startTextOrThinkingBlock(isThinking, output, stream);
553
+ }
554
+ if (currentBlock.type === "thinking") {
555
+ currentBlock.thinking += part.text;
556
+ currentBlock.thinkingSignature = retainThoughtSignature(
557
+ currentBlock.thinkingSignature,
558
+ part.thoughtSignature,
559
+ );
560
+ stream.push({
561
+ type: "thinking_delta",
562
+ contentIndex: blockIndex(),
563
+ delta: part.text,
564
+ partial: output,
565
+ });
566
+ } else {
567
+ currentBlock.text += part.text;
568
+ if (retainTextSignature) {
569
+ currentBlock.textSignature = retainThoughtSignature(
570
+ currentBlock.textSignature,
571
+ part.thoughtSignature,
572
+ );
573
+ }
574
+ stream.push({
575
+ type: "text_delta",
576
+ contentIndex: blockIndex(),
577
+ delta: part.text,
578
+ partial: output,
579
+ });
580
+ }
581
+ }
582
+
583
+ if (part.functionCall) {
584
+ if (currentBlock) {
585
+ flushCurrent();
586
+ currentBlock = null;
587
+ }
588
+
589
+ // Generate unique ID if not provided or if it's a duplicate
590
+ const providedId = part.functionCall.id;
591
+ const needsNewId = !providedId || output.content.some(b => b.type === "toolCall" && b.id === providedId);
592
+ const toolCallId = needsNewId ? nextToolCallId(part.functionCall.name || "tool") : providedId;
593
+
594
+ const toolCall: ToolCall = {
595
+ type: "toolCall",
596
+ id: toolCallId,
597
+ name: part.functionCall.name || "",
598
+ arguments: (part.functionCall.args ?? {}) as Record<string, any>,
599
+ ...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),
600
+ };
601
+
602
+ output.content.push(toolCall);
603
+ pushToolCallEvents(toolCall, blockIndex(), output, stream);
604
+ }
605
+ }
606
+ }
607
+
608
+ if (candidate?.finishReason) {
609
+ output.stopReason = mapStopReason(candidate.finishReason);
610
+ if (output.content.some(b => b.type === "toolCall")) {
611
+ output.stopReason = "toolUse";
612
+ }
613
+ }
614
+
615
+ if (chunk.usageMetadata) {
616
+ // promptTokenCount includes cachedContentTokenCount when cached content is used.
617
+ // Subtract to get non-cached input, matching the OpenAI convention where
618
+ // input = uncached prompt tokens and cacheRead = cached tokens so that
619
+ // input + cacheRead = total prompt tokens (no double-counting).
620
+ // Ref: https://ai.google.dev/api/generate-content#v1beta.GenerateContentResponse.UsageMetadata
621
+ const cachedTokens = chunk.usageMetadata.cachedContentTokenCount || 0;
622
+ const thinkingTokens = chunk.usageMetadata.thoughtsTokenCount || 0;
623
+ output.usage = {
624
+ input: (chunk.usageMetadata.promptTokenCount || 0) - cachedTokens,
625
+ output: (chunk.usageMetadata.candidatesTokenCount || 0) + thinkingTokens,
626
+ cacheRead: cachedTokens,
627
+ cacheWrite: 0,
628
+ totalTokens: chunk.usageMetadata.totalTokenCount || 0,
629
+ ...(thinkingTokens > 0 ? { reasoningTokens: thinkingTokens } : {}),
630
+ cost: {
631
+ input: 0,
632
+ output: 0,
633
+ cacheRead: 0,
634
+ cacheWrite: 0,
635
+ total: 0,
636
+ },
637
+ };
638
+ calculateCost(model, output.usage);
639
+ }
640
+ }
641
+
642
+ flushCurrent();
643
+
644
+ if (options?.signal?.aborted) {
645
+ throw new Error("Request was aborted");
646
+ }
647
+
648
+ if (output.stopReason === "aborted" || output.stopReason === "error") {
649
+ throw new Error(output.errorMessage ?? "An unknown error occurred");
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Generation/sampling fields that map directly onto Gemini's `GenerateContentConfig`.
655
+ * Excludes any provider-specific extensions (`topP`/`topK`/etc are all forwarded as-is).
656
+ */
657
+ interface GoogleGenerationConfig extends GenerateContentConfig {
658
+ topP?: number;
659
+ topK?: number;
660
+ minP?: number;
661
+ presencePenalty?: number;
662
+ repetitionPenalty?: number;
663
+ }
664
+
665
+ /**
666
+ * Build the `GenerateContentParameters` payload for the public Gemini API and Vertex AI.
667
+ * Both surfaces accept the same `GenerateContentConfig` shape — every numeric/string knob,
668
+ * tool-config, thinking-config, and system-instruction conversion is identical.
669
+ *
670
+ * `google-gemini-cli` is NOT routed through here: its `CloudCodeAssistRequest` body has a
671
+ * distinct top-level shape (project/request/requestType) and a different thinking-config
672
+ * placement on `generationConfig`.
673
+ */
674
+ export function buildGoogleGenerateContentParams<T extends "google-generative-ai" | "google-vertex">(
675
+ model: Model<T>,
676
+ context: Context,
677
+ options: GoogleSharedStreamOptions,
678
+ ): GenerateContentParameters {
679
+ const systemPrompts = normalizeSystemPrompts(context.systemPrompt);
680
+ const contents = convertMessages(model, context);
681
+
682
+ const generationConfig: GoogleGenerationConfig = {};
683
+ if (options.temperature !== undefined) generationConfig.temperature = options.temperature;
684
+ if (options.maxTokens !== undefined) generationConfig.maxOutputTokens = options.maxTokens;
685
+ if (options.topP !== undefined) generationConfig.topP = options.topP;
686
+ if (options.topK !== undefined) generationConfig.topK = options.topK;
687
+ if (options.minP !== undefined) generationConfig.minP = options.minP;
688
+ if (options.presencePenalty !== undefined) generationConfig.presencePenalty = options.presencePenalty;
689
+ if (options.repetitionPenalty !== undefined) generationConfig.repetitionPenalty = options.repetitionPenalty;
690
+
691
+ const config: GenerateContentConfig = {
692
+ ...(Object.keys(generationConfig).length > 0 && generationConfig),
693
+ ...(systemPrompts.length > 0 && { systemInstruction: { parts: systemPrompts.map(text => ({ text })) } }),
694
+ ...(context.tools && context.tools.length > 0 && { tools: convertTools(context.tools, model) }),
695
+ };
696
+
697
+ if (context.tools && context.tools.length > 0 && options.toolChoice) {
698
+ const choice = options.toolChoice;
699
+ if (typeof choice === "string") {
700
+ config.toolConfig = {
701
+ functionCallingConfig: { mode: mapToolChoice(choice) },
702
+ };
703
+ } else {
704
+ // Named-tool routing — `mode: "ANY"` plus an explicit allow-list. The
705
+ // caller is responsible for ensuring the names exist in `context.tools`.
706
+ config.toolConfig = {
707
+ functionCallingConfig: {
708
+ mode: "ANY",
709
+ allowedFunctionNames: [...choice.allowedFunctionNames],
710
+ },
711
+ };
712
+ }
713
+ } else {
714
+ config.toolConfig = undefined;
715
+ }
716
+
717
+ if (options.thinking?.enabled && model.reasoning) {
718
+ const cfg: ThinkingConfig = { includeThoughts: true };
719
+ if (options.thinking.level !== undefined) {
720
+ // GoogleThinkingLevel mirrors the SDK's `ThinkingLevel` string enum values 1:1.
721
+ cfg.thinkingLevel = options.thinking.level as ThinkingLevel;
722
+ } else if (options.thinking.budgetTokens !== undefined) {
723
+ cfg.thinkingBudget = options.thinking.budgetTokens;
724
+ }
725
+ config.thinkingConfig = cfg;
726
+ }
727
+
728
+ if (options.signal) {
729
+ if (options.signal.aborted) {
730
+ throw new Error("Request aborted");
731
+ }
732
+ config.abortSignal = options.signal;
733
+ }
734
+
735
+ return {
736
+ model: model.id,
737
+ contents,
738
+ config,
739
+ };
740
+ }
741
+
742
+ /**
743
+ * Drive the `streamGoogle` / `streamGoogleVertex` event flow: build the assistant message,
744
+ * push start/done/error events, run `consumeGoogleStream`, and translate thrown errors into
745
+ * the canonical `error` event shape.
746
+ *
747
+ * Caller-supplied `prepare()` runs inside the try-block so any failure (missing project,
748
+ * bad auth, etc.) is funneled through the same error path as a streaming failure.
749
+ */
750
+ export interface GoogleGenAIRequestPlan {
751
+ params: GenerateContentParameters;
752
+ url: string;
753
+ headers: Record<string, string>;
754
+ fetch?: FetchImpl;
755
+ }
756
+
757
+ export function streamGoogleGenAI<T extends "google-generative-ai" | "google-vertex">(args: {
758
+ model: Model<T>;
759
+ options: GoogleSharedStreamOptions | undefined;
760
+ api: T;
761
+ retainTextSignature?: boolean;
762
+ prepare: () => GoogleGenAIRequestPlan | Promise<GoogleGenAIRequestPlan>;
763
+ }): AssistantMessageEventStream {
764
+ const { model, options, api, retainTextSignature, prepare } = args;
765
+ const stream = new AssistantMessageEventStream();
766
+
767
+ (async () => {
768
+ const startTime = Date.now();
769
+ let firstTokenTime: number | undefined;
770
+
771
+ const output: AssistantMessage = {
772
+ role: "assistant",
773
+ content: [],
774
+ api: api as Api,
775
+ provider: model.provider,
776
+ model: model.id,
777
+ usage: {
778
+ input: 0,
779
+ output: 0,
780
+ cacheRead: 0,
781
+ cacheWrite: 0,
782
+ totalTokens: 0,
783
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
784
+ },
785
+ stopReason: "stop",
786
+ timestamp: Date.now(),
787
+ };
788
+ let rawRequestDump: RawHttpRequestDump | undefined;
789
+
790
+ try {
791
+ const plan = await prepare();
792
+ let params = plan.params;
793
+ const replacement = await options?.onPayload?.(params, model);
794
+ if (replacement !== undefined) {
795
+ params = replacement as GenerateContentParameters;
796
+ }
797
+ rawRequestDump = {
798
+ provider: model.provider,
799
+ api: output.api,
800
+ model: model.id,
801
+ method: "POST",
802
+ url: plan.url,
803
+ body: params,
804
+ headers: plan.headers,
805
+ };
806
+
807
+ const wireBody = paramsToWireBody(params);
808
+ const fetchImpl = plan.fetch ?? options?.fetch ?? (globalThis.fetch.bind(globalThis) as FetchImpl);
809
+ const response = await fetchImpl(plan.url, {
810
+ method: "POST",
811
+ headers: { ...plan.headers, "Content-Type": "application/json", Accept: "text/event-stream" },
812
+ body: JSON.stringify(wireBody),
813
+ signal: options?.signal,
814
+ });
815
+ if (!response.ok) {
816
+ const errorText = await response.text().catch(() => "");
817
+ throw withHttpStatus(
818
+ new Error(`Google API error (${response.status}): ${extractGoogleErrorMessage(errorText)}`),
819
+ response.status,
820
+ );
821
+ }
822
+ if (!response.body) {
823
+ throw new Error("Google API returned an empty response body");
824
+ }
825
+
826
+ const googleStream = readSseJson<GenerateContentResponse>(response.body, options?.signal, event =>
827
+ options?.onSseEvent?.({ event: event.event, data: event.data, raw: [...event.raw] }, model),
828
+ );
829
+
830
+ stream.push({ type: "start", partial: output });
831
+ await consumeGoogleStream({
832
+ googleStream,
833
+ output,
834
+ stream,
835
+ model,
836
+ options,
837
+ retainTextSignature,
838
+ onFirstToken: () => {
839
+ firstTokenTime = Date.now();
840
+ },
841
+ });
842
+
843
+ output.duration = Date.now() - startTime;
844
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
845
+ stream.push({ type: "done", reason: output.stopReason as "length" | "stop" | "toolUse", message: output });
846
+ stream.end();
847
+ } catch (error) {
848
+ for (const block of output.content) {
849
+ if ("index" in block) {
850
+ delete (block as { index?: number }).index;
851
+ }
852
+ }
853
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
854
+ output.errorStatus = extractHttpStatusFromError(error);
855
+ output.errorMessage = await finalizeErrorMessage(error, rawRequestDump);
856
+ output.duration = Date.now() - startTime;
857
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
858
+ stream.push({ type: "error", reason: output.stopReason, error: output });
859
+ stream.end();
860
+ }
861
+ })();
862
+
863
+ return stream;
864
+ }
865
+
866
+ /**
867
+ * Lift the SDK's `params.config` fields out of `config` and place them where the
868
+ * Gemini / Vertex AI REST API expects them on the request body. Mirrors the
869
+ * generateContentParametersTo{Mldev,Vertex} transformation in @google/genai
870
+ * for the subset of fields this codebase actually sets.
871
+ *
872
+ * `abortSignal` is intentionally dropped — the SDK propagates it via `fetch.signal`,
873
+ * which our caller already wires up through `options.signal`.
874
+ */
875
+ function paramsToWireBody(params: GenerateContentParameters): Record<string, unknown> {
876
+ const body: Record<string, unknown> = { contents: params.contents };
877
+ const config = params.config;
878
+ if (!config) return body;
879
+
880
+ if (config.systemInstruction !== undefined) body.systemInstruction = config.systemInstruction;
881
+ if (config.tools !== undefined) body.tools = config.tools;
882
+ if (config.toolConfig !== undefined) body.toolConfig = config.toolConfig;
883
+ if (config.safetySettings !== undefined) body.safetySettings = config.safetySettings;
884
+ if (config.cachedContent !== undefined) body.cachedContent = config.cachedContent;
885
+
886
+ const gen: Record<string, unknown> = {};
887
+ if (config.temperature !== undefined) gen.temperature = config.temperature;
888
+ if (config.maxOutputTokens !== undefined) gen.maxOutputTokens = config.maxOutputTokens;
889
+ if (config.topP !== undefined) gen.topP = config.topP;
890
+ if (config.topK !== undefined) gen.topK = config.topK;
891
+ if (config.candidateCount !== undefined) gen.candidateCount = config.candidateCount;
892
+ if (config.stopSequences !== undefined) gen.stopSequences = config.stopSequences;
893
+ if (config.presencePenalty !== undefined) gen.presencePenalty = config.presencePenalty;
894
+ if (config.frequencyPenalty !== undefined) gen.frequencyPenalty = config.frequencyPenalty;
895
+ if (config.seed !== undefined) gen.seed = config.seed;
896
+ if (config.responseMimeType !== undefined) gen.responseMimeType = config.responseMimeType;
897
+ if (config.responseSchema !== undefined) gen.responseSchema = config.responseSchema;
898
+ if (config.responseJsonSchema !== undefined) gen.responseJsonSchema = config.responseJsonSchema;
899
+ if (config.responseModalities !== undefined) gen.responseModalities = config.responseModalities;
900
+ if (config.thinkingConfig !== undefined) gen.thinkingConfig = config.thinkingConfig;
901
+ const generationConfig = config as unknown as { minP?: number; repetitionPenalty?: number };
902
+ if (generationConfig.minP !== undefined) gen.minP = generationConfig.minP;
903
+ if (generationConfig.repetitionPenalty !== undefined) gen.repetitionPenalty = generationConfig.repetitionPenalty;
904
+ if (Object.keys(gen).length > 0) body.generationConfig = gen;
905
+ return body;
906
+ }
907
+
908
+ function extractGoogleErrorMessage(errorText: string): string {
909
+ if (!errorText) return "Unknown error";
910
+ try {
911
+ const parsed = JSON.parse(errorText) as { error?: { message?: string } };
912
+ if (parsed.error?.message) return parsed.error.message;
913
+ } catch {
914
+ // fall through to raw text
915
+ }
916
+ return errorText;
917
+ }