@copilotkit/aimock 1.45.1 → 1.46.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 (425) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +13 -0
  4. package/README.md +7 -1
  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/batches.cjs +2 -4
  25. package/dist/batches.cjs.map +1 -1
  26. package/dist/batches.d.cts.map +1 -1
  27. package/dist/batches.d.ts.map +1 -1
  28. package/dist/batches.js +2 -4
  29. package/dist/batches.js.map +1 -1
  30. package/dist/bedrock-converse.cjs +61 -80
  31. package/dist/bedrock-converse.cjs.map +1 -1
  32. package/dist/bedrock-converse.d.cts.map +1 -1
  33. package/dist/bedrock-converse.d.ts.map +1 -1
  34. package/dist/bedrock-converse.js +58 -77
  35. package/dist/bedrock-converse.js.map +1 -1
  36. package/dist/bedrock.cjs +65 -97
  37. package/dist/bedrock.cjs.map +1 -1
  38. package/dist/bedrock.d.cts.map +1 -1
  39. package/dist/bedrock.d.ts.map +1 -1
  40. package/dist/bedrock.js +62 -94
  41. package/dist/bedrock.js.map +1 -1
  42. package/dist/byteplus-video.cjs +11 -10
  43. package/dist/byteplus-video.cjs.map +1 -1
  44. package/dist/byteplus-video.d.cts.map +1 -1
  45. package/dist/byteplus-video.d.ts.map +1 -1
  46. package/dist/byteplus-video.js +5 -4
  47. package/dist/byteplus-video.js.map +1 -1
  48. package/dist/chaos.cjs +5 -4
  49. package/dist/chaos.cjs.map +1 -1
  50. package/dist/chaos.d.cts.map +1 -1
  51. package/dist/chaos.d.ts.map +1 -1
  52. package/dist/chaos.js +5 -4
  53. package/dist/chaos.js.map +1 -1
  54. package/dist/cli.cjs +15 -0
  55. package/dist/cli.cjs.map +1 -1
  56. package/dist/cli.js +15 -0
  57. package/dist/cli.js.map +1 -1
  58. package/dist/cohere.cjs +49 -74
  59. package/dist/cohere.cjs.map +1 -1
  60. package/dist/cohere.d.cts.map +1 -1
  61. package/dist/cohere.d.ts.map +1 -1
  62. package/dist/cohere.js +47 -72
  63. package/dist/cohere.js.map +1 -1
  64. package/dist/config-loader.cjs +29 -1
  65. package/dist/config-loader.cjs.map +1 -1
  66. package/dist/config-loader.d.cts +7 -1
  67. package/dist/config-loader.d.cts.map +1 -1
  68. package/dist/config-loader.d.ts +7 -1
  69. package/dist/config-loader.d.ts.map +1 -1
  70. package/dist/config-loader.js +29 -2
  71. package/dist/config-loader.js.map +1 -1
  72. package/dist/count-tokens.cjs +146 -0
  73. package/dist/count-tokens.cjs.map +1 -0
  74. package/dist/count-tokens.js +146 -0
  75. package/dist/count-tokens.js.map +1 -0
  76. package/dist/elevenlabs-audio.cjs +36 -34
  77. package/dist/elevenlabs-audio.cjs.map +1 -1
  78. package/dist/elevenlabs-audio.d.cts.map +1 -1
  79. package/dist/elevenlabs-audio.d.ts.map +1 -1
  80. package/dist/elevenlabs-audio.js +33 -31
  81. package/dist/elevenlabs-audio.js.map +1 -1
  82. package/dist/elevenlabs-voice.cjs +38 -37
  83. package/dist/elevenlabs-voice.cjs.map +1 -1
  84. package/dist/elevenlabs-voice.d.cts.map +1 -1
  85. package/dist/elevenlabs-voice.d.ts.map +1 -1
  86. package/dist/elevenlabs-voice.js +33 -32
  87. package/dist/elevenlabs-voice.js.map +1 -1
  88. package/dist/embedding-request.cjs +16 -0
  89. package/dist/embedding-request.cjs.map +1 -0
  90. package/dist/embedding-request.js +14 -0
  91. package/dist/embedding-request.js.map +1 -0
  92. package/dist/embeddings.cjs +30 -20
  93. package/dist/embeddings.cjs.map +1 -1
  94. package/dist/embeddings.d.cts.map +1 -1
  95. package/dist/embeddings.d.ts.map +1 -1
  96. package/dist/embeddings.js +30 -20
  97. package/dist/embeddings.js.map +1 -1
  98. package/dist/fal-audio.cjs +45 -43
  99. package/dist/fal-audio.cjs.map +1 -1
  100. package/dist/fal-audio.d.cts.map +1 -1
  101. package/dist/fal-audio.d.ts.map +1 -1
  102. package/dist/fal-audio.js +42 -40
  103. package/dist/fal-audio.js.map +1 -1
  104. package/dist/fal.cjs +17 -15
  105. package/dist/fal.cjs.map +1 -1
  106. package/dist/fal.js +16 -14
  107. package/dist/fal.js.map +1 -1
  108. package/dist/files.cjs +2 -4
  109. package/dist/files.cjs.map +1 -1
  110. package/dist/files.d.cts.map +1 -1
  111. package/dist/files.d.ts.map +1 -1
  112. package/dist/files.js +2 -4
  113. package/dist/files.js.map +1 -1
  114. package/dist/fine-tuning.cjs +2 -4
  115. package/dist/fine-tuning.cjs.map +1 -1
  116. package/dist/fine-tuning.d.cts.map +1 -1
  117. package/dist/fine-tuning.d.ts.map +1 -1
  118. package/dist/fine-tuning.js +2 -4
  119. package/dist/fine-tuning.js.map +1 -1
  120. package/dist/fixture-loader.cjs +37 -42
  121. package/dist/fixture-loader.cjs.map +1 -1
  122. package/dist/fixture-loader.d.cts.map +1 -1
  123. package/dist/fixture-loader.d.ts.map +1 -1
  124. package/dist/fixture-loader.js +37 -42
  125. package/dist/fixture-loader.js.map +1 -1
  126. package/dist/gemini-embeddings.cjs +26 -24
  127. package/dist/gemini-embeddings.cjs.map +1 -1
  128. package/dist/gemini-embeddings.js +26 -24
  129. package/dist/gemini-embeddings.js.map +1 -1
  130. package/dist/gemini-interactions.cjs +50 -35
  131. package/dist/gemini-interactions.cjs.map +1 -1
  132. package/dist/gemini-interactions.d.cts +0 -1
  133. package/dist/gemini-interactions.d.cts.map +1 -1
  134. package/dist/gemini-interactions.d.ts +0 -1
  135. package/dist/gemini-interactions.d.ts.map +1 -1
  136. package/dist/gemini-interactions.js +49 -34
  137. package/dist/gemini-interactions.js.map +1 -1
  138. package/dist/gemini.cjs +57 -67
  139. package/dist/gemini.cjs.map +1 -1
  140. package/dist/gemini.d.cts.map +1 -1
  141. package/dist/gemini.d.ts.map +1 -1
  142. package/dist/gemini.js +56 -67
  143. package/dist/gemini.js.map +1 -1
  144. package/dist/grok-video.cjs +14 -12
  145. package/dist/grok-video.cjs.map +1 -1
  146. package/dist/grok-video.d.cts.map +1 -1
  147. package/dist/grok-video.d.ts.map +1 -1
  148. package/dist/grok-video.js +8 -6
  149. package/dist/grok-video.js.map +1 -1
  150. package/dist/helpers.cjs +11 -68
  151. package/dist/helpers.cjs.map +1 -1
  152. package/dist/helpers.d.cts.map +1 -1
  153. package/dist/helpers.d.ts.map +1 -1
  154. package/dist/helpers.js +12 -67
  155. package/dist/helpers.js.map +1 -1
  156. package/dist/images.cjs +30 -28
  157. package/dist/images.cjs.map +1 -1
  158. package/dist/images.d.cts.map +1 -1
  159. package/dist/images.d.ts.map +1 -1
  160. package/dist/images.js +27 -25
  161. package/dist/images.js.map +1 -1
  162. package/dist/index.d.cts +2 -2
  163. package/dist/index.d.ts +2 -2
  164. package/dist/journal.cjs +3 -1
  165. package/dist/journal.cjs.map +1 -1
  166. package/dist/journal.d.cts.map +1 -1
  167. package/dist/journal.d.ts.map +1 -1
  168. package/dist/journal.js +2 -1
  169. package/dist/journal.js.map +1 -1
  170. package/dist/llmock.cjs +3 -0
  171. package/dist/llmock.cjs.map +1 -1
  172. package/dist/llmock.d.cts.map +1 -1
  173. package/dist/llmock.d.ts.map +1 -1
  174. package/dist/llmock.js +3 -0
  175. package/dist/llmock.js.map +1 -1
  176. package/dist/mcp-handler.cjs +7 -3
  177. package/dist/mcp-handler.cjs.map +1 -1
  178. package/dist/mcp-handler.js +7 -3
  179. package/dist/mcp-handler.js.map +1 -1
  180. package/dist/mcp-recorder.cjs +2 -1
  181. package/dist/mcp-recorder.cjs.map +1 -1
  182. package/dist/mcp-recorder.js +3 -2
  183. package/dist/mcp-recorder.js.map +1 -1
  184. package/dist/messages.cjs +111 -96
  185. package/dist/messages.cjs.map +1 -1
  186. package/dist/messages.d.cts.map +1 -1
  187. package/dist/messages.d.ts.map +1 -1
  188. package/dist/messages.js +109 -95
  189. package/dist/messages.js.map +1 -1
  190. package/dist/metrics.cjs +5 -2
  191. package/dist/metrics.cjs.map +1 -1
  192. package/dist/metrics.d.cts.map +1 -1
  193. package/dist/metrics.d.ts.map +1 -1
  194. package/dist/metrics.js +6 -3
  195. package/dist/metrics.js.map +1 -1
  196. package/dist/model-utils.cjs +1 -0
  197. package/dist/model-utils.cjs.map +1 -1
  198. package/dist/model-utils.js +1 -1
  199. package/dist/model-utils.js.map +1 -1
  200. package/dist/moderation.cjs +15 -12
  201. package/dist/moderation.cjs.map +1 -1
  202. package/dist/moderation.js +14 -11
  203. package/dist/moderation.js.map +1 -1
  204. package/dist/ollama.cjs +128 -68
  205. package/dist/ollama.cjs.map +1 -1
  206. package/dist/ollama.d.cts.map +1 -1
  207. package/dist/ollama.d.ts.map +1 -1
  208. package/dist/ollama.js +124 -64
  209. package/dist/ollama.js.map +1 -1
  210. package/dist/openapi.cjs +323 -3
  211. package/dist/openapi.cjs.map +1 -1
  212. package/dist/openapi.d.cts.map +1 -1
  213. package/dist/openapi.d.ts.map +1 -1
  214. package/dist/openapi.js +324 -4
  215. package/dist/openapi.js.map +1 -1
  216. package/dist/openrouter-chat.cjs +11 -155
  217. package/dist/openrouter-chat.cjs.map +1 -1
  218. package/dist/openrouter-chat.js +11 -155
  219. package/dist/openrouter-chat.js.map +1 -1
  220. package/dist/openrouter-video.cjs +51 -74
  221. package/dist/openrouter-video.cjs.map +1 -1
  222. package/dist/openrouter-video.d.cts.map +1 -1
  223. package/dist/openrouter-video.d.ts.map +1 -1
  224. package/dist/openrouter-video.js +37 -60
  225. package/dist/openrouter-video.js.map +1 -1
  226. package/dist/recorder.cjs +176 -95
  227. package/dist/recorder.cjs.map +1 -1
  228. package/dist/recorder.d.cts.map +1 -1
  229. package/dist/recorder.d.ts.map +1 -1
  230. package/dist/recorder.js +176 -95
  231. package/dist/recorder.js.map +1 -1
  232. package/dist/rerank.cjs +13 -11
  233. package/dist/rerank.cjs.map +1 -1
  234. package/dist/rerank.js +12 -10
  235. package/dist/rerank.js.map +1 -1
  236. package/dist/response/catalog/anthropic-page.cjs +39 -0
  237. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  238. package/dist/response/catalog/anthropic-page.js +38 -0
  239. package/dist/response/catalog/anthropic-page.js.map +1 -0
  240. package/dist/response/catalog/collect.cjs +61 -0
  241. package/dist/response/catalog/collect.cjs.map +1 -0
  242. package/dist/response/catalog/collect.js +59 -0
  243. package/dist/response/catalog/collect.js.map +1 -0
  244. package/dist/response/catalog/gemini-page.cjs +25 -0
  245. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  246. package/dist/response/catalog/gemini-page.js +24 -0
  247. package/dist/response/catalog/gemini-page.js.map +1 -0
  248. package/dist/response/catalog/render.cjs +182 -0
  249. package/dist/response/catalog/render.cjs.map +1 -0
  250. package/dist/response/catalog/render.js +171 -0
  251. package/dist/response/catalog/render.js.map +1 -0
  252. package/dist/response/errors.cjs +225 -0
  253. package/dist/response/errors.cjs.map +1 -0
  254. package/dist/response/errors.js +224 -0
  255. package/dist/response/errors.js.map +1 -0
  256. package/dist/response/finish.cjs +39 -0
  257. package/dist/response/finish.cjs.map +1 -0
  258. package/dist/response/finish.js +35 -0
  259. package/dist/response/finish.js.map +1 -0
  260. package/dist/response/policy.cjs +126 -0
  261. package/dist/response/policy.cjs.map +1 -0
  262. package/dist/response/policy.js +120 -0
  263. package/dist/response/policy.js.map +1 -0
  264. package/dist/response/profiles.cjs +69 -0
  265. package/dist/response/profiles.cjs.map +1 -0
  266. package/dist/response/profiles.js +65 -0
  267. package/dist/response/profiles.js.map +1 -0
  268. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  269. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  270. package/dist/response/usage/codecs/anthropic.js +50 -0
  271. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  272. package/dist/response/usage/codecs/cohere.cjs +36 -0
  273. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  274. package/dist/response/usage/codecs/cohere.js +35 -0
  275. package/dist/response/usage/codecs/cohere.js.map +1 -0
  276. package/dist/response/usage/codecs/converse.cjs +16 -0
  277. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  278. package/dist/response/usage/codecs/converse.js +15 -0
  279. package/dist/response/usage/codecs/converse.js.map +1 -0
  280. package/dist/response/usage/codecs/gemini.cjs +31 -0
  281. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  282. package/dist/response/usage/codecs/gemini.js +31 -0
  283. package/dist/response/usage/codecs/gemini.js.map +1 -0
  284. package/dist/response/usage/codecs/interactions.cjs +22 -0
  285. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  286. package/dist/response/usage/codecs/interactions.js +21 -0
  287. package/dist/response/usage/codecs/interactions.js.map +1 -0
  288. package/dist/response/usage/codecs/ollama.cjs +17 -0
  289. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  290. package/dist/response/usage/codecs/ollama.js +17 -0
  291. package/dist/response/usage/codecs/ollama.js.map +1 -0
  292. package/dist/response/usage/codecs/openai.cjs +52 -0
  293. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  294. package/dist/response/usage/codecs/openai.js +50 -0
  295. package/dist/response/usage/codecs/openai.js.map +1 -0
  296. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  297. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  298. package/dist/response/usage/codecs/openrouter.js +67 -0
  299. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  300. package/dist/response/usage/codecs/realtime.cjs +19 -0
  301. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  302. package/dist/response/usage/codecs/realtime.js +18 -0
  303. package/dist/response/usage/codecs/realtime.js.map +1 -0
  304. package/dist/response/usage/codecs/responses.cjs +21 -0
  305. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  306. package/dist/response/usage/codecs/responses.js +20 -0
  307. package/dist/response/usage/codecs/responses.js.map +1 -0
  308. package/dist/response/usage/estimate.cjs +23 -0
  309. package/dist/response/usage/estimate.cjs.map +1 -0
  310. package/dist/response/usage/estimate.js +21 -0
  311. package/dist/response/usage/estimate.js.map +1 -0
  312. package/dist/response/usage/fields.cjs +72 -0
  313. package/dist/response/usage/fields.cjs.map +1 -0
  314. package/dist/response/usage/fields.js +70 -0
  315. package/dist/response/usage/fields.js.map +1 -0
  316. package/dist/response/usage/recording.cjs +52 -0
  317. package/dist/response/usage/recording.cjs.map +1 -0
  318. package/dist/response/usage/recording.js +52 -0
  319. package/dist/response/usage/recording.js.map +1 -0
  320. package/dist/responses.cjs +45 -58
  321. package/dist/responses.cjs.map +1 -1
  322. package/dist/responses.d.cts.map +1 -1
  323. package/dist/responses.d.ts.map +1 -1
  324. package/dist/responses.js +44 -57
  325. package/dist/responses.js.map +1 -1
  326. package/dist/route-registry.cjs +45 -0
  327. package/dist/route-registry.cjs.map +1 -1
  328. package/dist/route-registry.d.cts.map +1 -1
  329. package/dist/route-registry.d.ts.map +1 -1
  330. package/dist/route-registry.js +41 -1
  331. package/dist/route-registry.js.map +1 -1
  332. package/dist/router.cjs +4 -0
  333. package/dist/router.cjs.map +1 -1
  334. package/dist/router.d.cts.map +1 -1
  335. package/dist/router.d.ts.map +1 -1
  336. package/dist/router.js +3 -0
  337. package/dist/router.js.map +1 -1
  338. package/dist/search.cjs +13 -11
  339. package/dist/search.cjs.map +1 -1
  340. package/dist/search.js +12 -10
  341. package/dist/search.js.map +1 -1
  342. package/dist/server.cjs +222 -146
  343. package/dist/server.cjs.map +1 -1
  344. package/dist/server.d.cts.map +1 -1
  345. package/dist/server.d.ts.map +1 -1
  346. package/dist/server.js +223 -147
  347. package/dist/server.js.map +1 -1
  348. package/dist/speech.cjs +20 -18
  349. package/dist/speech.cjs.map +1 -1
  350. package/dist/speech.d.cts.map +1 -1
  351. package/dist/speech.d.ts.map +1 -1
  352. package/dist/speech.js +19 -17
  353. package/dist/speech.js.map +1 -1
  354. package/dist/sse-writer.cjs +7 -0
  355. package/dist/sse-writer.cjs.map +1 -1
  356. package/dist/sse-writer.d.cts.map +1 -1
  357. package/dist/sse-writer.d.ts.map +1 -1
  358. package/dist/sse-writer.js +7 -1
  359. package/dist/sse-writer.js.map +1 -1
  360. package/dist/stream-collapse.cjs +118 -11
  361. package/dist/stream-collapse.cjs.map +1 -1
  362. package/dist/stream-collapse.d.cts +10 -9
  363. package/dist/stream-collapse.d.cts.map +1 -1
  364. package/dist/stream-collapse.d.ts +10 -9
  365. package/dist/stream-collapse.d.ts.map +1 -1
  366. package/dist/stream-collapse.js +118 -11
  367. package/dist/stream-collapse.js.map +1 -1
  368. package/dist/transcription.cjs +12 -10
  369. package/dist/transcription.cjs.map +1 -1
  370. package/dist/transcription.d.cts.map +1 -1
  371. package/dist/transcription.d.ts.map +1 -1
  372. package/dist/transcription.js +11 -9
  373. package/dist/transcription.js.map +1 -1
  374. package/dist/types.cjs.map +1 -1
  375. package/dist/types.d.cts +62 -3
  376. package/dist/types.d.cts.map +1 -1
  377. package/dist/types.d.ts +62 -3
  378. package/dist/types.d.ts.map +1 -1
  379. package/dist/types.js.map +1 -1
  380. package/dist/vector-mock.cjs +3 -8
  381. package/dist/vector-mock.cjs.map +1 -1
  382. package/dist/vector-mock.d.cts.map +1 -1
  383. package/dist/vector-mock.d.ts.map +1 -1
  384. package/dist/vector-mock.js +3 -8
  385. package/dist/vector-mock.js.map +1 -1
  386. package/dist/vector-stores.cjs +2 -4
  387. package/dist/vector-stores.cjs.map +1 -1
  388. package/dist/vector-stores.d.cts.map +1 -1
  389. package/dist/vector-stores.d.ts.map +1 -1
  390. package/dist/vector-stores.js +2 -4
  391. package/dist/vector-stores.js.map +1 -1
  392. package/dist/veo-video.cjs +16 -14
  393. package/dist/veo-video.cjs.map +1 -1
  394. package/dist/veo-video.js +10 -8
  395. package/dist/veo-video.js.map +1 -1
  396. package/dist/video.cjs +21 -19
  397. package/dist/video.cjs.map +1 -1
  398. package/dist/video.d.cts.map +1 -1
  399. package/dist/video.d.ts.map +1 -1
  400. package/dist/video.js +20 -18
  401. package/dist/video.js.map +1 -1
  402. package/dist/ws-gemini-live.cjs +11 -3
  403. package/dist/ws-gemini-live.cjs.map +1 -1
  404. package/dist/ws-gemini-live.d.cts +0 -1
  405. package/dist/ws-gemini-live.d.cts.map +1 -1
  406. package/dist/ws-gemini-live.d.ts +0 -1
  407. package/dist/ws-gemini-live.d.ts.map +1 -1
  408. package/dist/ws-gemini-live.js +10 -2
  409. package/dist/ws-gemini-live.js.map +1 -1
  410. package/dist/ws-live.cjs +6 -5
  411. package/dist/ws-live.cjs.map +1 -1
  412. package/dist/ws-live.js +5 -4
  413. package/dist/ws-live.js.map +1 -1
  414. package/dist/ws-realtime.cjs +28 -54
  415. package/dist/ws-realtime.cjs.map +1 -1
  416. package/dist/ws-realtime.d.cts.map +1 -1
  417. package/dist/ws-realtime.d.ts.map +1 -1
  418. package/dist/ws-realtime.js +25 -51
  419. package/dist/ws-realtime.js.map +1 -1
  420. package/dist/ws-responses.cjs +8 -6
  421. package/dist/ws-responses.cjs.map +1 -1
  422. package/dist/ws-responses.js +7 -5
  423. package/dist/ws-responses.js.map +1 -1
  424. package/package.json +4 -1
  425. package/skills/write-fixtures/SKILL.md +25 -16
package/dist/recorder.cjs CHANGED
@@ -1,10 +1,13 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ const require_errors = require('./response/errors.cjs');
2
3
  const require_constants = require('./constants.cjs');
3
4
  const require_model_utils = require('./model-utils.cjs');
4
5
  const require_api_key_auth = require('./api-key-auth.cjs');
5
6
  const require_helpers = require('./helpers.cjs');
6
7
  const require_sse_writer = require('./sse-writer.cjs');
8
+ const require_embedding_request = require('./embedding-request.cjs');
7
9
  const require_router = require('./router.cjs');
10
+ const require_recording = require('./response/usage/recording.cjs');
8
11
  const require_stream_collapse = require('./stream-collapse.cjs');
9
12
  const require_url = require('./url.cjs');
10
13
  const require_provider_auth = require('./provider-auth.cjs');
@@ -101,6 +104,7 @@ const STRIP_HEADERS = new Set([
101
104
  "accept-encoding",
102
105
  "x-test-id",
103
106
  "x-aimock-strict",
107
+ "x-aimock-policy",
104
108
  "x-aimock-context"
105
109
  ]);
106
110
  /**
@@ -299,19 +303,19 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
299
303
  } catch (err) {
300
304
  const msg = err instanceof Error ? err.message : String(err);
301
305
  defaults.logger.error(`Invalid upstream URL for provider "${providerKey}": ${upstreamUrl} (${msg})`);
302
- require_sse_writer.writeErrorResponse(res, 502, JSON.stringify({ error: {
306
+ require_sse_writer.writeErrorResponse(res, 502, require_errors.renderError("openai", "transport-failure", {
303
307
  message: `Invalid upstream URL: ${upstreamUrl}`,
304
308
  type: "proxy_error"
305
- } }));
309
+ }).body);
306
310
  return "relayed";
307
311
  }
308
312
  defaults.logger.warn(`NO FIXTURE MATCH — proxying to ${upstreamUrl}${pathname}`);
309
313
  const forwardHeaders = prepareEgressHeaders(req, target, providerKey, record.providerKeys?.[lookupKey]);
310
314
  if (!forwardHeaders) {
311
- require_sse_writer.writeErrorResponse(res, 502, JSON.stringify({ error: {
315
+ require_sse_writer.writeErrorResponse(res, 502, require_errors.renderError("openai", "transport-failure", {
312
316
  message: "No configured provider credential",
313
317
  type: "proxy_error"
314
- } }));
318
+ }).body);
315
319
  return "relayed";
316
320
  }
317
321
  const requestBody = rawBody ?? JSON.stringify(request);
@@ -353,10 +357,10 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
353
357
  defaults.logger.error(`Proxy request failed: ${msg}`);
354
358
  if (!res.headersSent) {
355
359
  res.writeHead(502, { "Content-Type": "application/json" });
356
- res.end(JSON.stringify({ error: {
360
+ res.end(require_errors.renderError("openai", "transport-failure", {
357
361
  message: `Proxy to upstream failed: ${msg}`,
358
362
  type: "proxy_error"
359
- } }));
363
+ }).body);
360
364
  } else res.destroy();
361
365
  return "relayed";
362
366
  }
@@ -366,10 +370,10 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
366
370
  const ceilingMiB = Math.round(PROXY_BUFFER_HARD_CEILING / (1024 * 1024));
367
371
  defaults.logger.error(`Upstream response exceeded the ${ceilingMiB} MiB proxy hard ceiling (saw ${totalBytes} bytes) on a non-progressive body — cannot relay the full body and refusing to relay a truncated one; returning 502`);
368
372
  res.writeHead(502, { "Content-Type": "application/json" });
369
- res.end(JSON.stringify({ error: {
373
+ res.end(require_errors.renderError("openai", "transport-failure", {
370
374
  message: `Upstream response exceeds ${ceilingMiB}MiB proxy ceiling`,
371
375
  type: "proxy_error"
372
- } }));
376
+ }).body);
373
377
  return "relayed";
374
378
  }
375
379
  defaults.logger.warn(`Upstream response exceeded ${capDetail} (saw ${totalBytes} bytes) — relayed to client, recording skipped`);
@@ -401,7 +405,14 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
401
405
  if (collapsed.truncated) defaults.logger.warn("Bedrock EventStream: CRC mismatch — response may be truncated");
402
406
  if (collapsed.droppedChunks && collapsed.droppedChunks > 0) defaults.logger.warn(`${collapsed.droppedChunks} chunk(s) dropped during stream collapse${collapsed.firstDroppedSample ? ` — first: ${collapsed.firstDroppedSample}` : ""}`);
403
407
  if (collapsed.harmonyUnparsed) defaults.logger.warn(`Harmony tokens present but unparseable — content preserved verbatim${collapsed.harmonyNote ? ` (${collapsed.harmonyNote})` : ""}`);
404
- const collapsedUsage = sanitizeRecordedUsage(collapsed.usage);
408
+ const collapsedUsage = require_recording.captureUsage({
409
+ provider: providerKey,
410
+ contentType: ctString,
411
+ operation: request._endpointType,
412
+ family: "final",
413
+ stage: "final",
414
+ observation: collapsed.usage
415
+ });
405
416
  const usageSpread = collapsedUsage ? { usage: collapsedUsage } : {};
406
417
  if (collapsed.transcription) fixtureResponse = { transcription: collapsed.transcription };
407
418
  else if (collapsed.audioB64) {
@@ -500,7 +511,7 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
500
511
  const msg = parseErr instanceof Error ? parseErr.message : "unknown";
501
512
  defaults.logger.warn(`Upstream response is not valid JSON (${msg}) — saving as error fixture`);
502
513
  }
503
- if ((request._endpointType === "fal" || request._endpointType === "elevenlabs-voice-design" || request._endpointType === "elevenlabs-voice" || request._endpointType === "elevenlabs-voice-get" || request._endpointType === "elevenlabs-voice-delete") && parsedResponse !== null) {
514
+ if ((providerKey === "ollama" && new URL(pathname, "http://localhost").pathname === "/api/embed" || request._endpointType === "fal" || request._endpointType === "elevenlabs-voice-design" || request._endpointType === "elevenlabs-voice" || request._endpointType === "elevenlabs-voice-get" || request._endpointType === "elevenlabs-voice-delete") && parsedResponse !== null) {
504
515
  const obj = parsedResponse;
505
516
  if (typeof obj.error === "object" && obj.error !== null && typeof obj.error.message === "string") {
506
517
  const err = obj.error;
@@ -516,7 +527,11 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
516
527
  json: parsedResponse,
517
528
  status: upstreamStatus
518
529
  };
519
- } else fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, defaults.logger, request);
530
+ } else fixtureResponse = buildFixtureResponse(parsedResponse, upstreamStatus, defaults.logger, request, {
531
+ provider: providerKey,
532
+ contentType: ctString,
533
+ operation: request._endpointType
534
+ });
520
535
  }
521
536
  if (clientDisconnected) {
522
537
  if (!sawDone) {
@@ -536,8 +551,15 @@ async function proxyAndRecord(req, res, request, providerKey, pathname, fixtures
536
551
  }
537
552
  const matchRequest = defaults.requestTransform ? defaults.requestTransform(request) : request;
538
553
  const metadata = buildFixtureMetadata(request);
554
+ const embeddingRequest = require_embedding_request.getEmbeddingRequest(request);
539
555
  const fixture = {
540
- match: buildFixtureMatch(matchRequest, defaults.record),
556
+ match: {
557
+ ...buildFixtureMatch(matchRequest, defaults.record),
558
+ ...embeddingRequest && {
559
+ embeddingRequest: structuredClone(embeddingRequest),
560
+ ...matchRequest.model !== void 0 && { model: matchRequest.model }
561
+ }
562
+ },
541
563
  response: fixtureResponse,
542
564
  ...recordedTimings && { recordedTimings },
543
565
  ...metadata && { metadata }
@@ -847,67 +869,10 @@ function toToolCallArguments(raw) {
847
869
  return JSON.stringify(raw);
848
870
  }
849
871
  /**
850
- * `usage` sub-objects that carry numeric breakdowns rather than a scalar. The
851
- * inner fields are NOT allowlisted here on purpose: the recorder keeps every
852
- * finite-numeric inner field (documented or forward-compat), matching the
853
- * load-time validator in fixture-loader.ts, whose index-signature escape hatch
854
- * accepts undocumented numeric inner keys too. A `Set` (own-key membership via
855
- * `.has()`, no prototype-chain walk) mirrors that validator's
856
- * `new Set(Object.keys(...))` classification so anything this recorder writes
857
- * loads cleanly.
858
- */
859
- const USAGE_OBJECT_FIELDS = new Set([
860
- "cost_details",
861
- "prompt_tokens_details",
862
- "completion_tokens_details"
863
- ]);
864
- /**
865
- * Sanitize a provider-reported `usage` object into the fixture
866
- * `response.usage` override shape (#368).
867
- *
868
- * The recorder persists real upstream usage so replay can serve the provider's
869
- * ACTUAL token counts (instead of the `ceil(len/4)` estimate) and OpenRouter's
870
- * `cost` — which previously never survived recording at all. Upstream usage is
871
- * provider-specific and forward-extending, so this passes fields through rather
872
- * than whitelisting them, but only in the shapes the fixture contract accepts:
873
- *
874
- * - numeric scalars (`prompt_tokens`, `cost`, `native_tokens_prompt`, …) — kept
875
- * - `is_byok` boolean — kept
876
- * - the documented breakdown objects — kept, with non-numeric inner fields
877
- * dropped (the load-time validator rejects those, and a fixture the recorder
878
- * writes must always load)
879
- * - anything else (strings, arrays, nulls, unknown objects) — DROPPED, because
880
- * `fixture-loader.ts` errors on a non-numeric extra `usage` key and a recorded
881
- * fixture that fails validation is worse than one missing an exotic field
882
- *
883
- * Returns `undefined` when nothing survives, so the caller omits `usage`
884
- * entirely and keeps pre-#368 fixtures byte-identical.
885
- */
886
- function sanitizeRecordedUsage(raw) {
887
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
888
- const out = {};
889
- for (const [key, val] of Object.entries(raw)) {
890
- if (val === void 0 || val === null) continue;
891
- if (USAGE_OBJECT_FIELDS.has(key)) {
892
- if (typeof val !== "object" || val === null || Array.isArray(val)) continue;
893
- const inner = {};
894
- for (const [innerKey, innerVal] of Object.entries(val)) if (typeof innerVal === "number" && Number.isFinite(innerVal)) inner[innerKey] = innerVal;
895
- if (Object.keys(inner).length > 0) out[key] = inner;
896
- continue;
897
- }
898
- if (key === "is_byok") {
899
- if (typeof val === "boolean") out[key] = val;
900
- continue;
901
- }
902
- if (typeof val === "number" && Number.isFinite(val)) out[key] = val;
903
- }
904
- return Object.keys(out).length > 0 ? out : void 0;
905
- }
906
- /**
907
872
  * Detect the response format from the parsed upstream JSON and convert
908
873
  * it into an aimock FixtureResponse.
909
874
  */
910
- function buildFixtureResponse(parsed, status, logger, request) {
875
+ function buildFixtureResponse(parsed, status, logger, request, captureContext = {}) {
911
876
  if (parsed === null || parsed === void 0) return {
912
877
  error: {
913
878
  message: "Upstream returned non-JSON response",
@@ -961,16 +926,32 @@ function buildFixtureResponse(parsed, status, logger, request) {
961
926
  return { images };
962
927
  }
963
928
  const isTranscriptionRequest = request?._endpointType === "transcription" || request?._endpointType === "translation";
964
- if (typeof obj.text === "string" && (isTranscriptionRequest || obj.task === "transcribe" || obj.language !== void 0 && obj.duration !== void 0) && !("choices" in obj) && !("candidates" in obj) && !("object" in obj) && !("message" in obj) && !("outputs" in obj)) return { transcription: {
965
- text: obj.text,
966
- ...obj.language ? { language: String(obj.language) } : {},
967
- ...Array.isArray(obj.languages) ? { languages: obj.languages.filter((language) => typeof language === "object" && language !== null && typeof language.code === "string").map((language) => ({ code: language.code })) } : {},
968
- ...obj.duration !== void 0 ? { duration: Number(obj.duration) } : {},
969
- ...obj.usage && typeof obj.usage === "object" ? { usage: obj.usage } : {},
970
- ...Array.isArray(obj.words) ? { words: obj.words } : {},
971
- ...Array.isArray(obj.segments) ? { segments: obj.segments } : {}
972
- } };
929
+ if (typeof obj.text === "string" && (isTranscriptionRequest || obj.task === "transcribe" || obj.language !== void 0 && obj.duration !== void 0) && !("choices" in obj) && !("candidates" in obj) && !("object" in obj) && !("message" in obj) && !("outputs" in obj)) {
930
+ const transcriptionUsage = require_recording.captureUsage({
931
+ ...captureContext,
932
+ family: "transcription",
933
+ stage: "nonstream",
934
+ observation: obj
935
+ });
936
+ const transcriptionUsageSpread = transcriptionUsage ? { usage: transcriptionUsage } : {};
937
+ return { transcription: {
938
+ text: obj.text,
939
+ ...obj.language ? { language: String(obj.language) } : {},
940
+ ...Array.isArray(obj.languages) ? { languages: obj.languages.filter((language) => typeof language === "object" && language !== null && typeof language.code === "string").map((language) => ({ code: language.code })) } : {},
941
+ ...obj.duration !== void 0 ? { duration: Number(obj.duration) } : {},
942
+ ...transcriptionUsageSpread,
943
+ ...Array.isArray(obj.words) ? { words: obj.words } : {},
944
+ ...Array.isArray(obj.segments) ? { segments: obj.segments } : {}
945
+ } };
946
+ }
973
947
  if (Array.isArray(obj.outputs) && obj.outputs.length > 0 && !("choices" in obj) && !("content" in obj) && !("candidates" in obj)) {
948
+ const capturedUsage = require_recording.captureUsage({
949
+ ...captureContext,
950
+ family: "interactions",
951
+ stage: "nonstream",
952
+ observation: obj
953
+ });
954
+ const usageSpread = capturedUsage ? { usage: capturedUsage } : {};
974
955
  const outputs = obj.outputs;
975
956
  const fnCallOutputs = outputs.filter((o) => o.type === "function_call");
976
957
  const textOutputs = outputs.filter((o) => o.type === "text" && typeof o.text === "string");
@@ -984,13 +965,23 @@ function buildFixtureResponse(parsed, status, logger, request) {
984
965
  ...o.id ? { id: String(o.id) } : {}
985
966
  }));
986
967
  if (hasContent) return {
968
+ ...usageSpread,
987
969
  content: joinedText,
988
970
  toolCalls
989
971
  };
990
- return { toolCalls };
972
+ return {
973
+ ...usageSpread,
974
+ toolCalls
975
+ };
991
976
  }
992
- if (hasContent) return { content: joinedText };
993
- return { content: "" };
977
+ if (hasContent) return {
978
+ ...usageSpread,
979
+ content: joinedText
980
+ };
981
+ return {
982
+ ...usageSpread,
983
+ content: ""
984
+ };
994
985
  }
995
986
  if (typeof obj.id === "string" && typeof obj.status === "string" && (obj.status === "completed" || obj.status === "in_progress" || obj.status === "failed") && !("choices" in obj) && !("content" in obj) && !("candidates" in obj) && !("message" in obj) && !("data" in obj) && !("object" in obj) && !("outputs" in obj) && !("model" in obj) && !("response" in obj) && !("done" in obj) && !("usage" in obj) && !("error" in obj)) {
996
987
  if (obj.status === "completed" && obj.url) return { video: {
@@ -1010,7 +1001,12 @@ function buildFixtureResponse(parsed, status, logger, request) {
1010
1001
  const hasToolCalls = Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
1011
1002
  const hasContent = typeof message.content === "string" && message.content.length > 0;
1012
1003
  const openaiReasoning = typeof message.reasoning_content === "string" && message.reasoning_content.length > 0 ? message.reasoning_content : typeof message.reasoning === "string" && message.reasoning.length > 0 ? message.reasoning : void 0;
1013
- const openaiUsage = sanitizeRecordedUsage(obj.usage);
1004
+ const openaiUsage = require_recording.captureUsage({
1005
+ ...captureContext,
1006
+ family: "openai",
1007
+ stage: "nonstream",
1008
+ observation: obj
1009
+ });
1014
1010
  const usageSpread = openaiUsage ? { usage: openaiUsage } : {};
1015
1011
  if (hasToolCalls) {
1016
1012
  const toolCalls = message.tool_calls.map((tc) => {
@@ -1046,6 +1042,13 @@ function buildFixtureResponse(parsed, status, logger, request) {
1046
1042
  }
1047
1043
  }
1048
1044
  if (Array.isArray(obj.content) && obj.content.length > 0) {
1045
+ const capturedUsage = require_recording.captureUsage({
1046
+ ...captureContext,
1047
+ family: "anthropic",
1048
+ stage: "nonstream",
1049
+ observation: obj
1050
+ });
1051
+ const usageSpread = capturedUsage ? { usage: capturedUsage } : {};
1049
1052
  const blocks = obj.content;
1050
1053
  const toolUseBlocks = blocks.filter((b) => b.type === "tool_use");
1051
1054
  const textBlocks = blocks.filter((b) => b.type === "text" && typeof b.text === "string");
@@ -1071,6 +1074,7 @@ function buildFixtureResponse(parsed, status, logger, request) {
1071
1074
  ...b.id ? { id: String(b.id) } : {}
1072
1075
  }));
1073
1076
  if (hasContent) return {
1077
+ ...usageSpread,
1074
1078
  content: joinedText,
1075
1079
  toolCalls,
1076
1080
  ...anthropicReasoning ? { reasoning: anthropicReasoning } : {},
@@ -1078,6 +1082,7 @@ function buildFixtureResponse(parsed, status, logger, request) {
1078
1082
  ...redactedThinkingSpread
1079
1083
  };
1080
1084
  return {
1085
+ ...usageSpread,
1081
1086
  toolCalls,
1082
1087
  ...anthropicReasoning ? { reasoning: anthropicReasoning } : {},
1083
1088
  ...reasoningSignatureSpread,
@@ -1085,12 +1090,14 @@ function buildFixtureResponse(parsed, status, logger, request) {
1085
1090
  };
1086
1091
  }
1087
1092
  if (hasContent) return {
1093
+ ...usageSpread,
1088
1094
  content: joinedText,
1089
1095
  ...anthropicReasoning ? { reasoning: anthropicReasoning } : {},
1090
1096
  ...reasoningSignatureSpread,
1091
1097
  ...redactedThinkingSpread
1092
1098
  };
1093
1099
  if (thinkingBlocks.length > 0 || redactedBlocks.length > 0) return {
1100
+ ...usageSpread,
1094
1101
  content: "",
1095
1102
  ...anthropicReasoning ? { reasoning: anthropicReasoning } : {},
1096
1103
  ...reasoningSignatureSpread,
@@ -1100,6 +1107,13 @@ function buildFixtureResponse(parsed, status, logger, request) {
1100
1107
  if (Array.isArray(obj.candidates) && obj.candidates.length > 0) {
1101
1108
  const content = obj.candidates[0].content;
1102
1109
  if (content && Array.isArray(content.parts)) {
1110
+ const capturedUsage = require_recording.captureUsage({
1111
+ ...captureContext,
1112
+ family: "gemini",
1113
+ stage: "nonstream",
1114
+ observation: obj
1115
+ });
1116
+ const usageSpread = capturedUsage ? { usage: capturedUsage } : {};
1103
1117
  const parts = content.parts;
1104
1118
  const audioParts = parts.filter((p) => {
1105
1119
  const inline = p.inlineData;
@@ -1110,10 +1124,13 @@ function buildFixtureResponse(parsed, status, logger, request) {
1110
1124
  });
1111
1125
  if (audioParts.length > 0) {
1112
1126
  const inlineData = audioParts[0].inlineData;
1113
- return { audio: {
1114
- b64Json: String(inlineData.data ?? ""),
1115
- contentType: typeof inlineData.mimeType === "string" && inlineData.mimeType.length > 0 ? inlineData.mimeType : "audio/mpeg"
1116
- } };
1127
+ return {
1128
+ ...usageSpread,
1129
+ audio: {
1130
+ b64Json: String(inlineData.data ?? ""),
1131
+ contentType: typeof inlineData.mimeType === "string" && inlineData.mimeType.length > 0 ? inlineData.mimeType : "audio/mpeg"
1132
+ }
1133
+ };
1117
1134
  }
1118
1135
  const fnCallParts = parts.filter((p) => p.functionCall);
1119
1136
  const textParts = parts.filter((p) => typeof p.text === "string" && !p.thought);
@@ -1131,20 +1148,24 @@ function buildFixtureResponse(parsed, status, logger, request) {
1131
1148
  };
1132
1149
  });
1133
1150
  if (hasContent) return {
1151
+ ...usageSpread,
1134
1152
  content: joinedText,
1135
1153
  toolCalls,
1136
1154
  ...geminiReasoning ? { reasoning: geminiReasoning } : {}
1137
1155
  };
1138
1156
  return {
1157
+ ...usageSpread,
1139
1158
  toolCalls,
1140
1159
  ...geminiReasoning ? { reasoning: geminiReasoning } : {}
1141
1160
  };
1142
1161
  }
1143
1162
  if (hasContent) return {
1163
+ ...usageSpread,
1144
1164
  content: joinedText,
1145
1165
  ...geminiReasoning ? { reasoning: geminiReasoning } : {}
1146
1166
  };
1147
1167
  return {
1168
+ ...usageSpread,
1148
1169
  content: "",
1149
1170
  ...geminiReasoning ? { reasoning: geminiReasoning } : {}
1150
1171
  };
@@ -1153,6 +1174,13 @@ function buildFixtureResponse(parsed, status, logger, request) {
1153
1174
  if (obj.output && typeof obj.output === "object") {
1154
1175
  const msg = obj.output.message;
1155
1176
  if (msg && Array.isArray(msg.content)) {
1177
+ const capturedUsage = require_recording.captureUsage({
1178
+ ...captureContext,
1179
+ family: "bedrock-converse",
1180
+ stage: "nonstream",
1181
+ observation: obj
1182
+ });
1183
+ const usageSpread = capturedUsage ? { usage: capturedUsage } : {};
1156
1184
  const blocks = msg.content;
1157
1185
  const toolUseBlocks = blocks.filter((b) => b.toolUse);
1158
1186
  const textBlocks = blocks.filter((b) => typeof b.text === "string");
@@ -1174,26 +1202,37 @@ function buildFixtureResponse(parsed, status, logger, request) {
1174
1202
  };
1175
1203
  });
1176
1204
  if (hasContent) return {
1205
+ ...usageSpread,
1177
1206
  content: joinedText,
1178
1207
  toolCalls,
1179
1208
  ...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
1180
1209
  };
1181
1210
  return {
1211
+ ...usageSpread,
1182
1212
  toolCalls,
1183
1213
  ...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
1184
1214
  };
1185
1215
  }
1186
1216
  if (hasContent) return {
1217
+ ...usageSpread,
1187
1218
  content: joinedText,
1188
1219
  ...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
1189
1220
  };
1190
1221
  return {
1222
+ ...usageSpread,
1191
1223
  content: "",
1192
1224
  ...bedrockReasoning ? { reasoning: bedrockReasoning } : {}
1193
1225
  };
1194
1226
  }
1195
1227
  }
1196
1228
  if (typeof obj.finish_reason === "string" && obj.message && typeof obj.message === "object" && Array.isArray(obj.message.content)) {
1229
+ const capturedUsage = require_recording.captureUsage({
1230
+ ...captureContext,
1231
+ family: "cohere",
1232
+ stage: "nonstream",
1233
+ observation: obj
1234
+ });
1235
+ const usageSpread = capturedUsage ? { usage: capturedUsage } : {};
1197
1236
  const msg = obj.message;
1198
1237
  const contentBlocks = msg.content;
1199
1238
  const joinedText = contentBlocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => String(b.text ?? "")).join("");
@@ -1207,10 +1246,14 @@ function buildFixtureResponse(parsed, status, logger, request) {
1207
1246
  ...b.id ? { id: String(b.id) } : {}
1208
1247
  }));
1209
1248
  if (hasContent) return {
1249
+ ...usageSpread,
1210
1250
  content: joinedText,
1211
1251
  toolCalls
1212
1252
  };
1213
- return { toolCalls };
1253
+ return {
1254
+ ...usageSpread,
1255
+ toolCalls
1256
+ };
1214
1257
  }
1215
1258
  if (msgToolCalls.length > 0) {
1216
1259
  const toolCalls = msgToolCalls.map((tc) => {
@@ -1222,15 +1265,32 @@ function buildFixtureResponse(parsed, status, logger, request) {
1222
1265
  };
1223
1266
  });
1224
1267
  if (hasContent) return {
1268
+ ...usageSpread,
1225
1269
  content: joinedText,
1226
1270
  toolCalls
1227
1271
  };
1228
- return { toolCalls };
1272
+ return {
1273
+ ...usageSpread,
1274
+ toolCalls
1275
+ };
1229
1276
  }
1230
- if (hasContent) return { content: joinedText };
1231
- return { content: "" };
1277
+ if (hasContent) return {
1278
+ ...usageSpread,
1279
+ content: joinedText
1280
+ };
1281
+ return {
1282
+ ...usageSpread,
1283
+ content: ""
1284
+ };
1232
1285
  }
1233
1286
  if (obj.message && typeof obj.message === "object") {
1287
+ const capturedUsage = require_recording.captureUsage({
1288
+ ...captureContext,
1289
+ family: "ollama",
1290
+ stage: "nonstream",
1291
+ observation: obj
1292
+ });
1293
+ const usageSpread = capturedUsage ? { usage: capturedUsage } : {};
1234
1294
  const msg = obj.message;
1235
1295
  const hasOllamaToolCalls = Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;
1236
1296
  const hasOllamaContent = typeof msg.content === "string" && msg.content.length > 0;
@@ -1243,18 +1303,39 @@ function buildFixtureResponse(parsed, status, logger, request) {
1243
1303
  };
1244
1304
  });
1245
1305
  if (hasOllamaContent) return {
1306
+ ...usageSpread,
1246
1307
  content: msg.content,
1247
1308
  toolCalls
1248
1309
  };
1249
- return { toolCalls };
1310
+ return {
1311
+ ...usageSpread,
1312
+ toolCalls
1313
+ };
1250
1314
  }
1251
- if (hasOllamaContent) return { content: msg.content };
1315
+ if (hasOllamaContent) return {
1316
+ ...usageSpread,
1317
+ content: msg.content
1318
+ };
1252
1319
  if (Array.isArray(msg.content) && msg.content.length > 0) {
1253
1320
  const first = msg.content[0];
1254
- if (typeof first.text === "string") return { content: first.text };
1321
+ if (typeof first.text === "string") return {
1322
+ ...usageSpread,
1323
+ content: first.text
1324
+ };
1255
1325
  }
1256
1326
  }
1257
- if (typeof obj.response === "string" && typeof obj.done === "boolean") return { content: obj.response };
1327
+ if (typeof obj.response === "string" && typeof obj.done === "boolean") {
1328
+ const capturedUsage = require_recording.captureUsage({
1329
+ ...captureContext,
1330
+ family: "ollama",
1331
+ stage: "nonstream",
1332
+ observation: obj
1333
+ });
1334
+ return {
1335
+ ...capturedUsage ? { usage: capturedUsage } : {},
1336
+ content: obj.response
1337
+ };
1338
+ }
1258
1339
  logger?.warn(`Could not detect response format from upstream (status=${status}) — saving as error fixture; top-level keys: [${Object.keys(obj).join(", ")}]`);
1259
1340
  return {
1260
1341
  error: {