@copilotkit/aimock 1.45.1 → 1.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (492) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -1
  4. package/README.md +11 -2
  5. package/dist/agui-mock.cjs +25 -25
  6. package/dist/agui-mock.cjs.map +1 -1
  7. package/dist/agui-mock.d.cts.map +1 -1
  8. package/dist/agui-mock.d.ts.map +1 -1
  9. package/dist/agui-mock.js +25 -25
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-types.d.cts.map +1 -1
  12. package/dist/aimock-cli.cjs +15 -1
  13. package/dist/aimock-cli.cjs.map +1 -1
  14. package/dist/aimock-cli.d.cts.map +1 -1
  15. package/dist/aimock-cli.d.ts.map +1 -1
  16. package/dist/aimock-cli.js +16 -2
  17. package/dist/aimock-cli.js.map +1 -1
  18. package/dist/api-key-auth.cjs +10 -9
  19. package/dist/api-key-auth.cjs.map +1 -1
  20. package/dist/api-key-auth.d.cts.map +1 -1
  21. package/dist/api-key-auth.d.ts.map +1 -1
  22. package/dist/api-key-auth.js +11 -10
  23. package/dist/api-key-auth.js.map +1 -1
  24. package/dist/aws-event-stream.cjs +20 -23
  25. package/dist/aws-event-stream.cjs.map +1 -1
  26. package/dist/aws-event-stream.d.cts +0 -1
  27. package/dist/aws-event-stream.d.ts +0 -1
  28. package/dist/aws-event-stream.js +20 -24
  29. package/dist/aws-event-stream.js.map +1 -1
  30. package/dist/batches.cjs +2 -4
  31. package/dist/batches.cjs.map +1 -1
  32. package/dist/batches.d.cts.map +1 -1
  33. package/dist/batches.d.ts.map +1 -1
  34. package/dist/batches.js +2 -4
  35. package/dist/batches.js.map +1 -1
  36. package/dist/bedrock-converse.cjs +139 -161
  37. package/dist/bedrock-converse.cjs.map +1 -1
  38. package/dist/bedrock-converse.d.cts.map +1 -1
  39. package/dist/bedrock-converse.d.ts.map +1 -1
  40. package/dist/bedrock-converse.js +137 -159
  41. package/dist/bedrock-converse.js.map +1 -1
  42. package/dist/bedrock.cjs +175 -216
  43. package/dist/bedrock.cjs.map +1 -1
  44. package/dist/bedrock.d.cts.map +1 -1
  45. package/dist/bedrock.d.ts.map +1 -1
  46. package/dist/bedrock.js +172 -213
  47. package/dist/bedrock.js.map +1 -1
  48. package/dist/byteplus-video.cjs +11 -10
  49. package/dist/byteplus-video.cjs.map +1 -1
  50. package/dist/byteplus-video.d.cts.map +1 -1
  51. package/dist/byteplus-video.d.ts.map +1 -1
  52. package/dist/byteplus-video.js +5 -4
  53. package/dist/byteplus-video.js.map +1 -1
  54. package/dist/chaos.cjs +5 -4
  55. package/dist/chaos.cjs.map +1 -1
  56. package/dist/chaos.d.cts.map +1 -1
  57. package/dist/chaos.d.ts.map +1 -1
  58. package/dist/chaos.js +5 -4
  59. package/dist/chaos.js.map +1 -1
  60. package/dist/cli.cjs +15 -0
  61. package/dist/cli.cjs.map +1 -1
  62. package/dist/cli.js +15 -0
  63. package/dist/cli.js.map +1 -1
  64. package/dist/cohere.cjs +107 -180
  65. package/dist/cohere.cjs.map +1 -1
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts.map +1 -1
  68. package/dist/cohere.js +106 -179
  69. package/dist/cohere.js.map +1 -1
  70. package/dist/config-loader.cjs +29 -1
  71. package/dist/config-loader.cjs.map +1 -1
  72. package/dist/config-loader.d.cts +7 -1
  73. package/dist/config-loader.d.cts.map +1 -1
  74. package/dist/config-loader.d.ts +7 -1
  75. package/dist/config-loader.d.ts.map +1 -1
  76. package/dist/config-loader.js +29 -2
  77. package/dist/config-loader.js.map +1 -1
  78. package/dist/count-tokens.cjs +146 -0
  79. package/dist/count-tokens.cjs.map +1 -0
  80. package/dist/count-tokens.js +146 -0
  81. package/dist/count-tokens.js.map +1 -0
  82. package/dist/elevenlabs-audio.cjs +36 -34
  83. package/dist/elevenlabs-audio.cjs.map +1 -1
  84. package/dist/elevenlabs-audio.d.cts.map +1 -1
  85. package/dist/elevenlabs-audio.d.ts.map +1 -1
  86. package/dist/elevenlabs-audio.js +33 -31
  87. package/dist/elevenlabs-audio.js.map +1 -1
  88. package/dist/elevenlabs-voice.cjs +38 -37
  89. package/dist/elevenlabs-voice.cjs.map +1 -1
  90. package/dist/elevenlabs-voice.d.cts.map +1 -1
  91. package/dist/elevenlabs-voice.d.ts.map +1 -1
  92. package/dist/elevenlabs-voice.js +33 -32
  93. package/dist/elevenlabs-voice.js.map +1 -1
  94. package/dist/embedding-request.cjs +16 -0
  95. package/dist/embedding-request.cjs.map +1 -0
  96. package/dist/embedding-request.js +14 -0
  97. package/dist/embedding-request.js.map +1 -0
  98. package/dist/embeddings.cjs +30 -20
  99. package/dist/embeddings.cjs.map +1 -1
  100. package/dist/embeddings.d.cts.map +1 -1
  101. package/dist/embeddings.d.ts.map +1 -1
  102. package/dist/embeddings.js +30 -20
  103. package/dist/embeddings.js.map +1 -1
  104. package/dist/fal-audio.cjs +45 -43
  105. package/dist/fal-audio.cjs.map +1 -1
  106. package/dist/fal-audio.d.cts.map +1 -1
  107. package/dist/fal-audio.d.ts.map +1 -1
  108. package/dist/fal-audio.js +42 -40
  109. package/dist/fal-audio.js.map +1 -1
  110. package/dist/fal.cjs +17 -15
  111. package/dist/fal.cjs.map +1 -1
  112. package/dist/fal.js +16 -14
  113. package/dist/fal.js.map +1 -1
  114. package/dist/files.cjs +2 -4
  115. package/dist/files.cjs.map +1 -1
  116. package/dist/files.d.cts.map +1 -1
  117. package/dist/files.d.ts.map +1 -1
  118. package/dist/files.js +2 -4
  119. package/dist/files.js.map +1 -1
  120. package/dist/fine-tuning.cjs +2 -4
  121. package/dist/fine-tuning.cjs.map +1 -1
  122. package/dist/fine-tuning.d.cts.map +1 -1
  123. package/dist/fine-tuning.d.ts.map +1 -1
  124. package/dist/fine-tuning.js +2 -4
  125. package/dist/fine-tuning.js.map +1 -1
  126. package/dist/fixture-loader.cjs +140 -44
  127. package/dist/fixture-loader.cjs.map +1 -1
  128. package/dist/fixture-loader.d.cts.map +1 -1
  129. package/dist/fixture-loader.d.ts.map +1 -1
  130. package/dist/fixture-loader.js +141 -46
  131. package/dist/fixture-loader.js.map +1 -1
  132. package/dist/gemini-embeddings.cjs +26 -24
  133. package/dist/gemini-embeddings.cjs.map +1 -1
  134. package/dist/gemini-embeddings.js +26 -24
  135. package/dist/gemini-embeddings.js.map +1 -1
  136. package/dist/gemini-interactions.cjs +113 -156
  137. package/dist/gemini-interactions.cjs.map +1 -1
  138. package/dist/gemini-interactions.d.cts +0 -1
  139. package/dist/gemini-interactions.d.cts.map +1 -1
  140. package/dist/gemini-interactions.d.ts +0 -1
  141. package/dist/gemini-interactions.d.ts.map +1 -1
  142. package/dist/gemini-interactions.js +113 -156
  143. package/dist/gemini-interactions.js.map +1 -1
  144. package/dist/gemini.cjs +295 -218
  145. package/dist/gemini.cjs.map +1 -1
  146. package/dist/gemini.d.cts.map +1 -1
  147. package/dist/gemini.d.ts.map +1 -1
  148. package/dist/gemini.js +295 -219
  149. package/dist/gemini.js.map +1 -1
  150. package/dist/grok-video.cjs +14 -12
  151. package/dist/grok-video.cjs.map +1 -1
  152. package/dist/grok-video.d.cts.map +1 -1
  153. package/dist/grok-video.d.ts.map +1 -1
  154. package/dist/grok-video.js +8 -6
  155. package/dist/grok-video.js.map +1 -1
  156. package/dist/helpers.cjs +411 -75
  157. package/dist/helpers.cjs.map +1 -1
  158. package/dist/helpers.d.cts +51 -1
  159. package/dist/helpers.d.cts.map +1 -1
  160. package/dist/helpers.d.ts +51 -1
  161. package/dist/helpers.d.ts.map +1 -1
  162. package/dist/helpers.js +402 -74
  163. package/dist/helpers.js.map +1 -1
  164. package/dist/images.cjs +30 -28
  165. package/dist/images.cjs.map +1 -1
  166. package/dist/images.d.cts.map +1 -1
  167. package/dist/images.d.ts.map +1 -1
  168. package/dist/images.js +27 -25
  169. package/dist/images.js.map +1 -1
  170. package/dist/index.cjs +4 -2
  171. package/dist/index.d.cts +4 -4
  172. package/dist/index.d.ts +4 -4
  173. package/dist/index.js +4 -4
  174. package/dist/journal.cjs +3 -1
  175. package/dist/journal.cjs.map +1 -1
  176. package/dist/journal.d.cts.map +1 -1
  177. package/dist/journal.d.ts.map +1 -1
  178. package/dist/journal.js +2 -1
  179. package/dist/journal.js.map +1 -1
  180. package/dist/llmock.cjs +3 -0
  181. package/dist/llmock.cjs.map +1 -1
  182. package/dist/llmock.d.cts.map +1 -1
  183. package/dist/llmock.d.ts.map +1 -1
  184. package/dist/llmock.js +3 -0
  185. package/dist/llmock.js.map +1 -1
  186. package/dist/mcp-fakes.d.cts.map +1 -1
  187. package/dist/mcp-handler.cjs +7 -3
  188. package/dist/mcp-handler.cjs.map +1 -1
  189. package/dist/mcp-handler.js +7 -3
  190. package/dist/mcp-handler.js.map +1 -1
  191. package/dist/mcp-recorder.cjs +2 -1
  192. package/dist/mcp-recorder.cjs.map +1 -1
  193. package/dist/mcp-recorder.js +3 -2
  194. package/dist/mcp-recorder.js.map +1 -1
  195. package/dist/messages.cjs +235 -202
  196. package/dist/messages.cjs.map +1 -1
  197. package/dist/messages.d.cts.map +1 -1
  198. package/dist/messages.d.ts.map +1 -1
  199. package/dist/messages.js +234 -202
  200. package/dist/messages.js.map +1 -1
  201. package/dist/metrics.cjs +5 -2
  202. package/dist/metrics.cjs.map +1 -1
  203. package/dist/metrics.d.cts.map +1 -1
  204. package/dist/metrics.d.ts.map +1 -1
  205. package/dist/metrics.js +6 -3
  206. package/dist/metrics.js.map +1 -1
  207. package/dist/misbehavior.cjs +62 -3
  208. package/dist/misbehavior.cjs.map +1 -1
  209. package/dist/misbehavior.d.cts.map +1 -1
  210. package/dist/misbehavior.d.ts.map +1 -1
  211. package/dist/misbehavior.js +63 -4
  212. package/dist/misbehavior.js.map +1 -1
  213. package/dist/model-utils.cjs +1 -0
  214. package/dist/model-utils.cjs.map +1 -1
  215. package/dist/model-utils.js +1 -1
  216. package/dist/model-utils.js.map +1 -1
  217. package/dist/moderation.cjs +15 -12
  218. package/dist/moderation.cjs.map +1 -1
  219. package/dist/moderation.js +14 -11
  220. package/dist/moderation.js.map +1 -1
  221. package/dist/ndjson-writer.cjs +21 -23
  222. package/dist/ndjson-writer.cjs.map +1 -1
  223. package/dist/ndjson-writer.d.cts +0 -1
  224. package/dist/ndjson-writer.d.cts.map +1 -1
  225. package/dist/ndjson-writer.d.ts +0 -1
  226. package/dist/ndjson-writer.d.ts.map +1 -1
  227. package/dist/ndjson-writer.js +21 -24
  228. package/dist/ndjson-writer.js.map +1 -1
  229. package/dist/ollama.cjs +276 -210
  230. package/dist/ollama.cjs.map +1 -1
  231. package/dist/ollama.d.cts.map +1 -1
  232. package/dist/ollama.d.ts.map +1 -1
  233. package/dist/ollama.js +273 -207
  234. package/dist/ollama.js.map +1 -1
  235. package/dist/openapi.cjs +323 -3
  236. package/dist/openapi.cjs.map +1 -1
  237. package/dist/openapi.d.cts.map +1 -1
  238. package/dist/openapi.d.ts.map +1 -1
  239. package/dist/openapi.js +324 -4
  240. package/dist/openapi.js.map +1 -1
  241. package/dist/openrouter-chat.cjs +11 -155
  242. package/dist/openrouter-chat.cjs.map +1 -1
  243. package/dist/openrouter-chat.js +11 -155
  244. package/dist/openrouter-chat.js.map +1 -1
  245. package/dist/openrouter-video.cjs +51 -74
  246. package/dist/openrouter-video.cjs.map +1 -1
  247. package/dist/openrouter-video.d.cts.map +1 -1
  248. package/dist/openrouter-video.d.ts.map +1 -1
  249. package/dist/openrouter-video.js +37 -60
  250. package/dist/openrouter-video.js.map +1 -1
  251. package/dist/raw-validation.cjs +177 -0
  252. package/dist/raw-validation.cjs.map +1 -0
  253. package/dist/raw-validation.js +176 -0
  254. package/dist/raw-validation.js.map +1 -0
  255. package/dist/recorder.cjs +176 -95
  256. package/dist/recorder.cjs.map +1 -1
  257. package/dist/recorder.d.cts.map +1 -1
  258. package/dist/recorder.d.ts.map +1 -1
  259. package/dist/recorder.js +176 -95
  260. package/dist/recorder.js.map +1 -1
  261. package/dist/rerank.cjs +13 -11
  262. package/dist/rerank.cjs.map +1 -1
  263. package/dist/rerank.js +12 -10
  264. package/dist/rerank.js.map +1 -1
  265. package/dist/response/catalog/anthropic-page.cjs +39 -0
  266. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  267. package/dist/response/catalog/anthropic-page.js +38 -0
  268. package/dist/response/catalog/anthropic-page.js.map +1 -0
  269. package/dist/response/catalog/collect.cjs +61 -0
  270. package/dist/response/catalog/collect.cjs.map +1 -0
  271. package/dist/response/catalog/collect.js +59 -0
  272. package/dist/response/catalog/collect.js.map +1 -0
  273. package/dist/response/catalog/gemini-page.cjs +25 -0
  274. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  275. package/dist/response/catalog/gemini-page.js +24 -0
  276. package/dist/response/catalog/gemini-page.js.map +1 -0
  277. package/dist/response/catalog/render.cjs +182 -0
  278. package/dist/response/catalog/render.cjs.map +1 -0
  279. package/dist/response/catalog/render.js +171 -0
  280. package/dist/response/catalog/render.js.map +1 -0
  281. package/dist/response/deliver.cjs +144 -0
  282. package/dist/response/deliver.cjs.map +1 -0
  283. package/dist/response/deliver.js +143 -0
  284. package/dist/response/deliver.js.map +1 -0
  285. package/dist/response/errors.cjs +225 -0
  286. package/dist/response/errors.cjs.map +1 -0
  287. package/dist/response/errors.js +224 -0
  288. package/dist/response/errors.js.map +1 -0
  289. package/dist/response/finish.cjs +39 -0
  290. package/dist/response/finish.cjs.map +1 -0
  291. package/dist/response/finish.js +35 -0
  292. package/dist/response/finish.js.map +1 -0
  293. package/dist/response/frame-loop.cjs +102 -0
  294. package/dist/response/frame-loop.cjs.map +1 -0
  295. package/dist/response/frame-loop.js +100 -0
  296. package/dist/response/frame-loop.js.map +1 -0
  297. package/dist/response/lifecycle.cjs +223 -0
  298. package/dist/response/lifecycle.cjs.map +1 -0
  299. package/dist/response/lifecycle.js +211 -0
  300. package/dist/response/lifecycle.js.map +1 -0
  301. package/dist/response/policy.cjs +126 -0
  302. package/dist/response/policy.cjs.map +1 -0
  303. package/dist/response/policy.js +120 -0
  304. package/dist/response/policy.js.map +1 -0
  305. package/dist/response/profiles.cjs +69 -0
  306. package/dist/response/profiles.cjs.map +1 -0
  307. package/dist/response/profiles.js +65 -0
  308. package/dist/response/profiles.js.map +1 -0
  309. package/dist/response/proxy.cjs +71 -0
  310. package/dist/response/proxy.cjs.map +1 -0
  311. package/dist/response/proxy.js +70 -0
  312. package/dist/response/proxy.js.map +1 -0
  313. package/dist/response/raw.cjs +228 -0
  314. package/dist/response/raw.cjs.map +1 -0
  315. package/dist/response/raw.js +227 -0
  316. package/dist/response/raw.js.map +1 -0
  317. package/dist/response/timing.cjs +157 -0
  318. package/dist/response/timing.cjs.map +1 -0
  319. package/dist/response/timing.js +150 -0
  320. package/dist/response/timing.js.map +1 -0
  321. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  322. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  323. package/dist/response/usage/codecs/anthropic.js +50 -0
  324. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  325. package/dist/response/usage/codecs/cohere.cjs +36 -0
  326. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  327. package/dist/response/usage/codecs/cohere.js +35 -0
  328. package/dist/response/usage/codecs/cohere.js.map +1 -0
  329. package/dist/response/usage/codecs/converse.cjs +16 -0
  330. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  331. package/dist/response/usage/codecs/converse.js +15 -0
  332. package/dist/response/usage/codecs/converse.js.map +1 -0
  333. package/dist/response/usage/codecs/gemini.cjs +31 -0
  334. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  335. package/dist/response/usage/codecs/gemini.js +31 -0
  336. package/dist/response/usage/codecs/gemini.js.map +1 -0
  337. package/dist/response/usage/codecs/interactions.cjs +22 -0
  338. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  339. package/dist/response/usage/codecs/interactions.js +21 -0
  340. package/dist/response/usage/codecs/interactions.js.map +1 -0
  341. package/dist/response/usage/codecs/ollama.cjs +17 -0
  342. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  343. package/dist/response/usage/codecs/ollama.js +17 -0
  344. package/dist/response/usage/codecs/ollama.js.map +1 -0
  345. package/dist/response/usage/codecs/openai.cjs +52 -0
  346. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  347. package/dist/response/usage/codecs/openai.js +50 -0
  348. package/dist/response/usage/codecs/openai.js.map +1 -0
  349. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  350. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  351. package/dist/response/usage/codecs/openrouter.js +67 -0
  352. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  353. package/dist/response/usage/codecs/realtime.cjs +19 -0
  354. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  355. package/dist/response/usage/codecs/realtime.js +18 -0
  356. package/dist/response/usage/codecs/realtime.js.map +1 -0
  357. package/dist/response/usage/codecs/responses.cjs +21 -0
  358. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  359. package/dist/response/usage/codecs/responses.js +20 -0
  360. package/dist/response/usage/codecs/responses.js.map +1 -0
  361. package/dist/response/usage/estimate.cjs +23 -0
  362. package/dist/response/usage/estimate.cjs.map +1 -0
  363. package/dist/response/usage/estimate.js +21 -0
  364. package/dist/response/usage/estimate.js.map +1 -0
  365. package/dist/response/usage/fields.cjs +72 -0
  366. package/dist/response/usage/fields.cjs.map +1 -0
  367. package/dist/response/usage/fields.js +70 -0
  368. package/dist/response/usage/fields.js.map +1 -0
  369. package/dist/response/usage/recording.cjs +52 -0
  370. package/dist/response/usage/recording.cjs.map +1 -0
  371. package/dist/response/usage/recording.js +52 -0
  372. package/dist/response/usage/recording.js.map +1 -0
  373. package/dist/responses.cjs +304 -196
  374. package/dist/responses.cjs.map +1 -1
  375. package/dist/responses.d.cts.map +1 -1
  376. package/dist/responses.d.ts.map +1 -1
  377. package/dist/responses.js +303 -195
  378. package/dist/responses.js.map +1 -1
  379. package/dist/route-registry.cjs +45 -0
  380. package/dist/route-registry.cjs.map +1 -1
  381. package/dist/route-registry.d.cts.map +1 -1
  382. package/dist/route-registry.d.ts.map +1 -1
  383. package/dist/route-registry.js +41 -1
  384. package/dist/route-registry.js.map +1 -1
  385. package/dist/router.cjs +24 -4
  386. package/dist/router.cjs.map +1 -1
  387. package/dist/router.d.cts +7 -1
  388. package/dist/router.d.cts.map +1 -1
  389. package/dist/router.d.ts +7 -1
  390. package/dist/router.d.ts.map +1 -1
  391. package/dist/router.js +24 -5
  392. package/dist/router.js.map +1 -1
  393. package/dist/search.cjs +13 -11
  394. package/dist/search.cjs.map +1 -1
  395. package/dist/search.js +12 -10
  396. package/dist/search.js.map +1 -1
  397. package/dist/server.cjs +293 -248
  398. package/dist/server.cjs.map +1 -1
  399. package/dist/server.d.cts.map +1 -1
  400. package/dist/server.d.ts.map +1 -1
  401. package/dist/server.js +294 -249
  402. package/dist/server.js.map +1 -1
  403. package/dist/speech.cjs +20 -18
  404. package/dist/speech.cjs.map +1 -1
  405. package/dist/speech.d.cts.map +1 -1
  406. package/dist/speech.d.ts.map +1 -1
  407. package/dist/speech.js +19 -17
  408. package/dist/speech.js.map +1 -1
  409. package/dist/sse-writer.cjs +56 -30
  410. package/dist/sse-writer.cjs.map +1 -1
  411. package/dist/sse-writer.d.cts +0 -1
  412. package/dist/sse-writer.d.cts.map +1 -1
  413. package/dist/sse-writer.d.ts +0 -1
  414. package/dist/sse-writer.d.ts.map +1 -1
  415. package/dist/sse-writer.js +55 -31
  416. package/dist/sse-writer.js.map +1 -1
  417. package/dist/stream-collapse.cjs +118 -11
  418. package/dist/stream-collapse.cjs.map +1 -1
  419. package/dist/stream-collapse.d.cts +10 -9
  420. package/dist/stream-collapse.d.cts.map +1 -1
  421. package/dist/stream-collapse.d.ts +10 -9
  422. package/dist/stream-collapse.d.ts.map +1 -1
  423. package/dist/stream-collapse.js +118 -11
  424. package/dist/stream-collapse.js.map +1 -1
  425. package/dist/transcription.cjs +31 -43
  426. package/dist/transcription.cjs.map +1 -1
  427. package/dist/transcription.d.cts.map +1 -1
  428. package/dist/transcription.d.ts.map +1 -1
  429. package/dist/transcription.js +31 -43
  430. package/dist/transcription.js.map +1 -1
  431. package/dist/types.cjs.map +1 -1
  432. package/dist/types.d.cts +231 -9
  433. package/dist/types.d.cts.map +1 -1
  434. package/dist/types.d.ts +231 -9
  435. package/dist/types.d.ts.map +1 -1
  436. package/dist/types.js.map +1 -1
  437. package/dist/vector-mock.cjs +3 -8
  438. package/dist/vector-mock.cjs.map +1 -1
  439. package/dist/vector-mock.d.cts.map +1 -1
  440. package/dist/vector-mock.d.ts.map +1 -1
  441. package/dist/vector-mock.js +3 -8
  442. package/dist/vector-mock.js.map +1 -1
  443. package/dist/vector-stores.cjs +2 -4
  444. package/dist/vector-stores.cjs.map +1 -1
  445. package/dist/vector-stores.d.cts.map +1 -1
  446. package/dist/vector-stores.d.ts.map +1 -1
  447. package/dist/vector-stores.js +2 -4
  448. package/dist/vector-stores.js.map +1 -1
  449. package/dist/veo-video.cjs +16 -14
  450. package/dist/veo-video.cjs.map +1 -1
  451. package/dist/veo-video.js +10 -8
  452. package/dist/veo-video.js.map +1 -1
  453. package/dist/video.cjs +21 -19
  454. package/dist/video.cjs.map +1 -1
  455. package/dist/video.d.cts.map +1 -1
  456. package/dist/video.d.ts.map +1 -1
  457. package/dist/video.js +20 -18
  458. package/dist/video.js.map +1 -1
  459. package/dist/ws-framing.cjs +12 -6
  460. package/dist/ws-framing.cjs.map +1 -1
  461. package/dist/ws-framing.d.cts +2 -0
  462. package/dist/ws-framing.d.cts.map +1 -1
  463. package/dist/ws-framing.d.ts +2 -0
  464. package/dist/ws-framing.d.ts.map +1 -1
  465. package/dist/ws-framing.js +12 -6
  466. package/dist/ws-framing.js.map +1 -1
  467. package/dist/ws-gemini-live.cjs +131 -96
  468. package/dist/ws-gemini-live.cjs.map +1 -1
  469. package/dist/ws-gemini-live.d.cts +0 -1
  470. package/dist/ws-gemini-live.d.cts.map +1 -1
  471. package/dist/ws-gemini-live.d.ts +0 -1
  472. package/dist/ws-gemini-live.d.ts.map +1 -1
  473. package/dist/ws-gemini-live.js +130 -95
  474. package/dist/ws-gemini-live.js.map +1 -1
  475. package/dist/ws-live.cjs +6 -5
  476. package/dist/ws-live.cjs.map +1 -1
  477. package/dist/ws-live.js +5 -4
  478. package/dist/ws-live.js.map +1 -1
  479. package/dist/ws-realtime.cjs +108 -124
  480. package/dist/ws-realtime.cjs.map +1 -1
  481. package/dist/ws-realtime.d.cts.map +1 -1
  482. package/dist/ws-realtime.d.ts.map +1 -1
  483. package/dist/ws-realtime.js +105 -121
  484. package/dist/ws-realtime.js.map +1 -1
  485. package/dist/ws-responses.cjs +80 -59
  486. package/dist/ws-responses.cjs.map +1 -1
  487. package/dist/ws-responses.d.cts.map +1 -1
  488. package/dist/ws-responses.d.ts.map +1 -1
  489. package/dist/ws-responses.js +79 -58
  490. package/dist/ws-responses.js.map +1 -1
  491. package/package.json +4 -1
  492. package/skills/write-fixtures/SKILL.md +57 -16
package/dist/responses.js CHANGED
@@ -1,11 +1,18 @@
1
+ import { renderError } from "./response/errors.js";
1
2
  import { isReasoningModel } from "./model-utils.js";
2
- import { assertCustomToolCalls, assertResponsesToolCalls, extractOverrides, flattenHeaders, generateId, generateToolCallId, getContext, getTestId, isCombinedFixtureResponse, isErrorResponse, isJsonObject, isTextResponse, isToolCallResponse, prepareOpenAIChatMisbehavior, resolveFixtureBlocks, resolveOpenAIChatMisbehaviorUsage, resolveReasoningForModel, resolveResponse, resolveServedBlockOutcome, resolveStrictMode, serializeErrorResponse, servedToolCalls, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, toolCallFixtureBlock, withoutLegacyNamespaces } from "./helpers.js";
3
- import { calculateDelay, delay, writeErrorResponse } from "./sse-writer.js";
3
+ import { resolveRequestDecisions, strictOverrideField } from "./response/policy.js";
4
+ import { UnsupportedGeneratedFileError, assertCustomToolCalls, assertResponsesToolCalls, extractOverrides, fixtureCitations, fixtureFiles, flattenHeaders, generateId, generateToolCallId, getContext, getTestId, isCombinedFixtureResponse, isErrorResponse, isJsonObject, isRawResponse, isTextResponse, isToolCallResponse, prepareOpenAIChatMisbehavior, resolveFixtureBlocks, resolveOpenAIChatMisbehaviorUsage, resolveReasoningForModel, resolveResponse, resolveServedBlockOutcome, serializeErrorResponse, servedToolCalls, strictNoMatchLogLine, strictNoMatchMessage, surrogateSafeChunks, toolCallFixtureBlock, withoutLegacyNamespaces } from "./helpers.js";
5
+ import { typedSseCodec, writeErrorResponse } from "./sse-writer.js";
4
6
  import { applyChaosAsync } from "./chaos.js";
5
7
  import { planMisbehavior, recordMisbehaviorOutcome } from "./misbehavior.js";
6
8
  import { matchFixtureDiagnostic, recordMatchOptions, setResponsesOfferedTools } from "./router.js";
7
9
  import { proxyAndRecord } from "./recorder.js";
8
- import { createInterruptionSignal } from "./interruption.js";
10
+ import { extendRecordedTimings, misbehaviorPayloadLength, nonStreamingTimingEnabled, responsesOutputFrames, responsesPayloadLength } from "./response/timing.js";
11
+ import { deliverJson, deliverStream } from "./response/deliver.js";
12
+ import { proxyInterruption, withHangUpDetection } from "./response/proxy.js";
13
+ import { serveRawResponse } from "./response/raw.js";
14
+ import { render } from "./response/usage/codecs/responses.js";
15
+ import { mapResponsesFinish } from "./response/finish.js";
9
16
  import { randomBytes } from "node:crypto";
10
17
 
11
18
  //#region src/responses.ts
@@ -310,25 +317,7 @@ function validateResponsesTools(tools) {
310
317
  }
311
318
  function responsesStatus(finishReason, defaultStatus) {
312
319
  if (!finishReason) return defaultStatus;
313
- if (finishReason === "stop") return "completed";
314
- if (finishReason === "tool_calls") return "completed";
315
- if (finishReason === "length") return "incomplete";
316
- if (finishReason === "content_filter") return "failed";
317
- return finishReason;
318
- }
319
- function responsesUsage(overrides) {
320
- if (!overrides?.usage) return {
321
- input_tokens: 0,
322
- output_tokens: 0,
323
- total_tokens: 0
324
- };
325
- const input = overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0;
326
- const output = overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0;
327
- return {
328
- input_tokens: input,
329
- output_tokens: output,
330
- total_tokens: overrides.usage.total_tokens ?? input + output
331
- };
320
+ return mapResponsesFinish(finishReason);
332
321
  }
333
322
  function responseId() {
334
323
  return generateId("resp");
@@ -338,7 +327,7 @@ function itemId() {
338
327
  }
339
328
  function buildTextStreamEvents(content, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
340
329
  const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
341
- const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex);
330
+ const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase, buildAnnotations(content, overrides?.citations));
342
331
  events.push(...msgEvents);
343
332
  events.push({
344
333
  type: "response.completed",
@@ -349,7 +338,7 @@ function buildTextStreamEvents(content, model, chunkSize, reasoning, webSearches
349
338
  model: overrides?.model ?? model,
350
339
  status: responsesStatus(overrides?.finishReason, "completed"),
351
340
  output: [...prefixOutputItems, msgItem],
352
- usage: responsesUsage(overrides)
341
+ usage: render(overrides)
353
342
  }
354
343
  });
355
344
  return events;
@@ -447,6 +436,7 @@ function buildToolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSe
447
436
  }
448
437
  function toolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
449
438
  assertResponsesToolCalls(toolCalls);
439
+ fixtureCitations("", overrides?.citations);
450
440
  const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
451
441
  const fcOutputItems = [];
452
442
  for (let idx = 0; idx < toolCalls.length; idx++) {
@@ -463,7 +453,7 @@ function toolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSearche
463
453
  model: overrides?.model ?? model,
464
454
  status: responsesStatus(overrides?.finishReason, "completed"),
465
455
  output: [...prefixOutputItems, ...fcOutputItems],
466
- usage: responsesUsage(overrides)
456
+ usage: render(overrides)
467
457
  }
468
458
  });
469
459
  return events;
@@ -683,6 +673,79 @@ function buildWebSearchStreamEvents(queries, startOutputIndex) {
683
673
  }
684
674
  return events;
685
675
  }
676
+ /**
677
+ * A Responses `image_generation_call` output item for one fixture file (see
678
+ * `ResponseOutputItem.ImageGenerationCall` in the openai SDK): `result` holds
679
+ * the base64 bytes once completed and is null while in progress.
680
+ */
681
+ function imageGenerationCallItem(id, file, status) {
682
+ return {
683
+ type: "image_generation_call",
684
+ id,
685
+ status,
686
+ result: status === "completed" ? file.data : null
687
+ };
688
+ }
689
+ /**
690
+ * A fixture file that is not `image/*` reached the Responses API, which
691
+ * carries generated files only as `image_generation_call` items. Surfaces as
692
+ * the same coded 500 (`aimock_unsupported_generated_file`), naming `mimeType`.
693
+ */
694
+ var NonImageGeneratedFileError = class extends UnsupportedGeneratedFileError {
695
+ constructor(mimeType) {
696
+ super("the OpenAI Responses API");
697
+ this.message = `aimock: fixture "files" on the OpenAI Responses API carry only image/* files (as image_generation_call items); got mimeType "${mimeType}"`;
698
+ }
699
+ };
700
+ /**
701
+ * Throw {@link NonImageGeneratedFileError} for the first fixture file whose
702
+ * `mimeType` is not `image/*`. Called before any output item is built, so
703
+ * nothing reaches the client.
704
+ */
705
+ function requireImageFiles(files) {
706
+ const nonImage = files.find((f) => !f.mimeType.toLowerCase().startsWith("image/"));
707
+ if (nonImage) throw new NonImageGeneratedFileError(nonImage.mimeType);
708
+ }
709
+ /** Fixture `files` as completed `image_generation_call` items (non-streaming). */
710
+ function imageGenerationCallItems(files) {
711
+ const validated = fixtureFiles(files);
712
+ requireImageFiles(validated);
713
+ return validated.map((f) => imageGenerationCallItem(generateId("ig"), f, "completed"));
714
+ }
715
+ /**
716
+ * Streamed form of {@link imageGenerationCallItems}: per file, the item's
717
+ * `output_item.added`, the `image_generation_call` `in_progress`,
718
+ * `generating` and `completed` events, then `output_item.done` carrying the
719
+ * completed item.
720
+ */
721
+ function buildImageGenerationStreamEvents(files, startOutputIndex) {
722
+ requireImageFiles(files);
723
+ const events = [];
724
+ files.forEach((file, i) => {
725
+ const id = generateId("ig");
726
+ const outputIndex = startOutputIndex + i;
727
+ events.push({
728
+ type: "response.output_item.added",
729
+ output_index: outputIndex,
730
+ item: imageGenerationCallItem(id, file, "in_progress")
731
+ });
732
+ for (const phase of [
733
+ "in_progress",
734
+ "generating",
735
+ "completed"
736
+ ]) events.push({
737
+ type: `response.image_generation_call.${phase}`,
738
+ output_index: outputIndex,
739
+ item_id: id
740
+ });
741
+ events.push({
742
+ type: "response.output_item.done",
743
+ output_index: outputIndex,
744
+ item: imageGenerationCallItem(id, file, "completed")
745
+ });
746
+ });
747
+ return events;
748
+ }
686
749
  function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, forceReasoning = false) {
687
750
  const respId = overrides?.id ?? responseId();
688
751
  const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
@@ -726,6 +789,13 @@ function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrid
726
789
  for (const de of doneEvents) prefixOutputItems.push(de.item);
727
790
  nextOutputIndex += webSearches.length;
728
791
  }
792
+ const files = fixtureFiles(overrides?.files);
793
+ if (files.length > 0) {
794
+ const fileEvents = buildImageGenerationStreamEvents(files, nextOutputIndex);
795
+ events.push(...fileEvents);
796
+ for (const e of fileEvents) if (e.type === "response.output_item.done") prefixOutputItems.push(e.item);
797
+ nextOutputIndex += files.length;
798
+ }
729
799
  return {
730
800
  respId,
731
801
  created,
@@ -734,7 +804,84 @@ function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrid
734
804
  nextOutputIndex
735
805
  };
736
806
  }
737
- function buildMessageOutputEvents(content, chunkSize, outputIndex) {
807
+ /**
808
+ * Fixture `citations` as `output_text` annotations for each text in `texts`
809
+ * (one per message item), in fixture order: a URL citation becomes
810
+ * `url_citation`, a file citation (`type: "file"`) becomes `file_citation`
811
+ * (`file_id`, `filename`, `index`; see `ResponseOutputText.FileCitation` in
812
+ * the openai SDK). Offsets index the joined text of all texts, as on every
813
+ * other provider. A URL citation is a range and goes only to the text holding
814
+ * its `startIndex`. A file citation is a point after the cited text, so it
815
+ * goes to the last text starting before its `index`: an `index` equal to a
816
+ * text's end stays at the end of that text (not at 0 of the next text), and
817
+ * an `index` of 0 goes to the first text. Each citation is rebased onto its
818
+ * text since Responses offsets are per `output_text`, with `end_index`
819
+ * clamped to it. Anthropic and Gemini need no point rule: they have no file
820
+ * citation. Anthropic URL citations follow the same start-owns-range rule;
821
+ * Gemini does not, and splits a citation that spans several text parts into
822
+ * one grounding support per part (see geminiGroundingSupports).
823
+ * Missing offsets take the defaults `resolveCitations` applies: a URL
824
+ * citation starts at 0 and ends at the end of the joined text, so one without
825
+ * offsets goes on the first text and covers all of it; a file citation's
826
+ * `index` defaults to the end of the joined text (the last text).
827
+ */
828
+ function blockAnnotations(texts, citations) {
829
+ const perText = texts.map(() => []);
830
+ if (texts.length === 0) {
831
+ fixtureCitations("", citations);
832
+ return perText;
833
+ }
834
+ const starts = [];
835
+ let offset = 0;
836
+ for (const text of texts) {
837
+ starts.push(offset);
838
+ offset += text.length;
839
+ }
840
+ const joinedLength = offset;
841
+ const ownerOf = (start) => {
842
+ let owner = 0;
843
+ for (let k = 1; k < starts.length; k++) if (starts[k] <= start) owner = k;
844
+ return owner;
845
+ };
846
+ const pointOwnerOf = (index) => {
847
+ let owner = 0;
848
+ for (let k = 1; k < starts.length; k++) if (starts[k] < index) owner = k;
849
+ return owner;
850
+ };
851
+ for (const c of fixtureCitations(texts.join(""), citations)) {
852
+ if (c.type === "file") {
853
+ const index = c.index ?? joinedLength;
854
+ const owner = pointOwnerOf(index);
855
+ perText[owner].push({
856
+ type: "file_citation",
857
+ file_id: c.fileId,
858
+ filename: c.filename,
859
+ index: index - starts[owner]
860
+ });
861
+ continue;
862
+ }
863
+ const start = c.startIndex ?? 0;
864
+ const end = c.endIndex ?? joinedLength;
865
+ const owner = ownerOf(start);
866
+ perText[owner].push({
867
+ type: "url_citation",
868
+ url: c.url,
869
+ title: c.title ?? c.url,
870
+ start_index: start - starts[owner],
871
+ end_index: Math.min(end - starts[owner], texts[owner].length)
872
+ });
873
+ }
874
+ return perText;
875
+ }
876
+ /** {@link blockAnnotations} for a turn with a single text. */
877
+ function buildAnnotations(content, citations) {
878
+ return blockAnnotations([content], citations)[0];
879
+ }
880
+ /** The annotations of each text block in `blocks`, in block order. */
881
+ function orderedBlockAnnotations(blocks, citations) {
882
+ return blockAnnotations(blocks.flatMap((b) => b.type === "text" ? [b.text] : []), citations);
883
+ }
884
+ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase, annotations) {
738
885
  const msgId = itemId();
739
886
  const events = [];
740
887
  events.push({
@@ -745,6 +892,7 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex) {
745
892
  id: msgId,
746
893
  status: "in_progress",
747
894
  role: "assistant",
895
+ ...phase !== void 0 ? { phase } : {},
748
896
  content: []
749
897
  }
750
898
  });
@@ -773,6 +921,16 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex) {
773
921
  content_index: 0,
774
922
  text: content
775
923
  });
924
+ annotations.forEach((annotation, annotationIndex) => {
925
+ events.push({
926
+ type: "response.output_text.annotation.added",
927
+ item_id: msgId,
928
+ output_index: outputIndex,
929
+ content_index: 0,
930
+ annotation_index: annotationIndex,
931
+ annotation
932
+ });
933
+ });
776
934
  events.push({
777
935
  type: "response.content_part.done",
778
936
  item_id: msgId,
@@ -781,7 +939,7 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex) {
781
939
  part: {
782
940
  type: "output_text",
783
941
  text: content,
784
- annotations: []
942
+ annotations
785
943
  }
786
944
  });
787
945
  const msgItem = {
@@ -789,10 +947,11 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex) {
789
947
  id: msgId,
790
948
  status: "completed",
791
949
  role: "assistant",
950
+ ...phase !== void 0 ? { phase } : {},
792
951
  content: [{
793
952
  type: "output_text",
794
953
  text: content,
795
- annotations: []
954
+ annotations
796
955
  }]
797
956
  };
798
957
  events.push({
@@ -847,24 +1006,6 @@ function buildFunctionCallOutputEvents(toolCall, chunkSize, outputIndex) {
847
1006
  };
848
1007
  }
849
1008
  /**
850
- * `text` cut into slices of at most `chunkSize` UTF-16 units, never between
851
- * the two halves of a surrogate pair: a cut that would land inside a pair
852
- * moves back before it, or, when that would leave the slice empty
853
- * (`chunkSize` 1), forward past it. The slices rejoin to `text` exactly.
854
- */
855
- function surrogateSafeChunks(text, chunkSize) {
856
- const chunks = [];
857
- for (let i = 0; i < text.length;) {
858
- let end = Math.min(i + chunkSize, text.length);
859
- const high = text.charCodeAt(end - 1);
860
- const low = text.charCodeAt(end);
861
- if (high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343) end = end - 1 > i ? end - 1 : end + 1;
862
- chunks.push(text.slice(i, end));
863
- i = end;
864
- }
865
- return chunks;
866
- }
867
- /**
868
1009
  * Emit output_item.added → custom_tool_call_input.delta×N →
869
1010
  * custom_tool_call_input.done → output_item.done for one `custom_tool_call` at
870
1011
  * `outputIndex`, returning the completed item. An empty `input`
@@ -907,7 +1048,7 @@ function buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) {
907
1048
  function buildToolCallOutputEvents(call, chunkSize, outputIndex) {
908
1049
  return isCustomEmittable(call) ? buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) : buildFunctionCallOutputEvents(call, chunkSize, outputIndex);
909
1050
  }
910
- function buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
1051
+ function buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, phase, citations, files) {
911
1052
  const output = [];
912
1053
  if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(generateId("rs"), {
913
1054
  summaryText: reasoning,
@@ -922,15 +1063,17 @@ function buildOutputPrefix(content, model, reasoning, webSearches, emitEncrypted
922
1063
  query
923
1064
  }
924
1065
  });
1066
+ output.push(...imageGenerationCallItems(files));
925
1067
  output.push({
926
1068
  type: "message",
927
1069
  id: itemId(),
928
1070
  status: "completed",
929
1071
  role: "assistant",
1072
+ ...phase !== void 0 ? { phase } : {},
930
1073
  content: [{
931
1074
  type: "output_text",
932
1075
  text: content,
933
- annotations: []
1076
+ annotations: buildAnnotations(content, citations)
934
1077
  }]
935
1078
  });
936
1079
  return output;
@@ -943,14 +1086,15 @@ function buildResponseEnvelope(model, output, overrides) {
943
1086
  model: overrides?.model ?? model,
944
1087
  status: responsesStatus(overrides?.finishReason, "completed"),
945
1088
  output,
946
- usage: responsesUsage(overrides)
1089
+ usage: render(overrides)
947
1090
  };
948
1091
  }
949
1092
  function buildTextResponse(content, model, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
950
- return buildResponseEnvelope(model, buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning), overrides);
1093
+ return buildResponseEnvelope(model, buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase, overrides?.citations, overrides?.files), overrides);
951
1094
  }
952
1095
  function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
953
1096
  assertResponsesToolCalls(toolCalls);
1097
+ fixtureCitations("", overrides?.citations);
954
1098
  const output = [];
955
1099
  if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(generateId("rs"), {
956
1100
  summaryText: reasoning,
@@ -965,6 +1109,7 @@ function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrid
965
1109
  query
966
1110
  }
967
1111
  });
1112
+ output.push(...imageGenerationCallItems(overrides?.files));
968
1113
  for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
969
1114
  return buildResponseEnvelope(model, output, overrides);
970
1115
  }
@@ -982,9 +1127,11 @@ function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize,
982
1127
  const orderedOutputItems = [];
983
1128
  if (ordered && ordered.length > 0) {
984
1129
  let outputIndex = nextOutputIndex;
1130
+ const textAnnotations = orderedBlockAnnotations(ordered, overrides?.citations);
1131
+ let textPos = 0;
985
1132
  for (const block of ordered) {
986
1133
  if (block.type === "text") {
987
- const { events: msgEvents, msgItem } = buildMessageOutputEvents(block.text, chunkSize, outputIndex);
1134
+ const { events: msgEvents, msgItem } = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides?.phase, textAnnotations[textPos++]);
988
1135
  events.push(...msgEvents);
989
1136
  orderedOutputItems.push(msgItem);
990
1137
  } else {
@@ -996,7 +1143,7 @@ function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize,
996
1143
  }
997
1144
  } else {
998
1145
  assertResponsesToolCalls(toolCalls);
999
- const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex);
1146
+ const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase, buildAnnotations(content, overrides?.citations));
1000
1147
  events.push(...msgEvents);
1001
1148
  orderedOutputItems.push(msgItem);
1002
1149
  for (let idx = 0; idx < toolCalls.length; idx++) {
@@ -1014,7 +1161,7 @@ function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize,
1014
1161
  model: overrides?.model ?? model,
1015
1162
  status: responsesStatus(overrides?.finishReason, "completed"),
1016
1163
  output: [...prefixOutputItems, ...orderedOutputItems],
1017
- usage: responsesUsage(overrides)
1164
+ usage: render(overrides)
1018
1165
  }
1019
1166
  });
1020
1167
  return events;
@@ -1024,16 +1171,17 @@ function buildToolCallOutputItem(tc) {
1024
1171
  if (isCustomEmittable(tc)) return customToolCallItem(tc, customToolCallItemId(), tc.id || generateToolCallId(), requireFixtureToolInput(tc.input), "completed");
1025
1172
  return functionCallItem(tc, generateId("fc"), tc.id || generateToolCallId(), requireFixtureToolArguments(tc.arguments), "completed");
1026
1173
  }
1027
- function buildMessageOutputItem(content) {
1174
+ function buildMessageOutputItem(content, phase, annotations) {
1028
1175
  return {
1029
1176
  type: "message",
1030
1177
  id: itemId(),
1031
1178
  status: "completed",
1032
1179
  role: "assistant",
1180
+ ...phase !== void 0 ? { phase } : {},
1033
1181
  content: [{
1034
1182
  type: "output_text",
1035
1183
  text: content,
1036
- annotations: []
1184
+ annotations
1037
1185
  }]
1038
1186
  };
1039
1187
  }
@@ -1053,40 +1201,18 @@ function buildContentWithToolCallsResponse(content, toolCalls, model, reasoning,
1053
1201
  query
1054
1202
  }
1055
1203
  });
1056
- for (const block of ordered) if (block.type === "text") output.push(buildMessageOutputItem(block.text));
1204
+ output.push(...imageGenerationCallItems(overrides?.files));
1205
+ const textAnnotations = orderedBlockAnnotations(ordered, overrides?.citations);
1206
+ let textPos = 0;
1207
+ for (const block of ordered) if (block.type === "text") output.push(buildMessageOutputItem(block.text, overrides?.phase, textAnnotations[textPos++]));
1057
1208
  else output.push(buildToolCallOutputItem(block));
1058
1209
  return buildResponseEnvelope(model, output, overrides);
1059
1210
  }
1060
1211
  assertResponsesToolCalls(toolCalls);
1061
- const output = buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1212
+ const output = buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase, overrides?.citations, overrides?.files);
1062
1213
  for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
1063
1214
  return buildResponseEnvelope(model, output, overrides);
1064
1215
  }
1065
- async function writeResponsesSSEStream(res, events, optionsOrLatency) {
1066
- const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
1067
- const latency = opts.latency ?? 0;
1068
- const profile = opts.streamingProfile;
1069
- const { recordedTimings, replaySpeed } = opts;
1070
- const signal = opts.signal;
1071
- const onChunkSent = opts.onChunkSent;
1072
- if (res.writableEnded) return true;
1073
- res.setHeader("Content-Type", "text/event-stream");
1074
- res.setHeader("Cache-Control", "no-cache");
1075
- res.setHeader("Connection", "keep-alive");
1076
- let chunkIndex = 0;
1077
- for (const event of events) {
1078
- const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
1079
- if (chunkDelay > 0) await delay(chunkDelay, signal);
1080
- if (signal?.aborted) return false;
1081
- if (res.writableEnded) return true;
1082
- res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
1083
- onChunkSent?.();
1084
- if (signal?.aborted) return false;
1085
- chunkIndex++;
1086
- }
1087
- if (!res.writableEnded) res.end();
1088
- return true;
1089
- }
1090
1216
  async function handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
1091
1217
  setCorsHeaders(res);
1092
1218
  let responsesReq;
@@ -1104,11 +1230,11 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1104
1230
  fixture: null
1105
1231
  }
1106
1232
  });
1107
- writeErrorResponse(res, 400, JSON.stringify({ error: {
1233
+ writeErrorResponse(res, 400, renderError("openai", "validation", {
1108
1234
  message: `Malformed JSON: ${detail}`,
1109
1235
  type: "invalid_request_error",
1110
1236
  code: "invalid_json"
1111
- } }));
1237
+ }).body);
1112
1238
  return;
1113
1239
  }
1114
1240
  if (!isJsonObject(responsesReq)) {
@@ -1122,10 +1248,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1122
1248
  fixture: null
1123
1249
  }
1124
1250
  });
1125
- writeErrorResponse(res, 400, JSON.stringify({ error: {
1251
+ writeErrorResponse(res, 400, renderError("openai", "validation", {
1126
1252
  message: "Request body must be a JSON object",
1127
1253
  type: "invalid_request_error"
1128
- } }));
1254
+ }).body);
1129
1255
  return;
1130
1256
  }
1131
1257
  let inputError;
@@ -1152,10 +1278,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1152
1278
  fixture: null
1153
1279
  }
1154
1280
  });
1155
- writeErrorResponse(res, 400, JSON.stringify({ error: {
1281
+ writeErrorResponse(res, 400, renderError("openai", "validation", {
1156
1282
  message: validationError,
1157
1283
  type: "invalid_request_error"
1158
- } }));
1284
+ }).body);
1159
1285
  return;
1160
1286
  }
1161
1287
  const extendedTools = defaults.responsesTools === "extended";
@@ -1168,7 +1294,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1168
1294
  const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);
1169
1295
  const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);
1170
1296
  const testId = getTestId(req);
1171
- const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, recordMatchOptions(!!defaults.record, defaults.logger));
1297
+ const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, {
1298
+ ...recordMatchOptions(!!defaults.record, defaults.logger),
1299
+ wire: "openai-responses"
1300
+ });
1172
1301
  if (fixture) {
1173
1302
  defaults.logger.debug(`Responses fixture matched for ${req.method ?? "POST"} ${req.url ?? "/v1/responses"}`);
1174
1303
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
@@ -1180,7 +1309,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1180
1309
  body: completionReq
1181
1310
  }, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
1182
1311
  if (!fixture) {
1183
- if (resolveStrictMode(defaults.strict, req.headers)) {
1312
+ if (resolveRequestDecisions(defaults.strict, req.headers).fixtureMiss) {
1184
1313
  const strictStatus = 503;
1185
1314
  const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);
1186
1315
  defaults.logger.error(strictNoMatchLogLine(req.method ?? "POST", req.url ?? "/v1/responses", skippedBySequenceOrTurn));
@@ -1195,15 +1324,15 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1195
1324
  ...strictOverrideField(defaults.strict, req.headers)
1196
1325
  }
1197
1326
  });
1198
- writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
1327
+ writeErrorResponse(res, strictStatus, renderError("openai", "strict-miss", {
1199
1328
  message: strictMessage,
1200
1329
  type: "invalid_request_error",
1201
1330
  code: "no_fixture_match"
1202
- } }));
1331
+ }).body);
1203
1332
  return;
1204
1333
  }
1205
1334
  if (defaults.record) {
1206
- const outcome = await proxyAndRecord(req, res, completionReq, "openai", req.url ?? "/v1/responses", fixtures, defaults, raw);
1335
+ const { outcome, clientHungUp } = await withHangUpDetection(req, res, () => proxyAndRecord(req, res, completionReq, "openai", req.url ?? "/v1/responses", fixtures, defaults, raw));
1207
1336
  if (outcome === "handled_by_hook") return;
1208
1337
  if (outcome !== "not_configured") {
1209
1338
  journal.add({
@@ -1214,7 +1343,8 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1214
1343
  response: {
1215
1344
  status: res.statusCode ?? 200,
1216
1345
  fixture: null,
1217
- source: "proxy"
1346
+ source: "proxy",
1347
+ ...proxyInterruption(res, clientHungUp)
1218
1348
  }
1219
1349
  });
1220
1350
  return;
@@ -1231,11 +1361,11 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1231
1361
  ...strictOverrideField(defaults.strict, req.headers)
1232
1362
  }
1233
1363
  });
1234
- writeErrorResponse(res, 404, JSON.stringify({ error: {
1364
+ writeErrorResponse(res, 404, renderError("openai", "fixture-miss", {
1235
1365
  message: "No fixture matched",
1236
1366
  type: "invalid_request_error",
1237
1367
  code: "no_fixture_match"
1238
- } }));
1368
+ }).body);
1239
1369
  return;
1240
1370
  }
1241
1371
  const resolvedResponse = await resolveResponse(fixture, completionReq);
@@ -1268,20 +1398,34 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1268
1398
  testId
1269
1399
  });
1270
1400
  if (!misbehavior.summary?.evaluations.some((evaluation) => evaluation.outcome === "error")) defaults.logger.error(misbehavior.message);
1271
- writeErrorResponse(res, misbehavior.status, JSON.stringify({ error: {
1401
+ writeErrorResponse(res, misbehavior.status, renderError("openai", "misbehavior", {
1272
1402
  message: misbehavior.message,
1273
1403
  type: "invalid_request_error",
1274
1404
  code: misbehavior.code
1275
- } }));
1405
+ }).body);
1276
1406
  return;
1277
1407
  }
1278
1408
  const latency = fixture.latency ?? defaults.latency;
1279
1409
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
1280
1410
  const fixtureTimings = fixture.recordedTimings;
1281
1411
  const effectiveReplaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
1412
+ const streamTiming = {
1413
+ latency,
1414
+ streamingProfile: fixture.streamingProfile,
1415
+ recordedTimings: fixtureTimings,
1416
+ replaySpeed: effectiveReplaySpeed
1417
+ };
1418
+ const timedJson = nonStreamingTimingEnabled(fixture, "other");
1419
+ const jsonPayloadLength = (r, reasoning) => responsesPayloadLength(r, reasoning) + responsesOutputFrames(r) * chunkSize;
1420
+ const jsonOpts = (payloadLength, duplicateId = false) => ({
1421
+ surface: "other",
1422
+ payloadLength: timedJson ? payloadLength() : 0,
1423
+ chunkSize,
1424
+ recordedTimings: fixtureTimings,
1425
+ duplicateId,
1426
+ replaySpeed: effectiveReplaySpeed
1427
+ });
1282
1428
  if (misbehavior.kind === "applied") {
1283
- const prepared = prepareResponsesMisbehavior(misbehavior);
1284
- const output = buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
1285
1429
  const entry = journal.add({
1286
1430
  method: req.method ?? "POST",
1287
1431
  path: req.url ?? "/v1/responses",
@@ -1292,41 +1436,50 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1292
1436
  fixture
1293
1437
  }
1294
1438
  });
1295
- recordMisbehaviorOutcome({
1439
+ const prepared = prepareResponsesMisbehavior(misbehavior);
1440
+ const build = () => buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
1441
+ const record = () => recordMisbehaviorOutcome({
1296
1442
  entry,
1297
1443
  summary: prepared.summary,
1298
1444
  defaults,
1299
1445
  testId
1300
1446
  });
1447
+ const output = timedJson && responsesReq.stream !== true ? void 0 : build();
1448
+ if (output) record();
1301
1449
  if (responsesReq.stream !== true) {
1302
- res.writeHead(200, { "Content-Type": "application/json" });
1303
- res.end(JSON.stringify(output.response));
1450
+ const servedReasoning = prepared.reasoning ?? ("reasoning" in prepared.response ? prepared.response.reasoning : void 0);
1451
+ await deliverJson(res, entry, fixture, () => {
1452
+ if (output) return output.response;
1453
+ const built = build();
1454
+ record();
1455
+ return built.response;
1456
+ }, jsonOpts(() => misbehaviorPayloadLength(prepared, servedReasoning, jsonPayloadLength), !!prepared.duplicateId));
1304
1457
  } else {
1305
- const appliedTimings = prepared.duplicateId && fixtureTimings ? {
1306
- ...fixtureTimings,
1307
- interChunkDelaysMs: [...fixtureTimings.interChunkDelaysMs]
1308
- } : fixtureTimings;
1309
- if (prepared.duplicateId && appliedTimings) {
1310
- const lastGap = appliedTimings.interChunkDelaysMs.at(-1) ?? 0;
1311
- while (appliedTimings.interChunkDelaysMs.length < output.events.length - 1) appliedTimings.interChunkDelaysMs.push(lastGap);
1312
- }
1313
- const interruption = createInterruptionSignal(fixture);
1314
- if (!await writeResponsesSSEStream(res, output.events, {
1315
- latency,
1316
- streamingProfile: fixture.streamingProfile,
1317
- recordedTimings: appliedTimings,
1318
- replaySpeed: effectiveReplaySpeed,
1319
- signal: interruption?.signal,
1320
- onChunkSent: interruption?.tick
1321
- })) {
1322
- if (!res.writableEnded) res.destroy();
1323
- entry.response.interrupted = true;
1324
- entry.response.interruptReason = interruption?.reason();
1325
- }
1326
- interruption?.cleanup();
1458
+ const { events } = output ?? build();
1459
+ await deliverStream(res, entry, fixture, typedSseCodec(), events, {
1460
+ ...streamTiming,
1461
+ recordedTimings: extendRecordedTimings(fixtureTimings, events.length, !!prepared.duplicateId)
1462
+ });
1327
1463
  }
1328
1464
  return;
1329
1465
  }
1466
+ if (isRawResponse(response)) {
1467
+ await serveRawResponse({
1468
+ wire: "openai-responses",
1469
+ stream: responsesReq.stream === true,
1470
+ req,
1471
+ res,
1472
+ fixture,
1473
+ response,
1474
+ body: completionReq,
1475
+ journal,
1476
+ defaults,
1477
+ testId,
1478
+ misbehaviorSummary: misbehavior.summary,
1479
+ errorWire: "openai"
1480
+ });
1481
+ return;
1482
+ }
1330
1483
  if (isErrorResponse(response)) {
1331
1484
  const status = response.status ?? 500;
1332
1485
  recordMisbehaviorOutcome({
@@ -1349,7 +1502,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1349
1502
  }
1350
1503
  if (isCombinedFixtureResponse(response)) {
1351
1504
  const overrides = extractOverrides(response);
1352
- const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
1505
+ const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
1353
1506
  const effReasoning = resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
1354
1507
  const journalEntry = journal.add({
1355
1508
  method: req.method ?? "POST",
@@ -1370,32 +1523,16 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1370
1523
  const ordered = resolveServedBlockOutcome(response)?.ordered;
1371
1524
  if (!ordered) assertCustomToolCalls(response);
1372
1525
  const toolCalls = servedToolCalls(response);
1373
- if (responsesReq.stream !== true) {
1374
- const body = buildContentWithToolCallsResponse(response.content ?? "", toolCalls, completionReq.model, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1375
- res.writeHead(200, { "Content-Type": "application/json" });
1376
- res.end(JSON.stringify(body));
1377
- } else {
1526
+ if (responsesReq.stream !== true) await deliverJson(res, journalEntry, fixture, () => buildContentWithToolCallsResponse(response.content ?? "", toolCalls, completionReq.model, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
1527
+ else {
1378
1528
  const events = contentWithToolCallsStreamEvents(response.content ?? "", toolCalls, completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1379
- const interruption = createInterruptionSignal(fixture);
1380
- if (!await writeResponsesSSEStream(res, events, {
1381
- latency,
1382
- streamingProfile: fixture.streamingProfile,
1383
- recordedTimings: fixtureTimings,
1384
- replaySpeed: effectiveReplaySpeed,
1385
- signal: interruption?.signal,
1386
- onChunkSent: interruption?.tick
1387
- })) {
1388
- if (!res.writableEnded) res.destroy();
1389
- journalEntry.response.interrupted = true;
1390
- journalEntry.response.interruptReason = interruption?.reason();
1391
- }
1392
- interruption?.cleanup();
1529
+ await deliverStream(res, journalEntry, fixture, typedSseCodec(), events, streamTiming);
1393
1530
  }
1394
1531
  return;
1395
1532
  }
1396
1533
  if (isTextResponse(response)) {
1397
1534
  const overrides = extractOverrides(response);
1398
- const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
1535
+ const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
1399
1536
  const effReasoning = resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
1400
1537
  const journalEntry = journal.add({
1401
1538
  method: req.method ?? "POST",
@@ -1413,32 +1550,16 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1413
1550
  defaults,
1414
1551
  testId
1415
1552
  });
1416
- if (responsesReq.stream !== true) {
1417
- const body = buildTextResponse(response.content, completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1418
- res.writeHead(200, { "Content-Type": "application/json" });
1419
- res.end(JSON.stringify(body));
1420
- } else {
1553
+ if (responsesReq.stream !== true) await deliverJson(res, journalEntry, fixture, () => buildTextResponse(response.content, completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
1554
+ else {
1421
1555
  const events = buildTextStreamEvents(response.content, completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1422
- const interruption = createInterruptionSignal(fixture);
1423
- if (!await writeResponsesSSEStream(res, events, {
1424
- latency,
1425
- streamingProfile: fixture.streamingProfile,
1426
- recordedTimings: fixtureTimings,
1427
- replaySpeed: effectiveReplaySpeed,
1428
- signal: interruption?.signal,
1429
- onChunkSent: interruption?.tick
1430
- })) {
1431
- if (!res.writableEnded) res.destroy();
1432
- journalEntry.response.interrupted = true;
1433
- journalEntry.response.interruptReason = interruption?.reason();
1434
- }
1435
- interruption?.cleanup();
1556
+ await deliverStream(res, journalEntry, fixture, typedSseCodec(), events, streamTiming);
1436
1557
  }
1437
1558
  return;
1438
1559
  }
1439
1560
  if (isToolCallResponse(response)) {
1440
1561
  const overrides = extractOverrides(response);
1441
- const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
1562
+ const effectiveStrict = resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression;
1442
1563
  const effReasoning = resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger);
1443
1564
  const journalEntry = journal.add({
1444
1565
  method: req.method ?? "POST",
@@ -1457,26 +1578,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1457
1578
  testId
1458
1579
  });
1459
1580
  assertCustomToolCalls(response);
1460
- if (responsesReq.stream !== true) {
1461
- const body = buildToolCallResponse(servedToolCalls(response), completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1462
- res.writeHead(200, { "Content-Type": "application/json" });
1463
- res.end(JSON.stringify(body));
1464
- } else {
1581
+ if (responsesReq.stream !== true) await deliverJson(res, journalEntry, fixture, () => buildToolCallResponse(servedToolCalls(response), completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
1582
+ else {
1465
1583
  const events = toolCallStreamEvents(servedToolCalls(response), completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1466
- const interruption = createInterruptionSignal(fixture);
1467
- if (!await writeResponsesSSEStream(res, events, {
1468
- latency,
1469
- streamingProfile: fixture.streamingProfile,
1470
- recordedTimings: fixtureTimings,
1471
- replaySpeed: effectiveReplaySpeed,
1472
- signal: interruption?.signal,
1473
- onChunkSent: interruption?.tick
1474
- })) {
1475
- if (!res.writableEnded) res.destroy();
1476
- journalEntry.response.interrupted = true;
1477
- journalEntry.response.interruptReason = interruption?.reason();
1478
- }
1479
- interruption?.cleanup();
1584
+ await deliverStream(res, journalEntry, fixture, typedSseCodec(), events, streamTiming);
1480
1585
  }
1481
1586
  return;
1482
1587
  }
@@ -1495,10 +1600,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1495
1600
  defaults,
1496
1601
  testId
1497
1602
  });
1498
- writeErrorResponse(res, 500, JSON.stringify({ error: {
1603
+ writeErrorResponse(res, 500, renderError("openai", "unknown-response", {
1499
1604
  message: "Fixture response did not match any known type",
1500
1605
  type: "server_error"
1501
- } }));
1606
+ }).body);
1502
1607
  }
1503
1608
  /** Prepare call IDs once, retaining the engine's effective blocks and exact served-call metadata. */
1504
1609
  function prepareResponsesMisbehavior(plan) {
@@ -1511,7 +1616,11 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
1511
1616
  const overrides = extractOverrides(response);
1512
1617
  const reasoning = plan.reasoning ?? ("reasoning" in response ? response.reasoning : void 0);
1513
1618
  const webSearches = "webSearches" in response ? response.webSearches : void 0;
1514
- const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(request.model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, false, plan.reasoning !== void 0);
1619
+ const outputWithheld = plan.refusal !== void 0 || plan.summary.fault === "empty-response" || plan.stop === "content_filter" || plan.reasoning !== void 0;
1620
+ const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(request.model, chunkSize, reasoning, webSearches, outputWithheld ? {
1621
+ ...overrides,
1622
+ files: void 0
1623
+ } : overrides, emitEncryptedReasoning, false, plan.reasoning !== void 0);
1515
1624
  const output = [...prefixOutputItems];
1516
1625
  let outputIndex = nextOutputIndex;
1517
1626
  const incomplete = plan.stop === "length" || plan.stop === "content_filter";
@@ -1526,6 +1635,7 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
1526
1635
  type: "message",
1527
1636
  role: "assistant",
1528
1637
  status: "completed",
1638
+ ...overrides.phase !== void 0 ? { phase: overrides.phase } : {},
1529
1639
  content: [part]
1530
1640
  };
1531
1641
  const position = {
@@ -1568,16 +1678,18 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
1568
1678
  item
1569
1679
  });
1570
1680
  output.push(item);
1571
- } else if (plan.summary.fault !== "empty-response" && plan.stop !== "content_filter" && plan.reasoning === void 0) {
1681
+ } else if (!outputWithheld) {
1572
1682
  const combined = isCombinedFixtureResponse(response);
1573
1683
  const blocks = (combined ? resolveServedBlockOutcome(response) : void 0)?.ordered ?? [..."content" in response && typeof response.content === "string" ? [{
1574
1684
  type: "text",
1575
1685
  text: response.content
1576
1686
  }] : [], ...combined || isToolCallResponse(response) ? servedToolCalls(response).map(toolCallFixtureBlock) : []];
1577
1687
  let callIndex = 0;
1688
+ const textAnnotations = orderedBlockAnnotations(blocks, overrides.citations);
1689
+ let textPos = 0;
1578
1690
  for (const block of blocks) {
1579
1691
  if (block.type === "text") {
1580
- const message = buildMessageOutputEvents(block.text, chunkSize, outputIndex);
1692
+ const message = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides.phase, textAnnotations[textPos++]);
1581
1693
  events.push(...message.events);
1582
1694
  output.push(message.msgItem);
1583
1695
  } else {
@@ -1606,11 +1718,7 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
1606
1718
  status: incomplete ? "incomplete" : responsesStatus(overrides?.finishReason, "completed"),
1607
1719
  ...incomplete ? { incomplete_details: { reason: plan.stop === "content_filter" ? "content_filter" : "max_output_tokens" } } : {},
1608
1720
  output,
1609
- usage: {
1610
- input_tokens: usage.prompt_tokens,
1611
- output_tokens: usage.completion_tokens,
1612
- total_tokens: usage.total_tokens
1613
- }
1721
+ usage: render({ usage })
1614
1722
  };
1615
1723
  events.push({
1616
1724
  type: incomplete ? "response.incomplete" : "response.completed",