@copilotkit/aimock 1.45.1 → 1.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (492) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -1
  4. package/README.md +11 -2
  5. package/dist/agui-mock.cjs +25 -25
  6. package/dist/agui-mock.cjs.map +1 -1
  7. package/dist/agui-mock.d.cts.map +1 -1
  8. package/dist/agui-mock.d.ts.map +1 -1
  9. package/dist/agui-mock.js +25 -25
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-types.d.cts.map +1 -1
  12. package/dist/aimock-cli.cjs +15 -1
  13. package/dist/aimock-cli.cjs.map +1 -1
  14. package/dist/aimock-cli.d.cts.map +1 -1
  15. package/dist/aimock-cli.d.ts.map +1 -1
  16. package/dist/aimock-cli.js +16 -2
  17. package/dist/aimock-cli.js.map +1 -1
  18. package/dist/api-key-auth.cjs +10 -9
  19. package/dist/api-key-auth.cjs.map +1 -1
  20. package/dist/api-key-auth.d.cts.map +1 -1
  21. package/dist/api-key-auth.d.ts.map +1 -1
  22. package/dist/api-key-auth.js +11 -10
  23. package/dist/api-key-auth.js.map +1 -1
  24. package/dist/aws-event-stream.cjs +20 -23
  25. package/dist/aws-event-stream.cjs.map +1 -1
  26. package/dist/aws-event-stream.d.cts +0 -1
  27. package/dist/aws-event-stream.d.ts +0 -1
  28. package/dist/aws-event-stream.js +20 -24
  29. package/dist/aws-event-stream.js.map +1 -1
  30. package/dist/batches.cjs +2 -4
  31. package/dist/batches.cjs.map +1 -1
  32. package/dist/batches.d.cts.map +1 -1
  33. package/dist/batches.d.ts.map +1 -1
  34. package/dist/batches.js +2 -4
  35. package/dist/batches.js.map +1 -1
  36. package/dist/bedrock-converse.cjs +139 -161
  37. package/dist/bedrock-converse.cjs.map +1 -1
  38. package/dist/bedrock-converse.d.cts.map +1 -1
  39. package/dist/bedrock-converse.d.ts.map +1 -1
  40. package/dist/bedrock-converse.js +137 -159
  41. package/dist/bedrock-converse.js.map +1 -1
  42. package/dist/bedrock.cjs +175 -216
  43. package/dist/bedrock.cjs.map +1 -1
  44. package/dist/bedrock.d.cts.map +1 -1
  45. package/dist/bedrock.d.ts.map +1 -1
  46. package/dist/bedrock.js +172 -213
  47. package/dist/bedrock.js.map +1 -1
  48. package/dist/byteplus-video.cjs +11 -10
  49. package/dist/byteplus-video.cjs.map +1 -1
  50. package/dist/byteplus-video.d.cts.map +1 -1
  51. package/dist/byteplus-video.d.ts.map +1 -1
  52. package/dist/byteplus-video.js +5 -4
  53. package/dist/byteplus-video.js.map +1 -1
  54. package/dist/chaos.cjs +5 -4
  55. package/dist/chaos.cjs.map +1 -1
  56. package/dist/chaos.d.cts.map +1 -1
  57. package/dist/chaos.d.ts.map +1 -1
  58. package/dist/chaos.js +5 -4
  59. package/dist/chaos.js.map +1 -1
  60. package/dist/cli.cjs +15 -0
  61. package/dist/cli.cjs.map +1 -1
  62. package/dist/cli.js +15 -0
  63. package/dist/cli.js.map +1 -1
  64. package/dist/cohere.cjs +107 -180
  65. package/dist/cohere.cjs.map +1 -1
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts.map +1 -1
  68. package/dist/cohere.js +106 -179
  69. package/dist/cohere.js.map +1 -1
  70. package/dist/config-loader.cjs +29 -1
  71. package/dist/config-loader.cjs.map +1 -1
  72. package/dist/config-loader.d.cts +7 -1
  73. package/dist/config-loader.d.cts.map +1 -1
  74. package/dist/config-loader.d.ts +7 -1
  75. package/dist/config-loader.d.ts.map +1 -1
  76. package/dist/config-loader.js +29 -2
  77. package/dist/config-loader.js.map +1 -1
  78. package/dist/count-tokens.cjs +146 -0
  79. package/dist/count-tokens.cjs.map +1 -0
  80. package/dist/count-tokens.js +146 -0
  81. package/dist/count-tokens.js.map +1 -0
  82. package/dist/elevenlabs-audio.cjs +36 -34
  83. package/dist/elevenlabs-audio.cjs.map +1 -1
  84. package/dist/elevenlabs-audio.d.cts.map +1 -1
  85. package/dist/elevenlabs-audio.d.ts.map +1 -1
  86. package/dist/elevenlabs-audio.js +33 -31
  87. package/dist/elevenlabs-audio.js.map +1 -1
  88. package/dist/elevenlabs-voice.cjs +38 -37
  89. package/dist/elevenlabs-voice.cjs.map +1 -1
  90. package/dist/elevenlabs-voice.d.cts.map +1 -1
  91. package/dist/elevenlabs-voice.d.ts.map +1 -1
  92. package/dist/elevenlabs-voice.js +33 -32
  93. package/dist/elevenlabs-voice.js.map +1 -1
  94. package/dist/embedding-request.cjs +16 -0
  95. package/dist/embedding-request.cjs.map +1 -0
  96. package/dist/embedding-request.js +14 -0
  97. package/dist/embedding-request.js.map +1 -0
  98. package/dist/embeddings.cjs +30 -20
  99. package/dist/embeddings.cjs.map +1 -1
  100. package/dist/embeddings.d.cts.map +1 -1
  101. package/dist/embeddings.d.ts.map +1 -1
  102. package/dist/embeddings.js +30 -20
  103. package/dist/embeddings.js.map +1 -1
  104. package/dist/fal-audio.cjs +45 -43
  105. package/dist/fal-audio.cjs.map +1 -1
  106. package/dist/fal-audio.d.cts.map +1 -1
  107. package/dist/fal-audio.d.ts.map +1 -1
  108. package/dist/fal-audio.js +42 -40
  109. package/dist/fal-audio.js.map +1 -1
  110. package/dist/fal.cjs +17 -15
  111. package/dist/fal.cjs.map +1 -1
  112. package/dist/fal.js +16 -14
  113. package/dist/fal.js.map +1 -1
  114. package/dist/files.cjs +2 -4
  115. package/dist/files.cjs.map +1 -1
  116. package/dist/files.d.cts.map +1 -1
  117. package/dist/files.d.ts.map +1 -1
  118. package/dist/files.js +2 -4
  119. package/dist/files.js.map +1 -1
  120. package/dist/fine-tuning.cjs +2 -4
  121. package/dist/fine-tuning.cjs.map +1 -1
  122. package/dist/fine-tuning.d.cts.map +1 -1
  123. package/dist/fine-tuning.d.ts.map +1 -1
  124. package/dist/fine-tuning.js +2 -4
  125. package/dist/fine-tuning.js.map +1 -1
  126. package/dist/fixture-loader.cjs +140 -44
  127. package/dist/fixture-loader.cjs.map +1 -1
  128. package/dist/fixture-loader.d.cts.map +1 -1
  129. package/dist/fixture-loader.d.ts.map +1 -1
  130. package/dist/fixture-loader.js +141 -46
  131. package/dist/fixture-loader.js.map +1 -1
  132. package/dist/gemini-embeddings.cjs +26 -24
  133. package/dist/gemini-embeddings.cjs.map +1 -1
  134. package/dist/gemini-embeddings.js +26 -24
  135. package/dist/gemini-embeddings.js.map +1 -1
  136. package/dist/gemini-interactions.cjs +113 -156
  137. package/dist/gemini-interactions.cjs.map +1 -1
  138. package/dist/gemini-interactions.d.cts +0 -1
  139. package/dist/gemini-interactions.d.cts.map +1 -1
  140. package/dist/gemini-interactions.d.ts +0 -1
  141. package/dist/gemini-interactions.d.ts.map +1 -1
  142. package/dist/gemini-interactions.js +113 -156
  143. package/dist/gemini-interactions.js.map +1 -1
  144. package/dist/gemini.cjs +295 -218
  145. package/dist/gemini.cjs.map +1 -1
  146. package/dist/gemini.d.cts.map +1 -1
  147. package/dist/gemini.d.ts.map +1 -1
  148. package/dist/gemini.js +295 -219
  149. package/dist/gemini.js.map +1 -1
  150. package/dist/grok-video.cjs +14 -12
  151. package/dist/grok-video.cjs.map +1 -1
  152. package/dist/grok-video.d.cts.map +1 -1
  153. package/dist/grok-video.d.ts.map +1 -1
  154. package/dist/grok-video.js +8 -6
  155. package/dist/grok-video.js.map +1 -1
  156. package/dist/helpers.cjs +411 -75
  157. package/dist/helpers.cjs.map +1 -1
  158. package/dist/helpers.d.cts +51 -1
  159. package/dist/helpers.d.cts.map +1 -1
  160. package/dist/helpers.d.ts +51 -1
  161. package/dist/helpers.d.ts.map +1 -1
  162. package/dist/helpers.js +402 -74
  163. package/dist/helpers.js.map +1 -1
  164. package/dist/images.cjs +30 -28
  165. package/dist/images.cjs.map +1 -1
  166. package/dist/images.d.cts.map +1 -1
  167. package/dist/images.d.ts.map +1 -1
  168. package/dist/images.js +27 -25
  169. package/dist/images.js.map +1 -1
  170. package/dist/index.cjs +4 -2
  171. package/dist/index.d.cts +4 -4
  172. package/dist/index.d.ts +4 -4
  173. package/dist/index.js +4 -4
  174. package/dist/journal.cjs +3 -1
  175. package/dist/journal.cjs.map +1 -1
  176. package/dist/journal.d.cts.map +1 -1
  177. package/dist/journal.d.ts.map +1 -1
  178. package/dist/journal.js +2 -1
  179. package/dist/journal.js.map +1 -1
  180. package/dist/llmock.cjs +3 -0
  181. package/dist/llmock.cjs.map +1 -1
  182. package/dist/llmock.d.cts.map +1 -1
  183. package/dist/llmock.d.ts.map +1 -1
  184. package/dist/llmock.js +3 -0
  185. package/dist/llmock.js.map +1 -1
  186. package/dist/mcp-fakes.d.cts.map +1 -1
  187. package/dist/mcp-handler.cjs +7 -3
  188. package/dist/mcp-handler.cjs.map +1 -1
  189. package/dist/mcp-handler.js +7 -3
  190. package/dist/mcp-handler.js.map +1 -1
  191. package/dist/mcp-recorder.cjs +2 -1
  192. package/dist/mcp-recorder.cjs.map +1 -1
  193. package/dist/mcp-recorder.js +3 -2
  194. package/dist/mcp-recorder.js.map +1 -1
  195. package/dist/messages.cjs +235 -202
  196. package/dist/messages.cjs.map +1 -1
  197. package/dist/messages.d.cts.map +1 -1
  198. package/dist/messages.d.ts.map +1 -1
  199. package/dist/messages.js +234 -202
  200. package/dist/messages.js.map +1 -1
  201. package/dist/metrics.cjs +5 -2
  202. package/dist/metrics.cjs.map +1 -1
  203. package/dist/metrics.d.cts.map +1 -1
  204. package/dist/metrics.d.ts.map +1 -1
  205. package/dist/metrics.js +6 -3
  206. package/dist/metrics.js.map +1 -1
  207. package/dist/misbehavior.cjs +62 -3
  208. package/dist/misbehavior.cjs.map +1 -1
  209. package/dist/misbehavior.d.cts.map +1 -1
  210. package/dist/misbehavior.d.ts.map +1 -1
  211. package/dist/misbehavior.js +63 -4
  212. package/dist/misbehavior.js.map +1 -1
  213. package/dist/model-utils.cjs +1 -0
  214. package/dist/model-utils.cjs.map +1 -1
  215. package/dist/model-utils.js +1 -1
  216. package/dist/model-utils.js.map +1 -1
  217. package/dist/moderation.cjs +15 -12
  218. package/dist/moderation.cjs.map +1 -1
  219. package/dist/moderation.js +14 -11
  220. package/dist/moderation.js.map +1 -1
  221. package/dist/ndjson-writer.cjs +21 -23
  222. package/dist/ndjson-writer.cjs.map +1 -1
  223. package/dist/ndjson-writer.d.cts +0 -1
  224. package/dist/ndjson-writer.d.cts.map +1 -1
  225. package/dist/ndjson-writer.d.ts +0 -1
  226. package/dist/ndjson-writer.d.ts.map +1 -1
  227. package/dist/ndjson-writer.js +21 -24
  228. package/dist/ndjson-writer.js.map +1 -1
  229. package/dist/ollama.cjs +276 -210
  230. package/dist/ollama.cjs.map +1 -1
  231. package/dist/ollama.d.cts.map +1 -1
  232. package/dist/ollama.d.ts.map +1 -1
  233. package/dist/ollama.js +273 -207
  234. package/dist/ollama.js.map +1 -1
  235. package/dist/openapi.cjs +323 -3
  236. package/dist/openapi.cjs.map +1 -1
  237. package/dist/openapi.d.cts.map +1 -1
  238. package/dist/openapi.d.ts.map +1 -1
  239. package/dist/openapi.js +324 -4
  240. package/dist/openapi.js.map +1 -1
  241. package/dist/openrouter-chat.cjs +11 -155
  242. package/dist/openrouter-chat.cjs.map +1 -1
  243. package/dist/openrouter-chat.js +11 -155
  244. package/dist/openrouter-chat.js.map +1 -1
  245. package/dist/openrouter-video.cjs +51 -74
  246. package/dist/openrouter-video.cjs.map +1 -1
  247. package/dist/openrouter-video.d.cts.map +1 -1
  248. package/dist/openrouter-video.d.ts.map +1 -1
  249. package/dist/openrouter-video.js +37 -60
  250. package/dist/openrouter-video.js.map +1 -1
  251. package/dist/raw-validation.cjs +177 -0
  252. package/dist/raw-validation.cjs.map +1 -0
  253. package/dist/raw-validation.js +176 -0
  254. package/dist/raw-validation.js.map +1 -0
  255. package/dist/recorder.cjs +176 -95
  256. package/dist/recorder.cjs.map +1 -1
  257. package/dist/recorder.d.cts.map +1 -1
  258. package/dist/recorder.d.ts.map +1 -1
  259. package/dist/recorder.js +176 -95
  260. package/dist/recorder.js.map +1 -1
  261. package/dist/rerank.cjs +13 -11
  262. package/dist/rerank.cjs.map +1 -1
  263. package/dist/rerank.js +12 -10
  264. package/dist/rerank.js.map +1 -1
  265. package/dist/response/catalog/anthropic-page.cjs +39 -0
  266. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  267. package/dist/response/catalog/anthropic-page.js +38 -0
  268. package/dist/response/catalog/anthropic-page.js.map +1 -0
  269. package/dist/response/catalog/collect.cjs +61 -0
  270. package/dist/response/catalog/collect.cjs.map +1 -0
  271. package/dist/response/catalog/collect.js +59 -0
  272. package/dist/response/catalog/collect.js.map +1 -0
  273. package/dist/response/catalog/gemini-page.cjs +25 -0
  274. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  275. package/dist/response/catalog/gemini-page.js +24 -0
  276. package/dist/response/catalog/gemini-page.js.map +1 -0
  277. package/dist/response/catalog/render.cjs +182 -0
  278. package/dist/response/catalog/render.cjs.map +1 -0
  279. package/dist/response/catalog/render.js +171 -0
  280. package/dist/response/catalog/render.js.map +1 -0
  281. package/dist/response/deliver.cjs +144 -0
  282. package/dist/response/deliver.cjs.map +1 -0
  283. package/dist/response/deliver.js +143 -0
  284. package/dist/response/deliver.js.map +1 -0
  285. package/dist/response/errors.cjs +225 -0
  286. package/dist/response/errors.cjs.map +1 -0
  287. package/dist/response/errors.js +224 -0
  288. package/dist/response/errors.js.map +1 -0
  289. package/dist/response/finish.cjs +39 -0
  290. package/dist/response/finish.cjs.map +1 -0
  291. package/dist/response/finish.js +35 -0
  292. package/dist/response/finish.js.map +1 -0
  293. package/dist/response/frame-loop.cjs +102 -0
  294. package/dist/response/frame-loop.cjs.map +1 -0
  295. package/dist/response/frame-loop.js +100 -0
  296. package/dist/response/frame-loop.js.map +1 -0
  297. package/dist/response/lifecycle.cjs +223 -0
  298. package/dist/response/lifecycle.cjs.map +1 -0
  299. package/dist/response/lifecycle.js +211 -0
  300. package/dist/response/lifecycle.js.map +1 -0
  301. package/dist/response/policy.cjs +126 -0
  302. package/dist/response/policy.cjs.map +1 -0
  303. package/dist/response/policy.js +120 -0
  304. package/dist/response/policy.js.map +1 -0
  305. package/dist/response/profiles.cjs +69 -0
  306. package/dist/response/profiles.cjs.map +1 -0
  307. package/dist/response/profiles.js +65 -0
  308. package/dist/response/profiles.js.map +1 -0
  309. package/dist/response/proxy.cjs +71 -0
  310. package/dist/response/proxy.cjs.map +1 -0
  311. package/dist/response/proxy.js +70 -0
  312. package/dist/response/proxy.js.map +1 -0
  313. package/dist/response/raw.cjs +228 -0
  314. package/dist/response/raw.cjs.map +1 -0
  315. package/dist/response/raw.js +227 -0
  316. package/dist/response/raw.js.map +1 -0
  317. package/dist/response/timing.cjs +157 -0
  318. package/dist/response/timing.cjs.map +1 -0
  319. package/dist/response/timing.js +150 -0
  320. package/dist/response/timing.js.map +1 -0
  321. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  322. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  323. package/dist/response/usage/codecs/anthropic.js +50 -0
  324. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  325. package/dist/response/usage/codecs/cohere.cjs +36 -0
  326. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  327. package/dist/response/usage/codecs/cohere.js +35 -0
  328. package/dist/response/usage/codecs/cohere.js.map +1 -0
  329. package/dist/response/usage/codecs/converse.cjs +16 -0
  330. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  331. package/dist/response/usage/codecs/converse.js +15 -0
  332. package/dist/response/usage/codecs/converse.js.map +1 -0
  333. package/dist/response/usage/codecs/gemini.cjs +31 -0
  334. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  335. package/dist/response/usage/codecs/gemini.js +31 -0
  336. package/dist/response/usage/codecs/gemini.js.map +1 -0
  337. package/dist/response/usage/codecs/interactions.cjs +22 -0
  338. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  339. package/dist/response/usage/codecs/interactions.js +21 -0
  340. package/dist/response/usage/codecs/interactions.js.map +1 -0
  341. package/dist/response/usage/codecs/ollama.cjs +17 -0
  342. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  343. package/dist/response/usage/codecs/ollama.js +17 -0
  344. package/dist/response/usage/codecs/ollama.js.map +1 -0
  345. package/dist/response/usage/codecs/openai.cjs +52 -0
  346. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  347. package/dist/response/usage/codecs/openai.js +50 -0
  348. package/dist/response/usage/codecs/openai.js.map +1 -0
  349. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  350. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  351. package/dist/response/usage/codecs/openrouter.js +67 -0
  352. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  353. package/dist/response/usage/codecs/realtime.cjs +19 -0
  354. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  355. package/dist/response/usage/codecs/realtime.js +18 -0
  356. package/dist/response/usage/codecs/realtime.js.map +1 -0
  357. package/dist/response/usage/codecs/responses.cjs +21 -0
  358. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  359. package/dist/response/usage/codecs/responses.js +20 -0
  360. package/dist/response/usage/codecs/responses.js.map +1 -0
  361. package/dist/response/usage/estimate.cjs +23 -0
  362. package/dist/response/usage/estimate.cjs.map +1 -0
  363. package/dist/response/usage/estimate.js +21 -0
  364. package/dist/response/usage/estimate.js.map +1 -0
  365. package/dist/response/usage/fields.cjs +72 -0
  366. package/dist/response/usage/fields.cjs.map +1 -0
  367. package/dist/response/usage/fields.js +70 -0
  368. package/dist/response/usage/fields.js.map +1 -0
  369. package/dist/response/usage/recording.cjs +52 -0
  370. package/dist/response/usage/recording.cjs.map +1 -0
  371. package/dist/response/usage/recording.js +52 -0
  372. package/dist/response/usage/recording.js.map +1 -0
  373. package/dist/responses.cjs +304 -196
  374. package/dist/responses.cjs.map +1 -1
  375. package/dist/responses.d.cts.map +1 -1
  376. package/dist/responses.d.ts.map +1 -1
  377. package/dist/responses.js +303 -195
  378. package/dist/responses.js.map +1 -1
  379. package/dist/route-registry.cjs +45 -0
  380. package/dist/route-registry.cjs.map +1 -1
  381. package/dist/route-registry.d.cts.map +1 -1
  382. package/dist/route-registry.d.ts.map +1 -1
  383. package/dist/route-registry.js +41 -1
  384. package/dist/route-registry.js.map +1 -1
  385. package/dist/router.cjs +24 -4
  386. package/dist/router.cjs.map +1 -1
  387. package/dist/router.d.cts +7 -1
  388. package/dist/router.d.cts.map +1 -1
  389. package/dist/router.d.ts +7 -1
  390. package/dist/router.d.ts.map +1 -1
  391. package/dist/router.js +24 -5
  392. package/dist/router.js.map +1 -1
  393. package/dist/search.cjs +13 -11
  394. package/dist/search.cjs.map +1 -1
  395. package/dist/search.js +12 -10
  396. package/dist/search.js.map +1 -1
  397. package/dist/server.cjs +293 -248
  398. package/dist/server.cjs.map +1 -1
  399. package/dist/server.d.cts.map +1 -1
  400. package/dist/server.d.ts.map +1 -1
  401. package/dist/server.js +294 -249
  402. package/dist/server.js.map +1 -1
  403. package/dist/speech.cjs +20 -18
  404. package/dist/speech.cjs.map +1 -1
  405. package/dist/speech.d.cts.map +1 -1
  406. package/dist/speech.d.ts.map +1 -1
  407. package/dist/speech.js +19 -17
  408. package/dist/speech.js.map +1 -1
  409. package/dist/sse-writer.cjs +56 -30
  410. package/dist/sse-writer.cjs.map +1 -1
  411. package/dist/sse-writer.d.cts +0 -1
  412. package/dist/sse-writer.d.cts.map +1 -1
  413. package/dist/sse-writer.d.ts +0 -1
  414. package/dist/sse-writer.d.ts.map +1 -1
  415. package/dist/sse-writer.js +55 -31
  416. package/dist/sse-writer.js.map +1 -1
  417. package/dist/stream-collapse.cjs +118 -11
  418. package/dist/stream-collapse.cjs.map +1 -1
  419. package/dist/stream-collapse.d.cts +10 -9
  420. package/dist/stream-collapse.d.cts.map +1 -1
  421. package/dist/stream-collapse.d.ts +10 -9
  422. package/dist/stream-collapse.d.ts.map +1 -1
  423. package/dist/stream-collapse.js +118 -11
  424. package/dist/stream-collapse.js.map +1 -1
  425. package/dist/transcription.cjs +31 -43
  426. package/dist/transcription.cjs.map +1 -1
  427. package/dist/transcription.d.cts.map +1 -1
  428. package/dist/transcription.d.ts.map +1 -1
  429. package/dist/transcription.js +31 -43
  430. package/dist/transcription.js.map +1 -1
  431. package/dist/types.cjs.map +1 -1
  432. package/dist/types.d.cts +231 -9
  433. package/dist/types.d.cts.map +1 -1
  434. package/dist/types.d.ts +231 -9
  435. package/dist/types.d.ts.map +1 -1
  436. package/dist/types.js.map +1 -1
  437. package/dist/vector-mock.cjs +3 -8
  438. package/dist/vector-mock.cjs.map +1 -1
  439. package/dist/vector-mock.d.cts.map +1 -1
  440. package/dist/vector-mock.d.ts.map +1 -1
  441. package/dist/vector-mock.js +3 -8
  442. package/dist/vector-mock.js.map +1 -1
  443. package/dist/vector-stores.cjs +2 -4
  444. package/dist/vector-stores.cjs.map +1 -1
  445. package/dist/vector-stores.d.cts.map +1 -1
  446. package/dist/vector-stores.d.ts.map +1 -1
  447. package/dist/vector-stores.js +2 -4
  448. package/dist/vector-stores.js.map +1 -1
  449. package/dist/veo-video.cjs +16 -14
  450. package/dist/veo-video.cjs.map +1 -1
  451. package/dist/veo-video.js +10 -8
  452. package/dist/veo-video.js.map +1 -1
  453. package/dist/video.cjs +21 -19
  454. package/dist/video.cjs.map +1 -1
  455. package/dist/video.d.cts.map +1 -1
  456. package/dist/video.d.ts.map +1 -1
  457. package/dist/video.js +20 -18
  458. package/dist/video.js.map +1 -1
  459. package/dist/ws-framing.cjs +12 -6
  460. package/dist/ws-framing.cjs.map +1 -1
  461. package/dist/ws-framing.d.cts +2 -0
  462. package/dist/ws-framing.d.cts.map +1 -1
  463. package/dist/ws-framing.d.ts +2 -0
  464. package/dist/ws-framing.d.ts.map +1 -1
  465. package/dist/ws-framing.js +12 -6
  466. package/dist/ws-framing.js.map +1 -1
  467. package/dist/ws-gemini-live.cjs +131 -96
  468. package/dist/ws-gemini-live.cjs.map +1 -1
  469. package/dist/ws-gemini-live.d.cts +0 -1
  470. package/dist/ws-gemini-live.d.cts.map +1 -1
  471. package/dist/ws-gemini-live.d.ts +0 -1
  472. package/dist/ws-gemini-live.d.ts.map +1 -1
  473. package/dist/ws-gemini-live.js +130 -95
  474. package/dist/ws-gemini-live.js.map +1 -1
  475. package/dist/ws-live.cjs +6 -5
  476. package/dist/ws-live.cjs.map +1 -1
  477. package/dist/ws-live.js +5 -4
  478. package/dist/ws-live.js.map +1 -1
  479. package/dist/ws-realtime.cjs +108 -124
  480. package/dist/ws-realtime.cjs.map +1 -1
  481. package/dist/ws-realtime.d.cts.map +1 -1
  482. package/dist/ws-realtime.d.ts.map +1 -1
  483. package/dist/ws-realtime.js +105 -121
  484. package/dist/ws-realtime.js.map +1 -1
  485. package/dist/ws-responses.cjs +80 -59
  486. package/dist/ws-responses.cjs.map +1 -1
  487. package/dist/ws-responses.d.cts.map +1 -1
  488. package/dist/ws-responses.d.ts.map +1 -1
  489. package/dist/ws-responses.js +79 -58
  490. package/dist/ws-responses.js.map +1 -1
  491. package/package.json +4 -1
  492. package/skills/write-fixtures/SKILL.md +57 -16
@@ -1,10 +1,14 @@
1
+ const require_errors = require('./response/errors.cjs');
1
2
  const require_constants = require('./constants.cjs');
3
+ const require_policy = require('./response/policy.cjs');
2
4
  const require_helpers = require('./helpers.cjs');
3
- const require_sse_writer = require('./sse-writer.cjs');
5
+ const require_frame_loop = require('./response/frame-loop.cjs');
4
6
  const require_misbehavior = require('./misbehavior.cjs');
5
7
  const require_router = require('./router.cjs');
6
8
  require('./journal.cjs');
7
- const require_interruption = require('./interruption.cjs');
9
+ const require_lifecycle = require('./response/lifecycle.cjs');
10
+ const require_timing = require('./response/timing.cjs');
11
+ const require_raw = require('./response/raw.cjs');
8
12
  const require_responses = require('./responses.cjs');
9
13
 
10
14
  //#region src/ws-responses.ts
@@ -14,15 +18,16 @@ function isResponseCreateMessage(msg) {
14
18
  function buildErrorEvent(message, type = "invalid_request_error", code) {
15
19
  return {
16
20
  type: "error",
17
- error: {
21
+ error: require_errors.buildOpenAIError({
18
22
  message,
19
23
  type,
20
24
  code
21
- }
25
+ })
22
26
  };
23
27
  }
24
28
  function handleWebSocketResponses(ws, fixtures, journal, defaults) {
25
29
  const { logger } = defaults;
30
+ require_lifecycle.watchWsClose(ws);
26
31
  let pending = Promise.resolve();
27
32
  ws.on("message", (raw) => {
28
33
  pending = pending.then(async () => {
@@ -78,10 +83,10 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
78
83
  const contextHeader = defaults.upgradeHeaders?.["x-aimock-context"];
79
84
  completionReq._context = typeof contextHeader === "string" ? contextHeader : Array.isArray(contextHeader) && contextHeader.length > 0 ? contextHeader[0] : void 0;
80
85
  const testId = defaults.testId ?? require_constants.DEFAULT_TEST_ID;
81
- const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
86
+ const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, { wire: "openai-responses" });
82
87
  if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
83
88
  if (!fixture) {
84
- if (require_helpers.resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {
89
+ if (require_policy.resolveRequestDecisions(defaults.strict, defaults.upgradeHeaders).fixtureMiss) {
85
90
  const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
86
91
  defaults.logger.error(require_helpers.strictNoMatchLogLine("WS", "/v1/responses", skippedBySequenceOrTurn));
87
92
  journal.add({
@@ -92,7 +97,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
92
97
  response: {
93
98
  status: 503,
94
99
  fixture: null,
95
- ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
100
+ ...require_policy.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
96
101
  }
97
102
  });
98
103
  ws.close(1008, strictMessage);
@@ -106,7 +111,7 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
106
111
  response: {
107
112
  status: 404,
108
113
  fixture: null,
109
- ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
114
+ ...require_policy.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
110
115
  }
111
116
  });
112
117
  ws.send(JSON.stringify(buildErrorEvent("No fixture matched", "invalid_request_error", "no_fixture_match")));
@@ -156,8 +161,6 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
156
161
  const latency = fixture.latency ?? defaults.latency;
157
162
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
158
163
  if (misbehavior.kind === "applied") {
159
- const prepared = require_responses.prepareResponsesMisbehavior(misbehavior);
160
- const output = require_responses.buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
161
164
  const entry = journal.add({
162
165
  method: "WS",
163
166
  path: "/v1/responses",
@@ -168,28 +171,41 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
168
171
  fixture
169
172
  }
170
173
  });
174
+ const prepared = require_responses.prepareResponsesMisbehavior(misbehavior);
175
+ const output = require_helpers.journalFixtureToolCallError(entry, () => require_responses.buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning));
171
176
  require_misbehavior.recordMisbehaviorOutcome({
172
177
  entry,
173
178
  summary: prepared.summary,
174
179
  defaults: misbehaviorDefaults,
175
180
  testId
176
181
  });
177
- const timings = fixture.recordedTimings;
178
- const gaps = timings?.interChunkDelaysMs;
179
- const appliedTimings = prepared.duplicateId && timings && gaps ? {
180
- ...timings,
181
- interChunkDelaysMs: Array.from({ length: Math.max(gaps.length, output.events.length - 1) }, (_, index) => gaps[index] ?? gaps.at(-1) ?? 0)
182
- } : timings;
183
- const interruption = require_interruption.createInterruptionSignal(fixture);
184
- if (!await sendEvents(ws, output.events, latency, interruption?.signal, interruption?.tick, appliedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
185
- ws.destroy();
186
- entry.response.interrupted = true;
187
- entry.response.interruptReason = interruption?.reason();
188
- }
189
- interruption?.cleanup();
182
+ const appliedTimings = require_timing.extendRecordedTimings(fixture.recordedTimings, output.events.length, !!prepared.duplicateId);
183
+ await deliverTurn(ws, entry, fixture, output.events, {
184
+ latency,
185
+ recordedTimings: appliedTimings,
186
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
187
+ });
188
+ return;
189
+ }
190
+ const effectiveStrict = require_policy.resolveRequestDecisions(defaults.strict, defaults.upgradeHeaders).reasoningSuppression;
191
+ if (require_helpers.isRawResponse(response)) {
192
+ const message = "raw fixtures are not yet supported on the WebSocket transport";
193
+ const entry = journal.add({
194
+ method: "WS",
195
+ path: "/v1/responses",
196
+ headers: require_helpers.flattenHeaders(defaults.upgradeHeaders ?? {}),
197
+ body: completionReq,
198
+ response: {
199
+ status: 500,
200
+ fixture
201
+ }
202
+ });
203
+ entry.response.error = message;
204
+ recordOutcome(entry);
205
+ defaults.logger.error(`WebSocket responses error: ${require_raw.RAW_MODE_UNAVAILABLE}: ${message}`);
206
+ ws.send(JSON.stringify(buildErrorEvent(message, "server_error", require_raw.RAW_MODE_UNAVAILABLE)));
190
207
  return;
191
208
  }
192
- const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, defaults.upgradeHeaders);
193
209
  if (require_helpers.isErrorResponse(response)) {
194
210
  const status = response.status ?? 500;
195
211
  recordOutcome(journal.add({
@@ -227,13 +243,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
227
243
  if (require_helpers.isFixtureToolCallError(error)) journalEntry.response.error = error.message;
228
244
  throw error;
229
245
  }
230
- const interruption = require_interruption.createInterruptionSignal(fixture);
231
- if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
232
- ws.destroy();
233
- journalEntry.response.interrupted = true;
234
- journalEntry.response.interruptReason = interruption?.reason();
235
- }
236
- interruption?.cleanup();
246
+ await deliverTurn(ws, journalEntry, fixture, events, {
247
+ latency,
248
+ recordedTimings: fixture.recordedTimings,
249
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
250
+ });
237
251
  return;
238
252
  }
239
253
  if (require_helpers.isTextResponse(response)) {
@@ -248,14 +262,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
248
262
  }
249
263
  });
250
264
  recordOutcome(journalEntry);
251
- const events = require_responses.buildTextStreamEvents(response.content, completionReq.model, chunkSize, require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, require_helpers.extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning);
252
- const interruption = require_interruption.createInterruptionSignal(fixture);
253
- if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
254
- ws.destroy();
255
- journalEntry.response.interrupted = true;
256
- journalEntry.response.interruptReason = interruption?.reason();
257
- }
258
- interruption?.cleanup();
265
+ await deliverTurn(ws, journalEntry, fixture, require_helpers.journalFixtureToolCallError(journalEntry, () => require_responses.buildTextStreamEvents(response.content, completionReq.model, chunkSize, require_helpers.resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, require_helpers.extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning)), {
266
+ latency,
267
+ recordedTimings: fixture.recordedTimings,
268
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
269
+ });
259
270
  return;
260
271
  }
261
272
  if (require_helpers.isToolCallResponse(response)) {
@@ -279,13 +290,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
279
290
  if (require_helpers.isFixtureToolCallError(error)) journalEntry.response.error = error.message;
280
291
  throw error;
281
292
  }
282
- const interruption = require_interruption.createInterruptionSignal(fixture);
283
- if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
284
- ws.destroy();
285
- journalEntry.response.interrupted = true;
286
- journalEntry.response.interruptReason = interruption?.reason();
287
- }
288
- interruption?.cleanup();
293
+ await deliverTurn(ws, journalEntry, fixture, events, {
294
+ latency,
295
+ recordedTimings: fixture.recordedTimings,
296
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
297
+ });
289
298
  return;
290
299
  }
291
300
  recordOutcome(journal.add({
@@ -300,20 +309,32 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
300
309
  }));
301
310
  ws.send(JSON.stringify(buildErrorEvent("Fixture response did not match any known type", "server_error")));
302
311
  }
303
- async function sendEvents(ws, events, latency, signal, onChunkSent, recordedTimings, replaySpeed) {
304
- let eventIndex = 0;
305
- for (const event of events) {
306
- if (ws.isClosed) return false;
307
- const chunkDelay = require_sse_writer.calculateDelay(eventIndex, void 0, latency, recordedTimings, replaySpeed);
308
- if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
309
- if (signal?.aborted) return false;
310
- if (ws.isClosed) return false;
311
- ws.send(JSON.stringify(event));
312
- eventIndex++;
313
- onChunkSent?.();
314
- if (signal?.aborted) return false;
312
+ /** WS Responses frames: one JSON text message per event, no head, no trailer. */
313
+ const wsEventCodec = {
314
+ headers: {},
315
+ encode: (event) => JSON.stringify(event),
316
+ checkGoneBeforeDelay: true
317
+ };
318
+ /**
319
+ * Send one turn's events under a WS lifecycle. The lifecycle owns the fixture
320
+ * interruption and also stops the turn when the client closes, so a pending
321
+ * delay ends at once. A stopped turn marks the entry by the shared WS rule
322
+ * ({@link markWsTurnStopped}) and destroys the socket.
323
+ * `streamingProfile` is not applied on this transport, as before.
324
+ */
325
+ async function deliverTurn(ws, entry, fixture, events, timing) {
326
+ const lifecycle = require_lifecycle.wsLifecycle(ws, fixture);
327
+ try {
328
+ if (!await require_frame_loop.runFrameLoop(require_frame_loop.wsSink(ws), events, wsEventCodec, timing, {
329
+ lifecycle,
330
+ onChunkSent: lifecycle.tick
331
+ })) {
332
+ require_lifecycle.markWsTurnStopped(entry, ws, lifecycle);
333
+ ws.destroy();
334
+ }
335
+ } finally {
336
+ lifecycle.dispose();
315
337
  }
316
- return true;
317
338
  }
318
339
 
319
340
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.cjs","names":["isFixtureToolCallError","fixtureToolCallErrorCode","requestWantsEncryptedReasoning","requestIncludesEncryptedReasoning","responsesToCompletionRequest","DEFAULT_TEST_ID","matchFixtureDiagnostic","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","flattenHeaders","strictOverrideField","resolveResponse","withoutLegacyNamespaces","planMisbehavior","recordMisbehaviorOutcome","prepareResponsesMisbehavior","buildResponsesMisbehavior","createInterruptionSignal","isErrorResponse","isCombinedFixtureResponse","resolveServedBlockOutcome","contentWithToolCallsStreamEvents","servedToolCalls","resolveReasoningForModel","extractOverrides","isTextResponse","buildTextStreamEvents","isToolCallResponse","toolCallStreamEvents","calculateDelay","delay"],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { Fixture, HandlerDefaults } from \"./types.js\";\nimport { planMisbehavior, recordMisbehaviorOutcome } from \"./misbehavior.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n requestIncludesEncryptedReasoning,\n requestWantsEncryptedReasoning,\n buildTextStreamEvents,\n toolCallStreamEvents,\n contentWithToolCallsStreamEvents,\n prepareResponsesMisbehavior,\n buildResponsesMisbehavior,\n type ResponsesSSEEvent,\n type ResponsesInputItem,\n type ResponsesToolDef,\n} from \"./responses.js\";\nimport {\n fixtureToolCallErrorCode,\n isFixtureToolCallError,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n extractOverrides,\n resolveResponse,\n resolveStrictMode,\n resolveReasoningForModel,\n strictOverrideField,\n flattenHeaders,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n assertCustomToolCalls,\n resolveServedBlockOutcome,\n servedToolCalls,\n withoutLegacyNamespaces,\n} from \"./helpers.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport { delay, calculateDelay } from \"./sse-writer.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\ntype ResponsesWebSocketDefaults = Omit<HandlerDefaults, \"replaySpeed\"> & {\n replaySpeed?: number;\n model: string;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n /** @internal Called before each message is processed (the server assigns fixture positions). */\n beforeProcessMessage?: () => void;\n};\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: { message, type, code },\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: ResponsesWebSocketDefaults,\n): void {\n const { logger } = defaults;\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(async () => {\n try {\n defaults.beforeProcessMessage?.();\n await processMessage(raw, ws, fixtures, journal, defaults);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n const code = isFixtureToolCallError(err) ? fixtureToolCallErrorCode(err) : undefined;\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\", code)));\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }\n });\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: ResponsesWebSocketDefaults,\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n ws.send(\n JSON.stringify(\n buildErrorEvent(`Malformed JSON: ${detail}`, \"invalid_request_error\", \"invalid_json\"),\n ),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n // Same shapes as HTTP: namespace / custom tools, additional_tools items\n // and custom tool call items reach responsesToCompletionRequest unchanged.\n input: (parsed.input ?? []) as ResponsesInputItem[],\n instructions: parsed.instructions,\n tools: parsed.tools as ResponsesToolDef[] | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n include: (parsed as { include?: string[] }).include,\n store: (parsed as { store?: boolean }).store,\n };\n\n // Gate encrypted-reasoning emission identically to the HTTP transport so the\n // agent-framework#7233 stateless-replay path works over WebSocket too.\n const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);\n // Synthesizing a reasoning item the fixture never declared takes the NARROWER\n // explicit-`include` gate, transport-identically to HTTP — a `store: false`\n // request with a summary-less fixture still gets no reasoning item.\n const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);\n\n const extendedTools = defaults.responsesTools === \"extended\";\n const completionReq = responsesToCompletionRequest(responsesReq, {\n extended: extendedTools,\n logger: defaults.logger,\n });\n completionReq._endpointType = \"chat\";\n const contextHeader = defaults.upgradeHeaders?.[\"x-aimock-context\"];\n completionReq._context =\n typeof contextHeader === \"string\"\n ? contextHeader\n : Array.isArray(contextHeader) && contextHeader.length > 0\n ? contextHeader[0]\n : undefined;\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(strictNoMatchLogLine(\"WS\", \"/v1/responses\", skippedBySequenceOrTurn));\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, strictMessage);\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n // Without responsesTools \"extended\", a namespace on a toolCalls entry or a\n // toolCall block is not emitted, as in earlier releases.\n const resolvedResponse = await resolveResponse(fixture, completionReq);\n const response = extendedTools ? resolvedResponse : withoutLegacyNamespaces(resolvedResponse);\n const misbehaviorDefaults: HandlerDefaults = {\n ...defaults,\n replaySpeed: defaults.replaySpeed ?? 1,\n };\n const misbehavior = planMisbehavior({\n wire: \"openai-responses\",\n fixture,\n response,\n request: completionReq,\n // Responses WebSocket always emits streaming events, even with stream:false.\n stream: true,\n emitsToolCallIds: true,\n defaults: misbehaviorDefaults,\n // The server has already resolved the upgrade's header/query test ID.\n rawHeaders: { ...defaults.upgradeHeaders, \"x-test-id\": testId },\n url: \"/v1/responses\",\n });\n const recordOutcome = (entry: ReturnType<Journal[\"add\"]>) =>\n recordMisbehaviorOutcome({\n entry,\n summary: misbehavior.summary,\n defaults: misbehaviorDefaults,\n testId,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: misbehavior.status, fixture },\n });\n recordOutcome(entry);\n if (!misbehavior.summary?.evaluations.length) {\n defaults.logger.error(\n `[misbehavior] ${misbehavior.code}: ${misbehavior.message} (testId=${testId})`,\n );\n }\n ws.send(\n JSON.stringify(\n buildErrorEvent(misbehavior.message, \"invalid_request_error\", misbehavior.code),\n ),\n );\n return;\n }\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n if (misbehavior.kind === \"applied\") {\n const prepared = prepareResponsesMisbehavior(misbehavior);\n const output = buildResponsesMisbehavior(\n prepared,\n completionReq,\n chunkSize,\n emitEncryptedReasoning,\n );\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry,\n summary: prepared.summary,\n defaults: misbehaviorDefaults,\n testId,\n });\n const timings = fixture.recordedTimings;\n const gaps = timings?.interChunkDelaysMs;\n // K4 can add events; extend only its replay using the last recorded gap.\n const appliedTimings =\n prepared.duplicateId && timings && gaps\n ? {\n ...timings,\n interChunkDelaysMs: Array.from(\n { length: Math.max(gaps.length, output.events.length - 1) },\n (_, index) => gaps[index] ?? gaps.at(-1) ?? 0,\n ),\n }\n : timings;\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n output.events,\n latency,\n interruption?.signal,\n interruption?.tick,\n appliedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n entry.response.interrupted = true;\n entry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // The WS path has no per-request `req.headers`; strict is resolved from the\n // connection's upgrade headers (see the `!fixture` branch above). Used below\n // to gate the synthesized reasoning channel on the requested model's capability.\n const effectiveStrict = resolveStrictMode(defaults.strict, defaults.upgradeHeaders);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n recordOutcome(entry);\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isCombinedFixtureResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n\n let events: ResponsesSSEEvent[];\n try {\n const ordered = resolveServedBlockOutcome(response)?.ordered;\n if (!ordered) assertCustomToolCalls(response);\n events = contentWithToolCallsStreamEvents(\n response.content ?? \"\",\n servedToolCalls(response),\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n ordered,\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n } catch (error) {\n journalEntry.response.status = 500;\n if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;\n throw error;\n }\n\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n\n const events = buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n let events: ResponsesSSEEvent[];\n try {\n assertCustomToolCalls(response);\n events = toolCallStreamEvents(\n servedToolCalls(response),\n completionReq.model,\n chunkSize,\n // Gate the synthesized reasoning channel on the requested model's\n // capability, matching the WS text / content+tool branches and the HTTP\n // tool-only path so reasoning emission is transport-independent.\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n } catch (error) {\n journalEntry.response.status = 500;\n if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;\n throw error;\n }\n const interruption = createInterruptionSignal(fixture);\n const completed = await sendEvents(\n ws,\n events,\n latency,\n interruption?.signal,\n interruption?.tick,\n fixture.recordedTimings,\n fixture.replaySpeed ?? defaults.replaySpeed,\n );\n if (!completed) {\n ws.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Unknown response type\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordOutcome(entry);\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\nasync function sendEvents(\n ws: WebSocketConnection,\n events: ResponsesSSEEvent[],\n latency: number,\n signal?: AbortSignal,\n onChunkSent?: () => void,\n recordedTimings?: import(\"./types.js\").RecordedTimings,\n replaySpeed?: number,\n): Promise<boolean> {\n let eventIndex = 0;\n for (const event of events) {\n if (ws.isClosed) return false;\n const chunkDelay = calculateDelay(eventIndex, undefined, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (ws.isClosed) return false;\n ws.send(JSON.stringify(event));\n eventIndex++;\n onChunkSent?.();\n if (signal?.aborted) return false;\n }\n return true;\n}\n"],"mappings":";;;;;;;;;;AAuEA,SAAS,wBAAwB,KAA4C;AAC3E,QACE,OAAO,QAAQ,YACf,QAAQ,QACP,IAA8B,SAAS;;AAI5C,SAAS,gBACP,SACA,OAAO,yBACP,MACmB;AACnB,QAAO;EACL,MAAM;EACN,OAAO;GAAE;GAAS;GAAM;GAAM;EAC/B;;AAGH,SAAgB,yBACd,IACA,UACA,SACA,UACM;CACN,MAAM,EAAE,WAAW;CAEnB,IAAI,UAAU,QAAQ,SAAS;AAC/B,IAAG,GAAG,YAAY,QAAgB;AAChC,YAAU,QAAQ,KAAK,YAAY;AACjC,OAAI;AACF,aAAS,wBAAwB;AACjC,UAAM,eAAe,KAAK,IAAI,UAAU,SAAS,SAAS;YACnD,KAAc;IACrB,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU;AACjD,WAAO,MAAM,8BAA8B,MAAM;AACjD,QAAI;KACF,MAAM,OAAOA,uCAAuB,IAAI,GAAGC,yCAAyB,IAAI,GAAG;AAC3E,QAAG,KAAK,KAAK,UAAU,gBAAgB,KAAK,gBAAgB,KAAK,CAAC,CAAC;aAC5D,SAAS;AAChB,cAAS,OAAO,MACd,mCAAmC,mBAAmB,QAAQ,QAAQ,UAAU,YACjF;;;IAGL;GACF;;AAGJ,eAAe,eACb,KACA,IACA,UACA,SACA,UACe;CACf,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,IAAI;UACjB,UAAU;EACjB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,KAAG,KACD,KAAK,UACH,gBAAgB,mBAAmB,UAAU,yBAAyB,eAAe,CACtF,CACF;AACD;;AAGF,KAAI,CAAC,wBAAwB,OAAO,EAAE;AACpC,KAAG,KACD,KAAK,UACH,gBACE,6CACA,yBACA,uBACD,CACF,CACF;AACD;;CAGF,MAAM,eAAe;EACnB,OAAO,OAAO,SAAS,SAAS;EAGhC,OAAQ,OAAO,SAAS,EAAE;EAC1B,cAAc,OAAO;EACrB,OAAO,OAAO;EACd,aAAa,OAAO;EACpB,QAAQ,OAAO;EACf,aAAa,OAAO;EACpB,mBAAmB,OAAO;EAC1B,SAAU,OAAkC;EAC5C,OAAQ,OAA+B;EACxC;CAID,MAAM,yBAAyBC,iDAA+B,aAAa;CAI3E,MAAM,iCAAiCC,oDAAkC,aAAa;CAEtF,MAAM,gBAAgB,SAAS,mBAAmB;CAClD,MAAM,gBAAgBC,+CAA6B,cAAc;EAC/D,UAAU;EACV,QAAQ,SAAS;EAClB,CAAC;AACF,eAAc,gBAAgB;CAC9B,MAAM,gBAAgB,SAAS,iBAAiB;AAChD,eAAc,WACZ,OAAO,kBAAkB,WACrB,gBACA,MAAM,QAAQ,cAAc,IAAI,cAAc,SAAS,IACrD,cAAc,KACd;CACR,MAAM,SAAS,SAAS,UAAUC;CAClC,MAAM,EAAE,SAAS,4BAA4BC,sCAC3C,UACA,eACA,QAAQ,6BAA6B,OAAO,EAC5C,SAAS,iBACV;AAED,KAAI,QACF,SAAQ,2BAA2B,SAAS,UAAU,OAAO;AAG/D,KAAI,CAAC,SAAS;AACZ,MAAIC,kCAAkB,SAAS,QAAQ,SAAS,eAAe,EAAE;GAC/D,MAAM,gBAAgBC,qCAAqB,wBAAwB;AACnE,YAAS,OAAO,MAAMC,qCAAqB,MAAM,iBAAiB,wBAAwB,CAAC;AAC3F,WAAQ,IAAI;IACV,QAAQ;IACR,MAAM;IACN,SAASC,+BAAe,SAAS,kBAAkB,EAAE,CAAC;IACtD,MAAM;IACN,UAAU;KACR,QAAQ;KACR,SAAS;KACT,GAAGC,oCAAoB,SAAS,QAAQ,SAAS,eAAe;KACjE;IACF,CAAC;AACF,MAAG,MAAM,MAAM,cAAc;AAC7B;;AAEF,UAAQ,IAAI;GACV,QAAQ;GACR,MAAM;GACN,SAASD,+BAAe,SAAS,kBAAkB,EAAE,CAAC;GACtD,MAAM;GACN,UAAU;IACR,QAAQ;IACR,SAAS;IACT,GAAGC,oCAAoB,SAAS,QAAQ,SAAS,eAAe;IACjE;GACF,CAAC;AACF,KAAG,KACD,KAAK,UACH,gBAAgB,sBAAsB,yBAAyB,mBAAmB,CACnF,CACF;AACD;;CAKF,MAAM,mBAAmB,MAAMC,gCAAgB,SAAS,cAAc;CACtE,MAAM,WAAW,gBAAgB,mBAAmBC,wCAAwB,iBAAiB;CAC7F,MAAM,sBAAuC;EAC3C,GAAG;EACH,aAAa,SAAS,eAAe;EACtC;CACD,MAAM,cAAcC,oCAAgB;EAClC,MAAM;EACN;EACA;EACA,SAAS;EAET,QAAQ;EACR,kBAAkB;EAClB,UAAU;EAEV,YAAY;GAAE,GAAG,SAAS;GAAgB,aAAa;GAAQ;EAC/D,KAAK;EACN,CAAC;CACF,MAAM,iBAAiB,UACrBC,6CAAyB;EACvB;EACA,SAAS,YAAY;EACrB,UAAU;EACV;EACD,CAAC;AACJ,KAAI,YAAY,SAAS,SAAS;AAQhC,gBAPc,QAAQ,IAAI;GACxB,QAAQ;GACR,MAAM;GACN,SAASL,+BAAe,SAAS,kBAAkB,EAAE,CAAC;GACtD,MAAM;GACN,UAAU;IAAE,QAAQ,YAAY;IAAQ;IAAS;GAClD,CAAC,CACkB;AACpB,MAAI,CAAC,YAAY,SAAS,YAAY,OACpC,UAAS,OAAO,MACd,iBAAiB,YAAY,KAAK,IAAI,YAAY,QAAQ,WAAW,OAAO,GAC7E;AAEH,KAAG,KACD,KAAK,UACH,gBAAgB,YAAY,SAAS,yBAAyB,YAAY,KAAK,CAChF,CACF;AACD;;CAEF,MAAM,UAAU,QAAQ,WAAW,SAAS;CAC5C,MAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,aAAa,SAAS,UAAU;AAEtE,KAAI,YAAY,SAAS,WAAW;EAClC,MAAM,WAAWM,8CAA4B,YAAY;EACzD,MAAM,SAASC,4CACb,UACA,eACA,WACA,uBACD;EACD,MAAM,QAAQ,QAAQ,IAAI;GACxB,QAAQ;GACR,MAAM;GACN,SAASP,+BAAe,SAAS,kBAAkB,EAAE,CAAC;GACtD,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,+CAAyB;GACvB;GACA,SAAS,SAAS;GAClB,UAAU;GACV;GACD,CAAC;EACF,MAAM,UAAU,QAAQ;EACxB,MAAM,OAAO,SAAS;EAEtB,MAAM,iBACJ,SAAS,eAAe,WAAW,OAC/B;GACE,GAAG;GACH,oBAAoB,MAAM,KACxB,EAAE,QAAQ,KAAK,IAAI,KAAK,QAAQ,OAAO,OAAO,SAAS,EAAE,EAAE,GAC1D,GAAG,UAAU,KAAK,UAAU,KAAK,GAAG,GAAG,IAAI,EAC7C;GACF,GACD;EACN,MAAM,eAAeQ,8CAAyB,QAAQ;AAUtD,MAAI,CATc,MAAM,WACtB,IACA,OAAO,QACP,SACA,cAAc,QACd,cAAc,MACd,gBACA,QAAQ,eAAe,SAAS,YACjC,EACe;AACd,MAAG,SAAS;AACZ,SAAM,SAAS,cAAc;AAC7B,SAAM,SAAS,kBAAkB,cAAc,QAAQ;;AAEzD,gBAAc,SAAS;AACvB;;CAMF,MAAM,kBAAkBX,kCAAkB,SAAS,QAAQ,SAAS,eAAe;AAGnF,KAAIY,gCAAgB,SAAS,EAAE;EAC7B,MAAM,SAAS,SAAS,UAAU;AAQlC,gBAPc,QAAQ,IAAI;GACxB,QAAQ;GACR,MAAM;GACN,SAAST,+BAAe,SAAS,kBAAkB,EAAE,CAAC;GACtD,MAAM;GACN,UAAU;IAAE;IAAQ;IAAS;GAC9B,CAAC,CACkB;AACpB,KAAG,KACD,KAAK,UACH,gBAAgB,SAAS,MAAM,SAAS,SAAS,MAAM,MAAM,SAAS,MAAM,KAAK,CAClF,CACF;AACD;;AAIF,KAAIU,0CAA0B,SAAS,EAAE;EACvC,MAAM,eAAe,QAAQ,IAAI;GAC/B,QAAQ;GACR,MAAM;GACN,SAASV,+BAAe,SAAS,kBAAkB,EAAE,CAAC;GACtD,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,gBAAc,aAAa;EAE3B,IAAI;AACJ,MAAI;GACF,MAAM,UAAUW,0CAA0B,SAAS,EAAE;AACrD,OAAI,CAAC,QAAS,uCAAsB,SAAS;AAC7C,YAASC,mDACP,SAAS,WAAW,IACpBC,gCAAgB,SAAS,EACzB,cAAc,OACd,WACAC,yCACE,SAAS,WACT,cAAc,OACd,iBACA,SAAS,OACV,EACD,SAAS,aACTC,iCAAiB,SAAS,EAC1B,SACA,wBACA,+BACD;WACM,OAAO;AACd,gBAAa,SAAS,SAAS;AAC/B,OAAIzB,uCAAuB,MAAM,CAAE,cAAa,SAAS,QAAQ,MAAM;AACvE,SAAM;;EAGR,MAAM,eAAekB,8CAAyB,QAAQ;AAUtD,MAAI,CATc,MAAM,WACtB,IACA,QACA,SACA,cAAc,QACd,cAAc,MACd,QAAQ,iBACR,QAAQ,eAAe,SAAS,YACjC,EACe;AACd,MAAG,SAAS;AACZ,gBAAa,SAAS,cAAc;AACpC,gBAAa,SAAS,kBAAkB,cAAc,QAAQ;;AAEhE,gBAAc,SAAS;AACvB;;AAIF,KAAIQ,+BAAe,SAAS,EAAE;EAC5B,MAAM,eAAe,QAAQ,IAAI;GAC/B,QAAQ;GACR,MAAM;GACN,SAAShB,+BAAe,SAAS,kBAAkB,EAAE,CAAC;GACtD,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,gBAAc,aAAa;EAE3B,MAAM,SAASiB,wCACb,SAAS,SACT,cAAc,OACd,WACAH,yCACE,SAAS,WACT,cAAc,OACd,iBACA,SAAS,OACV,EACD,SAAS,aACTC,iCAAiB,SAAS,EAC1B,wBACA,+BACD;EACD,MAAM,eAAeP,8CAAyB,QAAQ;AAUtD,MAAI,CATc,MAAM,WACtB,IACA,QACA,SACA,cAAc,QACd,cAAc,MACd,QAAQ,iBACR,QAAQ,eAAe,SAAS,YACjC,EACe;AACd,MAAG,SAAS;AACZ,gBAAa,SAAS,cAAc;AACpC,gBAAa,SAAS,kBAAkB,cAAc,QAAQ;;AAEhE,gBAAc,SAAS;AACvB;;AAIF,KAAIU,mCAAmB,SAAS,EAAE;EAChC,MAAM,eAAe,QAAQ,IAAI;GAC/B,QAAQ;GACR,MAAM;GACN,SAASlB,+BAAe,SAAS,kBAAkB,EAAE,CAAC;GACtD,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,gBAAc,aAAa;EAC3B,IAAI;AACJ,MAAI;AACF,yCAAsB,SAAS;AAC/B,YAASmB,uCACPN,gCAAgB,SAAS,EACzB,cAAc,OACd,WAIAC,yCACE,SAAS,WACT,cAAc,OACd,iBACA,SAAS,OACV,EACD,SAAS,aACTC,iCAAiB,SAAS,EAC1B,wBACA,+BACD;WACM,OAAO;AACd,gBAAa,SAAS,SAAS;AAC/B,OAAIzB,uCAAuB,MAAM,CAAE,cAAa,SAAS,QAAQ,MAAM;AACvE,SAAM;;EAER,MAAM,eAAekB,8CAAyB,QAAQ;AAUtD,MAAI,CATc,MAAM,WACtB,IACA,QACA,SACA,cAAc,QACd,cAAc,MACd,QAAQ,iBACR,QAAQ,eAAe,SAAS,YACjC,EACe;AACd,MAAG,SAAS;AACZ,gBAAa,SAAS,cAAc;AACpC,gBAAa,SAAS,kBAAkB,cAAc,QAAQ;;AAEhE,gBAAc,SAAS;AACvB;;AAWF,eAPc,QAAQ,IAAI;EACxB,QAAQ;EACR,MAAM;EACN,SAASR,+BAAe,SAAS,kBAAkB,EAAE,CAAC;EACtD,MAAM;EACN,UAAU;GAAE,QAAQ;GAAK;GAAS;EACnC,CAAC,CACkB;AACpB,IAAG,KACD,KAAK,UACH,gBAAgB,iDAAiD,eAAe,CACjF,CACF;;AAGH,eAAe,WACb,IACA,QACA,SACA,QACA,aACA,iBACA,aACkB;CAClB,IAAI,aAAa;AACjB,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,GAAG,SAAU,QAAO;EACxB,MAAM,aAAaoB,kCAAe,YAAY,QAAW,SAAS,iBAAiB,YAAY;AAC/F,MAAI,aAAa,EAAG,OAAMC,yBAAM,YAAY,OAAO;AACnD,MAAI,QAAQ,QAAS,QAAO;AAC5B,MAAI,GAAG,SAAU,QAAO;AACxB,KAAG,KAAK,KAAK,UAAU,MAAM,CAAC;AAC9B;AACA,iBAAe;AACf,MAAI,QAAQ,QAAS,QAAO;;AAE9B,QAAO"}
1
+ {"version":3,"file":"ws-responses.cjs","names":["buildOpenAIError","isFixtureToolCallError","fixtureToolCallErrorCode","requestWantsEncryptedReasoning","requestIncludesEncryptedReasoning","responsesToCompletionRequest","DEFAULT_TEST_ID","matchFixtureDiagnostic","resolveRequestDecisions","strictNoMatchMessage","strictNoMatchLogLine","flattenHeaders","strictOverrideField","resolveResponse","withoutLegacyNamespaces","planMisbehavior","recordMisbehaviorOutcome","prepareResponsesMisbehavior","journalFixtureToolCallError","buildResponsesMisbehavior","extendRecordedTimings","isRawResponse","RAW_MODE_UNAVAILABLE","isErrorResponse","isCombinedFixtureResponse","resolveServedBlockOutcome","contentWithToolCallsStreamEvents","servedToolCalls","resolveReasoningForModel","extractOverrides","isTextResponse","buildTextStreamEvents","isToolCallResponse","toolCallStreamEvents","wsLifecycle","runFrameLoop","wsSink"],"sources":["../src/ws-responses.ts"],"sourcesContent":["/**\n * WebSocket handler for OpenAI Responses API.\n *\n * Accepts `{ type: \"response.create\", model: \"...\", input: [...] }` messages over\n * WebSocket and sends back the same Responses API SSE events as the HTTP\n * handler, but as individual WebSocket text frames.\n */\n\nimport type { Fixture, HandlerDefaults, JournalEntry, RecordedTimings } from \"./types.js\";\nimport { planMisbehavior, recordMisbehaviorOutcome } from \"./misbehavior.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport {\n responsesToCompletionRequest,\n requestIncludesEncryptedReasoning,\n requestWantsEncryptedReasoning,\n buildTextStreamEvents,\n toolCallStreamEvents,\n contentWithToolCallsStreamEvents,\n prepareResponsesMisbehavior,\n buildResponsesMisbehavior,\n type ResponsesSSEEvent,\n type ResponsesInputItem,\n type ResponsesToolDef,\n} from \"./responses.js\";\nimport {\n fixtureToolCallErrorCode,\n isFixtureToolCallError,\n journalFixtureToolCallError,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n isRawResponse,\n extractOverrides,\n resolveResponse,\n resolveReasoningForModel,\n flattenHeaders,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n assertCustomToolCalls,\n resolveServedBlockOutcome,\n servedToolCalls,\n withoutLegacyNamespaces,\n} from \"./helpers.js\";\nimport { DEFAULT_TEST_ID, type Journal } from \"./journal.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\nimport { resolveRequestDecisions, strictOverrideField } from \"./response/policy.js\";\nimport { buildOpenAIError } from \"./response/errors.js\";\nimport { markWsTurnStopped, watchWsClose, wsLifecycle } from \"./response/lifecycle.js\";\nimport { runFrameLoop, wsSink, type FrameCodec } from \"./response/frame-loop.js\";\nimport { extendRecordedTimings } from \"./response/timing.js\";\nimport { RAW_MODE_UNAVAILABLE } from \"./response/raw.js\";\n\ntype ResponsesWebSocketDefaults = Omit<HandlerDefaults, \"replaySpeed\"> & {\n replaySpeed?: number;\n model: string;\n testId?: string;\n upgradeHeaders?: import(\"node:http\").IncomingHttpHeaders;\n /** @internal Called before each message is processed (the server assigns fixture positions). */\n beforeProcessMessage?: () => void;\n};\n\ninterface ResponseCreateMessage {\n type: \"response.create\";\n model?: string;\n input?: unknown[];\n instructions?: string;\n tools?: unknown[];\n tool_choice?: string | object;\n stream?: boolean;\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n}\n\nfunction isResponseCreateMessage(msg: unknown): msg is ResponseCreateMessage {\n return (\n typeof msg === \"object\" &&\n msg !== null &&\n (msg as ResponseCreateMessage).type === \"response.create\"\n );\n}\n\nfunction buildErrorEvent(\n message: string,\n type = \"invalid_request_error\",\n code?: string,\n): ResponsesSSEEvent {\n return {\n type: \"error\",\n error: buildOpenAIError({ message, type, code }),\n };\n}\n\nexport function handleWebSocketResponses(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: ResponsesWebSocketDefaults,\n): void {\n const { logger } = defaults;\n watchWsClose(ws);\n // Serialize message processing to prevent event interleaving\n let pending = Promise.resolve();\n ws.on(\"message\", (raw: string) => {\n pending = pending.then(async () => {\n try {\n defaults.beforeProcessMessage?.();\n await processMessage(raw, ws, fixtures, journal, defaults);\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : \"Internal error\";\n logger.error(`WebSocket responses error: ${msg}`);\n try {\n const code = isFixtureToolCallError(err) ? fixtureToolCallErrorCode(err) : undefined;\n ws.send(JSON.stringify(buildErrorEvent(msg, \"server_error\", code)));\n } catch (sendErr) {\n defaults.logger.debug(\n `Failed to send error to client: ${sendErr instanceof Error ? sendErr.message : \"unknown\"}`,\n );\n }\n }\n });\n });\n}\n\nasync function processMessage(\n raw: string,\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n defaults: ResponsesWebSocketDefaults,\n): Promise<void> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n ws.send(\n JSON.stringify(\n buildErrorEvent(`Malformed JSON: ${detail}`, \"invalid_request_error\", \"invalid_json\"),\n ),\n );\n return;\n }\n\n if (!isResponseCreateMessage(parsed)) {\n ws.send(\n JSON.stringify(\n buildErrorEvent(\n 'Expected message type \"response.create\"',\n \"invalid_request_error\",\n \"invalid_message_type\",\n ),\n ),\n );\n return;\n }\n\n const responsesReq = {\n model: parsed.model ?? defaults.model,\n // Same shapes as HTTP: namespace / custom tools, additional_tools items\n // and custom tool call items reach responsesToCompletionRequest unchanged.\n input: (parsed.input ?? []) as ResponsesInputItem[],\n instructions: parsed.instructions,\n tools: parsed.tools as ResponsesToolDef[] | undefined,\n tool_choice: parsed.tool_choice,\n stream: parsed.stream,\n temperature: parsed.temperature,\n max_output_tokens: parsed.max_output_tokens,\n include: (parsed as { include?: string[] }).include,\n store: (parsed as { store?: boolean }).store,\n };\n\n // Gate encrypted-reasoning emission identically to the HTTP transport so the\n // agent-framework#7233 stateless-replay path works over WebSocket too.\n const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);\n // Synthesizing a reasoning item the fixture never declared takes the NARROWER\n // explicit-`include` gate, transport-identically to HTTP — a `store: false`\n // request with a summary-less fixture still gets no reasoning item.\n const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);\n\n const extendedTools = defaults.responsesTools === \"extended\";\n const completionReq = responsesToCompletionRequest(responsesReq, {\n extended: extendedTools,\n logger: defaults.logger,\n });\n completionReq._endpointType = \"chat\";\n const contextHeader = defaults.upgradeHeaders?.[\"x-aimock-context\"];\n completionReq._context =\n typeof contextHeader === \"string\"\n ? contextHeader\n : Array.isArray(contextHeader) && contextHeader.length > 0\n ? contextHeader[0]\n : undefined;\n const testId = defaults.testId ?? DEFAULT_TEST_ID;\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n // Wire only: recordMatchOptions here would turn on strict turnIndex and the\n // relaxed-turnIndex warning, which this transport has never had.\n { wire: \"openai-responses\" },\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (!fixture) {\n if (resolveRequestDecisions(defaults.strict, defaults.upgradeHeaders).fixtureMiss) {\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(strictNoMatchLogLine(\"WS\", \"/v1/responses\", skippedBySequenceOrTurn));\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.close(1008, strictMessage);\n return;\n }\n journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, defaults.upgradeHeaders),\n },\n });\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"No fixture matched\", \"invalid_request_error\", \"no_fixture_match\"),\n ),\n );\n return;\n }\n\n // Without responsesTools \"extended\", a namespace on a toolCalls entry or a\n // toolCall block is not emitted, as in earlier releases.\n const resolvedResponse = await resolveResponse(fixture, completionReq);\n const response = extendedTools ? resolvedResponse : withoutLegacyNamespaces(resolvedResponse);\n const misbehaviorDefaults: HandlerDefaults = {\n ...defaults,\n replaySpeed: defaults.replaySpeed ?? 1,\n };\n const misbehavior = planMisbehavior({\n wire: \"openai-responses\",\n fixture,\n response,\n request: completionReq,\n // Responses WebSocket always emits streaming events, even with stream:false.\n stream: true,\n emitsToolCallIds: true,\n defaults: misbehaviorDefaults,\n // The server has already resolved the upgrade's header/query test ID.\n rawHeaders: { ...defaults.upgradeHeaders, \"x-test-id\": testId },\n url: \"/v1/responses\",\n });\n const recordOutcome = (entry: ReturnType<Journal[\"add\"]>) =>\n recordMisbehaviorOutcome({\n entry,\n summary: misbehavior.summary,\n defaults: misbehaviorDefaults,\n testId,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: misbehavior.status, fixture },\n });\n recordOutcome(entry);\n if (!misbehavior.summary?.evaluations.length) {\n defaults.logger.error(\n `[misbehavior] ${misbehavior.code}: ${misbehavior.message} (testId=${testId})`,\n );\n }\n ws.send(\n JSON.stringify(\n buildErrorEvent(misbehavior.message, \"invalid_request_error\", misbehavior.code),\n ),\n );\n return;\n }\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n if (misbehavior.kind === \"applied\") {\n // Journal first, as HTTP /v1/responses does, so a fixture error thrown\n // while building leaves a 500 entry that keeps the fixture. Record the\n // fault only once the reply is built: a thrown error was not served.\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n const prepared = prepareResponsesMisbehavior(misbehavior);\n const output = journalFixtureToolCallError(entry, () =>\n buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning),\n );\n recordMisbehaviorOutcome({\n entry,\n summary: prepared.summary,\n defaults: misbehaviorDefaults,\n testId,\n });\n // K4 can add events; extend only its replay using the last recorded gap.\n const appliedTimings = extendRecordedTimings(\n fixture.recordedTimings,\n output.events.length,\n !!prepared.duplicateId,\n );\n await deliverTurn(ws, entry, fixture, output.events, {\n latency,\n recordedTimings: appliedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n });\n return;\n }\n\n // The WS path has no per-request `req.headers`; strict is resolved from the\n // connection's upgrade headers (see the `!fixture` branch above). Used below\n // to gate the synthesized reasoning channel on the requested model's capability.\n const effectiveStrict = resolveRequestDecisions(\n defaults.strict,\n defaults.upgradeHeaders,\n ).reasoningSuppression;\n\n // Raw passthrough fixtures are served over HTTP only for now. Fail loudly\n // instead of the generic unknown-type error so the author sees why.\n if (isRawResponse(response)) {\n const message = \"raw fixtures are not yet supported on the WebSocket transport\";\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n entry.response.error = message;\n recordOutcome(entry);\n defaults.logger.error(`WebSocket responses error: ${RAW_MODE_UNAVAILABLE}: ${message}`);\n ws.send(JSON.stringify(buildErrorEvent(message, \"server_error\", RAW_MODE_UNAVAILABLE)));\n return;\n }\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status, fixture },\n });\n recordOutcome(entry);\n ws.send(\n JSON.stringify(\n buildErrorEvent(response.error.message, response.error.type, response.error.code),\n ),\n );\n return;\n }\n\n // Content + tool calls response (must be checked before isTextResponse / isToolCallResponse)\n if (isCombinedFixtureResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n\n let events: ResponsesSSEEvent[];\n try {\n const ordered = resolveServedBlockOutcome(response)?.ordered;\n if (!ordered) assertCustomToolCalls(response);\n events = contentWithToolCallsStreamEvents(\n response.content ?? \"\",\n servedToolCalls(response),\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n ordered,\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n } catch (error) {\n journalEntry.response.status = 500;\n if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;\n throw error;\n }\n\n await deliverTurn(ws, journalEntry, fixture, events, {\n latency,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n });\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n\n // A fixture error (malformed citations, a non-image file) marks the entry\n // a 500 carrying the message before the error event goes out.\n const events = journalFixtureToolCallError(journalEntry, () =>\n buildTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n ),\n );\n await deliverTurn(ws, journalEntry, fixture, events, {\n latency,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n });\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordOutcome(journalEntry);\n let events: ResponsesSSEEvent[];\n try {\n assertCustomToolCalls(response);\n events = toolCallStreamEvents(\n servedToolCalls(response),\n completionReq.model,\n chunkSize,\n // Gate the synthesized reasoning channel on the requested model's\n // capability, matching the WS text / content+tool branches and the HTTP\n // tool-only path so reasoning emission is transport-independent.\n resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n ),\n response.webSearches,\n extractOverrides(response),\n emitEncryptedReasoning,\n synthesizeSummarylessReasoning,\n );\n } catch (error) {\n journalEntry.response.status = 500;\n if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;\n throw error;\n }\n await deliverTurn(ws, journalEntry, fixture, events, {\n latency,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n });\n return;\n }\n\n // Unknown response type\n const entry = journal.add({\n method: \"WS\",\n path: \"/v1/responses\",\n headers: flattenHeaders(defaults.upgradeHeaders ?? {}),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordOutcome(entry);\n ws.send(\n JSON.stringify(\n buildErrorEvent(\"Fixture response did not match any known type\", \"server_error\"),\n ),\n );\n}\n\n/** WS Responses frames: one JSON text message per event, no head, no trailer. */\nconst wsEventCodec: FrameCodec<ResponsesSSEEvent> = {\n headers: {},\n encode: (event) => JSON.stringify(event),\n checkGoneBeforeDelay: true,\n};\n\n/**\n * Send one turn's events under a WS lifecycle. The lifecycle owns the fixture\n * interruption and also stops the turn when the client closes, so a pending\n * delay ends at once. A stopped turn marks the entry by the shared WS rule\n * ({@link markWsTurnStopped}) and destroys the socket.\n * `streamingProfile` is not applied on this transport, as before.\n */\nasync function deliverTurn(\n ws: WebSocketConnection,\n entry: JournalEntry,\n fixture: Fixture,\n events: ResponsesSSEEvent[],\n timing: { latency: number; recordedTimings?: RecordedTimings; replaySpeed?: number },\n): Promise<void> {\n const lifecycle = wsLifecycle(ws, fixture);\n try {\n const completed = await runFrameLoop(wsSink(ws), events, wsEventCodec, timing, {\n lifecycle,\n onChunkSent: lifecycle.tick,\n });\n if (!completed) {\n markWsTurnStopped(entry, ws, lifecycle);\n ws.destroy();\n }\n } finally {\n lifecycle.dispose();\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.d.cts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;KAiDK,0BAAA,GAA6B,IAIK,CAJA,eAIA,EAAA,aAAA,CAAA,GAAA;EAAmB,WAAA,CAAA,EAAA,MAAA;EAqC1C,KAAA,EAAA,MAAA;EAAwB,MAAA,CAAA,EAAA,MAAA;gBAClC,CAAA,EA1CgC,UAAA,CAIC,mBAsCjC;;sBAEK,CAAA,EAAA,GAAA,GAAA,IAAA;;AAC2B,iBAJtB,wBAAA,CAIsB,EAAA,EAHhC,mBAGgC,EAAA,QAAA,EAF1B,OAE0B,EAAA,EAAA,OAAA,EAD3B,OAC2B,EAAA,QAAA,EAA1B,0BAA0B,CAAA,EAAA,IAAA"}
1
+ {"version":3,"file":"ws-responses.d.cts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;KAqDK,0BAAA,GAA6B,IAIK,CAJA,eAIA,EAAA,aAAA,CAAA,GAAA;EAAmB,WAAA,CAAA,EAAA,MAAA;EAqC1C,KAAA,EAAA,MAAA;EAAwB,MAAA,CAAA,EAAA,MAAA;gBAClC,CAAA,EA1CgC,UAAA,CAIC,mBAsCjC;;sBAEK,CAAA,EAAA,GAAA,GAAA,IAAA;;AAC2B,iBAJtB,wBAAA,CAIsB,EAAA,EAHhC,mBAGgC,EAAA,QAAA,EAF1B,OAE0B,EAAA,EAAA,OAAA,EAD3B,OAC2B,EAAA,QAAA,EAA1B,0BAA0B,CAAA,EAAA,IAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.d.ts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;KAiDK,0BAAA,GAA6B,IAIK,CAJA,eAIA,EAAA,aAAA,CAAA,GAAA;EAAmB,WAAA,CAAA,EAAA,MAAA;EAqC1C,KAAA,EAAA,MAAA;EAAwB,MAAA,CAAA,EAAA,MAAA;gBAClC,CAAA,EA1CgC,UAAA,CAIC,mBAsCjC;;sBAEK,CAAA,EAAA,GAAA,GAAA,IAAA;;AAC2B,iBAJtB,wBAAA,CAIsB,EAAA,EAHhC,mBAGgC,EAAA,QAAA,EAF1B,OAE0B,EAAA,EAAA,OAAA,EAD3B,OAC2B,EAAA,QAAA,EAA1B,0BAA0B,CAAA,EAAA,IAAA"}
1
+ {"version":3,"file":"ws-responses.d.ts","names":[],"sources":["../src/ws-responses.ts"],"sourcesContent":[],"mappings":";;;;;;;KAqDK,0BAAA,GAA6B,IAIK,CAJA,eAIA,EAAA,aAAA,CAAA,GAAA;EAAmB,WAAA,CAAA,EAAA,MAAA;EAqC1C,KAAA,EAAA,MAAA;EAAwB,MAAA,CAAA,EAAA,MAAA;gBAClC,CAAA,EA1CgC,UAAA,CAIC,mBAsCjC;;sBAEK,CAAA,EAAA,GAAA,GAAA,IAAA;;AAC2B,iBAJtB,wBAAA,CAIsB,EAAA,EAHhC,mBAGgC,EAAA,QAAA,EAF1B,OAE0B,EAAA,EAAA,OAAA,EAD3B,OAC2B,EAAA,QAAA,EAA1B,0BAA0B,CAAA,EAAA,IAAA"}
@@ -1,10 +1,14 @@
1
+ import { buildOpenAIError } from "./response/errors.js";
1
2
  import { DEFAULT_TEST_ID } from "./constants.js";
2
- import { assertCustomToolCalls, extractOverrides, fixtureToolCallErrorCode, flattenHeaders, isCombinedFixtureResponse, isErrorResponse, isFixtureToolCallError, isTextResponse, isToolCallResponse, resolveReasoningForModel, resolveResponse, resolveServedBlockOutcome, resolveStrictMode, servedToolCalls, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, withoutLegacyNamespaces } from "./helpers.js";
3
- import { calculateDelay, delay } from "./sse-writer.js";
3
+ import { resolveRequestDecisions, strictOverrideField } from "./response/policy.js";
4
+ import { assertCustomToolCalls, extractOverrides, fixtureToolCallErrorCode, flattenHeaders, isCombinedFixtureResponse, isErrorResponse, isFixtureToolCallError, isRawResponse, isTextResponse, isToolCallResponse, journalFixtureToolCallError, resolveReasoningForModel, resolveResponse, resolveServedBlockOutcome, servedToolCalls, strictNoMatchLogLine, strictNoMatchMessage, withoutLegacyNamespaces } from "./helpers.js";
5
+ import { runFrameLoop, wsSink } from "./response/frame-loop.js";
4
6
  import { planMisbehavior, recordMisbehaviorOutcome } from "./misbehavior.js";
5
7
  import { matchFixtureDiagnostic } from "./router.js";
6
8
  import "./journal.js";
7
- import { createInterruptionSignal } from "./interruption.js";
9
+ import { markWsTurnStopped, watchWsClose, wsLifecycle } from "./response/lifecycle.js";
10
+ import { extendRecordedTimings } from "./response/timing.js";
11
+ import { RAW_MODE_UNAVAILABLE } from "./response/raw.js";
8
12
  import { buildResponsesMisbehavior, buildTextStreamEvents, contentWithToolCallsStreamEvents, prepareResponsesMisbehavior, requestIncludesEncryptedReasoning, requestWantsEncryptedReasoning, responsesToCompletionRequest, toolCallStreamEvents } from "./responses.js";
9
13
 
10
14
  //#region src/ws-responses.ts
@@ -14,15 +18,16 @@ function isResponseCreateMessage(msg) {
14
18
  function buildErrorEvent(message, type = "invalid_request_error", code) {
15
19
  return {
16
20
  type: "error",
17
- error: {
21
+ error: buildOpenAIError({
18
22
  message,
19
23
  type,
20
24
  code
21
- }
25
+ })
22
26
  };
23
27
  }
24
28
  function handleWebSocketResponses(ws, fixtures, journal, defaults) {
25
29
  const { logger } = defaults;
30
+ watchWsClose(ws);
26
31
  let pending = Promise.resolve();
27
32
  ws.on("message", (raw) => {
28
33
  pending = pending.then(async () => {
@@ -78,10 +83,10 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
78
83
  const contextHeader = defaults.upgradeHeaders?.["x-aimock-context"];
79
84
  completionReq._context = typeof contextHeader === "string" ? contextHeader : Array.isArray(contextHeader) && contextHeader.length > 0 ? contextHeader[0] : void 0;
80
85
  const testId = defaults.testId ?? DEFAULT_TEST_ID;
81
- const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
86
+ const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, { wire: "openai-responses" });
82
87
  if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
83
88
  if (!fixture) {
84
- if (resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {
89
+ if (resolveRequestDecisions(defaults.strict, defaults.upgradeHeaders).fixtureMiss) {
85
90
  const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);
86
91
  defaults.logger.error(strictNoMatchLogLine("WS", "/v1/responses", skippedBySequenceOrTurn));
87
92
  journal.add({
@@ -156,8 +161,6 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
156
161
  const latency = fixture.latency ?? defaults.latency;
157
162
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
158
163
  if (misbehavior.kind === "applied") {
159
- const prepared = prepareResponsesMisbehavior(misbehavior);
160
- const output = buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
161
164
  const entry = journal.add({
162
165
  method: "WS",
163
166
  path: "/v1/responses",
@@ -168,28 +171,41 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
168
171
  fixture
169
172
  }
170
173
  });
174
+ const prepared = prepareResponsesMisbehavior(misbehavior);
175
+ const output = journalFixtureToolCallError(entry, () => buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning));
171
176
  recordMisbehaviorOutcome({
172
177
  entry,
173
178
  summary: prepared.summary,
174
179
  defaults: misbehaviorDefaults,
175
180
  testId
176
181
  });
177
- const timings = fixture.recordedTimings;
178
- const gaps = timings?.interChunkDelaysMs;
179
- const appliedTimings = prepared.duplicateId && timings && gaps ? {
180
- ...timings,
181
- interChunkDelaysMs: Array.from({ length: Math.max(gaps.length, output.events.length - 1) }, (_, index) => gaps[index] ?? gaps.at(-1) ?? 0)
182
- } : timings;
183
- const interruption = createInterruptionSignal(fixture);
184
- if (!await sendEvents(ws, output.events, latency, interruption?.signal, interruption?.tick, appliedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
185
- ws.destroy();
186
- entry.response.interrupted = true;
187
- entry.response.interruptReason = interruption?.reason();
188
- }
189
- interruption?.cleanup();
182
+ const appliedTimings = extendRecordedTimings(fixture.recordedTimings, output.events.length, !!prepared.duplicateId);
183
+ await deliverTurn(ws, entry, fixture, output.events, {
184
+ latency,
185
+ recordedTimings: appliedTimings,
186
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
187
+ });
188
+ return;
189
+ }
190
+ const effectiveStrict = resolveRequestDecisions(defaults.strict, defaults.upgradeHeaders).reasoningSuppression;
191
+ if (isRawResponse(response)) {
192
+ const message = "raw fixtures are not yet supported on the WebSocket transport";
193
+ const entry = journal.add({
194
+ method: "WS",
195
+ path: "/v1/responses",
196
+ headers: flattenHeaders(defaults.upgradeHeaders ?? {}),
197
+ body: completionReq,
198
+ response: {
199
+ status: 500,
200
+ fixture
201
+ }
202
+ });
203
+ entry.response.error = message;
204
+ recordOutcome(entry);
205
+ defaults.logger.error(`WebSocket responses error: ${RAW_MODE_UNAVAILABLE}: ${message}`);
206
+ ws.send(JSON.stringify(buildErrorEvent(message, "server_error", RAW_MODE_UNAVAILABLE)));
190
207
  return;
191
208
  }
192
- const effectiveStrict = resolveStrictMode(defaults.strict, defaults.upgradeHeaders);
193
209
  if (isErrorResponse(response)) {
194
210
  const status = response.status ?? 500;
195
211
  recordOutcome(journal.add({
@@ -227,13 +243,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
227
243
  if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;
228
244
  throw error;
229
245
  }
230
- const interruption = createInterruptionSignal(fixture);
231
- if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
232
- ws.destroy();
233
- journalEntry.response.interrupted = true;
234
- journalEntry.response.interruptReason = interruption?.reason();
235
- }
236
- interruption?.cleanup();
246
+ await deliverTurn(ws, journalEntry, fixture, events, {
247
+ latency,
248
+ recordedTimings: fixture.recordedTimings,
249
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
250
+ });
237
251
  return;
238
252
  }
239
253
  if (isTextResponse(response)) {
@@ -248,14 +262,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
248
262
  }
249
263
  });
250
264
  recordOutcome(journalEntry);
251
- const events = buildTextStreamEvents(response.content, completionReq.model, chunkSize, resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning);
252
- const interruption = createInterruptionSignal(fixture);
253
- if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
254
- ws.destroy();
255
- journalEntry.response.interrupted = true;
256
- journalEntry.response.interruptReason = interruption?.reason();
257
- }
258
- interruption?.cleanup();
265
+ await deliverTurn(ws, journalEntry, fixture, journalFixtureToolCallError(journalEntry, () => buildTextStreamEvents(response.content, completionReq.model, chunkSize, resolveReasoningForModel(response.reasoning, completionReq.model, effectiveStrict, defaults.logger), response.webSearches, extractOverrides(response), emitEncryptedReasoning, synthesizeSummarylessReasoning)), {
266
+ latency,
267
+ recordedTimings: fixture.recordedTimings,
268
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
269
+ });
259
270
  return;
260
271
  }
261
272
  if (isToolCallResponse(response)) {
@@ -279,13 +290,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
279
290
  if (isFixtureToolCallError(error)) journalEntry.response.error = error.message;
280
291
  throw error;
281
292
  }
282
- const interruption = createInterruptionSignal(fixture);
283
- if (!await sendEvents(ws, events, latency, interruption?.signal, interruption?.tick, fixture.recordedTimings, fixture.replaySpeed ?? defaults.replaySpeed)) {
284
- ws.destroy();
285
- journalEntry.response.interrupted = true;
286
- journalEntry.response.interruptReason = interruption?.reason();
287
- }
288
- interruption?.cleanup();
293
+ await deliverTurn(ws, journalEntry, fixture, events, {
294
+ latency,
295
+ recordedTimings: fixture.recordedTimings,
296
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
297
+ });
289
298
  return;
290
299
  }
291
300
  recordOutcome(journal.add({
@@ -300,20 +309,32 @@ async function processMessage(raw, ws, fixtures, journal, defaults) {
300
309
  }));
301
310
  ws.send(JSON.stringify(buildErrorEvent("Fixture response did not match any known type", "server_error")));
302
311
  }
303
- async function sendEvents(ws, events, latency, signal, onChunkSent, recordedTimings, replaySpeed) {
304
- let eventIndex = 0;
305
- for (const event of events) {
306
- if (ws.isClosed) return false;
307
- const chunkDelay = calculateDelay(eventIndex, void 0, latency, recordedTimings, replaySpeed);
308
- if (chunkDelay > 0) await delay(chunkDelay, signal);
309
- if (signal?.aborted) return false;
310
- if (ws.isClosed) return false;
311
- ws.send(JSON.stringify(event));
312
- eventIndex++;
313
- onChunkSent?.();
314
- if (signal?.aborted) return false;
312
+ /** WS Responses frames: one JSON text message per event, no head, no trailer. */
313
+ const wsEventCodec = {
314
+ headers: {},
315
+ encode: (event) => JSON.stringify(event),
316
+ checkGoneBeforeDelay: true
317
+ };
318
+ /**
319
+ * Send one turn's events under a WS lifecycle. The lifecycle owns the fixture
320
+ * interruption and also stops the turn when the client closes, so a pending
321
+ * delay ends at once. A stopped turn marks the entry by the shared WS rule
322
+ * ({@link markWsTurnStopped}) and destroys the socket.
323
+ * `streamingProfile` is not applied on this transport, as before.
324
+ */
325
+ async function deliverTurn(ws, entry, fixture, events, timing) {
326
+ const lifecycle = wsLifecycle(ws, fixture);
327
+ try {
328
+ if (!await runFrameLoop(wsSink(ws), events, wsEventCodec, timing, {
329
+ lifecycle,
330
+ onChunkSent: lifecycle.tick
331
+ })) {
332
+ markWsTurnStopped(entry, ws, lifecycle);
333
+ ws.destroy();
334
+ }
335
+ } finally {
336
+ lifecycle.dispose();
315
337
  }
316
- return true;
317
338
  }
318
339
 
319
340
  //#endregion