@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
@@ -1 +1 @@
1
- {"version":3,"file":"ws-live.js","names":[],"sources":["../src/ws-live.ts"],"sourcesContent":["import { performance } from \"node:perf_hooks\";\nimport type { IncomingHttpHeaders } from \"node:http\";\nimport {\n flattenHeaders,\n isLiveResponse,\n resolveResponse,\n resolveStrictMode,\n strictNoMatchLogLine,\n strictNoMatchMessage,\n strictOverrideField,\n wouldProxyMiss,\n} from \"./helpers.js\";\nimport { normalizeLiveFixture, validateLiveClientEvent } from \"./live-fixture.js\";\nimport { createLiveRecorder, LiveRecorderError } from \"./live-recorder.js\";\nimport { applyProviderAuth } from \"./provider-auth.js\";\nimport { connectUpstreamWebSocket } from \"./ws-upstream.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport {\n clampTimeout,\n DEFAULT_MAX_PROXY_BUFFER_BYTES,\n DEFAULT_MAX_PROXY_BUFFER_FRAMES,\n} from \"./recorder.js\";\nimport { createLiveReplay } from \"./live-replay.js\";\nimport type {\n LiveFailureCategory,\n LiveJson,\n LiveMode,\n LiveObject,\n LiveOptions,\n} from \"./live-types.js\";\nimport type { Journal } from \"./journal.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n HandlerDefaults,\n JournalEntry,\n} from \"./types.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\nexport interface LiveSessionOptions {\n defaults: HandlerDefaults;\n limits: Required<LiveOptions>;\n testId: string;\n headers: IncomingHttpHeaders;\n localApiKeys?: readonly string[];\n onDispose: () => void;\n}\nfunction object(value: LiveJson | undefined): LiveObject | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value) ? value : undefined;\n}\n\n/** Node otherwise turns an overflowing delay into a one-millisecond timeout. */\nfunction scheduleLiveTimeout(callback: () => void, timeoutMs: number): () => void {\n const started = performance.now();\n const onTimeout = () => {\n const remaining = timeoutMs - (performance.now() - started);\n if (remaining > 0) timer = setTimeout(onTimeout, Math.min(remaining, 2_147_483_647));\n else callback();\n };\n let timer = setTimeout(onTimeout, Math.min(timeoutMs, 2_147_483_647));\n return () => clearTimeout(timer);\n}\n\n/** Diagnostic copies never retain executable members or mutate replay operands. */\nfunction liveJournalSnapshot(\n entry: Omit<JournalEntry, \"id\" | \"timestamp\">,\n secretValues: readonly string[],\n): Omit<JournalEntry, \"id\" | \"timestamp\"> {\n const secrets = secretValues.filter((secret) => secret.length > 0);\n if (!secrets.length) return entry;\n const containsSecret = (value: string) => secrets.some((secret) => value.includes(secret));\n // Whole-field redaction avoids creating another secret by joining substrings.\n const marker = containsSecret(\"[REDACTED]\") ? \"\" : \"[REDACTED]\";\n const redact = (value: string) => (containsSecret(value) ? marker : value);\n const ancestors = new Set<object>();\n const copy = (value: unknown): unknown => {\n if (typeof value === \"string\") return redact(value);\n if (typeof value === \"function\" || typeof value === \"symbol\") return undefined;\n if (value === null || typeof value !== \"object\") return value;\n if (ancestors.has(value)) return undefined;\n ancestors.add(value);\n try {\n if (Array.isArray(value)) return value.map(copy);\n // Diagnostics contain data only; omit getters and non-JSON objects.\n if (\n Object.getPrototypeOf(value) !== Object.prototype &&\n Object.getPrototypeOf(value) !== null\n )\n return undefined;\n return Object.fromEntries(\n Object.entries(Object.getOwnPropertyDescriptors(value))\n .filter(([, descriptor]) => descriptor.enumerable && \"value\" in descriptor)\n .map(([key, descriptor]) => [redact(key), copy(descriptor.value)]),\n );\n } finally {\n ancestors.delete(value);\n }\n };\n // Preserve the journal schema; copied operands are diagnostics, never replay inputs.\n return {\n ...entry,\n headers: copy(entry.headers) as JournalEntry[\"headers\"],\n body: copy(entry.body) as JournalEntry[\"body\"],\n response: { ...entry.response, fixture: copy(entry.response.fixture) as Fixture | null },\n };\n}\n\n/** Owns a single transport, replay cursor and every cancellation deadline. */\nexport function handleLiveSession(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n options: LiveSessionOptions,\n): () => void {\n const { defaults, limits, testId } = options;\n // HTTP accepts positive finite milliseconds; the connector requires whole safe milliseconds.\n const normalizeRecordTimeout = (value: number | undefined) =>\n Math.min(Number.MAX_SAFE_INTEGER, Math.ceil(clampTimeout(value, 30_000)));\n const upstreamTimeoutMs = normalizeRecordTimeout(defaults.record?.upstreamTimeoutMs);\n const bodyTimeoutMs = normalizeRecordTimeout(defaults.record?.bodyTimeoutMs);\n const controller = new AbortController();\n let state: \"awaiting-start\" | \"active\" | \"closing\" | \"closed\" = \"awaiting-start\";\n let mode: LiveMode | undefined;\n let starting = false;\n const pending: LiveObject[] = [];\n let pendingBytes = 0;\n const maxPendingBytes = Math.min(\n limits.maxBufferedBytes,\n defaults.record?.maxProxyBufferBytes ?? DEFAULT_MAX_PROXY_BUFFER_BYTES,\n );\n const maxPendingFrames = defaults.record?.maxProxyBufferFrames ?? DEFAULT_MAX_PROXY_BUFFER_FRAMES;\n let upstream: WebSocketConnection | undefined;\n let recorder: ReturnType<typeof createLiveRecorder> | undefined;\n let recordingSecrets: string[] = [];\n let terminalReceived = false;\n let captureStarted: number | undefined;\n let recordingJournaled = false;\n let upstreamIdle: (() => void) | undefined;\n let replay: ReturnType<typeof createLiveReplay> | undefined;\n const deadline = scheduleLiveTimeout(\n () => fail(\"timeout\", \"Live session duration exceeded\"),\n limits.maxDurationMs,\n );\n const startup = scheduleLiveTimeout(\n () => fail(\"timeout\", \"Live startup deadline exceeded\"),\n defaults.record ? upstreamTimeoutMs : limits.idleTimeoutMs,\n );\n\n function recordJournal(category?: LiveFailureCategory) {\n if (recordingJournaled || !recorder) return;\n recordingJournaled = true;\n journal.add(\n liveJournalSnapshot(\n {\n method: \"WS\",\n path: \"/v1/live/sessions\",\n headers: { \"x-test-id\": testId },\n body: { model: \"gpt-live-1\", messages: [] },\n response: {\n status: category ? 502 : 200,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, options.headers),\n ...(category ? { error: category } : {}),\n },\n },\n [...limits.secretValues, ...recordingSecrets],\n ),\n );\n }\n function cleanup() {\n if (state === \"closed\") return;\n state = \"closed\";\n deadline();\n startup();\n upstreamIdle?.();\n recordJournal(\"incomplete-capture\");\n recorder?.abort();\n upstream?.destroy();\n pending.length = 0;\n pendingBytes = 0;\n replay?.close();\n controller.abort();\n ws.removeListener(\"message\", receive);\n ws.removeListener(\"close\", cleanup);\n ws.removeListener(\"error\", cleanup);\n options.onDispose();\n }\n function dispose() {\n if (state === \"closed\") return;\n ws.close(1001, \"Live session disposed\");\n cleanup();\n }\n function fail(category: LiveFailureCategory, message: string) {\n if (state === \"closed\" || state === \"closing\") return;\n state = \"closing\";\n recorder?.abort(category);\n recordJournal(category);\n // The safe diagnostic contains no caller payload, fixture data or credential.\n ws.send(JSON.stringify({ type: \"aimock.error\", error: { category, message } }));\n ws.close(category === \"resource-limit\" ? 1009 : 1008, \"Live session failed\");\n cleanup();\n }\n function receive(data: string) {\n if (state === \"closed\" || state === \"closing\") return;\n let event: LiveObject;\n try {\n event = validateLiveClientEvent(JSON.parse(data), limits);\n } catch {\n fail(\"invalid-client\", \"Invalid Live client command\");\n return;\n }\n if (starting) {\n if (event.type === \"session.start\") {\n fail(\"invalid-client\", \"Live session has already started\");\n return;\n }\n const bytes = Buffer.byteLength(data);\n if (bytes > maxPendingBytes - pendingBytes || pending.length >= maxPendingFrames) {\n fail(\"resource-limit\", \"Live startup input queue exceeded\");\n return;\n }\n pendingBytes += bytes;\n pending.push(event);\n return;\n }\n accept(event);\n }\n function recordingError(error: unknown) {\n fail(\n error instanceof LiveRecorderError\n ? error.category\n : error instanceof Error && error.message === \"WebSocket write limit exceeded\"\n ? \"resource-limit\"\n : \"upstream-connect\",\n \"Live recording failed\",\n );\n }\n function touchUpstream() {\n upstreamIdle?.();\n upstreamIdle = scheduleLiveTimeout(\n () => fail(\"timeout\", \"Live upstream idle deadline exceeded\"),\n bodyTimeoutMs,\n );\n }\n function forward(event: LiveObject) {\n if (!upstream || !recorder || terminalReceived) return;\n try {\n const raw = JSON.stringify(event);\n const now = performance.now();\n captureStarted ??= now;\n recorder.acceptClient(raw, Math.floor(now - captureStarted));\n void upstream.sendAsync(raw, controller.signal).catch(recordingError);\n } catch (error) {\n recordingError(error);\n }\n }\n async function startRecording(first: LiveObject) {\n const record = defaults.record;\n if (!record?.providers.openai) {\n fail(\"fixture-mismatch\", \"No matching Live fixture\");\n return;\n }\n starting = true;\n try {\n const base = new URL(record.providers.openai);\n if (base.username || base.password || base.search || base.hash)\n throw new Error(\"Invalid URL\");\n base.pathname = base.pathname.replace(/\\/v1\\/?$/, \"/\");\n const target = resolveUpstreamUrl(base.href, \"/v1/live/sessions\");\n const headers: Record<string, string> = {};\n const incoming = options.headers.authorization;\n const credential = incoming?.match(/^\\s*(?:Bearer|Key)\\s+(.+)$/i)?.[1].trim();\n if (incoming && !options.localApiKeys?.includes(credential ?? \"\"))\n headers.Authorization = incoming;\n applyProviderAuth(headers, target, \"openai\", record.providerKeys?.openai);\n const selected = headers.Authorization?.replace(/^\\s*(?:Bearer|Key)\\s+/i, \"\").trim();\n recordingSecrets = [...(selected ? [selected] : []), ...(options.localApiKeys ?? [])];\n recorder = createLiveRecorder({\n record,\n fixtures,\n logger: defaults.logger,\n testId,\n live: limits,\n secretValues: recordingSecrets,\n provenance: {\n host: target.host,\n path: \"/v1/live/sessions\",\n observedAt: new Date().toISOString(),\n referenceVersion: \"gpt-live-1-observed-v3\",\n sanitationVersion: 1,\n },\n });\n upstream = await connectUpstreamWebSocket(\n target,\n headers,\n limits,\n controller.signal,\n upstreamTimeoutMs,\n );\n if (controller.signal.aborted) {\n upstream.destroy();\n return;\n }\n upstream.on(\"activity\", touchUpstream);\n upstream.on(\"message\", (raw: string) => {\n if (controller.signal.aborted) return;\n touchUpstream();\n try {\n // Capture at provider arrival, before downstream backpressure settles.\n recorder!.acceptServer(\n raw,\n captureStarted === undefined ? 0 : Math.floor(performance.now() - captureStarted),\n );\n const output: unknown = JSON.parse(raw);\n if (!output || typeof output !== \"object\" || !(\"type\" in output))\n throw new Error(\"Invalid event\");\n if (output.type === \"session.started\") {\n startup();\n starting = false;\n state = \"active\";\n const queued = pending.splice(0);\n pendingBytes = 0;\n for (const event of queued) {\n if (controller.signal.aborted) break;\n accept(event);\n }\n }\n const terminal = output.type === \"session.closed\";\n if (terminal) terminalReceived = true;\n void ws.sendAsync(raw, controller.signal).then(() => {\n if (!terminal || controller.signal.aborted) return;\n const result = recorder!.finish();\n if (result.kind === \"failed\") {\n fail(result.category, \"Live capture export failed\");\n return;\n }\n recordJournal();\n ws.close(1000, \"Live session complete\");\n cleanup();\n }, recordingError);\n } catch (error) {\n recordingError(error);\n }\n });\n upstream.on(\"error\", () => fail(\"upstream-connect\", \"Live upstream connection failed\"));\n upstream.on(\"close\", (code: number) => {\n if (!terminalReceived)\n fail(\n code === 1009 ? \"resource-limit\" : \"incomplete-capture\",\n \"Live upstream closed before completion\",\n );\n });\n touchUpstream();\n // Only startup is forwarded until the provider acknowledges the session.\n // The bounded queue retains commands that the caller pipelined meanwhile.\n forward(first);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"\";\n if (/HTTP 401/.test(message)) fail(\"upstream-auth\", \"Live upstream authentication refused\");\n else if (/HTTP (403|404|429)/.test(message))\n fail(\"upstream-access\", \"Live upstream access refused\");\n else recordingError(error);\n }\n }\n function startReplay(\n fixture: Fixture,\n request: ChatCompletionRequest,\n first: LiveObject,\n raw: FixtureResponse,\n ) {\n if (state === \"closed\" || state === \"closing\") return;\n try {\n if (!isLiveResponse(raw)) throw new Error(\"not a Live fixture\");\n const response = normalizeLiveFixture(raw, limits);\n replay = createLiveReplay(response.live, {\n limits,\n signal: controller.signal,\n timing: response.liveTiming ?? \"recorded\",\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n fail,\n send: async (output) => {\n await ws.sendAsync(JSON.stringify(output), controller.signal);\n if (output.type === \"session.closed\" && !controller.signal.aborted) {\n ws.close(1000, \"Live session complete\");\n cleanup();\n }\n },\n });\n } catch {\n fail(\"protocol-divergence\", \"Invalid Live replay configuration\");\n return;\n }\n journal.add(\n liveJournalSnapshot(\n {\n method: \"WS\",\n path: \"/v1/live/sessions\",\n headers: { ...flattenHeaders(options.headers), \"x-test-id\": testId },\n body: request,\n response: { status: 200, fixture },\n },\n limits.secretValues,\n ),\n fixture,\n );\n startup();\n starting = false;\n state = \"active\";\n replay.accept(first);\n const queued = pending.splice(0);\n pendingBytes = 0;\n for (const event of queued) {\n if (controller.signal.aborted) break;\n accept(event);\n }\n }\n function accept(event: LiveObject) {\n if (state === \"closed\" || state === \"closing\") return;\n if (state === \"awaiting-start\") {\n const session = object(event.session);\n const audio = object(session?.audio);\n const format = object(audio?.format);\n const delegation = object(session?.delegation);\n if (\n event.type !== \"session.start\" ||\n session?.model !== \"gpt-live-1\" ||\n format?.type !== \"audio/pcm\" ||\n format.rate !== 24000 ||\n (delegation?.type !== \"client\" && delegation?.type !== \"responses\")\n ) {\n fail(\n \"invalid-client\",\n \"Live requires session.start with explicit model, PCM24 audio and delegation\",\n );\n return;\n }\n mode = delegation.type === \"client\" ? \"client\" : \"managed\";\n const request: ChatCompletionRequest = {\n model: session.model,\n messages: [],\n _endpointType: \"openai-live\",\n session: structuredClone(session),\n };\n let fixture: Fixture | null;\n let skippedBySequenceOrTurn: number;\n try {\n ({ fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n request,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n ));\n } catch {\n fail(\"protocol-divergence\", \"Live fixture selection failed\");\n return;\n }\n if (!fixture) {\n const strict = resolveStrictMode(defaults.strict, options.headers);\n if (strict) {\n const message = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(\n strictNoMatchLogLine(\"WS\", \"/v1/live/sessions\", skippedBySequenceOrTurn),\n );\n // A rejected startup is diagnostic only: retain test ownership, never\n // caller authorization or session content, and do not create a recorder.\n journal.add(\n liveJournalSnapshot(\n {\n method: \"WS\",\n path: \"/v1/live/sessions\",\n headers: { \"x-test-id\": testId },\n body: { model: request.model, messages: [] },\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, options.headers),\n },\n },\n limits.secretValues,\n ),\n );\n fail(\"fixture-mismatch\", message);\n } else if (wouldProxyMiss(strict, defaults.record, \"openai\")) {\n void startRecording(event);\n } else {\n fail(\"fixture-mismatch\", \"No matching Live fixture\");\n }\n return;\n }\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n if (typeof fixture.response === \"function\") {\n starting = true;\n const selected = fixture;\n void resolveResponse(selected, request).then(\n (response) => startReplay(selected, request, event, response),\n () => fail(\"protocol-divergence\", \"Live response factory failed\"),\n );\n } else startReplay(fixture, request, event, fixture.response);\n return;\n } else if (event.type === \"session.start\") {\n fail(\"invalid-client\", \"Live session has already started\");\n return;\n }\n if (event.type === \"session.update\") {\n const patch = object(event.session);\n const delegation = object(patch?.delegation);\n const backend = object(delegation?.responses);\n if (\n mode !== \"managed\" ||\n !patch ||\n !delegation ||\n !backend ||\n Object.keys(patch).some((key) => key !== \"delegation\") ||\n Object.keys(delegation).some((key) => key !== \"type\" && key !== \"responses\") ||\n (delegation.type !== undefined && delegation.type !== \"responses\") ||\n Object.keys(backend).some((key) => key !== \"instructions\") ||\n !Object.hasOwn(backend, \"instructions\")\n ) {\n fail(\"invalid-client\", \"Only managed backend instructions can be updated\");\n return;\n }\n }\n // Input acceptance is synchronous; output pressure never serializes duplex input.\n if (upstream) forward(event);\n else replay?.accept(event);\n }\n ws.on(\"message\", receive);\n ws.on(\"close\", cleanup);\n ws.on(\"error\", cleanup);\n return dispose;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ws-live.js","names":[],"sources":["../src/ws-live.ts"],"sourcesContent":["import { performance } from \"node:perf_hooks\";\nimport type { IncomingHttpHeaders } from \"node:http\";\nimport { resolveRequestDecisions, strictOverrideField } from \"./response/policy.js\";\nimport {\n flattenHeaders,\n isLiveResponse,\n resolveResponse,\n strictNoMatchLogLine,\n strictNoMatchMessage,\n wouldProxyMiss,\n} from \"./helpers.js\";\nimport { normalizeLiveFixture, validateLiveClientEvent } from \"./live-fixture.js\";\nimport { createLiveRecorder, LiveRecorderError } from \"./live-recorder.js\";\nimport { applyProviderAuth } from \"./provider-auth.js\";\nimport { connectUpstreamWebSocket } from \"./ws-upstream.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport {\n clampTimeout,\n DEFAULT_MAX_PROXY_BUFFER_BYTES,\n DEFAULT_MAX_PROXY_BUFFER_FRAMES,\n} from \"./recorder.js\";\nimport { createLiveReplay } from \"./live-replay.js\";\nimport type {\n LiveFailureCategory,\n LiveJson,\n LiveMode,\n LiveObject,\n LiveOptions,\n} from \"./live-types.js\";\nimport type { Journal } from \"./journal.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureResponse,\n HandlerDefaults,\n JournalEntry,\n} from \"./types.js\";\nimport type { WebSocketConnection } from \"./ws-framing.js\";\n\nexport interface LiveSessionOptions {\n defaults: HandlerDefaults;\n limits: Required<LiveOptions>;\n testId: string;\n headers: IncomingHttpHeaders;\n localApiKeys?: readonly string[];\n onDispose: () => void;\n}\nfunction object(value: LiveJson | undefined): LiveObject | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value) ? value : undefined;\n}\n\n/** Node otherwise turns an overflowing delay into a one-millisecond timeout. */\nfunction scheduleLiveTimeout(callback: () => void, timeoutMs: number): () => void {\n const started = performance.now();\n const onTimeout = () => {\n const remaining = timeoutMs - (performance.now() - started);\n if (remaining > 0) timer = setTimeout(onTimeout, Math.min(remaining, 2_147_483_647));\n else callback();\n };\n let timer = setTimeout(onTimeout, Math.min(timeoutMs, 2_147_483_647));\n return () => clearTimeout(timer);\n}\n\n/** Diagnostic copies never retain executable members or mutate replay operands. */\nfunction liveJournalSnapshot(\n entry: Omit<JournalEntry, \"id\" | \"timestamp\">,\n secretValues: readonly string[],\n): Omit<JournalEntry, \"id\" | \"timestamp\"> {\n const secrets = secretValues.filter((secret) => secret.length > 0);\n if (!secrets.length) return entry;\n const containsSecret = (value: string) => secrets.some((secret) => value.includes(secret));\n // Whole-field redaction avoids creating another secret by joining substrings.\n const marker = containsSecret(\"[REDACTED]\") ? \"\" : \"[REDACTED]\";\n const redact = (value: string) => (containsSecret(value) ? marker : value);\n const ancestors = new Set<object>();\n const copy = (value: unknown): unknown => {\n if (typeof value === \"string\") return redact(value);\n if (typeof value === \"function\" || typeof value === \"symbol\") return undefined;\n if (value === null || typeof value !== \"object\") return value;\n if (ancestors.has(value)) return undefined;\n ancestors.add(value);\n try {\n if (Array.isArray(value)) return value.map(copy);\n // Diagnostics contain data only; omit getters and non-JSON objects.\n if (\n Object.getPrototypeOf(value) !== Object.prototype &&\n Object.getPrototypeOf(value) !== null\n )\n return undefined;\n return Object.fromEntries(\n Object.entries(Object.getOwnPropertyDescriptors(value))\n .filter(([, descriptor]) => descriptor.enumerable && \"value\" in descriptor)\n .map(([key, descriptor]) => [redact(key), copy(descriptor.value)]),\n );\n } finally {\n ancestors.delete(value);\n }\n };\n // Preserve the journal schema; copied operands are diagnostics, never replay inputs.\n return {\n ...entry,\n headers: copy(entry.headers) as JournalEntry[\"headers\"],\n body: copy(entry.body) as JournalEntry[\"body\"],\n response: { ...entry.response, fixture: copy(entry.response.fixture) as Fixture | null },\n };\n}\n\n/** Owns a single transport, replay cursor and every cancellation deadline. */\nexport function handleLiveSession(\n ws: WebSocketConnection,\n fixtures: Fixture[],\n journal: Journal,\n options: LiveSessionOptions,\n): () => void {\n const { defaults, limits, testId } = options;\n // HTTP accepts positive finite milliseconds; the connector requires whole safe milliseconds.\n const normalizeRecordTimeout = (value: number | undefined) =>\n Math.min(Number.MAX_SAFE_INTEGER, Math.ceil(clampTimeout(value, 30_000)));\n const upstreamTimeoutMs = normalizeRecordTimeout(defaults.record?.upstreamTimeoutMs);\n const bodyTimeoutMs = normalizeRecordTimeout(defaults.record?.bodyTimeoutMs);\n const controller = new AbortController();\n let state: \"awaiting-start\" | \"active\" | \"closing\" | \"closed\" = \"awaiting-start\";\n let mode: LiveMode | undefined;\n let starting = false;\n const pending: LiveObject[] = [];\n let pendingBytes = 0;\n const maxPendingBytes = Math.min(\n limits.maxBufferedBytes,\n defaults.record?.maxProxyBufferBytes ?? DEFAULT_MAX_PROXY_BUFFER_BYTES,\n );\n const maxPendingFrames = defaults.record?.maxProxyBufferFrames ?? DEFAULT_MAX_PROXY_BUFFER_FRAMES;\n let upstream: WebSocketConnection | undefined;\n let recorder: ReturnType<typeof createLiveRecorder> | undefined;\n let recordingSecrets: string[] = [];\n let terminalReceived = false;\n let captureStarted: number | undefined;\n let recordingJournaled = false;\n let upstreamIdle: (() => void) | undefined;\n let replay: ReturnType<typeof createLiveReplay> | undefined;\n const deadline = scheduleLiveTimeout(\n () => fail(\"timeout\", \"Live session duration exceeded\"),\n limits.maxDurationMs,\n );\n const startup = scheduleLiveTimeout(\n () => fail(\"timeout\", \"Live startup deadline exceeded\"),\n defaults.record ? upstreamTimeoutMs : limits.idleTimeoutMs,\n );\n\n function recordJournal(category?: LiveFailureCategory) {\n if (recordingJournaled || !recorder) return;\n recordingJournaled = true;\n journal.add(\n liveJournalSnapshot(\n {\n method: \"WS\",\n path: \"/v1/live/sessions\",\n headers: { \"x-test-id\": testId },\n body: { model: \"gpt-live-1\", messages: [] },\n response: {\n status: category ? 502 : 200,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, options.headers),\n ...(category ? { error: category } : {}),\n },\n },\n [...limits.secretValues, ...recordingSecrets],\n ),\n );\n }\n function cleanup() {\n if (state === \"closed\") return;\n state = \"closed\";\n deadline();\n startup();\n upstreamIdle?.();\n recordJournal(\"incomplete-capture\");\n recorder?.abort();\n upstream?.destroy();\n pending.length = 0;\n pendingBytes = 0;\n replay?.close();\n controller.abort();\n ws.removeListener(\"message\", receive);\n ws.removeListener(\"close\", cleanup);\n ws.removeListener(\"error\", cleanup);\n options.onDispose();\n }\n function dispose() {\n if (state === \"closed\") return;\n ws.close(1001, \"Live session disposed\");\n cleanup();\n }\n function fail(category: LiveFailureCategory, message: string) {\n if (state === \"closed\" || state === \"closing\") return;\n state = \"closing\";\n recorder?.abort(category);\n recordJournal(category);\n // The safe diagnostic contains no caller payload, fixture data or credential.\n ws.send(JSON.stringify({ type: \"aimock.error\", error: { category, message } }));\n ws.close(category === \"resource-limit\" ? 1009 : 1008, \"Live session failed\");\n cleanup();\n }\n function receive(data: string) {\n if (state === \"closed\" || state === \"closing\") return;\n let event: LiveObject;\n try {\n event = validateLiveClientEvent(JSON.parse(data), limits);\n } catch {\n fail(\"invalid-client\", \"Invalid Live client command\");\n return;\n }\n if (starting) {\n if (event.type === \"session.start\") {\n fail(\"invalid-client\", \"Live session has already started\");\n return;\n }\n const bytes = Buffer.byteLength(data);\n if (bytes > maxPendingBytes - pendingBytes || pending.length >= maxPendingFrames) {\n fail(\"resource-limit\", \"Live startup input queue exceeded\");\n return;\n }\n pendingBytes += bytes;\n pending.push(event);\n return;\n }\n accept(event);\n }\n function recordingError(error: unknown) {\n fail(\n error instanceof LiveRecorderError\n ? error.category\n : error instanceof Error && error.message === \"WebSocket write limit exceeded\"\n ? \"resource-limit\"\n : \"upstream-connect\",\n \"Live recording failed\",\n );\n }\n function touchUpstream() {\n upstreamIdle?.();\n upstreamIdle = scheduleLiveTimeout(\n () => fail(\"timeout\", \"Live upstream idle deadline exceeded\"),\n bodyTimeoutMs,\n );\n }\n function forward(event: LiveObject) {\n if (!upstream || !recorder || terminalReceived) return;\n try {\n const raw = JSON.stringify(event);\n const now = performance.now();\n captureStarted ??= now;\n recorder.acceptClient(raw, Math.floor(now - captureStarted));\n void upstream.sendAsync(raw, controller.signal).catch(recordingError);\n } catch (error) {\n recordingError(error);\n }\n }\n async function startRecording(first: LiveObject) {\n const record = defaults.record;\n if (!record?.providers.openai) {\n fail(\"fixture-mismatch\", \"No matching Live fixture\");\n return;\n }\n starting = true;\n try {\n const base = new URL(record.providers.openai);\n if (base.username || base.password || base.search || base.hash)\n throw new Error(\"Invalid URL\");\n base.pathname = base.pathname.replace(/\\/v1\\/?$/, \"/\");\n const target = resolveUpstreamUrl(base.href, \"/v1/live/sessions\");\n const headers: Record<string, string> = {};\n const incoming = options.headers.authorization;\n const credential = incoming?.match(/^\\s*(?:Bearer|Key)\\s+(.+)$/i)?.[1].trim();\n if (incoming && !options.localApiKeys?.includes(credential ?? \"\"))\n headers.Authorization = incoming;\n applyProviderAuth(headers, target, \"openai\", record.providerKeys?.openai);\n const selected = headers.Authorization?.replace(/^\\s*(?:Bearer|Key)\\s+/i, \"\").trim();\n recordingSecrets = [...(selected ? [selected] : []), ...(options.localApiKeys ?? [])];\n recorder = createLiveRecorder({\n record,\n fixtures,\n logger: defaults.logger,\n testId,\n live: limits,\n secretValues: recordingSecrets,\n provenance: {\n host: target.host,\n path: \"/v1/live/sessions\",\n observedAt: new Date().toISOString(),\n referenceVersion: \"gpt-live-1-observed-v3\",\n sanitationVersion: 1,\n },\n });\n upstream = await connectUpstreamWebSocket(\n target,\n headers,\n limits,\n controller.signal,\n upstreamTimeoutMs,\n );\n if (controller.signal.aborted) {\n upstream.destroy();\n return;\n }\n upstream.on(\"activity\", touchUpstream);\n upstream.on(\"message\", (raw: string) => {\n if (controller.signal.aborted) return;\n touchUpstream();\n try {\n // Capture at provider arrival, before downstream backpressure settles.\n recorder!.acceptServer(\n raw,\n captureStarted === undefined ? 0 : Math.floor(performance.now() - captureStarted),\n );\n const output: unknown = JSON.parse(raw);\n if (!output || typeof output !== \"object\" || !(\"type\" in output))\n throw new Error(\"Invalid event\");\n if (output.type === \"session.started\") {\n startup();\n starting = false;\n state = \"active\";\n const queued = pending.splice(0);\n pendingBytes = 0;\n for (const event of queued) {\n if (controller.signal.aborted) break;\n accept(event);\n }\n }\n const terminal = output.type === \"session.closed\";\n if (terminal) terminalReceived = true;\n void ws.sendAsync(raw, controller.signal).then(() => {\n if (!terminal || controller.signal.aborted) return;\n const result = recorder!.finish();\n if (result.kind === \"failed\") {\n fail(result.category, \"Live capture export failed\");\n return;\n }\n recordJournal();\n ws.close(1000, \"Live session complete\");\n cleanup();\n }, recordingError);\n } catch (error) {\n recordingError(error);\n }\n });\n upstream.on(\"error\", () => fail(\"upstream-connect\", \"Live upstream connection failed\"));\n upstream.on(\"close\", (code: number) => {\n if (!terminalReceived)\n fail(\n code === 1009 ? \"resource-limit\" : \"incomplete-capture\",\n \"Live upstream closed before completion\",\n );\n });\n touchUpstream();\n // Only startup is forwarded until the provider acknowledges the session.\n // The bounded queue retains commands that the caller pipelined meanwhile.\n forward(first);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"\";\n if (/HTTP 401/.test(message)) fail(\"upstream-auth\", \"Live upstream authentication refused\");\n else if (/HTTP (403|404|429)/.test(message))\n fail(\"upstream-access\", \"Live upstream access refused\");\n else recordingError(error);\n }\n }\n function startReplay(\n fixture: Fixture,\n request: ChatCompletionRequest,\n first: LiveObject,\n raw: FixtureResponse,\n ) {\n if (state === \"closed\" || state === \"closing\") return;\n try {\n if (!isLiveResponse(raw)) throw new Error(\"not a Live fixture\");\n const response = normalizeLiveFixture(raw, limits);\n replay = createLiveReplay(response.live, {\n limits,\n signal: controller.signal,\n timing: response.liveTiming ?? \"recorded\",\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n fail,\n send: async (output) => {\n await ws.sendAsync(JSON.stringify(output), controller.signal);\n if (output.type === \"session.closed\" && !controller.signal.aborted) {\n ws.close(1000, \"Live session complete\");\n cleanup();\n }\n },\n });\n } catch {\n fail(\"protocol-divergence\", \"Invalid Live replay configuration\");\n return;\n }\n journal.add(\n liveJournalSnapshot(\n {\n method: \"WS\",\n path: \"/v1/live/sessions\",\n headers: { ...flattenHeaders(options.headers), \"x-test-id\": testId },\n body: request,\n response: { status: 200, fixture },\n },\n limits.secretValues,\n ),\n fixture,\n );\n startup();\n starting = false;\n state = \"active\";\n replay.accept(first);\n const queued = pending.splice(0);\n pendingBytes = 0;\n for (const event of queued) {\n if (controller.signal.aborted) break;\n accept(event);\n }\n }\n function accept(event: LiveObject) {\n if (state === \"closed\" || state === \"closing\") return;\n if (state === \"awaiting-start\") {\n const session = object(event.session);\n const audio = object(session?.audio);\n const format = object(audio?.format);\n const delegation = object(session?.delegation);\n if (\n event.type !== \"session.start\" ||\n session?.model !== \"gpt-live-1\" ||\n format?.type !== \"audio/pcm\" ||\n format.rate !== 24000 ||\n (delegation?.type !== \"client\" && delegation?.type !== \"responses\")\n ) {\n fail(\n \"invalid-client\",\n \"Live requires session.start with explicit model, PCM24 audio and delegation\",\n );\n return;\n }\n mode = delegation.type === \"client\" ? \"client\" : \"managed\";\n const request: ChatCompletionRequest = {\n model: session.model,\n messages: [],\n _endpointType: \"openai-live\",\n session: structuredClone(session),\n };\n let fixture: Fixture | null;\n let skippedBySequenceOrTurn: number;\n try {\n ({ fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n request,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n ));\n } catch {\n fail(\"protocol-divergence\", \"Live fixture selection failed\");\n return;\n }\n if (!fixture) {\n const decisions = resolveRequestDecisions(defaults.strict, options.headers);\n if (decisions.fixtureMiss) {\n const message = strictNoMatchMessage(skippedBySequenceOrTurn);\n defaults.logger.error(\n strictNoMatchLogLine(\"WS\", \"/v1/live/sessions\", skippedBySequenceOrTurn),\n );\n // A rejected startup is diagnostic only: retain test ownership, never\n // caller authorization or session content, and do not create a recorder.\n journal.add(\n liveJournalSnapshot(\n {\n method: \"WS\",\n path: \"/v1/live/sessions\",\n headers: { \"x-test-id\": testId },\n body: { model: request.model, messages: [] },\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, options.headers),\n },\n },\n limits.secretValues,\n ),\n );\n fail(\"fixture-mismatch\", message);\n } else if (wouldProxyMiss(!decisions.upstream, defaults.record, \"openai\")) {\n void startRecording(event);\n } else {\n fail(\"fixture-mismatch\", \"No matching Live fixture\");\n }\n return;\n }\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n if (typeof fixture.response === \"function\") {\n starting = true;\n const selected = fixture;\n void resolveResponse(selected, request).then(\n (response) => startReplay(selected, request, event, response),\n () => fail(\"protocol-divergence\", \"Live response factory failed\"),\n );\n } else startReplay(fixture, request, event, fixture.response);\n return;\n } else if (event.type === \"session.start\") {\n fail(\"invalid-client\", \"Live session has already started\");\n return;\n }\n if (event.type === \"session.update\") {\n const patch = object(event.session);\n const delegation = object(patch?.delegation);\n const backend = object(delegation?.responses);\n if (\n mode !== \"managed\" ||\n !patch ||\n !delegation ||\n !backend ||\n Object.keys(patch).some((key) => key !== \"delegation\") ||\n Object.keys(delegation).some((key) => key !== \"type\" && key !== \"responses\") ||\n (delegation.type !== undefined && delegation.type !== \"responses\") ||\n Object.keys(backend).some((key) => key !== \"instructions\") ||\n !Object.hasOwn(backend, \"instructions\")\n ) {\n fail(\"invalid-client\", \"Only managed backend instructions can be updated\");\n return;\n }\n }\n // Input acceptance is synchronous; output pressure never serializes duplex input.\n if (upstream) forward(event);\n else replay?.accept(event);\n }\n ws.on(\"message\", receive);\n ws.on(\"close\", cleanup);\n ws.on(\"error\", cleanup);\n return dispose;\n}\n"],"mappings":"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"}
@@ -1,11 +1,14 @@
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');
4
+ const require_policy = require('./response/policy.cjs');
3
5
  const require_helpers = require('./helpers.cjs');
4
6
  const require_sse_writer = require('./sse-writer.cjs');
5
7
  const require_misbehavior = require('./misbehavior.cjs');
6
8
  const require_router = require('./router.cjs');
7
9
  require('./journal.cjs');
8
10
  const require_interruption = require('./interruption.cjs');
11
+ const require_realtime = require('./response/usage/codecs/realtime.cjs');
9
12
  let node_crypto = require("node:crypto");
10
13
 
11
14
  //#region src/ws-realtime.ts
@@ -316,11 +319,11 @@ function sendEvent(ws, event, isBeta) {
316
319
  function buildErrorRealtimeEvent(ws, message, isBeta, type = "invalid_request_error", code) {
317
320
  sendEvent(ws, {
318
321
  type: "error",
319
- error: {
322
+ error: require_errors.buildOpenAIError({
320
323
  message,
321
324
  type,
322
325
  code
323
- }
326
+ })
324
327
  }, isBeta);
325
328
  }
326
329
  /**
@@ -436,11 +439,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults, session, con
436
439
  if (!validTypes.has(s.type)) {
437
440
  sendEvent(ws, {
438
441
  type: "error",
439
- error: {
442
+ error: require_errors.buildOpenAIError({
440
443
  message: `Invalid session type: ${s.type}`,
441
444
  type: "invalid_request_error",
442
445
  code: "invalid_session_config"
443
- }
446
+ })
444
447
  }, isBeta);
445
448
  return;
446
449
  }
@@ -495,11 +498,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults, session, con
495
498
  Object.assign(session, prevSession);
496
499
  sendEvent(ws, {
497
500
  type: "error",
498
- error: {
501
+ error: require_errors.buildOpenAIError({
499
502
  message: `Model ${candidateTranscriptionModel} does not support session type transcription`,
500
503
  type: "invalid_request_error",
501
504
  code: "invalid_session_config"
502
- }
505
+ })
503
506
  }, isBeta);
504
507
  return;
505
508
  }
@@ -507,11 +510,11 @@ async function processMessage(raw, ws, fixtures, journal, defaults, session, con
507
510
  Object.assign(session, prevSession);
508
511
  sendEvent(ws, {
509
512
  type: "error",
510
- error: {
513
+ error: require_errors.buildOpenAIError({
511
514
  message: `Model ${s.model ?? prevSession.model} does not support session type translation`,
512
515
  type: "invalid_request_error",
513
516
  code: "invalid_session_config"
514
- }
517
+ })
515
518
  }, isBeta);
516
519
  return;
517
520
  }
@@ -586,7 +589,7 @@ async function emitLiveTranscriptionEvents(ws, fixtures, journal, defaults, sess
586
589
  const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, request, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
587
590
  const itemId = audioItem.id ?? realtimeId("item");
588
591
  if (!fixture) {
589
- if (require_helpers.resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {
592
+ if (require_policy.resolveRequestDecisions(defaults.strict, defaults.upgradeHeaders).fixtureMiss) {
590
593
  const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
591
594
  defaults.logger.error(require_helpers.strictNoMatchLogLine("WS", "/v1/realtime", skippedBySequenceOrTurn));
592
595
  journal.add({
@@ -597,7 +600,7 @@ async function emitLiveTranscriptionEvents(ws, fixtures, journal, defaults, sess
597
600
  response: {
598
601
  status: 503,
599
602
  fixture: null,
600
- ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
603
+ ...require_policy.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
601
604
  }
602
605
  });
603
606
  ws.close(1008, strictMessage);
@@ -611,7 +614,7 @@ async function emitLiveTranscriptionEvents(ws, fixtures, journal, defaults, sess
611
614
  response: {
612
615
  status: 404,
613
616
  fixture: null,
614
- ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
617
+ ...require_policy.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
615
618
  }
616
619
  });
617
620
  sendLiveTranscriptionFailure(ws, itemId, {
@@ -763,11 +766,7 @@ function checkRealtimeFixtureToolCalls(ws, journalEntry, responseId, isBeta, log
763
766
  code: require_helpers.fixtureToolCallErrorCode(err)
764
767
  }
765
768
  },
766
- usage: {
767
- total_tokens: 0,
768
- input_tokens: 0,
769
- output_tokens: 0
770
- }
769
+ usage: require_realtime.render({ kind: "ordinary-zero" })
771
770
  }
772
771
  }, isBeta);
773
772
  return;
@@ -795,7 +794,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
795
794
  const responseId = realtimeId("resp");
796
795
  if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
797
796
  if (!fixture) {
798
- if (require_helpers.resolveStrictMode(defaults.strict, defaults.upgradeHeaders)) {
797
+ if (require_policy.resolveRequestDecisions(defaults.strict, defaults.upgradeHeaders).fixtureMiss) {
799
798
  const strictMessage = require_helpers.strictNoMatchMessage(skippedBySequenceOrTurn);
800
799
  defaults.logger.error(require_helpers.strictNoMatchLogLine("WS", "/v1/realtime", skippedBySequenceOrTurn));
801
800
  journal.add({
@@ -806,7 +805,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
806
805
  response: {
807
806
  status: 503,
808
807
  fixture: null,
809
- ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
808
+ ...require_policy.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
810
809
  }
811
810
  });
812
811
  ws.close(1008, strictMessage);
@@ -820,7 +819,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
820
819
  response: {
821
820
  status: 404,
822
821
  fixture: null,
823
- ...require_helpers.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
822
+ ...require_policy.strictOverrideField(defaults.strict, defaults.upgradeHeaders)
824
823
  }
825
824
  });
826
825
  sendEvent(ws, {
@@ -849,11 +848,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
849
848
  code: "no_fixture_match"
850
849
  }
851
850
  },
852
- usage: {
853
- total_tokens: 0,
854
- input_tokens: 0,
855
- output_tokens: 0
856
- }
851
+ usage: require_realtime.render({ kind: "ordinary-zero" })
857
852
  }
858
853
  }, isBeta);
859
854
  return;
@@ -917,11 +912,10 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
917
912
  const usage = require_helpers.resolveOpenAIChatMisbehaviorUsage(misbehavior, completionReq, false);
918
913
  await streamRealtimeBlocks(ws, blocks, responseId, fixture, defaults, latency, chunkSize, isBeta, conversationItems, journalEntry, {
919
914
  stop: misbehavior.stop,
920
- usage: {
921
- input_tokens: usage.prompt_tokens,
922
- output_tokens: usage.completion_tokens,
923
- total_tokens: usage.total_tokens
924
- }
915
+ usage: require_realtime.render({
916
+ kind: "applied-misbehavior",
917
+ usage
918
+ })
925
919
  });
926
920
  return;
927
921
  }
@@ -953,11 +947,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
953
947
  code: response.error.code
954
948
  }
955
949
  },
956
- usage: {
957
- total_tokens: 0,
958
- input_tokens: 0,
959
- output_tokens: 0
960
- }
950
+ usage: require_realtime.render({ kind: "ordinary-zero" })
961
951
  }
962
952
  }, isBeta);
963
953
  return;
@@ -1235,11 +1225,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
1235
1225
  object: "realtime.response",
1236
1226
  status: "completed",
1237
1227
  output: allOutputItems,
1238
- usage: {
1239
- total_tokens: 0,
1240
- input_tokens: 0,
1241
- output_tokens: 0
1242
- }
1228
+ usage: require_realtime.render({ kind: "ordinary-zero" })
1243
1229
  }
1244
1230
  }, isBeta);
1245
1231
  conversationItems.push({
@@ -1395,11 +1381,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
1395
1381
  object: "realtime.response",
1396
1382
  status: "completed",
1397
1383
  output: [outputItem],
1398
- usage: {
1399
- total_tokens: 0,
1400
- input_tokens: 0,
1401
- output_tokens: 0
1402
- }
1384
+ usage: require_realtime.render({ kind: "ordinary-zero" })
1403
1385
  }
1404
1386
  }, isBeta);
1405
1387
  conversationItems.push({
@@ -1540,11 +1522,7 @@ async function handleResponseCreate(ws, fixtures, journal, defaults, session, co
1540
1522
  object: "realtime.response",
1541
1523
  status: "completed",
1542
1524
  output: outputItems,
1543
- usage: {
1544
- total_tokens: 0,
1545
- input_tokens: 0,
1546
- output_tokens: 0
1547
- }
1525
+ usage: require_realtime.render({ kind: "ordinary-zero" })
1548
1526
  }
1549
1527
  }, isBeta);
1550
1528
  for (const item of outputItems) conversationItems.push(item);
@@ -1809,11 +1787,7 @@ async function streamRealtimeBlocks(ws, blocks, responseId, fixture, defaults, l
1809
1787
  reason: fault.stop === "length" ? "max_output_tokens" : "content_filter"
1810
1788
  } } : {},
1811
1789
  output: allOutputItems,
1812
- usage: fault?.usage ?? {
1813
- total_tokens: 0,
1814
- input_tokens: 0,
1815
- output_tokens: 0
1816
- }
1790
+ usage: fault?.usage ?? require_realtime.render({ kind: "ordinary-zero" })
1817
1791
  }
1818
1792
  }, isBeta);
1819
1793
  for (const item of allOutputItems) {