@copilotkit/aimock 1.45.1 → 1.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (492) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -1
  4. package/README.md +11 -2
  5. package/dist/agui-mock.cjs +25 -25
  6. package/dist/agui-mock.cjs.map +1 -1
  7. package/dist/agui-mock.d.cts.map +1 -1
  8. package/dist/agui-mock.d.ts.map +1 -1
  9. package/dist/agui-mock.js +25 -25
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-types.d.cts.map +1 -1
  12. package/dist/aimock-cli.cjs +15 -1
  13. package/dist/aimock-cli.cjs.map +1 -1
  14. package/dist/aimock-cli.d.cts.map +1 -1
  15. package/dist/aimock-cli.d.ts.map +1 -1
  16. package/dist/aimock-cli.js +16 -2
  17. package/dist/aimock-cli.js.map +1 -1
  18. package/dist/api-key-auth.cjs +10 -9
  19. package/dist/api-key-auth.cjs.map +1 -1
  20. package/dist/api-key-auth.d.cts.map +1 -1
  21. package/dist/api-key-auth.d.ts.map +1 -1
  22. package/dist/api-key-auth.js +11 -10
  23. package/dist/api-key-auth.js.map +1 -1
  24. package/dist/aws-event-stream.cjs +20 -23
  25. package/dist/aws-event-stream.cjs.map +1 -1
  26. package/dist/aws-event-stream.d.cts +0 -1
  27. package/dist/aws-event-stream.d.ts +0 -1
  28. package/dist/aws-event-stream.js +20 -24
  29. package/dist/aws-event-stream.js.map +1 -1
  30. package/dist/batches.cjs +2 -4
  31. package/dist/batches.cjs.map +1 -1
  32. package/dist/batches.d.cts.map +1 -1
  33. package/dist/batches.d.ts.map +1 -1
  34. package/dist/batches.js +2 -4
  35. package/dist/batches.js.map +1 -1
  36. package/dist/bedrock-converse.cjs +139 -161
  37. package/dist/bedrock-converse.cjs.map +1 -1
  38. package/dist/bedrock-converse.d.cts.map +1 -1
  39. package/dist/bedrock-converse.d.ts.map +1 -1
  40. package/dist/bedrock-converse.js +137 -159
  41. package/dist/bedrock-converse.js.map +1 -1
  42. package/dist/bedrock.cjs +175 -216
  43. package/dist/bedrock.cjs.map +1 -1
  44. package/dist/bedrock.d.cts.map +1 -1
  45. package/dist/bedrock.d.ts.map +1 -1
  46. package/dist/bedrock.js +172 -213
  47. package/dist/bedrock.js.map +1 -1
  48. package/dist/byteplus-video.cjs +11 -10
  49. package/dist/byteplus-video.cjs.map +1 -1
  50. package/dist/byteplus-video.d.cts.map +1 -1
  51. package/dist/byteplus-video.d.ts.map +1 -1
  52. package/dist/byteplus-video.js +5 -4
  53. package/dist/byteplus-video.js.map +1 -1
  54. package/dist/chaos.cjs +5 -4
  55. package/dist/chaos.cjs.map +1 -1
  56. package/dist/chaos.d.cts.map +1 -1
  57. package/dist/chaos.d.ts.map +1 -1
  58. package/dist/chaos.js +5 -4
  59. package/dist/chaos.js.map +1 -1
  60. package/dist/cli.cjs +15 -0
  61. package/dist/cli.cjs.map +1 -1
  62. package/dist/cli.js +15 -0
  63. package/dist/cli.js.map +1 -1
  64. package/dist/cohere.cjs +107 -180
  65. package/dist/cohere.cjs.map +1 -1
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts.map +1 -1
  68. package/dist/cohere.js +106 -179
  69. package/dist/cohere.js.map +1 -1
  70. package/dist/config-loader.cjs +29 -1
  71. package/dist/config-loader.cjs.map +1 -1
  72. package/dist/config-loader.d.cts +7 -1
  73. package/dist/config-loader.d.cts.map +1 -1
  74. package/dist/config-loader.d.ts +7 -1
  75. package/dist/config-loader.d.ts.map +1 -1
  76. package/dist/config-loader.js +29 -2
  77. package/dist/config-loader.js.map +1 -1
  78. package/dist/count-tokens.cjs +146 -0
  79. package/dist/count-tokens.cjs.map +1 -0
  80. package/dist/count-tokens.js +146 -0
  81. package/dist/count-tokens.js.map +1 -0
  82. package/dist/elevenlabs-audio.cjs +36 -34
  83. package/dist/elevenlabs-audio.cjs.map +1 -1
  84. package/dist/elevenlabs-audio.d.cts.map +1 -1
  85. package/dist/elevenlabs-audio.d.ts.map +1 -1
  86. package/dist/elevenlabs-audio.js +33 -31
  87. package/dist/elevenlabs-audio.js.map +1 -1
  88. package/dist/elevenlabs-voice.cjs +38 -37
  89. package/dist/elevenlabs-voice.cjs.map +1 -1
  90. package/dist/elevenlabs-voice.d.cts.map +1 -1
  91. package/dist/elevenlabs-voice.d.ts.map +1 -1
  92. package/dist/elevenlabs-voice.js +33 -32
  93. package/dist/elevenlabs-voice.js.map +1 -1
  94. package/dist/embedding-request.cjs +16 -0
  95. package/dist/embedding-request.cjs.map +1 -0
  96. package/dist/embedding-request.js +14 -0
  97. package/dist/embedding-request.js.map +1 -0
  98. package/dist/embeddings.cjs +30 -20
  99. package/dist/embeddings.cjs.map +1 -1
  100. package/dist/embeddings.d.cts.map +1 -1
  101. package/dist/embeddings.d.ts.map +1 -1
  102. package/dist/embeddings.js +30 -20
  103. package/dist/embeddings.js.map +1 -1
  104. package/dist/fal-audio.cjs +45 -43
  105. package/dist/fal-audio.cjs.map +1 -1
  106. package/dist/fal-audio.d.cts.map +1 -1
  107. package/dist/fal-audio.d.ts.map +1 -1
  108. package/dist/fal-audio.js +42 -40
  109. package/dist/fal-audio.js.map +1 -1
  110. package/dist/fal.cjs +17 -15
  111. package/dist/fal.cjs.map +1 -1
  112. package/dist/fal.js +16 -14
  113. package/dist/fal.js.map +1 -1
  114. package/dist/files.cjs +2 -4
  115. package/dist/files.cjs.map +1 -1
  116. package/dist/files.d.cts.map +1 -1
  117. package/dist/files.d.ts.map +1 -1
  118. package/dist/files.js +2 -4
  119. package/dist/files.js.map +1 -1
  120. package/dist/fine-tuning.cjs +2 -4
  121. package/dist/fine-tuning.cjs.map +1 -1
  122. package/dist/fine-tuning.d.cts.map +1 -1
  123. package/dist/fine-tuning.d.ts.map +1 -1
  124. package/dist/fine-tuning.js +2 -4
  125. package/dist/fine-tuning.js.map +1 -1
  126. package/dist/fixture-loader.cjs +140 -44
  127. package/dist/fixture-loader.cjs.map +1 -1
  128. package/dist/fixture-loader.d.cts.map +1 -1
  129. package/dist/fixture-loader.d.ts.map +1 -1
  130. package/dist/fixture-loader.js +141 -46
  131. package/dist/fixture-loader.js.map +1 -1
  132. package/dist/gemini-embeddings.cjs +26 -24
  133. package/dist/gemini-embeddings.cjs.map +1 -1
  134. package/dist/gemini-embeddings.js +26 -24
  135. package/dist/gemini-embeddings.js.map +1 -1
  136. package/dist/gemini-interactions.cjs +113 -156
  137. package/dist/gemini-interactions.cjs.map +1 -1
  138. package/dist/gemini-interactions.d.cts +0 -1
  139. package/dist/gemini-interactions.d.cts.map +1 -1
  140. package/dist/gemini-interactions.d.ts +0 -1
  141. package/dist/gemini-interactions.d.ts.map +1 -1
  142. package/dist/gemini-interactions.js +113 -156
  143. package/dist/gemini-interactions.js.map +1 -1
  144. package/dist/gemini.cjs +295 -218
  145. package/dist/gemini.cjs.map +1 -1
  146. package/dist/gemini.d.cts.map +1 -1
  147. package/dist/gemini.d.ts.map +1 -1
  148. package/dist/gemini.js +295 -219
  149. package/dist/gemini.js.map +1 -1
  150. package/dist/grok-video.cjs +14 -12
  151. package/dist/grok-video.cjs.map +1 -1
  152. package/dist/grok-video.d.cts.map +1 -1
  153. package/dist/grok-video.d.ts.map +1 -1
  154. package/dist/grok-video.js +8 -6
  155. package/dist/grok-video.js.map +1 -1
  156. package/dist/helpers.cjs +411 -75
  157. package/dist/helpers.cjs.map +1 -1
  158. package/dist/helpers.d.cts +51 -1
  159. package/dist/helpers.d.cts.map +1 -1
  160. package/dist/helpers.d.ts +51 -1
  161. package/dist/helpers.d.ts.map +1 -1
  162. package/dist/helpers.js +402 -74
  163. package/dist/helpers.js.map +1 -1
  164. package/dist/images.cjs +30 -28
  165. package/dist/images.cjs.map +1 -1
  166. package/dist/images.d.cts.map +1 -1
  167. package/dist/images.d.ts.map +1 -1
  168. package/dist/images.js +27 -25
  169. package/dist/images.js.map +1 -1
  170. package/dist/index.cjs +4 -2
  171. package/dist/index.d.cts +4 -4
  172. package/dist/index.d.ts +4 -4
  173. package/dist/index.js +4 -4
  174. package/dist/journal.cjs +3 -1
  175. package/dist/journal.cjs.map +1 -1
  176. package/dist/journal.d.cts.map +1 -1
  177. package/dist/journal.d.ts.map +1 -1
  178. package/dist/journal.js +2 -1
  179. package/dist/journal.js.map +1 -1
  180. package/dist/llmock.cjs +3 -0
  181. package/dist/llmock.cjs.map +1 -1
  182. package/dist/llmock.d.cts.map +1 -1
  183. package/dist/llmock.d.ts.map +1 -1
  184. package/dist/llmock.js +3 -0
  185. package/dist/llmock.js.map +1 -1
  186. package/dist/mcp-fakes.d.cts.map +1 -1
  187. package/dist/mcp-handler.cjs +7 -3
  188. package/dist/mcp-handler.cjs.map +1 -1
  189. package/dist/mcp-handler.js +7 -3
  190. package/dist/mcp-handler.js.map +1 -1
  191. package/dist/mcp-recorder.cjs +2 -1
  192. package/dist/mcp-recorder.cjs.map +1 -1
  193. package/dist/mcp-recorder.js +3 -2
  194. package/dist/mcp-recorder.js.map +1 -1
  195. package/dist/messages.cjs +235 -202
  196. package/dist/messages.cjs.map +1 -1
  197. package/dist/messages.d.cts.map +1 -1
  198. package/dist/messages.d.ts.map +1 -1
  199. package/dist/messages.js +234 -202
  200. package/dist/messages.js.map +1 -1
  201. package/dist/metrics.cjs +5 -2
  202. package/dist/metrics.cjs.map +1 -1
  203. package/dist/metrics.d.cts.map +1 -1
  204. package/dist/metrics.d.ts.map +1 -1
  205. package/dist/metrics.js +6 -3
  206. package/dist/metrics.js.map +1 -1
  207. package/dist/misbehavior.cjs +62 -3
  208. package/dist/misbehavior.cjs.map +1 -1
  209. package/dist/misbehavior.d.cts.map +1 -1
  210. package/dist/misbehavior.d.ts.map +1 -1
  211. package/dist/misbehavior.js +63 -4
  212. package/dist/misbehavior.js.map +1 -1
  213. package/dist/model-utils.cjs +1 -0
  214. package/dist/model-utils.cjs.map +1 -1
  215. package/dist/model-utils.js +1 -1
  216. package/dist/model-utils.js.map +1 -1
  217. package/dist/moderation.cjs +15 -12
  218. package/dist/moderation.cjs.map +1 -1
  219. package/dist/moderation.js +14 -11
  220. package/dist/moderation.js.map +1 -1
  221. package/dist/ndjson-writer.cjs +21 -23
  222. package/dist/ndjson-writer.cjs.map +1 -1
  223. package/dist/ndjson-writer.d.cts +0 -1
  224. package/dist/ndjson-writer.d.cts.map +1 -1
  225. package/dist/ndjson-writer.d.ts +0 -1
  226. package/dist/ndjson-writer.d.ts.map +1 -1
  227. package/dist/ndjson-writer.js +21 -24
  228. package/dist/ndjson-writer.js.map +1 -1
  229. package/dist/ollama.cjs +276 -210
  230. package/dist/ollama.cjs.map +1 -1
  231. package/dist/ollama.d.cts.map +1 -1
  232. package/dist/ollama.d.ts.map +1 -1
  233. package/dist/ollama.js +273 -207
  234. package/dist/ollama.js.map +1 -1
  235. package/dist/openapi.cjs +323 -3
  236. package/dist/openapi.cjs.map +1 -1
  237. package/dist/openapi.d.cts.map +1 -1
  238. package/dist/openapi.d.ts.map +1 -1
  239. package/dist/openapi.js +324 -4
  240. package/dist/openapi.js.map +1 -1
  241. package/dist/openrouter-chat.cjs +11 -155
  242. package/dist/openrouter-chat.cjs.map +1 -1
  243. package/dist/openrouter-chat.js +11 -155
  244. package/dist/openrouter-chat.js.map +1 -1
  245. package/dist/openrouter-video.cjs +51 -74
  246. package/dist/openrouter-video.cjs.map +1 -1
  247. package/dist/openrouter-video.d.cts.map +1 -1
  248. package/dist/openrouter-video.d.ts.map +1 -1
  249. package/dist/openrouter-video.js +37 -60
  250. package/dist/openrouter-video.js.map +1 -1
  251. package/dist/raw-validation.cjs +177 -0
  252. package/dist/raw-validation.cjs.map +1 -0
  253. package/dist/raw-validation.js +176 -0
  254. package/dist/raw-validation.js.map +1 -0
  255. package/dist/recorder.cjs +176 -95
  256. package/dist/recorder.cjs.map +1 -1
  257. package/dist/recorder.d.cts.map +1 -1
  258. package/dist/recorder.d.ts.map +1 -1
  259. package/dist/recorder.js +176 -95
  260. package/dist/recorder.js.map +1 -1
  261. package/dist/rerank.cjs +13 -11
  262. package/dist/rerank.cjs.map +1 -1
  263. package/dist/rerank.js +12 -10
  264. package/dist/rerank.js.map +1 -1
  265. package/dist/response/catalog/anthropic-page.cjs +39 -0
  266. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  267. package/dist/response/catalog/anthropic-page.js +38 -0
  268. package/dist/response/catalog/anthropic-page.js.map +1 -0
  269. package/dist/response/catalog/collect.cjs +61 -0
  270. package/dist/response/catalog/collect.cjs.map +1 -0
  271. package/dist/response/catalog/collect.js +59 -0
  272. package/dist/response/catalog/collect.js.map +1 -0
  273. package/dist/response/catalog/gemini-page.cjs +25 -0
  274. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  275. package/dist/response/catalog/gemini-page.js +24 -0
  276. package/dist/response/catalog/gemini-page.js.map +1 -0
  277. package/dist/response/catalog/render.cjs +182 -0
  278. package/dist/response/catalog/render.cjs.map +1 -0
  279. package/dist/response/catalog/render.js +171 -0
  280. package/dist/response/catalog/render.js.map +1 -0
  281. package/dist/response/deliver.cjs +144 -0
  282. package/dist/response/deliver.cjs.map +1 -0
  283. package/dist/response/deliver.js +143 -0
  284. package/dist/response/deliver.js.map +1 -0
  285. package/dist/response/errors.cjs +225 -0
  286. package/dist/response/errors.cjs.map +1 -0
  287. package/dist/response/errors.js +224 -0
  288. package/dist/response/errors.js.map +1 -0
  289. package/dist/response/finish.cjs +39 -0
  290. package/dist/response/finish.cjs.map +1 -0
  291. package/dist/response/finish.js +35 -0
  292. package/dist/response/finish.js.map +1 -0
  293. package/dist/response/frame-loop.cjs +102 -0
  294. package/dist/response/frame-loop.cjs.map +1 -0
  295. package/dist/response/frame-loop.js +100 -0
  296. package/dist/response/frame-loop.js.map +1 -0
  297. package/dist/response/lifecycle.cjs +223 -0
  298. package/dist/response/lifecycle.cjs.map +1 -0
  299. package/dist/response/lifecycle.js +211 -0
  300. package/dist/response/lifecycle.js.map +1 -0
  301. package/dist/response/policy.cjs +126 -0
  302. package/dist/response/policy.cjs.map +1 -0
  303. package/dist/response/policy.js +120 -0
  304. package/dist/response/policy.js.map +1 -0
  305. package/dist/response/profiles.cjs +69 -0
  306. package/dist/response/profiles.cjs.map +1 -0
  307. package/dist/response/profiles.js +65 -0
  308. package/dist/response/profiles.js.map +1 -0
  309. package/dist/response/proxy.cjs +71 -0
  310. package/dist/response/proxy.cjs.map +1 -0
  311. package/dist/response/proxy.js +70 -0
  312. package/dist/response/proxy.js.map +1 -0
  313. package/dist/response/raw.cjs +228 -0
  314. package/dist/response/raw.cjs.map +1 -0
  315. package/dist/response/raw.js +227 -0
  316. package/dist/response/raw.js.map +1 -0
  317. package/dist/response/timing.cjs +157 -0
  318. package/dist/response/timing.cjs.map +1 -0
  319. package/dist/response/timing.js +150 -0
  320. package/dist/response/timing.js.map +1 -0
  321. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  322. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  323. package/dist/response/usage/codecs/anthropic.js +50 -0
  324. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  325. package/dist/response/usage/codecs/cohere.cjs +36 -0
  326. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  327. package/dist/response/usage/codecs/cohere.js +35 -0
  328. package/dist/response/usage/codecs/cohere.js.map +1 -0
  329. package/dist/response/usage/codecs/converse.cjs +16 -0
  330. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  331. package/dist/response/usage/codecs/converse.js +15 -0
  332. package/dist/response/usage/codecs/converse.js.map +1 -0
  333. package/dist/response/usage/codecs/gemini.cjs +31 -0
  334. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  335. package/dist/response/usage/codecs/gemini.js +31 -0
  336. package/dist/response/usage/codecs/gemini.js.map +1 -0
  337. package/dist/response/usage/codecs/interactions.cjs +22 -0
  338. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  339. package/dist/response/usage/codecs/interactions.js +21 -0
  340. package/dist/response/usage/codecs/interactions.js.map +1 -0
  341. package/dist/response/usage/codecs/ollama.cjs +17 -0
  342. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  343. package/dist/response/usage/codecs/ollama.js +17 -0
  344. package/dist/response/usage/codecs/ollama.js.map +1 -0
  345. package/dist/response/usage/codecs/openai.cjs +52 -0
  346. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  347. package/dist/response/usage/codecs/openai.js +50 -0
  348. package/dist/response/usage/codecs/openai.js.map +1 -0
  349. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  350. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  351. package/dist/response/usage/codecs/openrouter.js +67 -0
  352. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  353. package/dist/response/usage/codecs/realtime.cjs +19 -0
  354. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  355. package/dist/response/usage/codecs/realtime.js +18 -0
  356. package/dist/response/usage/codecs/realtime.js.map +1 -0
  357. package/dist/response/usage/codecs/responses.cjs +21 -0
  358. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  359. package/dist/response/usage/codecs/responses.js +20 -0
  360. package/dist/response/usage/codecs/responses.js.map +1 -0
  361. package/dist/response/usage/estimate.cjs +23 -0
  362. package/dist/response/usage/estimate.cjs.map +1 -0
  363. package/dist/response/usage/estimate.js +21 -0
  364. package/dist/response/usage/estimate.js.map +1 -0
  365. package/dist/response/usage/fields.cjs +72 -0
  366. package/dist/response/usage/fields.cjs.map +1 -0
  367. package/dist/response/usage/fields.js +70 -0
  368. package/dist/response/usage/fields.js.map +1 -0
  369. package/dist/response/usage/recording.cjs +52 -0
  370. package/dist/response/usage/recording.cjs.map +1 -0
  371. package/dist/response/usage/recording.js +52 -0
  372. package/dist/response/usage/recording.js.map +1 -0
  373. package/dist/responses.cjs +304 -196
  374. package/dist/responses.cjs.map +1 -1
  375. package/dist/responses.d.cts.map +1 -1
  376. package/dist/responses.d.ts.map +1 -1
  377. package/dist/responses.js +303 -195
  378. package/dist/responses.js.map +1 -1
  379. package/dist/route-registry.cjs +45 -0
  380. package/dist/route-registry.cjs.map +1 -1
  381. package/dist/route-registry.d.cts.map +1 -1
  382. package/dist/route-registry.d.ts.map +1 -1
  383. package/dist/route-registry.js +41 -1
  384. package/dist/route-registry.js.map +1 -1
  385. package/dist/router.cjs +24 -4
  386. package/dist/router.cjs.map +1 -1
  387. package/dist/router.d.cts +7 -1
  388. package/dist/router.d.cts.map +1 -1
  389. package/dist/router.d.ts +7 -1
  390. package/dist/router.d.ts.map +1 -1
  391. package/dist/router.js +24 -5
  392. package/dist/router.js.map +1 -1
  393. package/dist/search.cjs +13 -11
  394. package/dist/search.cjs.map +1 -1
  395. package/dist/search.js +12 -10
  396. package/dist/search.js.map +1 -1
  397. package/dist/server.cjs +293 -248
  398. package/dist/server.cjs.map +1 -1
  399. package/dist/server.d.cts.map +1 -1
  400. package/dist/server.d.ts.map +1 -1
  401. package/dist/server.js +294 -249
  402. package/dist/server.js.map +1 -1
  403. package/dist/speech.cjs +20 -18
  404. package/dist/speech.cjs.map +1 -1
  405. package/dist/speech.d.cts.map +1 -1
  406. package/dist/speech.d.ts.map +1 -1
  407. package/dist/speech.js +19 -17
  408. package/dist/speech.js.map +1 -1
  409. package/dist/sse-writer.cjs +56 -30
  410. package/dist/sse-writer.cjs.map +1 -1
  411. package/dist/sse-writer.d.cts +0 -1
  412. package/dist/sse-writer.d.cts.map +1 -1
  413. package/dist/sse-writer.d.ts +0 -1
  414. package/dist/sse-writer.d.ts.map +1 -1
  415. package/dist/sse-writer.js +55 -31
  416. package/dist/sse-writer.js.map +1 -1
  417. package/dist/stream-collapse.cjs +118 -11
  418. package/dist/stream-collapse.cjs.map +1 -1
  419. package/dist/stream-collapse.d.cts +10 -9
  420. package/dist/stream-collapse.d.cts.map +1 -1
  421. package/dist/stream-collapse.d.ts +10 -9
  422. package/dist/stream-collapse.d.ts.map +1 -1
  423. package/dist/stream-collapse.js +118 -11
  424. package/dist/stream-collapse.js.map +1 -1
  425. package/dist/transcription.cjs +31 -43
  426. package/dist/transcription.cjs.map +1 -1
  427. package/dist/transcription.d.cts.map +1 -1
  428. package/dist/transcription.d.ts.map +1 -1
  429. package/dist/transcription.js +31 -43
  430. package/dist/transcription.js.map +1 -1
  431. package/dist/types.cjs.map +1 -1
  432. package/dist/types.d.cts +231 -9
  433. package/dist/types.d.cts.map +1 -1
  434. package/dist/types.d.ts +231 -9
  435. package/dist/types.d.ts.map +1 -1
  436. package/dist/types.js.map +1 -1
  437. package/dist/vector-mock.cjs +3 -8
  438. package/dist/vector-mock.cjs.map +1 -1
  439. package/dist/vector-mock.d.cts.map +1 -1
  440. package/dist/vector-mock.d.ts.map +1 -1
  441. package/dist/vector-mock.js +3 -8
  442. package/dist/vector-mock.js.map +1 -1
  443. package/dist/vector-stores.cjs +2 -4
  444. package/dist/vector-stores.cjs.map +1 -1
  445. package/dist/vector-stores.d.cts.map +1 -1
  446. package/dist/vector-stores.d.ts.map +1 -1
  447. package/dist/vector-stores.js +2 -4
  448. package/dist/vector-stores.js.map +1 -1
  449. package/dist/veo-video.cjs +16 -14
  450. package/dist/veo-video.cjs.map +1 -1
  451. package/dist/veo-video.js +10 -8
  452. package/dist/veo-video.js.map +1 -1
  453. package/dist/video.cjs +21 -19
  454. package/dist/video.cjs.map +1 -1
  455. package/dist/video.d.cts.map +1 -1
  456. package/dist/video.d.ts.map +1 -1
  457. package/dist/video.js +20 -18
  458. package/dist/video.js.map +1 -1
  459. package/dist/ws-framing.cjs +12 -6
  460. package/dist/ws-framing.cjs.map +1 -1
  461. package/dist/ws-framing.d.cts +2 -0
  462. package/dist/ws-framing.d.cts.map +1 -1
  463. package/dist/ws-framing.d.ts +2 -0
  464. package/dist/ws-framing.d.ts.map +1 -1
  465. package/dist/ws-framing.js +12 -6
  466. package/dist/ws-framing.js.map +1 -1
  467. package/dist/ws-gemini-live.cjs +131 -96
  468. package/dist/ws-gemini-live.cjs.map +1 -1
  469. package/dist/ws-gemini-live.d.cts +0 -1
  470. package/dist/ws-gemini-live.d.cts.map +1 -1
  471. package/dist/ws-gemini-live.d.ts +0 -1
  472. package/dist/ws-gemini-live.d.ts.map +1 -1
  473. package/dist/ws-gemini-live.js +130 -95
  474. package/dist/ws-gemini-live.js.map +1 -1
  475. package/dist/ws-live.cjs +6 -5
  476. package/dist/ws-live.cjs.map +1 -1
  477. package/dist/ws-live.js +5 -4
  478. package/dist/ws-live.js.map +1 -1
  479. package/dist/ws-realtime.cjs +108 -124
  480. package/dist/ws-realtime.cjs.map +1 -1
  481. package/dist/ws-realtime.d.cts.map +1 -1
  482. package/dist/ws-realtime.d.ts.map +1 -1
  483. package/dist/ws-realtime.js +105 -121
  484. package/dist/ws-realtime.js.map +1 -1
  485. package/dist/ws-responses.cjs +80 -59
  486. package/dist/ws-responses.cjs.map +1 -1
  487. package/dist/ws-responses.d.cts.map +1 -1
  488. package/dist/ws-responses.d.ts.map +1 -1
  489. package/dist/ws-responses.js +79 -58
  490. package/dist/ws-responses.js.map +1 -1
  491. package/package.json +4 -1
  492. package/skills/write-fixtures/SKILL.md +57 -16
@@ -1 +1 @@
1
- {"version":3,"file":"ws-responses.js","names":[],"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":"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"}
1
+ {"version":3,"file":"ws-responses.js","names":[],"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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/aimock",
3
- "version": "1.45.1",
3
+ "version": "1.47.0",
4
4
  "description": "Mock infrastructure for AI application testing — point your SDK at one local port and every provider, protocol, and service answers deterministically.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -167,6 +167,8 @@
167
167
  "test": "vitest run",
168
168
  "test:drift": "vitest run --config vitest.config.drift.ts",
169
169
  "test:exports": "publint && attw --pack .",
170
+ "golden:capture": "tsx scripts/capture-golden.ts",
171
+ "api:report": "tsx scripts/api-report.ts",
170
172
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit && tsc -p scripts/tsconfig.json --noEmit",
171
173
  "lint": "eslint .",
172
174
  "format:check": "prettier --check .",
@@ -198,6 +200,7 @@
198
200
  "@commitlint/config-conventional": "^19.8.0",
199
201
  "@eslint/js": "^9.30.0",
200
202
  "@google/genai": "^1.0.0",
203
+ "@microsoft/api-extractor": "7.59.4",
201
204
  "@modelcontextprotocol/client": "2.2.0",
202
205
  "@modelcontextprotocol/sdk": "1.31.0",
203
206
  "@modelcontextprotocol/server-everything": "2026.8.31",
@@ -28,6 +28,7 @@ aimock is a zero-dependency mock infrastructure for AI apps. Fixture-driven. Mul
28
28
  | `systemMessage` | `RegExp` | Pattern test on the concatenated system-message text |
29
29
  | `inputText` | `string` | Substring of embedding input text (concatenated if multiple inputs) |
30
30
  | `inputText` | `RegExp` | Pattern test on embedding input text |
31
+ | `embeddingRequest` | `{ operation, input, dimensions? }` | Exact operation (`/api/embed`, `/api/embeddings` or `/v1/embeddings`), input shape and requested dimensions. Recordings set it alongside `inputText` and `model` so batches and widths stay distinct; omit it for broad embedding fixtures |
31
32
  | `toolName` | `string` | Exact match on any tool in request's `tools[]` array (by `function.name`). On the OpenAI Responses API, with `responsesTools: "extended"`, this also includes tools inside a `namespace` tool, `custom` tools (by `name`, in `req.customTools`), and tools in an `additional_tools` or `tool_search_output` input item; by default only top-level function tools, as in 1.44.0. Other Responses tool types (`web_search`, `mcp`, ...) are not visible |
32
33
  | `toolNamespace` | `string` | Exact OpenAI Responses tool namespace; needs `responsesTools: "extended"` (ignored otherwise). Alone: any offered tool sits in it. With `toolName`: one offered tool must carry both (Codex routes by this exact pair). No default namespace is assumed. Chat Completions passes request tools through, so a client-sent top-level `namespace` there also matches. With `"extended"` (`aimock validate --responses-tools extended`), `aimock validate` and `--validate-on-load` warn about a file fixture with `toolNamespace` and an `endpoint` other than `"chat"`. A Responses `namespace` tool with an empty or missing `name` is dropped. Codex names an MCP server's namespace `mcp__<server>` (no trailing `__`). aimock-pytest needs aimock 1.45.0+, its default pin from 0.7.0 |
33
34
  | `toolCallId` | `string` | Exact match on `tool_call_id` of last `role: "tool"` message |
@@ -197,6 +198,38 @@ Video uses async polling — `POST /v1/videos` creates, `GET /v1/videos/{id}` ch
197
198
  { error: { message: "Rate limited", type: "rate_limit_error" }, status: 429 }
198
199
  ```
199
200
 
201
+ ### Raw passthrough (provider events as written)
202
+
203
+ Prefer the typed responses above: they work on every provider and are drift-checked. Use `raw` only for an event, field or error shape aimock does not model yet (for example a new Responses event type). aimock sends `events` (streaming) or `json` (non-streaming) exactly as written and adds only the named wire's framing.
204
+
205
+ ```typescript
206
+ {
207
+ raw: {
208
+ wire: "openai-responses", // required; the fixture matches only this wire
209
+ events: [
210
+ {
211
+ type: "response.output_text.annotation.added",
212
+ annotation: { type: "url_citation", url: "https://example.com" },
213
+ },
214
+ ],
215
+ json: { id: "resp_1", object: "response", status: "completed", output: [] },
216
+ status: 200, // optional, 200-599, not 204/304
217
+ headers: { "x-request-id": "req_1" }, // optional; no content-length/transfer-encoding/connection/content-encoding
218
+ },
219
+ }
220
+ ```
221
+
222
+ - `wire` is one of `openai-responses`, `openai-chat`, `anthropic`, `gemini`, `gemini-interactions`, `cohere`, `ollama` (`/api/chat`), `ollama-generate` (`/api/generate`), `bedrock-invoke`, `bedrock-converse`. The router skips a raw fixture on every other wire and keeps matching; only a factory that returns a raw fixture for another wire gets a 500.
223
+ - Events on `openai-responses`, `anthropic` and `cohere` need a string `type`; a `bedrock-converse` event is `{ "<eventType>": { …payload } }`.
224
+ - `done: false` (openai-chat only, a boolean) omits `data: [DONE]`.
225
+ - Unless `headers` sets `Content-Type`, a stream gets the typed stream's default headers for its wire (`text/event-stream`, `application/x-ndjson` on Ollama, `application/vnd.amazon.eventstream` on Bedrock) and a non-streaming reply gets `application/json` on every wire. An authored header replaces the default of the same name in any letter case.
226
+ - An error reply (`status` >= 400) is never delayed, streaming or not. With `json`, it answers JSON even to a streaming request.
227
+ - A request whose mode has no body (`events` for streaming, `json` for non-streaming) gets the wire's 500 with `aimock_raw_mode_unavailable`. An invalid raw fixture registered with `on()` or a factory is not checked when added; it gets the same 500 with `aimock_raw_fixture_invalid`, as does a factory result for another wire. Every raw fixture is checked again when it is served. `--validate-on-load` reports invalid fixtures at load. Also invalid: events that are not plain JSON (holes, `undefined`, non-finite numbers, Dates, cycles), a CR or LF in a `type`, `customToolCalls` or `responsesBlocks` next to `raw`, and a raw fixture with its own `misbehavior`. Unknown keys inside `raw` are a warning.
228
+ - `chunkSize` does not apply (each event is one frame). Misbehavior faults skip raw fixtures: a server-level fault is journaled as skipped, an `X-AIMock-Misbehavior` header returns 501 `aimock_misbehavior_not_applicable`, and a fixture's own `misbehavior` makes it invalid (load error, 500 `aimock_raw_fixture_invalid`). Streaming timing, `truncateAfterChunks`, `disconnectAfterMs` and chaos apply.
229
+ - Non-streaming timing: `nonStreamingTiming: true` delays a raw `stream: false` reply on `openai-chat` only; `nonStreamingTimingAllProviders: true` on every raw wire. Either needs `streamingProfile` or `recordedTimings`; the wait is sized from the served JSON.
230
+ - OpenRouter `models` fallback: an `openai-chat` raw fixture with `status` >= 400 fails over like a typed error fixture.
231
+ - Not served over WebSocket Responses yet (error event with code `aimock_raw_mode_unavailable`). Docs: `/raw-passthrough`.
232
+
200
233
  ### Chaos (Failure Injection)
201
234
 
202
235
  The optional `chaos` field on a fixture enables probabilistic failure injection:
@@ -533,6 +566,7 @@ All providers share the same fixture pool — write fixtures once, they work for
533
566
  | `POST /v1/chat/completions` | OpenAI | HTTP |
534
567
  | `POST /v1/responses` | OpenAI | HTTP + WS |
535
568
  | `POST /v1/messages` | Anthropic | HTTP |
569
+ | `POST /v1/messages/count_tokens` | Anthropic | HTTP |
536
570
  | `POST /v1/embeddings` | OpenAI | HTTP |
537
571
  | `POST /v1beta/models/{model}:{method}` | Google Gemini | HTTP |
538
572
  | `POST /model/{modelId}/invoke` | AWS Bedrock | HTTP |
@@ -548,11 +582,13 @@ All providers share the same fixture pool — write fixtures once, they work for
548
582
  | `POST /api/chat` | Ollama | HTTP |
549
583
  | `POST /api/generate` | Ollama | HTTP |
550
584
  | `GET /api/tags` | Ollama | HTTP |
585
+ | `POST /api/embed` · `/api/embeddings` | Ollama | HTTP |
551
586
  | `POST /v2/chat` | Cohere | HTTP |
552
587
  | `POST /api/v1/chat/completions` | OpenRouter | HTTP |
553
588
  | `GET /api/v1/models` · `/api/v1/key` · `/api/v1/credits` | OpenRouter | HTTP |
554
589
  | `GET /metrics` | — | HTTP |
555
- | `GET /v1/models` | OpenAI-compat | HTTP |
590
+ | `GET /v1/models` · `/v1/models/{id}` | OpenAI-compat | HTTP |
591
+ | `GET /v1beta/models` · `/v1beta/models/{model}` | Google Gemini | HTTP |
556
592
  | `WS /v1/responses` | OpenAI | WebSocket |
557
593
  | `WS /v1/realtime` | OpenAI | WebSocket |
558
594
  | `WS /ws/google.ai...BidiGenerateContent` | Gemini Live | WebSocket |
@@ -563,23 +599,28 @@ All providers share the same fixture pool — write fixtures once, they work for
563
599
  | `POST /v1/videos` | OpenAI | HTTP |
564
600
  | `GET /v1/videos/{id}` | OpenAI | HTTP |
565
601
 
602
+ `GET /v1/models` returns the OpenAI-format list by default. With `policy.modelCatalog: "native"` (or `--policy modelCatalog=native`, or the `X-AIMock-Policy: modelCatalog=native` header), it returns the Anthropic list (native paging, `after_id` / `before_id`) when the request has `anthropic-version`, or `x-api-key` without `Authorization`; other callers still get the OpenAI format. `GET /v1/models/{id}` uses the same header selection without the opt-in. Catalogs list the fixture model IDs. Catalog and count routes (`count_tokens`, Gemini `:countTokens`) never proxy or record. Counts are a deterministic estimate of about 1 token per 4 characters of messages (plus tool definitions), minimum 1, and ignore fixture `usage`.
603
+
566
604
  ## Response Template Overrides
567
605
 
568
606
  Fixture responses can include optional override fields to control auto-generated envelope values. These are merged into the provider-specific response format (OpenAI, Claude, Gemini, Responses API).
569
607
 
570
- | Field | Type | Default | Description |
571
- | -------------------- | ------ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
572
- | `id` | string | auto-generated | Override response ID (e.g., `chatcmpl-custom`) |
573
- | `created` | number | `Date.now()/1000` | Override Unix timestamp |
574
- | `model` | string | echoes request | Override model name in response |
575
- | `usage` | object | zeroed | Override token counts: `{ prompt_tokens, completion_tokens, total_tokens }`. OpenAI Chat includes usage in response body; Responses API uses `response.usage`. When omitted, auto-computed from content length |
576
- | `finishReason` | string | `"stop"` / `"tool_calls"` | Override finish reason. Mappings: `stop` -> `end_turn` (Claude), `STOP` (Gemini); `tool_calls` -> `tool_use` (Claude), `FUNCTION_CALL` (Gemini); `length` -> `max_tokens` (Claude), `MAX_TOKENS` (Gemini); `content_filter` -> `SAFETY` (Gemini), `failed` (Responses API) |
577
- | `role` | string | `"assistant"` | Override message role |
578
- | `systemFingerprint` | string | (omitted) | Add `system_fingerprint` to response |
579
- | `provider` | string | slug author | OpenRouter only: top-level serving-provider display name (default = the winning model slug's author). Override to assert who served the request |
580
- | `nativeFinishReason` | string | mirrors `finishReason` | OpenRouter only: the raw upstream `native_finish_reason` alongside the normalized `finish_reason` |
581
- | `usage.cost` | number | (omitted) | OpenRouter only: per-request `usage.cost` (scriptable — powers budget-guard tests). When set, `usage.cost_details` is emitted too. Never fabricated when omitted |
582
- | `usage.is_byok` | bool | (omitted) | OpenRouter only: emit `usage.is_byok`. Also `usage.prompt_tokens_details`, `usage.completion_tokens_details` — emitted only when set |
608
+ | Field | Type | Default | Description |
609
+ | -------------------- | -------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
610
+ | `id` | string | auto-generated | Override response ID (e.g., `chatcmpl-custom`) |
611
+ | `created` | number | `Date.now()/1000` | Override Unix timestamp |
612
+ | `model` | string | echoes request | Override model name in response |
613
+ | `usage` | object | zeroed | Override token counts: `{ prompt_tokens, completion_tokens, total_tokens }`. OpenAI Chat includes usage in response body; Responses API uses `response.usage`. When omitted, auto-computed from content length |
614
+ | `finishReason` | string | `"stop"` / `"tool_calls"` | Override finish reason. Mappings: `stop` -> `end_turn` (Claude), `STOP` (Gemini); `tool_calls` -> `tool_use` (Claude), `FUNCTION_CALL` (Gemini); `length` -> `max_tokens` (Claude), `MAX_TOKENS` (Gemini); `content_filter` -> `SAFETY` (Gemini), `failed` (Responses API) |
615
+ | `role` | string | `"assistant"` | Override message role |
616
+ | `phase` | `"commentary" \| "final_answer"` | (omitted) | OpenAI Responses API (HTTP and WebSocket) only: labels each assistant `message` output item. Preserved in streaming added/done items, the completed response, and non-streaming output. Omitted by default; never inferred from content or finish reason. Does not label reasoning or tool-call items |
617
+ | `systemFingerprint` | string | (omitted) | Add `system_fingerprint` to response |
618
+ | `provider` | string | slug author | OpenRouter only: top-level serving-provider display name (default = the winning model slug's author). Override to assert who served the request |
619
+ | `nativeFinishReason` | string | mirrors `finishReason` | OpenRouter only: the raw upstream `native_finish_reason` alongside the normalized `finish_reason` |
620
+ | `usage.cost` | number | (omitted) | OpenRouter only: per-request `usage.cost` (scriptable — powers budget-guard tests). When set, `usage.cost_details` is emitted too. Never fabricated when omitted |
621
+ | `usage.is_byok` | bool | (omitted) | OpenRouter only: emit `usage.is_byok`. Also `usage.prompt_tokens_details`, `usage.completion_tokens_details` — emitted only when set |
622
+
623
+ Claude Messages and Bedrock Claude invoke also emit `usage.cache_read_input_tokens`, `usage.cache_creation_input_tokens` and a `usage.cache_creation` object (`ephemeral_5m_input_tokens`, `ephemeral_1h_input_tokens`) when a fixture supplies them.
583
624
 
584
625
  ### Example
585
626
 
@@ -657,9 +698,9 @@ Custom tool calls (`customToolCalls` / `customToolCall` blocks in `responsesBloc
657
698
 
658
699
  2. **`arguments` accepts both objects and strings** — `"arguments": {"key":"value"}` (preferred, auto-stringified) or `"arguments": "{\"key\":\"value\"}"` (legacy). The same applies to `content` fields that contain JSON. The fixture loader detects `typeof === "object"` and calls `JSON.stringify()` automatically.
659
700
 
660
- 3. **Latency is per-chunk, not total** — `latency: 100` means 100ms between each SSE chunk, not 100ms total response time. Similarly, `truncateAfterChunks` and `disconnectAfterMs` are for simulating stream interruptions (added in v1.3.0).
701
+ 3. **Latency is per-chunk, not total** — `latency: 100` means 100ms between each SSE chunk, not 100ms total response time. It never delays a non-streaming (`stream: false`) response, also with `nonStreamingTiming: true` or `nonStreamingTimingAllProviders: true`. Similarly, `truncateAfterChunks` and `disconnectAfterMs` are for simulating stream interruptions (added in v1.3.0).
661
702
 
662
- 4. **`streamingProfile` takes precedence over `latency`** — when both are set on a fixture, `streamingProfile` controls timing. Use one or the other.
703
+ 4. **`streamingProfile` takes precedence over `latency`** — when both are set on a fixture, `streamingProfile` controls timing. Use one or the other. By default, `streamingProfile` and `recordedTimings` delay only streamed replies: a `stream: false` reply returns immediately, also for recorded fixtures. Set `nonStreamingTiming: true` on the fixture to also delay `stream: false` OpenAI Chat Completions replies (text, tool calls, combined content and tool calls, and `blocks`) by the sum of the per-chunk delays a streamed reply at the same `chunkSize` would get. `nonStreamingTiming` does not delay other providers. To delay `stream: false` replies with the same formula on OpenAI Chat Completions, Anthropic Messages, OpenAI Responses, Gemini and Vertex AI `generateContent`, Gemini Interactions (`stream: false`), Cohere v2 chat, Bedrock InvokeModel and Converse, and Ollama `/api/chat` and `/api/generate`, set `nonStreamingTimingAllProviders: true` instead. Audio transcription replies are not delayed. Error replies are never delayed. If the client disconnects during the wait, nothing is written and the journal entry gets `interrupted: true`, `interruptReason: "client aborted"` and `status: 0`.
663
704
 
664
705
  5. **Tool result messages don't change the user message** — after a tool call, the client sends the same conversation + tool result. Matching on `userMessage` will hit the SAME fixture again → infinite loop. Always use `predicate` checking `role === "tool"` for tool results. Note: a whole-conversation `role === "tool"` check (e.g. `req.messages.some((m) => m.role === "tool")`) diverges from `hasToolResult`'s current-turn scoping in multi-turn flows — the built-in `hasToolResult` matcher only looks after the last user message, so a later turn whose history carries an earlier tool result still reads `false`.
665
706