@copilotkit/aimock 1.45.1 → 1.47.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 (492) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -1
  4. package/README.md +11 -2
  5. package/dist/agui-mock.cjs +25 -25
  6. package/dist/agui-mock.cjs.map +1 -1
  7. package/dist/agui-mock.d.cts.map +1 -1
  8. package/dist/agui-mock.d.ts.map +1 -1
  9. package/dist/agui-mock.js +25 -25
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-types.d.cts.map +1 -1
  12. package/dist/aimock-cli.cjs +15 -1
  13. package/dist/aimock-cli.cjs.map +1 -1
  14. package/dist/aimock-cli.d.cts.map +1 -1
  15. package/dist/aimock-cli.d.ts.map +1 -1
  16. package/dist/aimock-cli.js +16 -2
  17. package/dist/aimock-cli.js.map +1 -1
  18. package/dist/api-key-auth.cjs +10 -9
  19. package/dist/api-key-auth.cjs.map +1 -1
  20. package/dist/api-key-auth.d.cts.map +1 -1
  21. package/dist/api-key-auth.d.ts.map +1 -1
  22. package/dist/api-key-auth.js +11 -10
  23. package/dist/api-key-auth.js.map +1 -1
  24. package/dist/aws-event-stream.cjs +20 -23
  25. package/dist/aws-event-stream.cjs.map +1 -1
  26. package/dist/aws-event-stream.d.cts +0 -1
  27. package/dist/aws-event-stream.d.ts +0 -1
  28. package/dist/aws-event-stream.js +20 -24
  29. package/dist/aws-event-stream.js.map +1 -1
  30. package/dist/batches.cjs +2 -4
  31. package/dist/batches.cjs.map +1 -1
  32. package/dist/batches.d.cts.map +1 -1
  33. package/dist/batches.d.ts.map +1 -1
  34. package/dist/batches.js +2 -4
  35. package/dist/batches.js.map +1 -1
  36. package/dist/bedrock-converse.cjs +139 -161
  37. package/dist/bedrock-converse.cjs.map +1 -1
  38. package/dist/bedrock-converse.d.cts.map +1 -1
  39. package/dist/bedrock-converse.d.ts.map +1 -1
  40. package/dist/bedrock-converse.js +137 -159
  41. package/dist/bedrock-converse.js.map +1 -1
  42. package/dist/bedrock.cjs +175 -216
  43. package/dist/bedrock.cjs.map +1 -1
  44. package/dist/bedrock.d.cts.map +1 -1
  45. package/dist/bedrock.d.ts.map +1 -1
  46. package/dist/bedrock.js +172 -213
  47. package/dist/bedrock.js.map +1 -1
  48. package/dist/byteplus-video.cjs +11 -10
  49. package/dist/byteplus-video.cjs.map +1 -1
  50. package/dist/byteplus-video.d.cts.map +1 -1
  51. package/dist/byteplus-video.d.ts.map +1 -1
  52. package/dist/byteplus-video.js +5 -4
  53. package/dist/byteplus-video.js.map +1 -1
  54. package/dist/chaos.cjs +5 -4
  55. package/dist/chaos.cjs.map +1 -1
  56. package/dist/chaos.d.cts.map +1 -1
  57. package/dist/chaos.d.ts.map +1 -1
  58. package/dist/chaos.js +5 -4
  59. package/dist/chaos.js.map +1 -1
  60. package/dist/cli.cjs +15 -0
  61. package/dist/cli.cjs.map +1 -1
  62. package/dist/cli.js +15 -0
  63. package/dist/cli.js.map +1 -1
  64. package/dist/cohere.cjs +107 -180
  65. package/dist/cohere.cjs.map +1 -1
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts.map +1 -1
  68. package/dist/cohere.js +106 -179
  69. package/dist/cohere.js.map +1 -1
  70. package/dist/config-loader.cjs +29 -1
  71. package/dist/config-loader.cjs.map +1 -1
  72. package/dist/config-loader.d.cts +7 -1
  73. package/dist/config-loader.d.cts.map +1 -1
  74. package/dist/config-loader.d.ts +7 -1
  75. package/dist/config-loader.d.ts.map +1 -1
  76. package/dist/config-loader.js +29 -2
  77. package/dist/config-loader.js.map +1 -1
  78. package/dist/count-tokens.cjs +146 -0
  79. package/dist/count-tokens.cjs.map +1 -0
  80. package/dist/count-tokens.js +146 -0
  81. package/dist/count-tokens.js.map +1 -0
  82. package/dist/elevenlabs-audio.cjs +36 -34
  83. package/dist/elevenlabs-audio.cjs.map +1 -1
  84. package/dist/elevenlabs-audio.d.cts.map +1 -1
  85. package/dist/elevenlabs-audio.d.ts.map +1 -1
  86. package/dist/elevenlabs-audio.js +33 -31
  87. package/dist/elevenlabs-audio.js.map +1 -1
  88. package/dist/elevenlabs-voice.cjs +38 -37
  89. package/dist/elevenlabs-voice.cjs.map +1 -1
  90. package/dist/elevenlabs-voice.d.cts.map +1 -1
  91. package/dist/elevenlabs-voice.d.ts.map +1 -1
  92. package/dist/elevenlabs-voice.js +33 -32
  93. package/dist/elevenlabs-voice.js.map +1 -1
  94. package/dist/embedding-request.cjs +16 -0
  95. package/dist/embedding-request.cjs.map +1 -0
  96. package/dist/embedding-request.js +14 -0
  97. package/dist/embedding-request.js.map +1 -0
  98. package/dist/embeddings.cjs +30 -20
  99. package/dist/embeddings.cjs.map +1 -1
  100. package/dist/embeddings.d.cts.map +1 -1
  101. package/dist/embeddings.d.ts.map +1 -1
  102. package/dist/embeddings.js +30 -20
  103. package/dist/embeddings.js.map +1 -1
  104. package/dist/fal-audio.cjs +45 -43
  105. package/dist/fal-audio.cjs.map +1 -1
  106. package/dist/fal-audio.d.cts.map +1 -1
  107. package/dist/fal-audio.d.ts.map +1 -1
  108. package/dist/fal-audio.js +42 -40
  109. package/dist/fal-audio.js.map +1 -1
  110. package/dist/fal.cjs +17 -15
  111. package/dist/fal.cjs.map +1 -1
  112. package/dist/fal.js +16 -14
  113. package/dist/fal.js.map +1 -1
  114. package/dist/files.cjs +2 -4
  115. package/dist/files.cjs.map +1 -1
  116. package/dist/files.d.cts.map +1 -1
  117. package/dist/files.d.ts.map +1 -1
  118. package/dist/files.js +2 -4
  119. package/dist/files.js.map +1 -1
  120. package/dist/fine-tuning.cjs +2 -4
  121. package/dist/fine-tuning.cjs.map +1 -1
  122. package/dist/fine-tuning.d.cts.map +1 -1
  123. package/dist/fine-tuning.d.ts.map +1 -1
  124. package/dist/fine-tuning.js +2 -4
  125. package/dist/fine-tuning.js.map +1 -1
  126. package/dist/fixture-loader.cjs +140 -44
  127. package/dist/fixture-loader.cjs.map +1 -1
  128. package/dist/fixture-loader.d.cts.map +1 -1
  129. package/dist/fixture-loader.d.ts.map +1 -1
  130. package/dist/fixture-loader.js +141 -46
  131. package/dist/fixture-loader.js.map +1 -1
  132. package/dist/gemini-embeddings.cjs +26 -24
  133. package/dist/gemini-embeddings.cjs.map +1 -1
  134. package/dist/gemini-embeddings.js +26 -24
  135. package/dist/gemini-embeddings.js.map +1 -1
  136. package/dist/gemini-interactions.cjs +113 -156
  137. package/dist/gemini-interactions.cjs.map +1 -1
  138. package/dist/gemini-interactions.d.cts +0 -1
  139. package/dist/gemini-interactions.d.cts.map +1 -1
  140. package/dist/gemini-interactions.d.ts +0 -1
  141. package/dist/gemini-interactions.d.ts.map +1 -1
  142. package/dist/gemini-interactions.js +113 -156
  143. package/dist/gemini-interactions.js.map +1 -1
  144. package/dist/gemini.cjs +295 -218
  145. package/dist/gemini.cjs.map +1 -1
  146. package/dist/gemini.d.cts.map +1 -1
  147. package/dist/gemini.d.ts.map +1 -1
  148. package/dist/gemini.js +295 -219
  149. package/dist/gemini.js.map +1 -1
  150. package/dist/grok-video.cjs +14 -12
  151. package/dist/grok-video.cjs.map +1 -1
  152. package/dist/grok-video.d.cts.map +1 -1
  153. package/dist/grok-video.d.ts.map +1 -1
  154. package/dist/grok-video.js +8 -6
  155. package/dist/grok-video.js.map +1 -1
  156. package/dist/helpers.cjs +411 -75
  157. package/dist/helpers.cjs.map +1 -1
  158. package/dist/helpers.d.cts +51 -1
  159. package/dist/helpers.d.cts.map +1 -1
  160. package/dist/helpers.d.ts +51 -1
  161. package/dist/helpers.d.ts.map +1 -1
  162. package/dist/helpers.js +402 -74
  163. package/dist/helpers.js.map +1 -1
  164. package/dist/images.cjs +30 -28
  165. package/dist/images.cjs.map +1 -1
  166. package/dist/images.d.cts.map +1 -1
  167. package/dist/images.d.ts.map +1 -1
  168. package/dist/images.js +27 -25
  169. package/dist/images.js.map +1 -1
  170. package/dist/index.cjs +4 -2
  171. package/dist/index.d.cts +4 -4
  172. package/dist/index.d.ts +4 -4
  173. package/dist/index.js +4 -4
  174. package/dist/journal.cjs +3 -1
  175. package/dist/journal.cjs.map +1 -1
  176. package/dist/journal.d.cts.map +1 -1
  177. package/dist/journal.d.ts.map +1 -1
  178. package/dist/journal.js +2 -1
  179. package/dist/journal.js.map +1 -1
  180. package/dist/llmock.cjs +3 -0
  181. package/dist/llmock.cjs.map +1 -1
  182. package/dist/llmock.d.cts.map +1 -1
  183. package/dist/llmock.d.ts.map +1 -1
  184. package/dist/llmock.js +3 -0
  185. package/dist/llmock.js.map +1 -1
  186. package/dist/mcp-fakes.d.cts.map +1 -1
  187. package/dist/mcp-handler.cjs +7 -3
  188. package/dist/mcp-handler.cjs.map +1 -1
  189. package/dist/mcp-handler.js +7 -3
  190. package/dist/mcp-handler.js.map +1 -1
  191. package/dist/mcp-recorder.cjs +2 -1
  192. package/dist/mcp-recorder.cjs.map +1 -1
  193. package/dist/mcp-recorder.js +3 -2
  194. package/dist/mcp-recorder.js.map +1 -1
  195. package/dist/messages.cjs +235 -202
  196. package/dist/messages.cjs.map +1 -1
  197. package/dist/messages.d.cts.map +1 -1
  198. package/dist/messages.d.ts.map +1 -1
  199. package/dist/messages.js +234 -202
  200. package/dist/messages.js.map +1 -1
  201. package/dist/metrics.cjs +5 -2
  202. package/dist/metrics.cjs.map +1 -1
  203. package/dist/metrics.d.cts.map +1 -1
  204. package/dist/metrics.d.ts.map +1 -1
  205. package/dist/metrics.js +6 -3
  206. package/dist/metrics.js.map +1 -1
  207. package/dist/misbehavior.cjs +62 -3
  208. package/dist/misbehavior.cjs.map +1 -1
  209. package/dist/misbehavior.d.cts.map +1 -1
  210. package/dist/misbehavior.d.ts.map +1 -1
  211. package/dist/misbehavior.js +63 -4
  212. package/dist/misbehavior.js.map +1 -1
  213. package/dist/model-utils.cjs +1 -0
  214. package/dist/model-utils.cjs.map +1 -1
  215. package/dist/model-utils.js +1 -1
  216. package/dist/model-utils.js.map +1 -1
  217. package/dist/moderation.cjs +15 -12
  218. package/dist/moderation.cjs.map +1 -1
  219. package/dist/moderation.js +14 -11
  220. package/dist/moderation.js.map +1 -1
  221. package/dist/ndjson-writer.cjs +21 -23
  222. package/dist/ndjson-writer.cjs.map +1 -1
  223. package/dist/ndjson-writer.d.cts +0 -1
  224. package/dist/ndjson-writer.d.cts.map +1 -1
  225. package/dist/ndjson-writer.d.ts +0 -1
  226. package/dist/ndjson-writer.d.ts.map +1 -1
  227. package/dist/ndjson-writer.js +21 -24
  228. package/dist/ndjson-writer.js.map +1 -1
  229. package/dist/ollama.cjs +276 -210
  230. package/dist/ollama.cjs.map +1 -1
  231. package/dist/ollama.d.cts.map +1 -1
  232. package/dist/ollama.d.ts.map +1 -1
  233. package/dist/ollama.js +273 -207
  234. package/dist/ollama.js.map +1 -1
  235. package/dist/openapi.cjs +323 -3
  236. package/dist/openapi.cjs.map +1 -1
  237. package/dist/openapi.d.cts.map +1 -1
  238. package/dist/openapi.d.ts.map +1 -1
  239. package/dist/openapi.js +324 -4
  240. package/dist/openapi.js.map +1 -1
  241. package/dist/openrouter-chat.cjs +11 -155
  242. package/dist/openrouter-chat.cjs.map +1 -1
  243. package/dist/openrouter-chat.js +11 -155
  244. package/dist/openrouter-chat.js.map +1 -1
  245. package/dist/openrouter-video.cjs +51 -74
  246. package/dist/openrouter-video.cjs.map +1 -1
  247. package/dist/openrouter-video.d.cts.map +1 -1
  248. package/dist/openrouter-video.d.ts.map +1 -1
  249. package/dist/openrouter-video.js +37 -60
  250. package/dist/openrouter-video.js.map +1 -1
  251. package/dist/raw-validation.cjs +177 -0
  252. package/dist/raw-validation.cjs.map +1 -0
  253. package/dist/raw-validation.js +176 -0
  254. package/dist/raw-validation.js.map +1 -0
  255. package/dist/recorder.cjs +176 -95
  256. package/dist/recorder.cjs.map +1 -1
  257. package/dist/recorder.d.cts.map +1 -1
  258. package/dist/recorder.d.ts.map +1 -1
  259. package/dist/recorder.js +176 -95
  260. package/dist/recorder.js.map +1 -1
  261. package/dist/rerank.cjs +13 -11
  262. package/dist/rerank.cjs.map +1 -1
  263. package/dist/rerank.js +12 -10
  264. package/dist/rerank.js.map +1 -1
  265. package/dist/response/catalog/anthropic-page.cjs +39 -0
  266. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  267. package/dist/response/catalog/anthropic-page.js +38 -0
  268. package/dist/response/catalog/anthropic-page.js.map +1 -0
  269. package/dist/response/catalog/collect.cjs +61 -0
  270. package/dist/response/catalog/collect.cjs.map +1 -0
  271. package/dist/response/catalog/collect.js +59 -0
  272. package/dist/response/catalog/collect.js.map +1 -0
  273. package/dist/response/catalog/gemini-page.cjs +25 -0
  274. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  275. package/dist/response/catalog/gemini-page.js +24 -0
  276. package/dist/response/catalog/gemini-page.js.map +1 -0
  277. package/dist/response/catalog/render.cjs +182 -0
  278. package/dist/response/catalog/render.cjs.map +1 -0
  279. package/dist/response/catalog/render.js +171 -0
  280. package/dist/response/catalog/render.js.map +1 -0
  281. package/dist/response/deliver.cjs +144 -0
  282. package/dist/response/deliver.cjs.map +1 -0
  283. package/dist/response/deliver.js +143 -0
  284. package/dist/response/deliver.js.map +1 -0
  285. package/dist/response/errors.cjs +225 -0
  286. package/dist/response/errors.cjs.map +1 -0
  287. package/dist/response/errors.js +224 -0
  288. package/dist/response/errors.js.map +1 -0
  289. package/dist/response/finish.cjs +39 -0
  290. package/dist/response/finish.cjs.map +1 -0
  291. package/dist/response/finish.js +35 -0
  292. package/dist/response/finish.js.map +1 -0
  293. package/dist/response/frame-loop.cjs +102 -0
  294. package/dist/response/frame-loop.cjs.map +1 -0
  295. package/dist/response/frame-loop.js +100 -0
  296. package/dist/response/frame-loop.js.map +1 -0
  297. package/dist/response/lifecycle.cjs +223 -0
  298. package/dist/response/lifecycle.cjs.map +1 -0
  299. package/dist/response/lifecycle.js +211 -0
  300. package/dist/response/lifecycle.js.map +1 -0
  301. package/dist/response/policy.cjs +126 -0
  302. package/dist/response/policy.cjs.map +1 -0
  303. package/dist/response/policy.js +120 -0
  304. package/dist/response/policy.js.map +1 -0
  305. package/dist/response/profiles.cjs +69 -0
  306. package/dist/response/profiles.cjs.map +1 -0
  307. package/dist/response/profiles.js +65 -0
  308. package/dist/response/profiles.js.map +1 -0
  309. package/dist/response/proxy.cjs +71 -0
  310. package/dist/response/proxy.cjs.map +1 -0
  311. package/dist/response/proxy.js +70 -0
  312. package/dist/response/proxy.js.map +1 -0
  313. package/dist/response/raw.cjs +228 -0
  314. package/dist/response/raw.cjs.map +1 -0
  315. package/dist/response/raw.js +227 -0
  316. package/dist/response/raw.js.map +1 -0
  317. package/dist/response/timing.cjs +157 -0
  318. package/dist/response/timing.cjs.map +1 -0
  319. package/dist/response/timing.js +150 -0
  320. package/dist/response/timing.js.map +1 -0
  321. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  322. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  323. package/dist/response/usage/codecs/anthropic.js +50 -0
  324. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  325. package/dist/response/usage/codecs/cohere.cjs +36 -0
  326. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  327. package/dist/response/usage/codecs/cohere.js +35 -0
  328. package/dist/response/usage/codecs/cohere.js.map +1 -0
  329. package/dist/response/usage/codecs/converse.cjs +16 -0
  330. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  331. package/dist/response/usage/codecs/converse.js +15 -0
  332. package/dist/response/usage/codecs/converse.js.map +1 -0
  333. package/dist/response/usage/codecs/gemini.cjs +31 -0
  334. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  335. package/dist/response/usage/codecs/gemini.js +31 -0
  336. package/dist/response/usage/codecs/gemini.js.map +1 -0
  337. package/dist/response/usage/codecs/interactions.cjs +22 -0
  338. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  339. package/dist/response/usage/codecs/interactions.js +21 -0
  340. package/dist/response/usage/codecs/interactions.js.map +1 -0
  341. package/dist/response/usage/codecs/ollama.cjs +17 -0
  342. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  343. package/dist/response/usage/codecs/ollama.js +17 -0
  344. package/dist/response/usage/codecs/ollama.js.map +1 -0
  345. package/dist/response/usage/codecs/openai.cjs +52 -0
  346. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  347. package/dist/response/usage/codecs/openai.js +50 -0
  348. package/dist/response/usage/codecs/openai.js.map +1 -0
  349. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  350. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  351. package/dist/response/usage/codecs/openrouter.js +67 -0
  352. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  353. package/dist/response/usage/codecs/realtime.cjs +19 -0
  354. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  355. package/dist/response/usage/codecs/realtime.js +18 -0
  356. package/dist/response/usage/codecs/realtime.js.map +1 -0
  357. package/dist/response/usage/codecs/responses.cjs +21 -0
  358. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  359. package/dist/response/usage/codecs/responses.js +20 -0
  360. package/dist/response/usage/codecs/responses.js.map +1 -0
  361. package/dist/response/usage/estimate.cjs +23 -0
  362. package/dist/response/usage/estimate.cjs.map +1 -0
  363. package/dist/response/usage/estimate.js +21 -0
  364. package/dist/response/usage/estimate.js.map +1 -0
  365. package/dist/response/usage/fields.cjs +72 -0
  366. package/dist/response/usage/fields.cjs.map +1 -0
  367. package/dist/response/usage/fields.js +70 -0
  368. package/dist/response/usage/fields.js.map +1 -0
  369. package/dist/response/usage/recording.cjs +52 -0
  370. package/dist/response/usage/recording.cjs.map +1 -0
  371. package/dist/response/usage/recording.js +52 -0
  372. package/dist/response/usage/recording.js.map +1 -0
  373. package/dist/responses.cjs +304 -196
  374. package/dist/responses.cjs.map +1 -1
  375. package/dist/responses.d.cts.map +1 -1
  376. package/dist/responses.d.ts.map +1 -1
  377. package/dist/responses.js +303 -195
  378. package/dist/responses.js.map +1 -1
  379. package/dist/route-registry.cjs +45 -0
  380. package/dist/route-registry.cjs.map +1 -1
  381. package/dist/route-registry.d.cts.map +1 -1
  382. package/dist/route-registry.d.ts.map +1 -1
  383. package/dist/route-registry.js +41 -1
  384. package/dist/route-registry.js.map +1 -1
  385. package/dist/router.cjs +24 -4
  386. package/dist/router.cjs.map +1 -1
  387. package/dist/router.d.cts +7 -1
  388. package/dist/router.d.cts.map +1 -1
  389. package/dist/router.d.ts +7 -1
  390. package/dist/router.d.ts.map +1 -1
  391. package/dist/router.js +24 -5
  392. package/dist/router.js.map +1 -1
  393. package/dist/search.cjs +13 -11
  394. package/dist/search.cjs.map +1 -1
  395. package/dist/search.js +12 -10
  396. package/dist/search.js.map +1 -1
  397. package/dist/server.cjs +293 -248
  398. package/dist/server.cjs.map +1 -1
  399. package/dist/server.d.cts.map +1 -1
  400. package/dist/server.d.ts.map +1 -1
  401. package/dist/server.js +294 -249
  402. package/dist/server.js.map +1 -1
  403. package/dist/speech.cjs +20 -18
  404. package/dist/speech.cjs.map +1 -1
  405. package/dist/speech.d.cts.map +1 -1
  406. package/dist/speech.d.ts.map +1 -1
  407. package/dist/speech.js +19 -17
  408. package/dist/speech.js.map +1 -1
  409. package/dist/sse-writer.cjs +56 -30
  410. package/dist/sse-writer.cjs.map +1 -1
  411. package/dist/sse-writer.d.cts +0 -1
  412. package/dist/sse-writer.d.cts.map +1 -1
  413. package/dist/sse-writer.d.ts +0 -1
  414. package/dist/sse-writer.d.ts.map +1 -1
  415. package/dist/sse-writer.js +55 -31
  416. package/dist/sse-writer.js.map +1 -1
  417. package/dist/stream-collapse.cjs +118 -11
  418. package/dist/stream-collapse.cjs.map +1 -1
  419. package/dist/stream-collapse.d.cts +10 -9
  420. package/dist/stream-collapse.d.cts.map +1 -1
  421. package/dist/stream-collapse.d.ts +10 -9
  422. package/dist/stream-collapse.d.ts.map +1 -1
  423. package/dist/stream-collapse.js +118 -11
  424. package/dist/stream-collapse.js.map +1 -1
  425. package/dist/transcription.cjs +31 -43
  426. package/dist/transcription.cjs.map +1 -1
  427. package/dist/transcription.d.cts.map +1 -1
  428. package/dist/transcription.d.ts.map +1 -1
  429. package/dist/transcription.js +31 -43
  430. package/dist/transcription.js.map +1 -1
  431. package/dist/types.cjs.map +1 -1
  432. package/dist/types.d.cts +231 -9
  433. package/dist/types.d.cts.map +1 -1
  434. package/dist/types.d.ts +231 -9
  435. package/dist/types.d.ts.map +1 -1
  436. package/dist/types.js.map +1 -1
  437. package/dist/vector-mock.cjs +3 -8
  438. package/dist/vector-mock.cjs.map +1 -1
  439. package/dist/vector-mock.d.cts.map +1 -1
  440. package/dist/vector-mock.d.ts.map +1 -1
  441. package/dist/vector-mock.js +3 -8
  442. package/dist/vector-mock.js.map +1 -1
  443. package/dist/vector-stores.cjs +2 -4
  444. package/dist/vector-stores.cjs.map +1 -1
  445. package/dist/vector-stores.d.cts.map +1 -1
  446. package/dist/vector-stores.d.ts.map +1 -1
  447. package/dist/vector-stores.js +2 -4
  448. package/dist/vector-stores.js.map +1 -1
  449. package/dist/veo-video.cjs +16 -14
  450. package/dist/veo-video.cjs.map +1 -1
  451. package/dist/veo-video.js +10 -8
  452. package/dist/veo-video.js.map +1 -1
  453. package/dist/video.cjs +21 -19
  454. package/dist/video.cjs.map +1 -1
  455. package/dist/video.d.cts.map +1 -1
  456. package/dist/video.d.ts.map +1 -1
  457. package/dist/video.js +20 -18
  458. package/dist/video.js.map +1 -1
  459. package/dist/ws-framing.cjs +12 -6
  460. package/dist/ws-framing.cjs.map +1 -1
  461. package/dist/ws-framing.d.cts +2 -0
  462. package/dist/ws-framing.d.cts.map +1 -1
  463. package/dist/ws-framing.d.ts +2 -0
  464. package/dist/ws-framing.d.ts.map +1 -1
  465. package/dist/ws-framing.js +12 -6
  466. package/dist/ws-framing.js.map +1 -1
  467. package/dist/ws-gemini-live.cjs +131 -96
  468. package/dist/ws-gemini-live.cjs.map +1 -1
  469. package/dist/ws-gemini-live.d.cts +0 -1
  470. package/dist/ws-gemini-live.d.cts.map +1 -1
  471. package/dist/ws-gemini-live.d.ts +0 -1
  472. package/dist/ws-gemini-live.d.ts.map +1 -1
  473. package/dist/ws-gemini-live.js +130 -95
  474. package/dist/ws-gemini-live.js.map +1 -1
  475. package/dist/ws-live.cjs +6 -5
  476. package/dist/ws-live.cjs.map +1 -1
  477. package/dist/ws-live.js +5 -4
  478. package/dist/ws-live.js.map +1 -1
  479. package/dist/ws-realtime.cjs +108 -124
  480. package/dist/ws-realtime.cjs.map +1 -1
  481. package/dist/ws-realtime.d.cts.map +1 -1
  482. package/dist/ws-realtime.d.ts.map +1 -1
  483. package/dist/ws-realtime.js +105 -121
  484. package/dist/ws-realtime.js.map +1 -1
  485. package/dist/ws-responses.cjs +80 -59
  486. package/dist/ws-responses.cjs.map +1 -1
  487. package/dist/ws-responses.d.cts.map +1 -1
  488. package/dist/ws-responses.d.ts.map +1 -1
  489. package/dist/ws-responses.js +79 -58
  490. package/dist/ws-responses.js.map +1 -1
  491. package/package.json +4 -1
  492. package/skills/write-fixtures/SKILL.md +57 -16
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.js","names":[],"sources":["../src/gemini.ts"],"sourcesContent":["/**\n * Google Gemini GenerateContent API support.\n *\n * Translates incoming Gemini requests into the ChatCompletionRequest format\n * used by the fixture router, and converts fixture responses back into the\n * Gemini GenerateContent streaming (or non-streaming) format.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n AudioResponse,\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n HandlerDefaults,\n RecordedTimings,\n RecordProviderKey,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n isAudioResponse,\n extractOverrides,\n requireServedFunctionToolCalls,\n requireEmittedFunctionToolCalls,\n validateToolsField,\n formatToMime,\n flattenHeaders,\n isJsonObject,\n getContext,\n getTestId,\n resolveFixtureBlockOutcome,\n resolveResponse,\n resolveUsage,\n resolveStrictMode,\n resolveReasoningForModel,\n strictOverrideField,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n servedToolArgs,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic, recordMatchOptions } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaosAsync } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { planMisbehavior, recordMisbehaviorOutcome, type MisbehaviorPlan } from \"./misbehavior.js\";\n\n// ─── Gemini request types ───────────────────────────────────────────────────\n\ninterface GeminiPart {\n text?: string;\n thought?: boolean;\n functionCall?: { name: string; args: Record<string, unknown>; id?: string };\n functionResponse?: { name: string; response: unknown; id?: string };\n inlineData?: { mimeType: string; data: string };\n}\n\ninterface GeminiContent {\n role?: string;\n parts: GeminiPart[];\n}\n\ninterface GeminiFunctionDeclaration {\n name: string;\n description?: string;\n parameters?: object;\n parametersJsonSchema?: object;\n}\n\ninterface GeminiToolDef {\n functionDeclarations?: GeminiFunctionDeclaration[];\n}\n\ninterface GeminiRequest {\n contents?: GeminiContent[];\n systemInstruction?: GeminiContent;\n tools?: GeminiToolDef[];\n generationConfig?: {\n temperature?: number;\n maxOutputTokens?: number;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n// ─── Input conversion: Gemini → ChatCompletions messages ────────────────────\n\n/**\n * Validate the shape of a Gemini `generateContent` body.\n *\n * Returns an error detail string when the shape is wrong, or null when\n * `geminiToCompletionRequest` can safely consume it. Wrong types here used\n * to throw inside the converter (`parts.filter` on a missing `parts`,\n * iterating the characters of a string `contents`, `.flatMap` on a string\n * `tools`) and surface as 500s. A present-but-part-less content or\n * systemInstruction is tolerated (treated as no text) rather than rejected.\n */\nfunction validateGeminiBody(req: GeminiRequest): string | null {\n if (req.contents !== undefined && req.contents !== null && !Array.isArray(req.contents)) {\n return \"contents must be an array\";\n }\n if (Array.isArray(req.contents)) {\n for (let i = 0; i < req.contents.length; i++) {\n const content = req.contents[i] as unknown;\n if (content === null || typeof content !== \"object\" || Array.isArray(content)) {\n return `contents[${i}] must be an object`;\n }\n const parts = (content as { parts?: unknown }).parts;\n if (parts !== undefined && parts !== null && !Array.isArray(parts)) {\n return `contents[${i}].parts must be an array`;\n }\n }\n }\n if (req.systemInstruction !== undefined && req.systemInstruction !== null) {\n if (typeof req.systemInstruction !== \"object\" || Array.isArray(req.systemInstruction)) {\n return \"systemInstruction must be an object\";\n }\n const parts = req.systemInstruction.parts as unknown;\n if (parts !== undefined && parts !== null && !Array.isArray(parts)) {\n return \"systemInstruction.parts must be an array\";\n }\n }\n const toolsError = validateToolsField(req.tools);\n if (toolsError) return toolsError;\n if (Array.isArray(req.tools)) {\n for (const [toolIndex, tool] of req.tools.entries()) {\n if (!Array.isArray(tool.functionDeclarations)) continue;\n const declarationIndex = tool.functionDeclarations.findIndex(\n (declaration) => declaration === null,\n );\n if (declarationIndex !== -1) {\n return `tools[${toolIndex}].functionDeclarations[${declarationIndex}] must not be null`;\n }\n }\n }\n return null;\n}\n\nexport function geminiToCompletionRequest(\n req: GeminiRequest,\n model: string,\n stream: boolean,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // systemInstruction → system message\n if (req.systemInstruction) {\n const text = (req.systemInstruction.parts ?? [])\n .filter((p) => p !== null && p !== undefined && p.text !== undefined)\n .map((p) => p.text!)\n .join(\"\");\n if (text) {\n messages.push({ role: \"system\", content: text });\n }\n }\n\n if (req.contents) {\n let callCounter = 0;\n for (const content of req.contents) {\n const role = content.role ?? \"user\";\n\n if (role === \"user\") {\n // Check for functionResponse parts\n const parts = content.parts ?? [];\n const funcResponses = parts.filter(\n (p) => p !== null && p !== undefined && p.functionResponse,\n );\n const textParts = parts.filter(\n (p) => p !== null && p !== undefined && p.text !== undefined && !p.thought,\n );\n\n if (funcResponses.length > 0) {\n // functionResponse → tool message; match IDs from the preceding assistant's tool_calls\n const lastAssistant = [...messages]\n .reverse()\n .find((m) => m.role === \"assistant\" && m.tool_calls);\n const matchedToolCallIds = new Set<string>();\n for (const part of funcResponses) {\n const matchingCall = lastAssistant?.tool_calls?.find(\n (tc) =>\n tc.function.name === part.functionResponse!.name && !matchedToolCallIds.has(tc.id),\n );\n if (matchingCall) matchedToolCallIds.add(matchingCall.id);\n const toolCallId =\n matchingCall?.id ?? `call_gemini_${part.functionResponse!.name}_${callCounter++}`;\n messages.push({\n role: \"tool\",\n content:\n typeof part.functionResponse!.response === \"string\"\n ? part.functionResponse!.response\n : JSON.stringify(part.functionResponse!.response),\n tool_call_id: toolCallId,\n });\n }\n // Any text parts alongside → user message\n if (textParts.length > 0) {\n messages.push({\n role: \"user\",\n content: textParts.map((p) => p.text!).join(\"\"),\n });\n }\n } else {\n // Regular user text\n const text = textParts.map((p) => p.text!).join(\"\");\n messages.push({ role: \"user\", content: text });\n }\n } else if (role === \"model\") {\n // Check for functionCall parts\n const parts = content.parts ?? [];\n const funcCalls = parts.filter((p) => p !== null && p !== undefined && p.functionCall);\n const textParts = parts.filter(\n (p) => p !== null && p !== undefined && p.text !== undefined && !p.thought,\n );\n\n if (funcCalls.length > 0) {\n const text = textParts.map((p) => p.text!).join(\"\");\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: funcCalls.map((fc, i) => ({\n id: fc.functionCall!.id ?? `call_gemini_${fc.functionCall!.name}_${i}`,\n type: \"function\" as const,\n function: {\n name: fc.functionCall!.name,\n arguments: JSON.stringify(fc.functionCall!.args ?? {}),\n },\n })),\n });\n } else {\n const text = textParts.map((p) => p.text!).join(\"\");\n messages.push({ role: \"assistant\", content: text });\n }\n }\n // Unrecognized roles (not \"user\" or \"model\") are silently dropped.\n // Gemini only defines \"user\" and \"model\"; any other value indicates\n // a malformed request or an unsupported future role.\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n const decls = req.tools.flatMap((t) => t.functionDeclarations ?? []);\n if (decls.length > 0) {\n tools = decls.map((d) => ({\n type: \"function\" as const,\n function: {\n name: d.name,\n description: d.description,\n parameters: d.parameters ?? d.parametersJsonSchema,\n },\n }));\n }\n }\n\n return {\n model,\n messages,\n stream,\n temperature: req.generationConfig?.temperature,\n max_tokens: req.generationConfig?.maxOutputTokens,\n top_p: req.generationConfig?.topP as number | undefined,\n top_k: req.generationConfig?.topK as number | undefined,\n tools,\n };\n}\n\n// ─── Response building: fixture → Gemini format ─────────────────────────────\n\nfunction geminiFinishReason(finishReason: string | undefined, defaultReason: string): string {\n if (!finishReason) return defaultReason;\n if (finishReason === \"stop\") return \"STOP\";\n if (finishReason === \"tool_calls\") return \"FUNCTION_CALL\";\n if (finishReason === \"length\") return \"MAX_TOKENS\";\n if (finishReason === \"content_filter\") return \"SAFETY\";\n // Pass through unrecognized values as-is\n return finishReason;\n}\n\nfunction geminiUsageMetadata(overrides?: ResponseOverrides): {\n promptTokenCount: number;\n candidatesTokenCount: number;\n totalTokenCount: number;\n} {\n if (!overrides?.usage)\n return { promptTokenCount: 0, candidatesTokenCount: 0, totalTokenCount: 0 };\n const prompt =\n overrides.usage.promptTokenCount ??\n overrides.usage.prompt_tokens ??\n overrides.usage.input_tokens ??\n 0;\n const candidates =\n overrides.usage.candidatesTokenCount ??\n overrides.usage.completion_tokens ??\n overrides.usage.output_tokens ??\n 0;\n const total = overrides.usage.totalTokenCount ?? prompt + candidates;\n return {\n promptTokenCount: prompt,\n candidatesTokenCount: candidates,\n totalTokenCount: total,\n };\n}\n\ninterface GeminiResponseChunk {\n candidates: {\n content?: { role: string; parts: GeminiPart[] };\n finishReason?: string;\n finishMessage?: string;\n safetyRatings?: { category: string; probability: string; blocked: boolean }[];\n index: number;\n }[];\n usageMetadata?: {\n promptTokenCount: number;\n candidatesTokenCount: number;\n totalTokenCount: number;\n };\n}\n\nfunction buildGeminiTextStreamChunks(\n content: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk[] {\n const chunks: GeminiResponseChunk[] = [];\n const effectiveFinish = geminiFinishReason(overrides?.finishReason, \"STOP\");\n const usage = geminiUsageMetadata(overrides);\n\n // Reasoning chunks (thought: true)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice, thought: true }] },\n index: 0,\n },\n ],\n });\n }\n }\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n const isLast = i + chunkSize >= content.length;\n const chunk: GeminiResponseChunk = {\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice }] },\n index: 0,\n ...(isLast ? { finishReason: effectiveFinish } : {}),\n },\n ],\n ...(isLast ? { usageMetadata: usage } : {}),\n };\n chunks.push(chunk);\n }\n\n // Handle empty content\n if (content.length === 0) {\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: \"\" }] },\n finishReason: effectiveFinish,\n index: 0,\n },\n ],\n usageMetadata: usage,\n });\n }\n\n return chunks;\n}\n\nfunction parseToolCallPart(tc: ToolCall, logger: Logger): GeminiPart {\n const args = servedToolArgs(tc, \"object\", logger);\n // Surface the fixture's tool_call.id on the Gemini functionCall response\n // so clients can preserve it across the round-trip and any\n // toolCallId-keyed follow-up fixtures match. Pairs with v1.23.1's\n // INGEST-direction fix (#196) which preserves the id when aimock parses\n // an incoming Gemini request — that fix only helps if the id was in the\n // response body to begin with. Without this egress fix, aimock emits\n // `{ functionCall: { name, args } }` (no id), so even clients that\n // diligently preserve `functionCall.id` across the round-trip never see\n // an id to preserve. Backward-compatible: fixtures that don't pin a\n // tc.id continue to serialize without one (the ingest path's fallback\n // generator handles those).\n const functionCall: GeminiPart[\"functionCall\"] = {\n name: tc.name,\n args: args.value as Record<string, unknown>,\n };\n if (tc.id) {\n functionCall.id = tc.id;\n }\n return { functionCall };\n}\n\nfunction buildGeminiToolCallStreamChunks(\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk[] {\n const chunks: GeminiResponseChunk[] = [];\n\n // Reasoning chunks (thought: true) — mirror the content+tool / text paths so a\n // tool-only fixture on a reasoning-capable model still emits its thought channel.\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice, thought: true }] },\n index: 0,\n },\n ],\n });\n }\n }\n\n const parts: GeminiPart[] = toolCalls.map((tc) => parseToolCallPart(tc, logger));\n\n // Gemini sends all tool calls in a single response chunk\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"FUNCTION_CALL\"),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nfunction buildGeminiTextResponse(\n content: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk {\n const parts: GeminiPart[] = [];\n if (reasoning) {\n parts.push({ text: reasoning, thought: true });\n }\n parts.push({ text: content });\n\n return {\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"STOP\"),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n };\n}\n\nfunction buildGeminiToolCallResponse(\n toolCalls: ToolCall[],\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk {\n const parts: GeminiPart[] = [];\n if (reasoning) {\n parts.push({ text: reasoning, thought: true });\n }\n parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n\n return {\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"FUNCTION_CALL\"),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n };\n}\n\nfunction buildGeminiContentWithToolCallsStreamChunks(\n content: string,\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): GeminiResponseChunk[] {\n const chunks: GeminiResponseChunk[] = [];\n\n // Reasoning chunks (thought: true)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice, thought: true }] },\n index: 0,\n },\n ],\n });\n }\n }\n\n if (blocks && blocks.length > 0) {\n // NEW path (#274): stream chunks whose parts follow the blocks' ARRAY ORDER,\n // so a tool-first / interleaved fixture emits its functionCall part before\n // its text part. Gemini's ordered `parts` make this fully expressible. The\n // terminal block carries the finishReason regardless of its type. Legacy\n // fixtures (no `blocks`) never enter here — see the else branch below.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const resolved = outcome.ordered;\n resolved.forEach((block, i) => {\n const isLast = i === resolved.length - 1;\n const finishReason = isLast\n ? geminiFinishReason(\n overrides?.finishReason,\n outcome.hasToolCalls ? \"FUNCTION_CALL\" : \"STOP\",\n )\n : undefined;\n if (block.type === \"toolCall\") {\n const part = parseToolCallPart(\n { name: block.name, arguments: block.arguments, id: block.id },\n logger,\n );\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [part] },\n ...(finishReason ? { finishReason } : {}),\n index: 0,\n },\n ],\n ...(isLast ? { usageMetadata: geminiUsageMetadata(overrides) } : {}),\n });\n } else {\n const text = block.text;\n if (text.length === 0) {\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: \"\" }] },\n ...(finishReason ? { finishReason } : {}),\n index: 0,\n },\n ],\n ...(isLast ? { usageMetadata: geminiUsageMetadata(overrides) } : {}),\n });\n } else {\n for (let j = 0; j < text.length; j += chunkSize) {\n const slice = text.slice(j, j + chunkSize);\n const lastSlice = j + chunkSize >= text.length;\n const sliceFinish = isLast && lastSlice ? finishReason : undefined;\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice }] },\n ...(sliceFinish ? { finishReason: sliceFinish } : {}),\n index: 0,\n },\n ],\n ...(isLast && lastSlice ? { usageMetadata: geminiUsageMetadata(overrides) } : {}),\n });\n }\n }\n }\n });\n\n return chunks;\n }\n\n if (content.length === 0) {\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: \"\" }] },\n index: 0,\n },\n ],\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice }] },\n index: 0,\n },\n ],\n });\n }\n }\n\n const parts: GeminiPart[] = toolCalls.map((tc) => parseToolCallPart(tc, logger));\n\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"FUNCTION_CALL\"),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n });\n\n return chunks;\n}\n\nfunction buildGeminiContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): GeminiResponseChunk {\n const parts: GeminiPart[] = [];\n if (reasoning) {\n parts.push({ text: reasoning, thought: true });\n }\n\n let blockHasTools: boolean | undefined;\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-streaming `parts[]` array is positionally observable, so\n // emit parts in the fixture's ARRAY ORDER (after any leading thought part).\n // A toolCall block before a text block therefore yields a functionCall part\n // ahead of the text — matching the streaming path for the same `blocks`.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const resolved = outcome.ordered;\n blockHasTools = outcome.hasToolCalls;\n for (const block of resolved) {\n if (block.type === \"toolCall\") {\n parts.push(\n parseToolCallPart({ name: block.name, arguments: block.arguments, id: block.id }, logger),\n );\n } else {\n parts.push({ text: block.text });\n }\n }\n } else {\n // LEGACY PATH (unchanged): text part first, then functionCall parts.\n parts.push({ text: content });\n parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n }\n\n return {\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(\n overrides?.finishReason,\n blockHasTools === false ? \"STOP\" : \"FUNCTION_CALL\",\n ),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n };\n}\n\n// ─── Audio response builders ────────────────────────────────────────────────\n\nfunction resolveAudioInlineData(audio: AudioResponse): { mimeType: string; data: string } {\n if (typeof audio.audio === \"string\") {\n return { mimeType: formatToMime(audio.format ?? \"mp3\"), data: audio.audio };\n }\n return {\n mimeType: audio.audio.contentType ?? \"audio/mpeg\",\n data: audio.audio.b64Json,\n };\n}\n\n// Build the ordered Gemini parts for an audio turn: inlineData audio first,\n// then any companion modalities (reasoning/thought, text content, tool calls)\n// the recorder preserved on the AudioResponse. Without this, a recorded turn\n// that interleaves audio with a functionCall and/or text silently drops the\n// companions on replay.\n// NOTE: audio companions are only re-emitted on this Gemini replay path because\n// `audioB64` collapse is currently Gemini-only — a cross-provider audio fixture\n// would not replay its companions.\n// `effReasoning` is the capability-gated reasoning string (already passed\n// through resolveReasoningForModel at the dispatch). When the requested model\n// is not reasoning-capable (and strict mode is on), it is undefined and the\n// companion `thought` part is suppressed — mirroring the text/content+tool\n// branches so the audio path does not replay reasoning a real Gemini model\n// would never emit.\nfunction buildGeminiAudioParts(\n audio: AudioResponse,\n toolCalls: ToolCall[],\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiPart[] {\n const inlineData = resolveAudioInlineData(audio);\n const parts: GeminiPart[] = [{ inlineData }];\n if (effReasoning) {\n parts.push({ text: effReasoning, thought: true });\n }\n if (audio.content) {\n parts.push({ text: audio.content });\n }\n if (toolCalls.length) {\n parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n }\n return parts;\n}\n\nfunction buildGeminiAudioResponse(\n audio: AudioResponse,\n toolCalls: ToolCall[],\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk {\n return {\n candidates: [\n {\n content: {\n role: \"model\",\n parts: buildGeminiAudioParts(audio, toolCalls, logger, effReasoning),\n },\n finishReason: toolCalls.length ? \"FUNCTION_CALL\" : \"STOP\",\n index: 0,\n },\n ],\n usageMetadata: { promptTokenCount: 0, candidatesTokenCount: 0, totalTokenCount: 0 },\n };\n}\n\nfunction buildGeminiAudioStreamChunks(\n audio: AudioResponse,\n toolCalls: ToolCall[],\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk[] {\n return [\n {\n candidates: [\n {\n content: {\n role: \"model\",\n parts: buildGeminiAudioParts(audio, toolCalls, logger, effReasoning),\n },\n finishReason: toolCalls.length ? \"FUNCTION_CALL\" : \"STOP\",\n index: 0,\n },\n ],\n usageMetadata: { promptTokenCount: 0, candidatesTokenCount: 0, totalTokenCount: 0 },\n },\n ];\n}\n\n// ─── SSE writer for Gemini streaming ────────────────────────────────────────\n\ninterface GeminiStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeGeminiSSEStream(\n res: http.ServerResponse,\n chunks: GeminiResponseChunk[],\n optionsOrLatency?: number | GeminiStreamOptions,\n): Promise<boolean> {\n const opts: GeminiStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const { recordedTimings, replaySpeed } = opts;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const chunk of chunks) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n // Gemini uses data-only SSE (no event: prefix, no [DONE])\n res.write(`data: ${JSON.stringify(chunk)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n/** Prepare native fault output once; the journal describes these exact parts. */\nfunction prepareGeminiMisbehavior(\n plan: MisbehaviorPlan,\n request: ChatCompletionRequest,\n streaming: boolean,\n chunkSize: number,\n logger: Logger,\n strict: boolean,\n): { chunks: GeminiResponseChunk[]; summary: MisbehaviorPlan[\"summary\"] } {\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n if (!(combined || isToolCallResponse(response) || isTextResponse(response))) {\n throw new Error(\"Applied Gemini misbehavior requires a chat response\");\n }\n const outcome =\n combined && response.blocks?.length ? resolveFixtureBlockOutcome(response.blocks) : undefined;\n const content = outcome?.content ?? (\"content\" in response ? (response.content ?? \"\") : \"\");\n // The planner skips a custom-call fixture on this wire, so this narrowing\n // never throws; the normal path's guard rejects it instead.\n const calls = outcome?.toolCalls ?? (\"toolCalls\" in response ? (response.toolCalls ?? []) : []);\n const overrides = extractOverrides(response);\n delete overrides.usage;\n const fault = plan.summary.fault;\n const candidate: GeminiResponseChunk[\"candidates\"][number] & { content: GeminiContent } = {\n content: { role: \"model\", parts: [] },\n index: 0,\n };\n let chunks: GeminiResponseChunk[];\n if (fault === \"tool-args-invalid-json\") {\n const target = calls[plan.target?.index ?? 0];\n if (!target) throw new Error(\"Gemini malformed-call plan requires its selected tool call\");\n chunks = [\n {\n candidates: [\n {\n index: 0,\n finishReason: \"MALFORMED_FUNCTION_CALL\",\n finishMessage: `Malformed function call: ${target.name}(${target.arguments})`,\n },\n ],\n },\n ];\n } else if (fault === \"tool-unknown-name\" && !request.tools?.length) {\n candidate.finishReason = \"UNEXPECTED_TOOL_CALL\";\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"stop-length-mid-tool\") {\n // Object arguments cannot be partial; preserve only text preceding the cut.\n candidate.content.parts = content ? [{ text: content }] : [];\n candidate.finishReason = \"MAX_TOKENS\";\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"empty-response\") {\n candidate.content.parts = [{ text: \"\" }];\n candidate.finishReason = \"STOP\";\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"content-filter\") {\n candidate.finishReason = \"SAFETY\";\n candidate.safetyRatings = [\n { category: \"HARM_CATEGORY_DANGEROUS_CONTENT\", probability: \"HIGH\", blocked: true },\n ];\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"reasoning-only\") {\n candidate.content.parts = [{ text: plan.reasoning ?? \"\", thought: true }];\n candidate.finishReason = \"MAX_TOKENS\";\n chunks = [{ candidates: [candidate] }];\n } else {\n const reasoning = resolveReasoningForModel(response.reasoning, request.model, strict, logger);\n if (fault === \"tool-unknown-name\") overrides.finishReason = \"stop\";\n if (combined) {\n chunks = streaming\n ? buildGeminiContentWithToolCallsStreamChunks(\n content,\n calls,\n chunkSize,\n logger,\n reasoning,\n overrides,\n response.blocks,\n )\n : [\n buildGeminiContentWithToolCallsResponse(\n content,\n calls,\n logger,\n reasoning,\n overrides,\n response.blocks,\n ),\n ];\n } else if (isToolCallResponse(response)) {\n chunks = streaming\n ? buildGeminiToolCallStreamChunks(calls, chunkSize, logger, reasoning, overrides)\n : [buildGeminiToolCallResponse(calls, logger, reasoning, overrides)];\n } else {\n chunks = streaming\n ? buildGeminiTextStreamChunks(content, chunkSize, reasoning, overrides)\n : [buildGeminiTextResponse(content, reasoning, overrides)];\n }\n }\n const parts = chunks.flatMap((chunk) =>\n chunk.candidates.flatMap((item) => item.content?.parts ?? []),\n );\n const servedToolCalls = parts.flatMap((part) =>\n part.functionCall\n ? [\n {\n name: part.functionCall.name,\n arguments: JSON.stringify(part.functionCall.args),\n ...(part.functionCall.id ? { id: part.functionCall.id } : {}),\n },\n ]\n : [],\n );\n const prompt = request.messages\n .map((message) =>\n typeof message.content === \"string\"\n ? message.content\n : (message.content ?? []).map((part) => part.text ?? \"\").join(\"\"),\n )\n .join(\"\");\n const output = parts\n .map(\n (part) =>\n part.text ??\n (part.functionCall ? part.functionCall.name + JSON.stringify(part.functionCall.args) : \"\"),\n )\n .join(\"\");\n const usage = resolveUsage(undefined, prompt, output);\n for (const chunk of chunks) delete chunk.usageMetadata;\n chunks[chunks.length - 1].usageMetadata = {\n promptTokenCount: usage.prompt_tokens,\n candidatesTokenCount: usage.completion_tokens,\n totalTokenCount: usage.total_tokens,\n };\n return { chunks, summary: { ...plan.summary, servedToolCalls } };\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleGemini(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n streaming: boolean,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n providerKey: RecordProviderKey = \"gemini\",\n): Promise<void> {\n const { logger } = defaults;\n const wire = providerKey === \"vertexai\" ? \"Vertex AI\" : \"Gemini\";\n setCorsHeaders(res);\n\n let geminiReq: GeminiRequest;\n try {\n geminiReq = JSON.parse(raw) as GeminiRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? `/v1beta/models/${model}:generateContent`,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON body: ${detail}`,\n code: 400,\n status: \"INVALID_ARGUMENT\",\n },\n }),\n );\n return;\n }\n\n // Reject non-object bodies (e.g. `null`) before touching fields —\n // otherwise the converter dereference throws a TypeError that surfaces as\n // a 500 instead of a 400.\n if (!isJsonObject(geminiReq)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? `/v1beta/models/${model}:generateContent`,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n code: 400,\n status: \"INVALID_ARGUMENT\",\n },\n }),\n );\n return;\n }\n\n // Reject wrong-typed fields before the converter dereferences them.\n const geminiShapeError = validateGeminiBody(geminiReq);\n if (geminiShapeError) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? `/v1beta/models/${model}:generateContent`,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Invalid argument: ${geminiShapeError}`,\n code: 400,\n status: \"INVALID_ARGUMENT\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = geminiToCompletionRequest(geminiReq, model, streaming);\n completionReq._endpointType = \"chat\";\n completionReq._context = getContext(req);\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n // Record mode proxies on a miss to capture a fresh turn (see record gate\n // below), so keep turnIndex strict to prevent an earlier-turn fixture from\n // shadowing a longer request and skipping the new turn's recording.\n recordMatchOptions(!!defaults.record, defaults.logger),\n );\n const path = req.url ?? `/v1beta/models/${model}:generateContent`;\n\n if (fixture) {\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n await applyChaosAsync(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n {\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n if (effectiveStrict) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", path, skippedBySequenceOrTurn));\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 503,\n JSON.stringify({\n error: {\n message: strictMessage,\n code: 503,\n status: \"UNAVAILABLE\",\n },\n }),\n );\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n providerKey,\n path,\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({\n error: {\n message: \"No fixture matched\",\n code: 404,\n status: \"NOT_FOUND\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const misbehavior = planMisbehavior({\n wire: \"gemini\",\n toolCallIdMode: \"authored-nonempty\",\n fixture,\n response,\n request: completionReq,\n stream: streaming,\n defaults,\n rawHeaders: req.headers,\n url: req.url,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: misbehavior.status, fixture, error: misbehavior.message },\n });\n recordMisbehaviorOutcome({ entry, summary: misbehavior.summary, defaults, testId });\n if (misbehavior.summary?.evaluations.length === 0) {\n logger.error(`${req.method ?? \"POST\"} ${path}: ${misbehavior.message}`);\n }\n writeErrorResponse(\n res,\n misbehavior.status,\n JSON.stringify({\n error: {\n code: misbehavior.status,\n message: misbehavior.message,\n status: misbehavior.status === 400 ? \"INVALID_ARGUMENT\" : \"UNIMPLEMENTED\",\n },\n }),\n );\n return;\n }\n\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n\n if (misbehavior.kind === \"applied\") {\n const prepared = prepareGeminiMisbehavior(\n misbehavior,\n completionReq,\n streaming,\n chunkSize,\n logger,\n resolveStrictMode(defaults.strict, req.headers),\n );\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({ entry, summary: prepared.summary, defaults, testId });\n if (!streaming) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(prepared.chunks[0]));\n } else {\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiSSEStream(res, prepared.chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n entry.response.interrupted = true;\n entry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const errorEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n recordMisbehaviorOutcome({ entry: errorEntry, summary: misbehavior.summary, defaults, testId });\n // Gemini-style error format: { error: { code, message, status } }\n const geminiError = {\n error: {\n code: status,\n message: response.error.message,\n status: response.error.type ?? \"ERROR\",\n },\n };\n writeErrorResponse(res, status, JSON.stringify(geminiError), {\n retryAfter: response.retryAfter,\n });\n return;\n }\n\n // Audio response\n if (isAudioResponse(response)) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n // A non-array companion `toolCalls` emits no tool parts, as before custom calls existed.\n const audioToolCalls = Array.isArray(response.toolCalls)\n ? requireServedFunctionToolCalls(response, wire)\n : [];\n if (!streaming) {\n const body = buildGeminiAudioResponse(response, audioToolCalls, logger, effReasoning);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiAudioStreamChunks(response, audioToolCalls, logger, effReasoning);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiSSEStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isCombinedFixtureResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Gemini API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, wire);\n if (!streaming) {\n const body = buildGeminiContentWithToolCallsResponse(\n response.content ?? \"\",\n functionToolCalls,\n logger,\n effReasoning,\n overrides,\n response.blocks,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiContentWithToolCallsStreamChunks(\n response.content ?? \"\",\n functionToolCalls,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n response.blocks,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiSSEStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Gemini API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n if (!streaming) {\n const body = buildGeminiTextResponse(response.content, effReasoning, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiTextStreamChunks(\n response.content,\n chunkSize,\n effReasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiSSEStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Gemini API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, wire);\n if (!streaming) {\n const body = buildGeminiToolCallResponse(functionToolCalls, logger, effReasoning, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiToolCallStreamChunks(\n functionToolCalls,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiSSEStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n const unknownEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordMisbehaviorOutcome({ entry: unknownEntry, summary: misbehavior.summary, defaults, testId });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n code: 500,\n status: \"INTERNAL\",\n },\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gemini.js","names":["renderGeminiUsage"],"sources":["../src/gemini.ts"],"sourcesContent":["/**\n * Google Gemini GenerateContent API support.\n *\n * Translates incoming Gemini requests into the ChatCompletionRequest format\n * used by the fixture router, and converts fixture responses back into the\n * Gemini GenerateContent streaming (or non-streaming) format.\n */\nimport { resolveRequestDecisions, strictOverrideField } from \"./response/policy.js\";\nimport { renderError } from \"./response/errors.js\";\nimport { serveRawResponse } from \"./response/raw.js\";\n\nimport type * as http from \"node:http\";\nimport type {\n AudioResponse,\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n HandlerDefaults,\n RecordProviderKey,\n ResponseOverrides,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n isRawResponse,\n isAudioResponse,\n extractOverrides,\n requireServedFunctionToolCalls,\n requireEmittedFunctionToolCalls,\n validateToolsField,\n formatToMime,\n flattenHeaders,\n isJsonObject,\n getContext,\n getTestId,\n resolveFixtureBlockOutcome,\n resolveCitations,\n resolveResponse,\n resolveReasoningForModel,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n servedToolArgs,\n fixtureFiles,\n type ResolvedCitation,\n surrogateSafeChunks,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic, recordMatchOptions } from \"./router.js\";\nimport { writeErrorResponse, sseCodec } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { Logger } from \"./logger.js\";\nimport { applyChaosAsync } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { planMisbehavior, recordMisbehaviorOutcome, type MisbehaviorPlan } from \"./misbehavior.js\";\nimport { mapGeminiFinish } from \"./response/finish.js\";\nimport { deliverJson, deliverStream } from \"./response/deliver.js\";\nimport { proxyInterruption, withHangUpDetection } from \"./response/proxy.js\";\nimport { misbehaviorPayloadLength, servedPayloadLength } from \"./response/timing.js\";\nimport { render as renderGeminiUsage } from \"./response/usage/codecs/gemini.js\";\n\n// ─── Gemini request types ───────────────────────────────────────────────────\n\ninterface GeminiPart {\n text?: string;\n thought?: boolean;\n functionCall?: { name: string; args: Record<string, unknown>; id?: string };\n functionResponse?: { name: string; response: unknown; id?: string };\n inlineData?: { mimeType: string; data: string };\n}\n\ninterface GeminiContent {\n role?: string;\n parts: GeminiPart[];\n}\n\ninterface GeminiFunctionDeclaration {\n name: string;\n description?: string;\n parameters?: object;\n parametersJsonSchema?: object;\n}\n\ninterface GeminiToolDef {\n functionDeclarations?: GeminiFunctionDeclaration[];\n}\n\ninterface GeminiRequest {\n contents?: GeminiContent[];\n systemInstruction?: GeminiContent;\n tools?: GeminiToolDef[];\n generationConfig?: {\n temperature?: number;\n maxOutputTokens?: number;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\n// ─── Input conversion: Gemini → ChatCompletions messages ────────────────────\n\n/**\n * Validate the shape of a Gemini `generateContent` body.\n *\n * Returns an error detail string when the shape is wrong, or null when\n * `geminiToCompletionRequest` can safely consume it. Wrong types here used\n * to throw inside the converter (`parts.filter` on a missing `parts`,\n * iterating the characters of a string `contents`, `.flatMap` on a string\n * `tools`) and surface as 500s. A present-but-part-less content or\n * systemInstruction is tolerated (treated as no text) rather than rejected.\n */\nfunction validateGeminiBody(req: GeminiRequest): string | null {\n if (req.contents !== undefined && req.contents !== null && !Array.isArray(req.contents)) {\n return \"contents must be an array\";\n }\n if (Array.isArray(req.contents)) {\n for (let i = 0; i < req.contents.length; i++) {\n const content = req.contents[i] as unknown;\n if (content === null || typeof content !== \"object\" || Array.isArray(content)) {\n return `contents[${i}] must be an object`;\n }\n const parts = (content as { parts?: unknown }).parts;\n if (parts !== undefined && parts !== null && !Array.isArray(parts)) {\n return `contents[${i}].parts must be an array`;\n }\n }\n }\n if (req.systemInstruction !== undefined && req.systemInstruction !== null) {\n if (typeof req.systemInstruction !== \"object\" || Array.isArray(req.systemInstruction)) {\n return \"systemInstruction must be an object\";\n }\n const parts = req.systemInstruction.parts as unknown;\n if (parts !== undefined && parts !== null && !Array.isArray(parts)) {\n return \"systemInstruction.parts must be an array\";\n }\n }\n const toolsError = validateToolsField(req.tools);\n if (toolsError) return toolsError;\n if (Array.isArray(req.tools)) {\n for (const [toolIndex, tool] of req.tools.entries()) {\n if (!Array.isArray(tool.functionDeclarations)) continue;\n const declarationIndex = tool.functionDeclarations.findIndex(\n (declaration) => declaration === null,\n );\n if (declarationIndex !== -1) {\n return `tools[${toolIndex}].functionDeclarations[${declarationIndex}] must not be null`;\n }\n }\n }\n return null;\n}\n\nexport function geminiToCompletionRequest(\n req: GeminiRequest,\n model: string,\n stream: boolean,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // systemInstruction → system message\n if (req.systemInstruction) {\n const text = (req.systemInstruction.parts ?? [])\n .filter((p) => p !== null && p !== undefined && p.text !== undefined)\n .map((p) => p.text!)\n .join(\"\");\n if (text) {\n messages.push({ role: \"system\", content: text });\n }\n }\n\n if (req.contents) {\n let callCounter = 0;\n for (const content of req.contents) {\n const role = content.role ?? \"user\";\n\n if (role === \"user\") {\n // Check for functionResponse parts\n const parts = content.parts ?? [];\n const funcResponses = parts.filter(\n (p) => p !== null && p !== undefined && p.functionResponse,\n );\n const textParts = parts.filter(\n (p) => p !== null && p !== undefined && p.text !== undefined && !p.thought,\n );\n\n if (funcResponses.length > 0) {\n // functionResponse → tool message; match IDs from the preceding assistant's tool_calls\n const lastAssistant = [...messages]\n .reverse()\n .find((m) => m.role === \"assistant\" && m.tool_calls);\n const matchedToolCallIds = new Set<string>();\n for (const part of funcResponses) {\n const matchingCall = lastAssistant?.tool_calls?.find(\n (tc) =>\n tc.function.name === part.functionResponse!.name && !matchedToolCallIds.has(tc.id),\n );\n if (matchingCall) matchedToolCallIds.add(matchingCall.id);\n const toolCallId =\n matchingCall?.id ?? `call_gemini_${part.functionResponse!.name}_${callCounter++}`;\n messages.push({\n role: \"tool\",\n content:\n typeof part.functionResponse!.response === \"string\"\n ? part.functionResponse!.response\n : JSON.stringify(part.functionResponse!.response),\n tool_call_id: toolCallId,\n });\n }\n // Any text parts alongside → user message\n if (textParts.length > 0) {\n messages.push({\n role: \"user\",\n content: textParts.map((p) => p.text!).join(\"\"),\n });\n }\n } else {\n // Regular user text\n const text = textParts.map((p) => p.text!).join(\"\");\n messages.push({ role: \"user\", content: text });\n }\n } else if (role === \"model\") {\n // Check for functionCall parts\n const parts = content.parts ?? [];\n const funcCalls = parts.filter((p) => p !== null && p !== undefined && p.functionCall);\n const textParts = parts.filter(\n (p) => p !== null && p !== undefined && p.text !== undefined && !p.thought,\n );\n\n if (funcCalls.length > 0) {\n const text = textParts.map((p) => p.text!).join(\"\");\n messages.push({\n role: \"assistant\",\n content: text || null,\n tool_calls: funcCalls.map((fc, i) => ({\n id: fc.functionCall!.id ?? `call_gemini_${fc.functionCall!.name}_${i}`,\n type: \"function\" as const,\n function: {\n name: fc.functionCall!.name,\n arguments: JSON.stringify(fc.functionCall!.args ?? {}),\n },\n })),\n });\n } else {\n const text = textParts.map((p) => p.text!).join(\"\");\n messages.push({ role: \"assistant\", content: text });\n }\n }\n // Unrecognized roles (not \"user\" or \"model\") are silently dropped.\n // Gemini only defines \"user\" and \"model\"; any other value indicates\n // a malformed request or an unsupported future role.\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n const decls = req.tools.flatMap((t) => t.functionDeclarations ?? []);\n if (decls.length > 0) {\n tools = decls.map((d) => ({\n type: \"function\" as const,\n function: {\n name: d.name,\n description: d.description,\n parameters: d.parameters ?? d.parametersJsonSchema,\n },\n }));\n }\n }\n\n return {\n model,\n messages,\n stream,\n temperature: req.generationConfig?.temperature,\n max_tokens: req.generationConfig?.maxOutputTokens,\n top_p: req.generationConfig?.topP as number | undefined,\n top_k: req.generationConfig?.topK as number | undefined,\n tools,\n };\n}\n\n// ─── Response building: fixture → Gemini format ─────────────────────────────\n\nfunction geminiFinishReason(finishReason: string | undefined, defaultReason: string): string {\n if (!finishReason) return defaultReason;\n return mapGeminiFinish(finishReason);\n}\n\nfunction geminiUsageMetadata(overrides?: ResponseOverrides): {\n promptTokenCount: number;\n candidatesTokenCount: number;\n totalTokenCount: number;\n} {\n return renderGeminiUsage(overrides);\n}\n\ninterface GeminiResponseChunk {\n candidates: {\n content?: { role: string; parts: GeminiPart[] };\n finishReason?: string;\n finishMessage?: string;\n safetyRatings?: { category: string; probability: string; blocked: boolean }[];\n groundingMetadata?: GeminiGroundingMetadata;\n index: number;\n }[];\n usageMetadata?: {\n promptTokenCount: number;\n candidatesTokenCount: number;\n totalTokenCount: number;\n };\n}\n\ninterface GeminiGroundingMetadata {\n groundingChunks: { web: { uri: string; title: string } }[];\n groundingSupports: {\n segment: { partIndex: number; startIndex: number; endIndex: number; text: string };\n groundingChunkIndices: number[];\n }[];\n}\n\n// A text part of the Content that carries the grounding: its index in that\n// Content's `parts`, and the character span [start, end) of the cited text\n// it holds.\ninterface GeminiCitedTextPart {\n partIndex: number;\n start: number;\n end: number;\n}\n\ntype GeminiGroundingSupport = GeminiGroundingMetadata[\"groundingSupports\"][number];\n\n// The whole cited text as the single text part `partIndex`.\nfunction geminiWholeTextPart(text: string, partIndex: number): GeminiCitedTextPart[] {\n return [{ partIndex, start: 0, end: text.length }];\n}\n\n// Fixture `citations` as Gemini grounding supports, one or more per citation,\n// each pointing at grounding chunk `i` (the citation's index). The @google/genai\n// `Segment` type documents `startIndex` / `endIndex` as byte offsets \"in the\n// given Part\" — the Part at `partIndex` within its parent Content — so every\n// segment holds UTF-8 byte offsets into the text of one part. A citation that\n// spans several text parts becomes one support per part, in text order, all\n// naming the same grounding chunk; a client joining their segments gets the\n// whole cited text. An empty citation names the part holding its start.\nfunction geminiGroundingSupports(\n text: string,\n citations: ResolvedCitation[],\n textParts: GeminiCitedTextPart[],\n): GeminiGroundingSupport[] {\n const segment = (\n part: GeminiCitedTextPart,\n from: number,\n to: number,\n ): GeminiGroundingSupport[\"segment\"] => {\n const partText = text.slice(part.start, part.end);\n const start = Math.min(Math.max(from - part.start, 0), partText.length);\n const end = Math.min(Math.max(to - part.start, start), partText.length);\n const byteOffset = (charIndex: number) =>\n Buffer.byteLength(partText.slice(0, charIndex), \"utf8\");\n return {\n partIndex: part.partIndex,\n startIndex: byteOffset(start),\n endIndex: byteOffset(end),\n text: partText.slice(start, end),\n };\n };\n return citations.flatMap((c, i) => {\n const spanned = textParts.filter(\n (p) => Math.max(c.startIndex, p.start) < Math.min(c.endIndex, p.end),\n );\n const parts =\n spanned.length > 0\n ? spanned\n : [textParts.find((p) => c.startIndex < p.end) ?? textParts[textParts.length - 1]];\n return parts.map((p) => ({\n segment: segment(p, c.startIndex, c.endIndex),\n groundingChunkIndices: [i],\n }));\n });\n}\n\n// Fixture `citations` as Gemini `groundingMetadata` for one Content holding\n// `textParts`: one `web` grounding chunk per citation and the supports from\n// {@link geminiGroundingSupports}. File citations are skipped (see\n// resolveCitations): grounding chunks carry no file IDs. Nothing when the\n// fixture has no URL citations or the Content has no text part to cite.\nfunction geminiGroundingMetadata(\n text: string,\n overrides: ResponseOverrides | undefined,\n textParts: GeminiCitedTextPart[],\n): GeminiGroundingMetadata | undefined {\n const citations = resolveCitations(text, overrides?.citations);\n if (citations.length === 0 || textParts.length === 0) return undefined;\n return {\n groundingChunks: geminiGroundingChunks(citations),\n groundingSupports: geminiGroundingSupports(text, citations, textParts),\n };\n}\n\nfunction geminiGroundingChunks(\n citations: ResolvedCitation[],\n): GeminiGroundingMetadata[\"groundingChunks\"] {\n return citations.map((c) => ({ web: { uri: c.url, title: c.title } }));\n}\n\n// `{ groundingMetadata }` for the candidate holding `textParts`, or nothing\n// when {@link geminiGroundingMetadata} yields none.\nfunction geminiGroundingField(\n text: string,\n overrides: ResponseOverrides | undefined,\n textParts: GeminiCitedTextPart[],\n): { groundingMetadata?: GeminiGroundingMetadata } {\n const groundingMetadata = geminiGroundingMetadata(text, overrides, textParts);\n return groundingMetadata ? { groundingMetadata } : {};\n}\n\n// Streaming form of {@link geminiGroundingField}. `spans` are the character\n// spans of `text` that the streamed text chunks carry, in stream order, each\n// as part 0 of its own chunk. Each chunk's Content holds only that chunk's\n// text, so, per the `Segment` contract above, a chunk's supports must index\n// that chunk's part. (Neither @google/genai nor Google's public Gemini API\n// docs say streamed segments index the accumulated text instead.) Every chunk\n// holding some cited text carries groundingMetadata whose supports cover just\n// its part (partIndex 0, byte offsets into the chunk's text), splitting a\n// citation across chunks as across parts. Each grounded chunk lists every grounding\n// chunk, so `groundingChunkIndices` mean the same in every chunk and in the\n// non-streaming response. Returns one field per span; empty for a span that\n// holds no cited text.\nfunction geminiStreamGroundingFields(\n text: string,\n overrides: ResponseOverrides | undefined,\n spans: { start: number; end: number }[],\n): { groundingMetadata?: GeminiGroundingMetadata }[] {\n const fields: { groundingMetadata?: GeminiGroundingMetadata }[] = spans.map(() => ({}));\n const citations = resolveCitations(text, overrides?.citations);\n if (citations.length === 0 || spans.length === 0) return fields;\n // Number the spans as parts so each support says which chunk it belongs to.\n const textParts = spans.map((s, k) => ({ partIndex: k, start: s.start, end: s.end }));\n const groundingChunks = geminiGroundingChunks(citations);\n for (const support of geminiGroundingSupports(text, citations, textParts)) {\n const k = support.segment.partIndex;\n const metadata = (fields[k].groundingMetadata ??= { groundingChunks, groundingSupports: [] });\n metadata.groundingSupports.push({ ...support, segment: { ...support.segment, partIndex: 0 } });\n }\n return fields;\n}\n\n// The [start, end) spans of the chunks that streaming `text` as\n// {@link surrogateSafeChunks} yields, as offsets into the joined text, where\n// `text` starts at `offset`. Empty `text` yields one empty span at `offset`.\nfunction geminiTextChunkSpans(\n text: string,\n chunkSize: number,\n offset = 0,\n): { start: number; end: number }[] {\n if (text.length === 0) return [{ start: offset, end: offset }];\n const spans: { start: number; end: number }[] = [];\n let start = offset;\n for (const slice of surrogateSafeChunks(text, chunkSize)) {\n spans.push({ start, end: start + slice.length });\n start += slice.length;\n }\n return spans;\n}\n\n// Fixture `files` as Gemini `inlineData` parts. Every text and tool-call builder\n// emits these after any thought parts and before the text / functionCall parts.\nfunction geminiFileParts(overrides?: ResponseOverrides): GeminiPart[] {\n return fixtureFiles(overrides?.files).map((f) => ({\n inlineData: { mimeType: f.mimeType, data: f.data },\n }));\n}\n\n// Streaming form of {@link geminiFileParts}: one chunk holding every file part,\n// or nothing when the fixture has no files.\nfunction geminiFileChunks(overrides?: ResponseOverrides): GeminiResponseChunk[] {\n const parts = geminiFileParts(overrides);\n if (parts.length === 0) return [];\n return [{ candidates: [{ content: { role: \"model\", parts }, index: 0 }] }];\n}\n\nfunction buildGeminiTextStreamChunks(\n content: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk[] {\n const chunks: GeminiResponseChunk[] = [];\n const effectiveFinish = geminiFinishReason(overrides?.finishReason, \"STOP\");\n const usage = geminiUsageMetadata(overrides);\n\n // Reasoning chunks (thought: true)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice, thought: true }] },\n index: 0,\n },\n ],\n });\n }\n }\n\n chunks.push(...geminiFileChunks(overrides));\n\n // Each content chunk carries the grounding for the cited text it holds.\n const grounding = geminiStreamGroundingFields(\n content,\n overrides,\n geminiTextChunkSpans(content, chunkSize),\n );\n\n // Content chunks, never cutting a surrogate pair, so each chunk is valid\n // UTF-16 and its grounding byte offsets count whole characters.\n const contentSlices = surrogateSafeChunks(content, chunkSize);\n contentSlices.forEach((slice, k) => {\n const isLast = k === contentSlices.length - 1;\n const chunk: GeminiResponseChunk = {\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice }] },\n index: 0,\n ...(isLast ? { finishReason: effectiveFinish } : {}),\n ...grounding[k],\n },\n ],\n ...(isLast ? { usageMetadata: usage } : {}),\n };\n chunks.push(chunk);\n });\n\n // Handle empty content\n if (content.length === 0) {\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: \"\" }] },\n finishReason: effectiveFinish,\n ...grounding[0],\n index: 0,\n },\n ],\n usageMetadata: usage,\n });\n }\n\n return chunks;\n}\n\nfunction parseToolCallPart(tc: ToolCall, logger: Logger): GeminiPart {\n const args = servedToolArgs(tc, \"object\", logger);\n // Surface the fixture's tool_call.id on the Gemini functionCall response\n // so clients can preserve it across the round-trip and any\n // toolCallId-keyed follow-up fixtures match. Pairs with v1.23.1's\n // INGEST-direction fix (#196) which preserves the id when aimock parses\n // an incoming Gemini request — that fix only helps if the id was in the\n // response body to begin with. Without this egress fix, aimock emits\n // `{ functionCall: { name, args } }` (no id), so even clients that\n // diligently preserve `functionCall.id` across the round-trip never see\n // an id to preserve. Backward-compatible: fixtures that don't pin a\n // tc.id continue to serialize without one (the ingest path's fallback\n // generator handles those).\n const functionCall: GeminiPart[\"functionCall\"] = {\n name: tc.name,\n args: args.value as Record<string, unknown>,\n };\n if (tc.id) {\n functionCall.id = tc.id;\n }\n return { functionCall };\n}\n\nfunction buildGeminiToolCallStreamChunks(\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk[] {\n const chunks: GeminiResponseChunk[] = [];\n\n // Reasoning chunks (thought: true) — mirror the content+tool / text paths so a\n // tool-only fixture on a reasoning-capable model still emits its thought channel.\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice, thought: true }] },\n index: 0,\n },\n ],\n });\n }\n }\n\n chunks.push(...geminiFileChunks(overrides));\n\n // A tool-only turn has no text for `citations` to point at.\n resolveCitations(\"\", overrides?.citations);\n const parts: GeminiPart[] = toolCalls.map((tc) => parseToolCallPart(tc, logger));\n\n // Gemini sends all tool calls in a single response chunk\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"FUNCTION_CALL\"),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nfunction buildGeminiTextResponse(\n content: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk {\n const parts: GeminiPart[] = [];\n if (reasoning) {\n parts.push({ text: reasoning, thought: true });\n }\n parts.push(...geminiFileParts(overrides));\n const textParts = geminiWholeTextPart(content, parts.length);\n parts.push({ text: content });\n\n return {\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"STOP\"),\n ...geminiGroundingField(content, overrides, textParts),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n };\n}\n\nfunction buildGeminiToolCallResponse(\n toolCalls: ToolCall[],\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): GeminiResponseChunk {\n // A tool-only turn has no text for `citations` to point at.\n resolveCitations(\"\", overrides?.citations);\n const parts: GeminiPart[] = [];\n if (reasoning) {\n parts.push({ text: reasoning, thought: true });\n }\n parts.push(...geminiFileParts(overrides));\n parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n\n return {\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"FUNCTION_CALL\"),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n };\n}\n\nfunction buildGeminiContentWithToolCallsStreamChunks(\n content: string,\n toolCalls: ToolCall[],\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): GeminiResponseChunk[] {\n const chunks: GeminiResponseChunk[] = [];\n\n // Reasoning chunks (thought: true)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice, thought: true }] },\n index: 0,\n },\n ],\n });\n }\n }\n\n chunks.push(...geminiFileChunks(overrides));\n\n if (blocks && blocks.length > 0) {\n // NEW path (#274): stream chunks whose parts follow the blocks' ARRAY ORDER,\n // so a tool-first / interleaved fixture emits its functionCall part before\n // its text part. Gemini's ordered `parts` make this fully expressible. The\n // terminal block carries the finishReason regardless of its type. Legacy\n // fixtures (no `blocks`) never enter here — see the else branch below.\n // The grounding rides on the text chunks holding the cited text, never on\n // a functionCall chunk: each text block streams as chunks whose part 0 is\n // the text their offsets refer to.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const resolved = outcome.ordered;\n const spans: { start: number; end: number }[] = [];\n let offset = 0;\n for (const block of resolved) {\n if (block.type !== \"text\") continue;\n spans.push(...geminiTextChunkSpans(block.text, chunkSize, offset));\n offset += block.text.length;\n }\n const grounding = geminiStreamGroundingFields(outcome.content, overrides, spans);\n let textChunk = 0;\n resolved.forEach((block, i) => {\n const isLast = i === resolved.length - 1;\n const finishReason = isLast\n ? geminiFinishReason(\n overrides?.finishReason,\n outcome.hasToolCalls ? \"FUNCTION_CALL\" : \"STOP\",\n )\n : undefined;\n if (block.type === \"toolCall\") {\n const part = parseToolCallPart(\n { name: block.name, arguments: block.arguments, id: block.id },\n logger,\n );\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [part] },\n ...(finishReason ? { finishReason } : {}),\n index: 0,\n },\n ],\n ...(isLast ? { usageMetadata: geminiUsageMetadata(overrides) } : {}),\n });\n } else {\n const text = block.text;\n if (text.length === 0) {\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: \"\" }] },\n ...(finishReason ? { finishReason } : {}),\n ...grounding[textChunk++],\n index: 0,\n },\n ],\n ...(isLast ? { usageMetadata: geminiUsageMetadata(overrides) } : {}),\n });\n } else {\n const slices = surrogateSafeChunks(text, chunkSize);\n slices.forEach((slice, j) => {\n const lastSlice = j === slices.length - 1;\n const sliceFinish = isLast && lastSlice ? finishReason : undefined;\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice }] },\n ...(sliceFinish ? { finishReason: sliceFinish } : {}),\n ...grounding[textChunk++],\n index: 0,\n },\n ],\n ...(isLast && lastSlice ? { usageMetadata: geminiUsageMetadata(overrides) } : {}),\n });\n });\n }\n }\n });\n\n return chunks;\n }\n\n // The grounding rides on the text chunks holding the cited text, not on the\n // functionCall chunk, so their part 0 is the text the offsets refer to.\n const grounding = geminiStreamGroundingFields(\n content,\n overrides,\n geminiTextChunkSpans(content, chunkSize),\n );\n if (content.length === 0) {\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: \"\" }] },\n ...grounding[0],\n index: 0,\n },\n ],\n });\n } else {\n surrogateSafeChunks(content, chunkSize).forEach((slice, k) => {\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: slice }] },\n ...grounding[k],\n index: 0,\n },\n ],\n });\n });\n }\n\n const parts: GeminiPart[] = toolCalls.map((tc) => parseToolCallPart(tc, logger));\n\n chunks.push({\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(overrides?.finishReason, \"FUNCTION_CALL\"),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n });\n\n return chunks;\n}\n\nfunction buildGeminiContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): GeminiResponseChunk {\n const parts: GeminiPart[] = [];\n if (reasoning) {\n parts.push({ text: reasoning, thought: true });\n }\n parts.push(...geminiFileParts(overrides));\n\n let blockHasTools: boolean | undefined;\n let groundingText = content;\n // Each text part, with the span of `groundingText` it holds.\n const textParts: GeminiCitedTextPart[] = [];\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-streaming `parts[]` array is positionally observable, so\n // emit parts in the fixture's ARRAY ORDER (after any leading thought part).\n // A toolCall block before a text block therefore yields a functionCall part\n // ahead of the text — matching the streaming path for the same `blocks`.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const resolved = outcome.ordered;\n blockHasTools = outcome.hasToolCalls;\n groundingText = outcome.content;\n for (const block of resolved) {\n if (block.type === \"toolCall\") {\n parts.push(\n parseToolCallPart({ name: block.name, arguments: block.arguments, id: block.id }, logger),\n );\n } else {\n const start = textParts.length > 0 ? textParts[textParts.length - 1].end : 0;\n textParts.push({ partIndex: parts.length, start, end: start + block.text.length });\n parts.push({ text: block.text });\n }\n }\n } else {\n // LEGACY PATH: text part first, then functionCall parts.\n textParts.push(...geminiWholeTextPart(content, parts.length));\n parts.push({ text: content });\n parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n }\n\n return {\n candidates: [\n {\n content: { role: \"model\", parts },\n finishReason: geminiFinishReason(\n overrides?.finishReason,\n blockHasTools === false ? \"STOP\" : \"FUNCTION_CALL\",\n ),\n ...geminiGroundingField(groundingText, overrides, textParts),\n index: 0,\n },\n ],\n usageMetadata: geminiUsageMetadata(overrides),\n };\n}\n\n// ─── Audio response builders ────────────────────────────────────────────────\n\nfunction resolveAudioInlineData(audio: AudioResponse): { mimeType: string; data: string } {\n if (typeof audio.audio === \"string\") {\n return { mimeType: formatToMime(audio.format ?? \"mp3\"), data: audio.audio };\n }\n return {\n mimeType: audio.audio.contentType ?? \"audio/mpeg\",\n data: audio.audio.b64Json,\n };\n}\n\n// Build the ordered Gemini parts for an audio turn: inlineData audio first,\n// then any companion modalities (reasoning/thought, text content, tool calls)\n// the recorder preserved on the AudioResponse. Without this, a recorded turn\n// that interleaves audio with a functionCall and/or text silently drops the\n// companions on replay.\n// NOTE: audio companions are only re-emitted on this Gemini replay path because\n// `audioB64` collapse is currently Gemini-only — a cross-provider audio fixture\n// would not replay its companions.\n// `effReasoning` is the capability-gated reasoning string (already passed\n// through resolveReasoningForModel at the dispatch). When the requested model\n// is not reasoning-capable (and strict mode is on), it is undefined and the\n// companion `thought` part is suppressed — mirroring the text/content+tool\n// branches so the audio path does not replay reasoning a real Gemini model\n// would never emit.\nfunction buildGeminiAudioParts(\n audio: AudioResponse,\n toolCalls: ToolCall[],\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiPart[] {\n const inlineData = resolveAudioInlineData(audio);\n const parts: GeminiPart[] = [{ inlineData }];\n if (effReasoning) {\n parts.push({ text: effReasoning, thought: true });\n }\n if (audio.content) {\n parts.push({ text: audio.content });\n }\n if (toolCalls.length) {\n parts.push(...toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n }\n return parts;\n}\n\nfunction buildGeminiAudioResponse(\n audio: AudioResponse,\n toolCalls: ToolCall[],\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk {\n return {\n candidates: [\n {\n content: {\n role: \"model\",\n parts: buildGeminiAudioParts(audio, toolCalls, logger, effReasoning),\n },\n finishReason: toolCalls.length ? \"FUNCTION_CALL\" : \"STOP\",\n index: 0,\n },\n ],\n usageMetadata: renderGeminiUsage(),\n };\n}\n\nfunction buildGeminiAudioStreamChunks(\n audio: AudioResponse,\n toolCalls: ToolCall[],\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk[] {\n return [\n {\n candidates: [\n {\n content: {\n role: \"model\",\n parts: buildGeminiAudioParts(audio, toolCalls, logger, effReasoning),\n },\n finishReason: toolCalls.length ? \"FUNCTION_CALL\" : \"STOP\",\n index: 0,\n },\n ],\n usageMetadata: renderGeminiUsage(),\n },\n ];\n}\n\n// ─── SSE writer for Gemini streaming ────────────────────────────────────────\n\n// Gemini streams data-only SSE frames (no `event:` prefix) and sends no\n// `[DONE]` trailer.\nconst GEMINI_SSE = sseCodec<GeminiResponseChunk>({ done: false });\n\nconst SILENT_LOGGER = new Logger(\"silent\");\n\n/**\n * The reasoning a misbehavior reply's non-streaming wait is sized with: the\n * plan response's reasoning through the same model and strict gate the\n * builders apply. The gate runs with a silent logger, so the builder's own\n * warning stays the only one. A reasoning-only plan carries its reasoning on\n * the plan, and `misbehaviorPayloadLength` reads it there.\n */\nfunction misbehaviorPayloadReasoning(\n plan: MisbehaviorPlan,\n model: string | undefined,\n strict: boolean,\n): string | undefined {\n const response = plan.response;\n if (\n plan.reasoning !== undefined ||\n !(\n isCombinedFixtureResponse(response) ||\n isTextResponse(response) ||\n isToolCallResponse(response)\n )\n ) {\n return undefined;\n }\n return resolveReasoningForModel(response.reasoning, model, strict, SILENT_LOGGER);\n}\n\n/** Prepare native fault output once; the journal describes these exact parts. */\nfunction prepareGeminiMisbehavior(\n plan: MisbehaviorPlan,\n request: ChatCompletionRequest,\n streaming: boolean,\n chunkSize: number,\n logger: Logger,\n strict: boolean,\n): { chunks: GeminiResponseChunk[]; summary: MisbehaviorPlan[\"summary\"] } {\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n if (!(combined || isToolCallResponse(response) || isTextResponse(response))) {\n throw new Error(\"Applied Gemini misbehavior requires a chat response\");\n }\n const outcome =\n combined && response.blocks?.length ? resolveFixtureBlockOutcome(response.blocks) : undefined;\n const content = outcome?.content ?? (\"content\" in response ? (response.content ?? \"\") : \"\");\n // The planner skips a custom-call fixture on this wire, so this narrowing\n // never throws; the normal path's guard rejects it instead.\n const calls = outcome?.toolCalls ?? (\"toolCalls\" in response ? (response.toolCalls ?? []) : []);\n const overrides = extractOverrides(response);\n delete overrides.usage;\n const fault = plan.summary.fault;\n const candidate: GeminiResponseChunk[\"candidates\"][number] & { content: GeminiContent } = {\n content: { role: \"model\", parts: [] },\n index: 0,\n };\n let chunks: GeminiResponseChunk[];\n if (fault === \"tool-args-invalid-json\") {\n const target = calls[plan.target?.index ?? 0];\n if (!target) throw new Error(\"Gemini malformed-call plan requires its selected tool call\");\n chunks = [\n {\n candidates: [\n {\n index: 0,\n finishReason: \"MALFORMED_FUNCTION_CALL\",\n finishMessage: `Malformed function call: ${target.name}(${target.arguments})`,\n },\n ],\n },\n ];\n } else if (fault === \"tool-unknown-name\" && !request.tools?.length) {\n candidate.finishReason = \"UNEXPECTED_TOOL_CALL\";\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"stop-length-mid-tool\") {\n // Object arguments cannot be partial; preserve only text preceding the cut.\n // The fixture's files precede that text, as on the normal path.\n const parts = geminiFileParts(overrides);\n const textPartIndex = parts.length;\n if (content) parts.push({ text: content });\n candidate.content.parts = parts;\n // The planner drops `citations` whenever the fault changes the served\n // text in any way, not only when the cut removes cited text.\n if (content)\n Object.assign(\n candidate,\n geminiGroundingField(content, overrides, geminiWholeTextPart(content, textPartIndex)),\n );\n candidate.finishReason = \"MAX_TOKENS\";\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"empty-response\") {\n candidate.content.parts = [{ text: \"\" }];\n candidate.finishReason = \"STOP\";\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"content-filter\") {\n candidate.finishReason = \"SAFETY\";\n candidate.safetyRatings = [\n { category: \"HARM_CATEGORY_DANGEROUS_CONTENT\", probability: \"HIGH\", blocked: true },\n ];\n chunks = [{ candidates: [candidate] }];\n } else if (fault === \"reasoning-only\") {\n candidate.content.parts = [{ text: plan.reasoning ?? \"\", thought: true }];\n candidate.finishReason = \"MAX_TOKENS\";\n chunks = [{ candidates: [candidate] }];\n } else {\n const reasoning = resolveReasoningForModel(response.reasoning, request.model, strict, logger);\n if (fault === \"tool-unknown-name\") overrides.finishReason = \"stop\";\n if (combined) {\n chunks = streaming\n ? buildGeminiContentWithToolCallsStreamChunks(\n content,\n calls,\n chunkSize,\n logger,\n reasoning,\n overrides,\n response.blocks,\n )\n : [\n buildGeminiContentWithToolCallsResponse(\n content,\n calls,\n logger,\n reasoning,\n overrides,\n response.blocks,\n ),\n ];\n } else if (isToolCallResponse(response)) {\n chunks = streaming\n ? buildGeminiToolCallStreamChunks(calls, chunkSize, logger, reasoning, overrides)\n : [buildGeminiToolCallResponse(calls, logger, reasoning, overrides)];\n } else {\n chunks = streaming\n ? buildGeminiTextStreamChunks(content, chunkSize, reasoning, overrides)\n : [buildGeminiTextResponse(content, reasoning, overrides)];\n }\n }\n const parts = chunks.flatMap((chunk) =>\n chunk.candidates.flatMap((item) => item.content?.parts ?? []),\n );\n const servedToolCalls = parts.flatMap((part) =>\n part.functionCall\n ? [\n {\n name: part.functionCall.name,\n arguments: JSON.stringify(part.functionCall.args),\n ...(part.functionCall.id ? { id: part.functionCall.id } : {}),\n },\n ]\n : [],\n );\n const prompt = request.messages\n .map((message) =>\n typeof message.content === \"string\"\n ? message.content\n : (message.content ?? []).map((part) => part.text ?? \"\").join(\"\"),\n )\n .join(\"\");\n const output = parts\n .map(\n (part) =>\n part.text ??\n (part.functionCall ? part.functionCall.name + JSON.stringify(part.functionCall.args) : \"\"),\n )\n .join(\"\");\n for (const chunk of chunks) delete chunk.usageMetadata;\n chunks[chunks.length - 1].usageMetadata = renderGeminiUsage(undefined, {\n kind: \"applied-misbehavior\",\n promptText: prompt,\n completionText: output,\n });\n return { chunks, summary: { ...plan.summary, servedToolCalls } };\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleGemini(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n streaming: boolean,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n providerKey: RecordProviderKey = \"gemini\",\n): Promise<void> {\n const { logger } = defaults;\n const wire = providerKey === \"vertexai\" ? \"Vertex AI\" : \"Gemini\";\n setCorsHeaders(res);\n\n let geminiReq: GeminiRequest;\n try {\n geminiReq = JSON.parse(raw) as GeminiRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? `/v1beta/models/${model}:generateContent`,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"gemini\", \"validation\", {\n message: `Malformed JSON body: ${detail}`,\n bodyCode: 400,\n status: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Reject non-object bodies (e.g. `null`) before touching fields —\n // otherwise the converter dereference throws a TypeError that surfaces as\n // a 500 instead of a 400.\n if (!isJsonObject(geminiReq)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? `/v1beta/models/${model}:generateContent`,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"gemini\", \"validation\", {\n message: \"Request body must be a JSON object\",\n bodyCode: 400,\n status: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Reject wrong-typed fields before the converter dereferences them.\n const geminiShapeError = validateGeminiBody(geminiReq);\n if (geminiShapeError) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? `/v1beta/models/${model}:generateContent`,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"gemini\", \"validation\", {\n message: `Invalid argument: ${geminiShapeError}`,\n bodyCode: 400,\n status: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = geminiToCompletionRequest(geminiReq, model, streaming);\n completionReq._endpointType = \"chat\";\n completionReq._context = getContext(req);\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n // Record mode proxies on a miss to capture a fresh turn (see record gate\n // below), so keep turnIndex strict to prevent an earlier-turn fixture from\n // shadowing a longer request and skipping the new turn's recording.\n { ...recordMatchOptions(!!defaults.record, defaults.logger), wire: \"gemini\" },\n );\n const path = req.url ?? `/v1beta/models/${model}:generateContent`;\n\n if (fixture) {\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n await applyChaosAsync(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n {\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const decisions = resolveRequestDecisions(defaults.strict, req.headers);\n if (decisions.fixtureMiss) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", path, skippedBySequenceOrTurn));\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 503,\n renderError(\"gemini\", \"strict-miss\", {\n message: strictMessage,\n bodyCode: 503,\n status: \"UNAVAILABLE\",\n }).body,\n );\n return;\n }\n\n if (decisions.upstream && defaults.record) {\n const { outcome, clientHungUp } = await withHangUpDetection(req, res, () =>\n proxyAndRecord(req, res, completionReq, providerKey, path, fixtures, defaults, raw),\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: res.statusCode ?? 200,\n fixture: null,\n source: \"proxy\",\n ...proxyInterruption(res, clientHungUp),\n },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n renderError(\"gemini\", \"fixture-miss\", {\n message: \"No fixture matched\",\n bodyCode: 404,\n status: \"NOT_FOUND\",\n }).body,\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const misbehavior = planMisbehavior({\n wire: \"gemini\",\n toolCallIdMode: \"authored-nonempty\",\n fixture,\n response,\n request: completionReq,\n stream: streaming,\n defaults,\n rawHeaders: req.headers,\n url: req.url,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: misbehavior.status, fixture, error: misbehavior.message },\n });\n recordMisbehaviorOutcome({ entry, summary: misbehavior.summary, defaults, testId });\n if (misbehavior.summary?.evaluations.length === 0) {\n logger.error(`${req.method ?? \"POST\"} ${path}: ${misbehavior.message}`);\n }\n writeErrorResponse(\n res,\n misbehavior.status,\n renderError(\"gemini\", \"misbehavior\", {\n bodyCode: misbehavior.status,\n message: misbehavior.message,\n status: misbehavior.status === 400 ? \"INVALID_ARGUMENT\" : \"UNIMPLEMENTED\",\n }).body,\n );\n return;\n }\n\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const streamTiming = {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n };\n const jsonOpts = (payloadLength: number, duplicateId = false) => ({\n surface: \"other\" as const,\n payloadLength,\n chunkSize,\n recordedTimings: fixture.recordedTimings,\n duplicateId,\n replaySpeed,\n });\n\n if (misbehavior.kind === \"applied\") {\n // Journal first, as the success branches do, so a fixture error thrown\n // while building leaves a 500 entry that keeps the fixture. Record the\n // fault only once the reply is built: a thrown error was not served.\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const strictReasoning = resolveRequestDecisions(\n defaults.strict,\n req.headers,\n ).reasoningSuppression;\n const prepared = prepareGeminiMisbehavior(\n misbehavior,\n completionReq,\n streaming,\n chunkSize,\n logger,\n strictReasoning,\n );\n recordMisbehaviorOutcome({ entry, summary: prepared.summary, defaults, testId });\n if (!streaming) {\n const preparedBody = prepared.chunks[0];\n await deliverJson(\n res,\n entry,\n fixture,\n () => preparedBody,\n jsonOpts(\n misbehaviorPayloadLength(\n misbehavior,\n misbehaviorPayloadReasoning(misbehavior, completionReq.model, strictReasoning),\n ),\n !!misbehavior.duplicateId,\n ),\n );\n } else {\n await deliverStream(res, entry, fixture, GEMINI_SSE, prepared.chunks, streamTiming);\n }\n return;\n }\n\n // Raw passthrough (#549): the authored chunks or JSON, framed for this wire.\n // Covers the Vertex AI route, which shares this handler.\n if (isRawResponse(response)) {\n await serveRawResponse({\n wire: \"gemini\",\n stream: streaming,\n req,\n res,\n fixture,\n response,\n body: completionReq,\n journal,\n defaults,\n testId,\n misbehaviorSummary: misbehavior.summary,\n errorWire: \"gemini\",\n });\n return;\n }\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const errorEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n recordMisbehaviorOutcome({ entry: errorEntry, summary: misbehavior.summary, defaults, testId });\n writeErrorResponse(\n res,\n status,\n renderError(\"gemini\", \"fixture\", {\n bodyCode: status,\n message: response.error.message,\n status: response.error.type ?? \"ERROR\",\n }).body,\n { retryAfter: response.retryAfter },\n );\n return;\n }\n\n // Audio response\n if (isAudioResponse(response)) {\n const effectiveStrict = resolveRequestDecisions(\n defaults.strict,\n req.headers,\n ).reasoningSuppression;\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n // A non-array companion `toolCalls` emits no tool parts, as before custom calls existed.\n const audioToolCalls = Array.isArray(response.toolCalls)\n ? requireServedFunctionToolCalls(response, wire)\n : [];\n if (!streaming) {\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () => buildGeminiAudioResponse(response, audioToolCalls, logger, effReasoning),\n jsonOpts(servedPayloadLength(response, effReasoning)),\n );\n } else {\n const chunks = buildGeminiAudioStreamChunks(response, audioToolCalls, logger, effReasoning);\n await deliverStream(res, journalEntry, fixture, GEMINI_SSE, chunks, streamTiming);\n }\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isCombinedFixtureResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Gemini API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveRequestDecisions(\n defaults.strict,\n req.headers,\n ).reasoningSuppression;\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, wire);\n if (!streaming) {\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () =>\n buildGeminiContentWithToolCallsResponse(\n response.content ?? \"\",\n functionToolCalls,\n logger,\n effReasoning,\n overrides,\n response.blocks,\n ),\n jsonOpts(servedPayloadLength(response, effReasoning)),\n );\n } else {\n const chunks = buildGeminiContentWithToolCallsStreamChunks(\n response.content ?? \"\",\n functionToolCalls,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n response.blocks,\n );\n await deliverStream(res, journalEntry, fixture, GEMINI_SSE, chunks, streamTiming);\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Gemini API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveRequestDecisions(\n defaults.strict,\n req.headers,\n ).reasoningSuppression;\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n if (!streaming) {\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () => buildGeminiTextResponse(response.content, effReasoning, overrides),\n jsonOpts(servedPayloadLength(response, effReasoning)),\n );\n } else {\n const chunks = buildGeminiTextStreamChunks(\n response.content,\n chunkSize,\n effReasoning,\n overrides,\n );\n await deliverStream(res, journalEntry, fixture, GEMINI_SSE, chunks, streamTiming);\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Gemini API — ignoring\");\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveRequestDecisions(\n defaults.strict,\n req.headers,\n ).reasoningSuppression;\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, wire);\n if (!streaming) {\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () => buildGeminiToolCallResponse(functionToolCalls, logger, effReasoning, overrides),\n jsonOpts(servedPayloadLength(response, effReasoning)),\n );\n } else {\n const chunks = buildGeminiToolCallStreamChunks(\n functionToolCalls,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n );\n await deliverStream(res, journalEntry, fixture, GEMINI_SSE, chunks, streamTiming);\n }\n return;\n }\n\n // Unknown response type\n const unknownEntry = journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordMisbehaviorOutcome({ entry: unknownEntry, summary: misbehavior.summary, defaults, testId });\n writeErrorResponse(\n res,\n 500,\n renderError(\"gemini\", \"unknown-response\", {\n message: \"Fixture response did not match any known type\",\n bodyCode: 500,\n status: \"INTERNAL\",\n }).body,\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ const require_errors = require('./response/errors.cjs');
3
+ const require_policy = require('./response/policy.cjs');
2
4
  const require_helpers = require('./helpers.cjs');
3
5
  const require_sse_writer = require('./sse-writer.cjs');
4
6
  const require_chaos = require('./chaos.cjs');
@@ -242,9 +244,9 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
242
244
  path,
243
245
  headers: require_helpers.flattenHeaders(req.headers),
244
246
  body: syntheticReq
245
- }, fixture ? "fixture" : require_helpers.resolveStrictMode(defaults.strict, req.headers) ? "internal" : defaults.record?.providers.grok ? "proxy" : "internal", defaults.registry, defaults.logger)) return;
247
+ }, fixture ? "fixture" : require_policy.resolveRequestDecisions(defaults.strict, req.headers).fixtureMiss ? "internal" : defaults.record?.providers.grok ? "proxy" : "internal", defaults.registry, defaults.logger)) return;
246
248
  if (!fixture) {
247
- if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
249
+ if (require_policy.resolveRequestDecisions(defaults.strict, req.headers).fixtureMiss) {
248
250
  const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
249
251
  defaults.logger.error(require_helpers.strictNoMatchLogLine(method, path, skippedBySequenceOrTurn));
250
252
  journal.add({
@@ -255,7 +257,7 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
255
257
  response: {
256
258
  status: 503,
257
259
  fixture: null,
258
- ...require_helpers.strictOverrideField(defaults.strict, req.headers)
260
+ ...require_policy.strictOverrideField(defaults.strict, req.headers)
259
261
  }
260
262
  });
261
263
  require_sse_writer.writeErrorResponse(res, 503, JSON.stringify({
@@ -286,7 +288,7 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
286
288
  response: {
287
289
  status: 404,
288
290
  fixture: null,
289
- ...require_helpers.strictOverrideField(defaults.strict, req.headers)
291
+ ...require_policy.strictOverrideField(defaults.strict, req.headers)
290
292
  }
291
293
  });
292
294
  require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({
@@ -325,10 +327,10 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
325
327
  fixture
326
328
  }
327
329
  });
328
- require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
330
+ require_sse_writer.writeErrorResponse(res, 500, require_errors.renderError("openai", "unknown-response", {
329
331
  message: "Fixture response is not a video type",
330
332
  type: "server_error"
331
- } }));
333
+ }).body);
332
334
  return;
333
335
  }
334
336
  if (require_chaos.responseGone(res)) return;
@@ -380,7 +382,7 @@ async function handleGrokVideoStatus(req, res, id, fixtures, journal, defaults,
380
382
  body: null
381
383
  }, "internal", defaults.registry, defaults.logger)) return;
382
384
  if (job.kind === "record") {
383
- if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
385
+ if (!require_policy.resolveRequestDecisions(defaults.strict, req.headers).upstream) {
384
386
  defaults.logger.error(`STRICT: video job ${id} is proxied live upstream (record mode) — refusing the upstream poll`);
385
387
  journal.add({
386
388
  method,
@@ -390,7 +392,7 @@ async function handleGrokVideoStatus(req, res, id, fixtures, journal, defaults,
390
392
  response: {
391
393
  status: 503,
392
394
  fixture: null,
393
- ...require_helpers.strictOverrideField(defaults.strict, req.headers)
395
+ ...require_policy.strictOverrideField(defaults.strict, req.headers)
394
396
  }
395
397
  });
396
398
  require_sse_writer.writeErrorResponse(res, 503, JSON.stringify({
@@ -485,7 +487,7 @@ async function proxyGrokVideoSubmit(args) {
485
487
  status: 502,
486
488
  fixture: null,
487
489
  source: "proxy",
488
- ...require_helpers.strictOverrideField(defaults.strict, req.headers)
490
+ ...require_policy.strictOverrideField(defaults.strict, req.headers)
489
491
  }
490
492
  });
491
493
  require_sse_writer.writeErrorResponse(res, 502, JSON.stringify({
@@ -533,7 +535,7 @@ async function proxyGrokVideoSubmit(args) {
533
535
  status: fetched.status,
534
536
  fixture: null,
535
537
  source: "proxy",
536
- ...require_helpers.strictOverrideField(defaults.strict, req.headers)
538
+ ...require_policy.strictOverrideField(defaults.strict, req.headers)
537
539
  }
538
540
  });
539
541
  res.writeHead(fetched.status, { "Content-Type": fetched.contentType ?? "application/json" });
@@ -578,7 +580,7 @@ async function proxyGrokVideoSubmit(args) {
578
580
  status: 200,
579
581
  fixture: null,
580
582
  source: "proxy",
581
- ...require_helpers.strictOverrideField(defaults.strict, req.headers)
583
+ ...require_policy.strictOverrideField(defaults.strict, req.headers)
582
584
  }
583
585
  });
584
586
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -608,7 +610,7 @@ async function proxyGrokVideoRecordPoll(args) {
608
610
  status,
609
611
  fixture: null,
610
612
  source: "proxy",
611
- ...require_helpers.strictOverrideField(defaults.strict, req.headers)
613
+ ...require_policy.strictOverrideField(defaults.strict, req.headers)
612
614
  }
613
615
  });
614
616
  };