@copilotkit/aimock 1.42.1 → 1.44.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 (442) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +65 -0
  4. package/README.md +108 -16
  5. package/dist/agui-handler.d.ts.map +1 -1
  6. package/dist/agui-mock.cjs +1 -1
  7. package/dist/agui-mock.cjs.map +1 -1
  8. package/dist/agui-mock.d.cts.map +1 -1
  9. package/dist/agui-mock.d.ts.map +1 -1
  10. package/dist/agui-mock.js +1 -1
  11. package/dist/agui-mock.js.map +1 -1
  12. package/dist/agui-recorder.cjs +33 -11
  13. package/dist/agui-recorder.cjs.map +1 -1
  14. package/dist/agui-recorder.d.cts +10 -1
  15. package/dist/agui-recorder.d.cts.map +1 -1
  16. package/dist/agui-recorder.d.ts +10 -1
  17. package/dist/agui-recorder.d.ts.map +1 -1
  18. package/dist/agui-recorder.js +33 -11
  19. package/dist/agui-recorder.js.map +1 -1
  20. package/dist/agui-types.d.cts +1 -0
  21. package/dist/agui-types.d.cts.map +1 -1
  22. package/dist/agui-types.d.ts +1 -0
  23. package/dist/agui-types.d.ts.map +1 -1
  24. package/dist/aimock-cli.cjs +38 -10
  25. package/dist/aimock-cli.cjs.map +1 -1
  26. package/dist/aimock-cli.d.cts +5 -0
  27. package/dist/aimock-cli.d.cts.map +1 -1
  28. package/dist/aimock-cli.d.ts +5 -0
  29. package/dist/aimock-cli.d.ts.map +1 -1
  30. package/dist/aimock-cli.js +38 -10
  31. package/dist/aimock-cli.js.map +1 -1
  32. package/dist/batches.cjs +458 -0
  33. package/dist/batches.cjs.map +1 -0
  34. package/dist/batches.d.cts +57 -0
  35. package/dist/batches.d.cts.map +1 -0
  36. package/dist/batches.d.ts +57 -0
  37. package/dist/batches.d.ts.map +1 -0
  38. package/dist/batches.js +453 -0
  39. package/dist/batches.js.map +1 -0
  40. package/dist/bedrock-converse.cjs +44 -12
  41. package/dist/bedrock-converse.cjs.map +1 -1
  42. package/dist/bedrock-converse.d.cts.map +1 -1
  43. package/dist/bedrock-converse.d.ts.map +1 -1
  44. package/dist/bedrock-converse.js +46 -14
  45. package/dist/bedrock-converse.js.map +1 -1
  46. package/dist/bedrock.cjs +10 -8
  47. package/dist/bedrock.cjs.map +1 -1
  48. package/dist/bedrock.d.cts.map +1 -1
  49. package/dist/bedrock.d.ts.map +1 -1
  50. package/dist/bedrock.js +12 -10
  51. package/dist/bedrock.js.map +1 -1
  52. package/dist/byteplus-video.cjs +3 -3
  53. package/dist/byteplus-video.cjs.map +1 -1
  54. package/dist/byteplus-video.js +4 -4
  55. package/dist/byteplus-video.js.map +1 -1
  56. package/dist/chaos.cjs +625 -72
  57. package/dist/chaos.cjs.map +1 -1
  58. package/dist/chaos.d.cts +164 -7
  59. package/dist/chaos.d.cts.map +1 -1
  60. package/dist/chaos.d.ts +164 -7
  61. package/dist/chaos.d.ts.map +1 -1
  62. package/dist/chaos.js +616 -74
  63. package/dist/chaos.js.map +1 -1
  64. package/dist/cli.cjs +131 -30
  65. package/dist/cli.cjs.map +1 -1
  66. package/dist/cli.js +132 -31
  67. package/dist/cli.js.map +1 -1
  68. package/dist/cohere.cjs +37 -9
  69. package/dist/cohere.cjs.map +1 -1
  70. package/dist/cohere.d.cts.map +1 -1
  71. package/dist/cohere.d.ts.map +1 -1
  72. package/dist/cohere.js +39 -11
  73. package/dist/cohere.js.map +1 -1
  74. package/dist/config-loader.cjs +1 -1
  75. package/dist/config-loader.js +1 -1
  76. package/dist/constants.cjs +8 -0
  77. package/dist/constants.cjs.map +1 -1
  78. package/dist/constants.d.cts +6 -2
  79. package/dist/constants.d.ts +6 -2
  80. package/dist/constants.js +8 -1
  81. package/dist/constants.js.map +1 -1
  82. package/dist/convert.cjs +1 -1
  83. package/dist/convert.cjs.map +1 -1
  84. package/dist/convert.js +1 -1
  85. package/dist/convert.js.map +1 -1
  86. package/dist/echo-text.cjs +65 -0
  87. package/dist/echo-text.cjs.map +1 -0
  88. package/dist/echo-text.d.cts +22 -0
  89. package/dist/echo-text.d.cts.map +1 -0
  90. package/dist/echo-text.d.ts +22 -0
  91. package/dist/echo-text.d.ts.map +1 -0
  92. package/dist/echo-text.js +65 -0
  93. package/dist/echo-text.js.map +1 -0
  94. package/dist/elevenlabs-audio.cjs +3 -3
  95. package/dist/elevenlabs-audio.cjs.map +1 -1
  96. package/dist/elevenlabs-audio.js +4 -4
  97. package/dist/elevenlabs-audio.js.map +1 -1
  98. package/dist/elevenlabs-voice.cjs +1053 -0
  99. package/dist/elevenlabs-voice.cjs.map +1 -0
  100. package/dist/elevenlabs-voice.d.cts +72 -0
  101. package/dist/elevenlabs-voice.d.cts.map +1 -0
  102. package/dist/elevenlabs-voice.d.ts +72 -0
  103. package/dist/elevenlabs-voice.d.ts.map +1 -0
  104. package/dist/elevenlabs-voice.js +1048 -0
  105. package/dist/elevenlabs-voice.js.map +1 -0
  106. package/dist/embeddings.cjs +26 -5
  107. package/dist/embeddings.cjs.map +1 -1
  108. package/dist/embeddings.d.cts +1 -1
  109. package/dist/embeddings.d.cts.map +1 -1
  110. package/dist/embeddings.d.ts +1 -1
  111. package/dist/embeddings.d.ts.map +1 -1
  112. package/dist/embeddings.js +27 -6
  113. package/dist/embeddings.js.map +1 -1
  114. package/dist/fal-audio.cjs +3 -3
  115. package/dist/fal-audio.cjs.map +1 -1
  116. package/dist/fal-audio.js +4 -4
  117. package/dist/fal-audio.js.map +1 -1
  118. package/dist/fal.cjs +28 -4
  119. package/dist/fal.cjs.map +1 -1
  120. package/dist/fal.d.cts +16 -1
  121. package/dist/fal.d.cts.map +1 -1
  122. package/dist/fal.d.ts +16 -1
  123. package/dist/fal.d.ts.map +1 -1
  124. package/dist/fal.js +28 -5
  125. package/dist/fal.js.map +1 -1
  126. package/dist/files.cjs +1069 -0
  127. package/dist/files.cjs.map +1 -0
  128. package/dist/files.d.cts +68 -0
  129. package/dist/files.d.cts.map +1 -0
  130. package/dist/files.d.ts +68 -0
  131. package/dist/files.d.ts.map +1 -0
  132. package/dist/files.js +1056 -0
  133. package/dist/files.js.map +1 -0
  134. package/dist/fine-tuning.cjs +1033 -0
  135. package/dist/fine-tuning.cjs.map +1 -0
  136. package/dist/fine-tuning.d.cts +96 -0
  137. package/dist/fine-tuning.d.cts.map +1 -0
  138. package/dist/fine-tuning.d.ts +96 -0
  139. package/dist/fine-tuning.d.ts.map +1 -0
  140. package/dist/fine-tuning.js +1026 -0
  141. package/dist/fine-tuning.js.map +1 -0
  142. package/dist/fixture-loader-services.cjs +116 -0
  143. package/dist/fixture-loader-services.cjs.map +1 -0
  144. package/dist/fixture-loader-services.d.cts +48 -0
  145. package/dist/fixture-loader-services.d.cts.map +1 -0
  146. package/dist/fixture-loader-services.d.ts +48 -0
  147. package/dist/fixture-loader-services.d.ts.map +1 -0
  148. package/dist/fixture-loader-services.js +114 -0
  149. package/dist/fixture-loader-services.js.map +1 -0
  150. package/dist/fixture-loader.cjs +0 -0
  151. package/dist/fixture-loader.cjs.map +1 -1
  152. package/dist/fixture-loader.d.cts +131 -5
  153. package/dist/fixture-loader.d.cts.map +1 -1
  154. package/dist/fixture-loader.d.ts +131 -5
  155. package/dist/fixture-loader.d.ts.map +1 -1
  156. package/dist/fixture-loader.js +0 -0
  157. package/dist/fixture-loader.js.map +1 -1
  158. package/dist/gemini-embeddings.cjs +44 -4
  159. package/dist/gemini-embeddings.cjs.map +1 -1
  160. package/dist/gemini-embeddings.js +46 -6
  161. package/dist/gemini-embeddings.js.map +1 -1
  162. package/dist/gemini-interactions.cjs +51 -7
  163. package/dist/gemini-interactions.cjs.map +1 -1
  164. package/dist/gemini-interactions.d.cts.map +1 -1
  165. package/dist/gemini-interactions.d.ts.map +1 -1
  166. package/dist/gemini-interactions.js +53 -9
  167. package/dist/gemini-interactions.js.map +1 -1
  168. package/dist/gemini.cjs +19 -8
  169. package/dist/gemini.cjs.map +1 -1
  170. package/dist/gemini.d.cts.map +1 -1
  171. package/dist/gemini.d.ts.map +1 -1
  172. package/dist/gemini.js +21 -10
  173. package/dist/gemini.js.map +1 -1
  174. package/dist/grok-video.cjs +14 -14
  175. package/dist/grok-video.cjs.map +1 -1
  176. package/dist/grok-video.js +15 -15
  177. package/dist/grok-video.js.map +1 -1
  178. package/dist/helpers.cjs +497 -10
  179. package/dist/helpers.cjs.map +1 -1
  180. package/dist/helpers.d.cts +38 -2
  181. package/dist/helpers.d.cts.map +1 -1
  182. package/dist/helpers.d.ts +38 -2
  183. package/dist/helpers.d.ts.map +1 -1
  184. package/dist/helpers.js +487 -12
  185. package/dist/helpers.js.map +1 -1
  186. package/dist/images.cjs +57 -125
  187. package/dist/images.cjs.map +1 -1
  188. package/dist/images.d.cts.map +1 -1
  189. package/dist/images.d.ts.map +1 -1
  190. package/dist/images.js +58 -126
  191. package/dist/images.js.map +1 -1
  192. package/dist/index.cjs +71 -9
  193. package/dist/index.d.cts +17 -7
  194. package/dist/index.d.ts +17 -7
  195. package/dist/index.js +21 -11
  196. package/dist/jest.cjs +24 -12
  197. package/dist/jest.cjs.map +1 -1
  198. package/dist/jest.d.cts +6 -3
  199. package/dist/jest.d.cts.map +1 -1
  200. package/dist/jest.d.ts +6 -3
  201. package/dist/jest.d.ts.map +1 -1
  202. package/dist/jest.js +25 -13
  203. package/dist/jest.js.map +1 -1
  204. package/dist/journal.cjs +61 -4
  205. package/dist/journal.cjs.map +1 -1
  206. package/dist/journal.d.cts +60 -3
  207. package/dist/journal.d.cts.map +1 -1
  208. package/dist/journal.d.ts +60 -3
  209. package/dist/journal.d.ts.map +1 -1
  210. package/dist/journal.js +61 -5
  211. package/dist/journal.js.map +1 -1
  212. package/dist/live-fixture.cjs +1192 -0
  213. package/dist/live-fixture.cjs.map +1 -0
  214. package/dist/live-fixture.js +1187 -0
  215. package/dist/live-fixture.js.map +1 -0
  216. package/dist/live-recorder.cjs +256 -0
  217. package/dist/live-recorder.cjs.map +1 -0
  218. package/dist/live-recorder.js +254 -0
  219. package/dist/live-recorder.js.map +1 -0
  220. package/dist/live-replay.cjs +286 -0
  221. package/dist/live-replay.cjs.map +1 -0
  222. package/dist/live-replay.js +285 -0
  223. package/dist/live-replay.js.map +1 -0
  224. package/dist/live-sanitize.cjs +196 -0
  225. package/dist/live-sanitize.cjs.map +1 -0
  226. package/dist/live-sanitize.js +196 -0
  227. package/dist/live-sanitize.js.map +1 -0
  228. package/dist/live-types.d.cts +71 -0
  229. package/dist/live-types.d.cts.map +1 -0
  230. package/dist/live-types.d.ts +71 -0
  231. package/dist/live-types.d.ts.map +1 -0
  232. package/dist/llmock.cjs +132 -49
  233. package/dist/llmock.cjs.map +1 -1
  234. package/dist/llmock.d.cts +51 -5
  235. package/dist/llmock.d.cts.map +1 -1
  236. package/dist/llmock.d.ts +51 -5
  237. package/dist/llmock.d.ts.map +1 -1
  238. package/dist/llmock.js +133 -50
  239. package/dist/llmock.js.map +1 -1
  240. package/dist/mcp-fakes-mount.cjs +216 -0
  241. package/dist/mcp-fakes-mount.cjs.map +1 -0
  242. package/dist/mcp-fakes-mount.js +210 -0
  243. package/dist/mcp-fakes-mount.js.map +1 -0
  244. package/dist/mcp-fakes.cjs +1619 -0
  245. package/dist/mcp-fakes.cjs.map +1 -0
  246. package/dist/mcp-fakes.d.cts +101 -0
  247. package/dist/mcp-fakes.d.cts.map +1 -0
  248. package/dist/mcp-fakes.d.ts +101 -0
  249. package/dist/mcp-fakes.d.ts.map +1 -0
  250. package/dist/mcp-fakes.js +1614 -0
  251. package/dist/mcp-fakes.js.map +1 -0
  252. package/dist/mcp-handler.cjs +331 -17
  253. package/dist/mcp-handler.cjs.map +1 -1
  254. package/dist/mcp-handler.js +330 -18
  255. package/dist/mcp-handler.js.map +1 -1
  256. package/dist/mcp-mock.cjs +217 -49
  257. package/dist/mcp-mock.cjs.map +1 -1
  258. package/dist/mcp-mock.d.cts +66 -1
  259. package/dist/mcp-mock.d.cts.map +1 -1
  260. package/dist/mcp-mock.d.ts +66 -1
  261. package/dist/mcp-mock.d.ts.map +1 -1
  262. package/dist/mcp-mock.js +218 -50
  263. package/dist/mcp-mock.js.map +1 -1
  264. package/dist/mcp-stub.cjs +5 -1
  265. package/dist/mcp-stub.d.cts +3 -1
  266. package/dist/mcp-stub.d.ts +3 -1
  267. package/dist/mcp-stub.js +3 -1
  268. package/dist/mcp-types.d.cts +6 -0
  269. package/dist/mcp-types.d.ts +6 -0
  270. package/dist/message-text.cjs +192 -0
  271. package/dist/message-text.cjs.map +1 -0
  272. package/dist/message-text.d.cts +31 -0
  273. package/dist/message-text.d.cts.map +1 -0
  274. package/dist/message-text.d.ts +31 -0
  275. package/dist/message-text.d.ts.map +1 -0
  276. package/dist/message-text.js +184 -0
  277. package/dist/message-text.js.map +1 -0
  278. package/dist/messages.cjs +49 -7
  279. package/dist/messages.cjs.map +1 -1
  280. package/dist/messages.d.cts.map +1 -1
  281. package/dist/messages.d.ts.map +1 -1
  282. package/dist/messages.js +51 -9
  283. package/dist/messages.js.map +1 -1
  284. package/dist/metrics.cjs +278 -45
  285. package/dist/metrics.cjs.map +1 -1
  286. package/dist/metrics.d.cts +17 -7
  287. package/dist/metrics.d.cts.map +1 -1
  288. package/dist/metrics.d.ts +17 -7
  289. package/dist/metrics.d.ts.map +1 -1
  290. package/dist/metrics.js +270 -29
  291. package/dist/metrics.js.map +1 -1
  292. package/dist/moderation.cjs +10 -1
  293. package/dist/moderation.cjs.map +1 -1
  294. package/dist/moderation.d.cts.map +1 -1
  295. package/dist/moderation.d.ts.map +1 -1
  296. package/dist/moderation.js +10 -1
  297. package/dist/moderation.js.map +1 -1
  298. package/dist/ollama.cjs +10 -6
  299. package/dist/ollama.cjs.map +1 -1
  300. package/dist/ollama.d.cts.map +1 -1
  301. package/dist/ollama.d.ts.map +1 -1
  302. package/dist/ollama.js +11 -7
  303. package/dist/ollama.js.map +1 -1
  304. package/dist/openapi.cjs +3405 -0
  305. package/dist/openapi.cjs.map +1 -0
  306. package/dist/openapi.d.cts +30 -0
  307. package/dist/openapi.d.cts.map +1 -0
  308. package/dist/openapi.d.ts +30 -0
  309. package/dist/openapi.d.ts.map +1 -0
  310. package/dist/openapi.js +3404 -0
  311. package/dist/openapi.js.map +1 -0
  312. package/dist/openrouter-video.cjs +9 -9
  313. package/dist/openrouter-video.cjs.map +1 -1
  314. package/dist/openrouter-video.d.cts.map +1 -1
  315. package/dist/openrouter-video.d.ts.map +1 -1
  316. package/dist/openrouter-video.js +10 -10
  317. package/dist/openrouter-video.js.map +1 -1
  318. package/dist/recorder.cjs +54 -10
  319. package/dist/recorder.cjs.map +1 -1
  320. package/dist/recorder.d.cts.map +1 -1
  321. package/dist/recorder.d.ts.map +1 -1
  322. package/dist/recorder.js +52 -12
  323. package/dist/recorder.js.map +1 -1
  324. package/dist/responses.cjs +47 -7
  325. package/dist/responses.cjs.map +1 -1
  326. package/dist/responses.d.cts.map +1 -1
  327. package/dist/responses.d.ts.map +1 -1
  328. package/dist/responses.js +48 -9
  329. package/dist/responses.js.map +1 -1
  330. package/dist/route-registry.cjs +951 -0
  331. package/dist/route-registry.cjs.map +1 -0
  332. package/dist/route-registry.d.cts +33 -0
  333. package/dist/route-registry.d.cts.map +1 -0
  334. package/dist/route-registry.d.ts +33 -0
  335. package/dist/route-registry.d.ts.map +1 -0
  336. package/dist/route-registry.js +866 -0
  337. package/dist/route-registry.js.map +1 -0
  338. package/dist/router.cjs +21 -34
  339. package/dist/router.cjs.map +1 -1
  340. package/dist/router.d.cts +4 -1
  341. package/dist/router.d.cts.map +1 -1
  342. package/dist/router.d.ts +4 -1
  343. package/dist/router.d.ts.map +1 -1
  344. package/dist/router.js +21 -35
  345. package/dist/router.js.map +1 -1
  346. package/dist/server.cjs +1252 -777
  347. package/dist/server.cjs.map +1 -1
  348. package/dist/server.d.cts +5 -1
  349. package/dist/server.d.cts.map +1 -1
  350. package/dist/server.d.ts +5 -1
  351. package/dist/server.d.ts.map +1 -1
  352. package/dist/server.js +1185 -710
  353. package/dist/server.js.map +1 -1
  354. package/dist/speech.cjs +6 -4
  355. package/dist/speech.cjs.map +1 -1
  356. package/dist/speech.js +7 -5
  357. package/dist/speech.js.map +1 -1
  358. package/dist/suite.cjs +1 -1
  359. package/dist/suite.js +1 -1
  360. package/dist/transcription.cjs +6 -6
  361. package/dist/transcription.cjs.map +1 -1
  362. package/dist/transcription.d.cts.map +1 -1
  363. package/dist/transcription.d.ts.map +1 -1
  364. package/dist/transcription.js +7 -7
  365. package/dist/transcription.js.map +1 -1
  366. package/dist/types.cjs +18 -0
  367. package/dist/types.cjs.map +1 -0
  368. package/dist/types.d.cts +320 -17
  369. package/dist/types.d.cts.map +1 -1
  370. package/dist/types.d.ts +320 -17
  371. package/dist/types.d.ts.map +1 -1
  372. package/dist/types.js +17 -0
  373. package/dist/types.js.map +1 -0
  374. package/dist/validate-cli.cjs +983 -0
  375. package/dist/validate-cli.cjs.map +1 -0
  376. package/dist/validate-cli.js +982 -0
  377. package/dist/validate-cli.js.map +1 -0
  378. package/dist/vector-stores.cjs +1180 -0
  379. package/dist/vector-stores.cjs.map +1 -0
  380. package/dist/vector-stores.d.cts +106 -0
  381. package/dist/vector-stores.d.cts.map +1 -0
  382. package/dist/vector-stores.d.ts +106 -0
  383. package/dist/vector-stores.d.ts.map +1 -0
  384. package/dist/vector-stores.js +1164 -0
  385. package/dist/vector-stores.js.map +1 -0
  386. package/dist/vector-types.d.cts.map +1 -1
  387. package/dist/vector-types.d.ts.map +1 -1
  388. package/dist/veo-video.cjs +13 -13
  389. package/dist/veo-video.cjs.map +1 -1
  390. package/dist/veo-video.js +14 -14
  391. package/dist/veo-video.js.map +1 -1
  392. package/dist/video.cjs +23 -4
  393. package/dist/video.cjs.map +1 -1
  394. package/dist/video.d.cts +1 -1
  395. package/dist/video.d.cts.map +1 -1
  396. package/dist/video.d.ts +1 -1
  397. package/dist/video.d.ts.map +1 -1
  398. package/dist/video.js +24 -5
  399. package/dist/video.js.map +1 -1
  400. package/dist/vitest.cjs +23 -11
  401. package/dist/vitest.cjs.map +1 -1
  402. package/dist/vitest.d.cts +5 -2
  403. package/dist/vitest.d.cts.map +1 -1
  404. package/dist/vitest.d.ts +5 -2
  405. package/dist/vitest.d.ts.map +1 -1
  406. package/dist/vitest.js +25 -13
  407. package/dist/vitest.js.map +1 -1
  408. package/dist/watcher.cjs +2 -1
  409. package/dist/watcher.cjs.map +1 -1
  410. package/dist/watcher.js +2 -1
  411. package/dist/watcher.js.map +1 -1
  412. package/dist/ws-framing.cjs +150 -35
  413. package/dist/ws-framing.cjs.map +1 -1
  414. package/dist/ws-framing.d.cts +18 -3
  415. package/dist/ws-framing.d.cts.map +1 -1
  416. package/dist/ws-framing.d.ts +18 -3
  417. package/dist/ws-framing.d.ts.map +1 -1
  418. package/dist/ws-framing.js +151 -36
  419. package/dist/ws-framing.js.map +1 -1
  420. package/dist/ws-gemini-live.cjs +2 -2
  421. package/dist/ws-gemini-live.js +2 -2
  422. package/dist/ws-live.cjs +401 -0
  423. package/dist/ws-live.cjs.map +1 -0
  424. package/dist/ws-live.js +400 -0
  425. package/dist/ws-live.js.map +1 -0
  426. package/dist/ws-realtime.cjs +46 -2
  427. package/dist/ws-realtime.cjs.map +1 -1
  428. package/dist/ws-realtime.d.cts.map +1 -1
  429. package/dist/ws-realtime.d.ts.map +1 -1
  430. package/dist/ws-realtime.js +46 -2
  431. package/dist/ws-realtime.js.map +1 -1
  432. package/dist/ws-responses.cjs +16 -4
  433. package/dist/ws-responses.cjs.map +1 -1
  434. package/dist/ws-responses.js +16 -4
  435. package/dist/ws-responses.js.map +1 -1
  436. package/dist/ws-upstream.cjs +118 -0
  437. package/dist/ws-upstream.cjs.map +1 -0
  438. package/dist/ws-upstream.js +115 -0
  439. package/dist/ws-upstream.js.map +1 -0
  440. package/fixtures/openai-live-client.json +390 -0
  441. package/fixtures/openai-live-managed.json +709 -0
  442. package/package.json +4 -1
@@ -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 isContentWithToolCallsResponse,\n isErrorResponse,\n isAudioResponse,\n extractOverrides,\n validateToolsField,\n formatToMime,\n flattenHeaders,\n isJsonObject,\n getContext,\n getTestId,\n resolveFixtureBlocks,\n resolveResponse,\n resolveStrictMode,\n resolveReasoningForModel,\n strictOverrideField,\n strictNoMatchMessage,\n strictNoMatchLogLine,\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 { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.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}\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 return validateToolsField(req.tools);\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,\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 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 let argsObj: Record<string, unknown>;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\") as Record<string, unknown>;\n } catch {\n logger.warn(`Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`);\n argsObj = {};\n }\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: argsObj,\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 resolved = resolveFixtureBlocks(blocks);\n resolved.forEach((block, i) => {\n const isLast = i === resolved.length - 1;\n const finishReason = isLast\n ? geminiFinishReason(overrides?.finishReason, \"FUNCTION_CALL\")\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 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 resolved = resolveFixtureBlocks(blocks);\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(overrides?.finishReason, \"FUNCTION_CALL\"),\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 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 (audio.toolCalls?.length) {\n parts.push(...audio.toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n }\n return parts;\n}\n\nfunction buildGeminiAudioResponse(\n audio: AudioResponse,\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk {\n return {\n candidates: [\n {\n content: { role: \"model\", parts: buildGeminiAudioParts(audio, logger, effReasoning) },\n finishReason: audio.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 logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk[] {\n return [\n {\n candidates: [\n {\n content: { role: \"model\", parts: buildGeminiAudioParts(audio, logger, effReasoning) },\n finishReason: audio.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// ─── 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 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 applyChaos(\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 latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\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 if (!streaming) {\n const body = buildGeminiAudioResponse(response, logger, effReasoning);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiAudioStreamChunks(response, 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 (isContentWithToolCallsResponse(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 if (!streaming) {\n const body = buildGeminiContentWithToolCallsResponse(\n response.content ?? \"\",\n response.toolCalls ?? [],\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 response.toolCalls ?? [],\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 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 if (!streaming) {\n const body = buildGeminiToolCallResponse(response.toolCalls, logger, effReasoning, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiToolCallStreamChunks(\n response.toolCalls,\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 journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\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":[],"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 isContentWithToolCallsResponse,\n isErrorResponse,\n isAudioResponse,\n extractOverrides,\n validateToolsField,\n formatToMime,\n flattenHeaders,\n isJsonObject,\n getContext,\n getTestId,\n resolveFixtureBlockOutcome,\n resolveResponse,\n resolveStrictMode,\n resolveReasoningForModel,\n strictOverrideField,\n strictNoMatchMessage,\n strictNoMatchLogLine,\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\";\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}\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,\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 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 let argsObj: Record<string, unknown>;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\") as Record<string, unknown>;\n } catch {\n logger.warn(`Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`);\n argsObj = {};\n }\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: argsObj,\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 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 (audio.toolCalls?.length) {\n parts.push(...audio.toolCalls.map((tc) => parseToolCallPart(tc, logger)));\n }\n return parts;\n}\n\nfunction buildGeminiAudioResponse(\n audio: AudioResponse,\n logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk {\n return {\n candidates: [\n {\n content: { role: \"model\", parts: buildGeminiAudioParts(audio, logger, effReasoning) },\n finishReason: audio.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 logger: Logger,\n effReasoning: string | undefined,\n): GeminiResponseChunk[] {\n return [\n {\n candidates: [\n {\n content: { role: \"model\", parts: buildGeminiAudioParts(audio, logger, effReasoning) },\n finishReason: audio.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// ─── 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 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 latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\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 if (!streaming) {\n const body = buildGeminiAudioResponse(response, logger, effReasoning);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiAudioStreamChunks(response, 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 (isContentWithToolCallsResponse(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 if (!streaming) {\n const body = buildGeminiContentWithToolCallsResponse(\n response.content ?? \"\",\n response.toolCalls ?? [],\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 response.toolCalls ?? [],\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 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 if (!streaming) {\n const body = buildGeminiToolCallResponse(response.toolCalls, logger, effReasoning, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildGeminiToolCallStreamChunks(\n response.toolCalls,\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 journal.add({\n method: req.method ?? \"POST\",\n path,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\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,8 +1,8 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
2
  const require_helpers = require('./helpers.cjs');
3
- const require_router = require('./router.cjs');
4
3
  const require_sse_writer = require('./sse-writer.cjs');
5
4
  const require_chaos = require('./chaos.cjs');
5
+ const require_router = require('./router.cjs');
6
6
  const require_url = require('./url.cjs');
7
7
  const require_recorder = require('./recorder.cjs');
8
8
  const require_video = require('./video.cjs');
@@ -237,7 +237,7 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
237
237
  const snippet = videoReq.prompt.slice(0, 80);
238
238
  defaults.logger.debug(`No fixture matched for request (model=${syntheticReq.model}, msg="${snippet}")`);
239
239
  }
240
- if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
240
+ if (await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
241
241
  method,
242
242
  path,
243
243
  headers: require_helpers.flattenHeaders(req.headers),
@@ -298,7 +298,7 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
298
298
  const worldGeneration = jobs.generation;
299
299
  const response = await require_helpers.resolveResponse(fixture, syntheticReq);
300
300
  if (require_helpers.isErrorResponse(response)) {
301
- if (res.destroyed || res.writableEnded) return;
301
+ if (require_chaos.responseGone(res)) return;
302
302
  const status = response.status ?? 500;
303
303
  journal.add({
304
304
  method,
@@ -314,7 +314,7 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
314
314
  return;
315
315
  }
316
316
  if (!require_helpers.isVideoResponse(response)) {
317
- if (res.destroyed || res.writableEnded) return;
317
+ if (require_chaos.responseGone(res)) return;
318
318
  journal.add({
319
319
  method,
320
320
  path,
@@ -331,7 +331,7 @@ async function handleGrokVideoCreate(req, res, raw, fixtures, journal, defaults,
331
331
  } }));
332
332
  return;
333
333
  }
334
- if (res.destroyed || res.writableEnded) return;
334
+ if (require_chaos.responseGone(res)) return;
335
335
  journal.add({
336
336
  method,
337
337
  path,
@@ -367,13 +367,13 @@ async function handleGrokVideoStatus(req, res, id, fixtures, journal, defaults,
367
367
  const key = `${testId}:${id}`;
368
368
  const job = grokJobs.get(key);
369
369
  if (!job) {
370
- require_video.handleVideoStatus(req, res, id, journal, defaults, setCorsHeaders, videoStates);
370
+ await require_video.handleVideoStatus(req, res, id, journal, defaults, setCorsHeaders, videoStates);
371
371
  return;
372
372
  }
373
373
  setCorsHeaders(res);
374
374
  const path = req.url ?? `/v1/videos/${id}`;
375
375
  const method = req.method ?? "GET";
376
- if (require_chaos.applyChaos(res, null, defaults.chaos, req.headers, req.url, journal, {
376
+ if (await require_chaos.applyChaosAsync(res, null, defaults.chaos, req.headers, req.url, journal, {
377
377
  method,
378
378
  path,
379
379
  headers: require_helpers.flattenHeaders(req.headers),
@@ -414,7 +414,7 @@ async function handleGrokVideoStatus(req, res, id, fixtures, journal, defaults,
414
414
  });
415
415
  return;
416
416
  }
417
- if (res.destroyed || res.writableEnded) return;
417
+ if (require_chaos.responseGone(res)) return;
418
418
  advanceJob(job);
419
419
  grokJobs.set(key, job);
420
420
  journal.add({
@@ -475,7 +475,7 @@ async function proxyGrokVideoSubmit(args) {
475
475
  }
476
476
  const proxyError = (msg) => {
477
477
  defaults.logger.error(`Grok video submit proxy failed: ${msg}`);
478
- if (res.destroyed || res.writableEnded) return "handled";
478
+ if (require_chaos.responseGone(res)) return "handled";
479
479
  journal.add({
480
480
  method,
481
481
  path,
@@ -523,7 +523,7 @@ async function proxyGrokVideoSubmit(args) {
523
523
  }
524
524
  if (fetched.status === 401 || fetched.status === 403) {
525
525
  defaults.logger.warn(`Upstream rejected the video submit (${fetched.status}) — relaying the upstream status`);
526
- if (res.destroyed || res.writableEnded) return "handled";
526
+ if (require_chaos.responseGone(res)) return "handled";
527
527
  journal.add({
528
528
  method,
529
529
  path,
@@ -568,7 +568,7 @@ async function proxyGrokVideoSubmit(args) {
568
568
  };
569
569
  if (jobs.generation === worldGeneration) jobs.set(`${testId}:${requestId}`, job);
570
570
  else defaults.logger.warn(`Grok video submit for upstream request ${upstreamRequestId} completed after a fixtures reset — not inserting the job into the new world (its polls will 404)`);
571
- if (res.destroyed || res.writableEnded) return "handled";
571
+ if (require_chaos.responseGone(res)) return "handled";
572
572
  journal.add({
573
573
  method,
574
574
  path,
@@ -614,7 +614,7 @@ async function proxyGrokVideoRecordPoll(args) {
614
614
  };
615
615
  const proxyError = (msg) => {
616
616
  logger.error(`Grok video poll proxy failed: ${msg}`);
617
- if (res.destroyed || res.writableEnded) return;
617
+ if (require_chaos.responseGone(res)) return;
618
618
  journalProxy(502);
619
619
  require_sse_writer.writeErrorResponse(res, 502, JSON.stringify({
620
620
  code: "proxy_error",
@@ -648,7 +648,7 @@ async function proxyGrokVideoRecordPoll(args) {
648
648
  }
649
649
  if (fetched.status === 401 || fetched.status === 403) {
650
650
  logger.warn(`Upstream rejected the status poll for job ${job.upstreamRequestId} (${fetched.status}) — relaying the upstream status`);
651
- if (res.destroyed || res.writableEnded) return;
651
+ if (require_chaos.responseGone(res)) return;
652
652
  journalProxy(fetched.status);
653
653
  res.writeHead(fetched.status, { "Content-Type": fetched.contentType ?? "application/json" });
654
654
  res.end(fetched.text);
@@ -678,7 +678,7 @@ async function proxyGrokVideoRecordPoll(args) {
678
678
  request_id: job.requestId
679
679
  };
680
680
  const relayJson = (body) => {
681
- if (res.destroyed || res.writableEnded) return;
681
+ if (require_chaos.responseGone(res)) return;
682
682
  journalProxy(200);
683
683
  res.writeHead(200, { "Content-Type": "application/json" });
684
684
  res.end(JSON.stringify(body));