@copilotkit/aimock 1.45.1 → 1.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (492) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +25 -1
  4. package/README.md +11 -2
  5. package/dist/agui-mock.cjs +25 -25
  6. package/dist/agui-mock.cjs.map +1 -1
  7. package/dist/agui-mock.d.cts.map +1 -1
  8. package/dist/agui-mock.d.ts.map +1 -1
  9. package/dist/agui-mock.js +25 -25
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-types.d.cts.map +1 -1
  12. package/dist/aimock-cli.cjs +15 -1
  13. package/dist/aimock-cli.cjs.map +1 -1
  14. package/dist/aimock-cli.d.cts.map +1 -1
  15. package/dist/aimock-cli.d.ts.map +1 -1
  16. package/dist/aimock-cli.js +16 -2
  17. package/dist/aimock-cli.js.map +1 -1
  18. package/dist/api-key-auth.cjs +10 -9
  19. package/dist/api-key-auth.cjs.map +1 -1
  20. package/dist/api-key-auth.d.cts.map +1 -1
  21. package/dist/api-key-auth.d.ts.map +1 -1
  22. package/dist/api-key-auth.js +11 -10
  23. package/dist/api-key-auth.js.map +1 -1
  24. package/dist/aws-event-stream.cjs +20 -23
  25. package/dist/aws-event-stream.cjs.map +1 -1
  26. package/dist/aws-event-stream.d.cts +0 -1
  27. package/dist/aws-event-stream.d.ts +0 -1
  28. package/dist/aws-event-stream.js +20 -24
  29. package/dist/aws-event-stream.js.map +1 -1
  30. package/dist/batches.cjs +2 -4
  31. package/dist/batches.cjs.map +1 -1
  32. package/dist/batches.d.cts.map +1 -1
  33. package/dist/batches.d.ts.map +1 -1
  34. package/dist/batches.js +2 -4
  35. package/dist/batches.js.map +1 -1
  36. package/dist/bedrock-converse.cjs +139 -161
  37. package/dist/bedrock-converse.cjs.map +1 -1
  38. package/dist/bedrock-converse.d.cts.map +1 -1
  39. package/dist/bedrock-converse.d.ts.map +1 -1
  40. package/dist/bedrock-converse.js +137 -159
  41. package/dist/bedrock-converse.js.map +1 -1
  42. package/dist/bedrock.cjs +175 -216
  43. package/dist/bedrock.cjs.map +1 -1
  44. package/dist/bedrock.d.cts.map +1 -1
  45. package/dist/bedrock.d.ts.map +1 -1
  46. package/dist/bedrock.js +172 -213
  47. package/dist/bedrock.js.map +1 -1
  48. package/dist/byteplus-video.cjs +11 -10
  49. package/dist/byteplus-video.cjs.map +1 -1
  50. package/dist/byteplus-video.d.cts.map +1 -1
  51. package/dist/byteplus-video.d.ts.map +1 -1
  52. package/dist/byteplus-video.js +5 -4
  53. package/dist/byteplus-video.js.map +1 -1
  54. package/dist/chaos.cjs +5 -4
  55. package/dist/chaos.cjs.map +1 -1
  56. package/dist/chaos.d.cts.map +1 -1
  57. package/dist/chaos.d.ts.map +1 -1
  58. package/dist/chaos.js +5 -4
  59. package/dist/chaos.js.map +1 -1
  60. package/dist/cli.cjs +15 -0
  61. package/dist/cli.cjs.map +1 -1
  62. package/dist/cli.js +15 -0
  63. package/dist/cli.js.map +1 -1
  64. package/dist/cohere.cjs +107 -180
  65. package/dist/cohere.cjs.map +1 -1
  66. package/dist/cohere.d.cts.map +1 -1
  67. package/dist/cohere.d.ts.map +1 -1
  68. package/dist/cohere.js +106 -179
  69. package/dist/cohere.js.map +1 -1
  70. package/dist/config-loader.cjs +29 -1
  71. package/dist/config-loader.cjs.map +1 -1
  72. package/dist/config-loader.d.cts +7 -1
  73. package/dist/config-loader.d.cts.map +1 -1
  74. package/dist/config-loader.d.ts +7 -1
  75. package/dist/config-loader.d.ts.map +1 -1
  76. package/dist/config-loader.js +29 -2
  77. package/dist/config-loader.js.map +1 -1
  78. package/dist/count-tokens.cjs +146 -0
  79. package/dist/count-tokens.cjs.map +1 -0
  80. package/dist/count-tokens.js +146 -0
  81. package/dist/count-tokens.js.map +1 -0
  82. package/dist/elevenlabs-audio.cjs +36 -34
  83. package/dist/elevenlabs-audio.cjs.map +1 -1
  84. package/dist/elevenlabs-audio.d.cts.map +1 -1
  85. package/dist/elevenlabs-audio.d.ts.map +1 -1
  86. package/dist/elevenlabs-audio.js +33 -31
  87. package/dist/elevenlabs-audio.js.map +1 -1
  88. package/dist/elevenlabs-voice.cjs +38 -37
  89. package/dist/elevenlabs-voice.cjs.map +1 -1
  90. package/dist/elevenlabs-voice.d.cts.map +1 -1
  91. package/dist/elevenlabs-voice.d.ts.map +1 -1
  92. package/dist/elevenlabs-voice.js +33 -32
  93. package/dist/elevenlabs-voice.js.map +1 -1
  94. package/dist/embedding-request.cjs +16 -0
  95. package/dist/embedding-request.cjs.map +1 -0
  96. package/dist/embedding-request.js +14 -0
  97. package/dist/embedding-request.js.map +1 -0
  98. package/dist/embeddings.cjs +30 -20
  99. package/dist/embeddings.cjs.map +1 -1
  100. package/dist/embeddings.d.cts.map +1 -1
  101. package/dist/embeddings.d.ts.map +1 -1
  102. package/dist/embeddings.js +30 -20
  103. package/dist/embeddings.js.map +1 -1
  104. package/dist/fal-audio.cjs +45 -43
  105. package/dist/fal-audio.cjs.map +1 -1
  106. package/dist/fal-audio.d.cts.map +1 -1
  107. package/dist/fal-audio.d.ts.map +1 -1
  108. package/dist/fal-audio.js +42 -40
  109. package/dist/fal-audio.js.map +1 -1
  110. package/dist/fal.cjs +17 -15
  111. package/dist/fal.cjs.map +1 -1
  112. package/dist/fal.js +16 -14
  113. package/dist/fal.js.map +1 -1
  114. package/dist/files.cjs +2 -4
  115. package/dist/files.cjs.map +1 -1
  116. package/dist/files.d.cts.map +1 -1
  117. package/dist/files.d.ts.map +1 -1
  118. package/dist/files.js +2 -4
  119. package/dist/files.js.map +1 -1
  120. package/dist/fine-tuning.cjs +2 -4
  121. package/dist/fine-tuning.cjs.map +1 -1
  122. package/dist/fine-tuning.d.cts.map +1 -1
  123. package/dist/fine-tuning.d.ts.map +1 -1
  124. package/dist/fine-tuning.js +2 -4
  125. package/dist/fine-tuning.js.map +1 -1
  126. package/dist/fixture-loader.cjs +140 -44
  127. package/dist/fixture-loader.cjs.map +1 -1
  128. package/dist/fixture-loader.d.cts.map +1 -1
  129. package/dist/fixture-loader.d.ts.map +1 -1
  130. package/dist/fixture-loader.js +141 -46
  131. package/dist/fixture-loader.js.map +1 -1
  132. package/dist/gemini-embeddings.cjs +26 -24
  133. package/dist/gemini-embeddings.cjs.map +1 -1
  134. package/dist/gemini-embeddings.js +26 -24
  135. package/dist/gemini-embeddings.js.map +1 -1
  136. package/dist/gemini-interactions.cjs +113 -156
  137. package/dist/gemini-interactions.cjs.map +1 -1
  138. package/dist/gemini-interactions.d.cts +0 -1
  139. package/dist/gemini-interactions.d.cts.map +1 -1
  140. package/dist/gemini-interactions.d.ts +0 -1
  141. package/dist/gemini-interactions.d.ts.map +1 -1
  142. package/dist/gemini-interactions.js +113 -156
  143. package/dist/gemini-interactions.js.map +1 -1
  144. package/dist/gemini.cjs +295 -218
  145. package/dist/gemini.cjs.map +1 -1
  146. package/dist/gemini.d.cts.map +1 -1
  147. package/dist/gemini.d.ts.map +1 -1
  148. package/dist/gemini.js +295 -219
  149. package/dist/gemini.js.map +1 -1
  150. package/dist/grok-video.cjs +14 -12
  151. package/dist/grok-video.cjs.map +1 -1
  152. package/dist/grok-video.d.cts.map +1 -1
  153. package/dist/grok-video.d.ts.map +1 -1
  154. package/dist/grok-video.js +8 -6
  155. package/dist/grok-video.js.map +1 -1
  156. package/dist/helpers.cjs +411 -75
  157. package/dist/helpers.cjs.map +1 -1
  158. package/dist/helpers.d.cts +51 -1
  159. package/dist/helpers.d.cts.map +1 -1
  160. package/dist/helpers.d.ts +51 -1
  161. package/dist/helpers.d.ts.map +1 -1
  162. package/dist/helpers.js +402 -74
  163. package/dist/helpers.js.map +1 -1
  164. package/dist/images.cjs +30 -28
  165. package/dist/images.cjs.map +1 -1
  166. package/dist/images.d.cts.map +1 -1
  167. package/dist/images.d.ts.map +1 -1
  168. package/dist/images.js +27 -25
  169. package/dist/images.js.map +1 -1
  170. package/dist/index.cjs +4 -2
  171. package/dist/index.d.cts +4 -4
  172. package/dist/index.d.ts +4 -4
  173. package/dist/index.js +4 -4
  174. package/dist/journal.cjs +3 -1
  175. package/dist/journal.cjs.map +1 -1
  176. package/dist/journal.d.cts.map +1 -1
  177. package/dist/journal.d.ts.map +1 -1
  178. package/dist/journal.js +2 -1
  179. package/dist/journal.js.map +1 -1
  180. package/dist/llmock.cjs +3 -0
  181. package/dist/llmock.cjs.map +1 -1
  182. package/dist/llmock.d.cts.map +1 -1
  183. package/dist/llmock.d.ts.map +1 -1
  184. package/dist/llmock.js +3 -0
  185. package/dist/llmock.js.map +1 -1
  186. package/dist/mcp-fakes.d.cts.map +1 -1
  187. package/dist/mcp-handler.cjs +7 -3
  188. package/dist/mcp-handler.cjs.map +1 -1
  189. package/dist/mcp-handler.js +7 -3
  190. package/dist/mcp-handler.js.map +1 -1
  191. package/dist/mcp-recorder.cjs +2 -1
  192. package/dist/mcp-recorder.cjs.map +1 -1
  193. package/dist/mcp-recorder.js +3 -2
  194. package/dist/mcp-recorder.js.map +1 -1
  195. package/dist/messages.cjs +235 -202
  196. package/dist/messages.cjs.map +1 -1
  197. package/dist/messages.d.cts.map +1 -1
  198. package/dist/messages.d.ts.map +1 -1
  199. package/dist/messages.js +234 -202
  200. package/dist/messages.js.map +1 -1
  201. package/dist/metrics.cjs +5 -2
  202. package/dist/metrics.cjs.map +1 -1
  203. package/dist/metrics.d.cts.map +1 -1
  204. package/dist/metrics.d.ts.map +1 -1
  205. package/dist/metrics.js +6 -3
  206. package/dist/metrics.js.map +1 -1
  207. package/dist/misbehavior.cjs +62 -3
  208. package/dist/misbehavior.cjs.map +1 -1
  209. package/dist/misbehavior.d.cts.map +1 -1
  210. package/dist/misbehavior.d.ts.map +1 -1
  211. package/dist/misbehavior.js +63 -4
  212. package/dist/misbehavior.js.map +1 -1
  213. package/dist/model-utils.cjs +1 -0
  214. package/dist/model-utils.cjs.map +1 -1
  215. package/dist/model-utils.js +1 -1
  216. package/dist/model-utils.js.map +1 -1
  217. package/dist/moderation.cjs +15 -12
  218. package/dist/moderation.cjs.map +1 -1
  219. package/dist/moderation.js +14 -11
  220. package/dist/moderation.js.map +1 -1
  221. package/dist/ndjson-writer.cjs +21 -23
  222. package/dist/ndjson-writer.cjs.map +1 -1
  223. package/dist/ndjson-writer.d.cts +0 -1
  224. package/dist/ndjson-writer.d.cts.map +1 -1
  225. package/dist/ndjson-writer.d.ts +0 -1
  226. package/dist/ndjson-writer.d.ts.map +1 -1
  227. package/dist/ndjson-writer.js +21 -24
  228. package/dist/ndjson-writer.js.map +1 -1
  229. package/dist/ollama.cjs +276 -210
  230. package/dist/ollama.cjs.map +1 -1
  231. package/dist/ollama.d.cts.map +1 -1
  232. package/dist/ollama.d.ts.map +1 -1
  233. package/dist/ollama.js +273 -207
  234. package/dist/ollama.js.map +1 -1
  235. package/dist/openapi.cjs +323 -3
  236. package/dist/openapi.cjs.map +1 -1
  237. package/dist/openapi.d.cts.map +1 -1
  238. package/dist/openapi.d.ts.map +1 -1
  239. package/dist/openapi.js +324 -4
  240. package/dist/openapi.js.map +1 -1
  241. package/dist/openrouter-chat.cjs +11 -155
  242. package/dist/openrouter-chat.cjs.map +1 -1
  243. package/dist/openrouter-chat.js +11 -155
  244. package/dist/openrouter-chat.js.map +1 -1
  245. package/dist/openrouter-video.cjs +51 -74
  246. package/dist/openrouter-video.cjs.map +1 -1
  247. package/dist/openrouter-video.d.cts.map +1 -1
  248. package/dist/openrouter-video.d.ts.map +1 -1
  249. package/dist/openrouter-video.js +37 -60
  250. package/dist/openrouter-video.js.map +1 -1
  251. package/dist/raw-validation.cjs +177 -0
  252. package/dist/raw-validation.cjs.map +1 -0
  253. package/dist/raw-validation.js +176 -0
  254. package/dist/raw-validation.js.map +1 -0
  255. package/dist/recorder.cjs +176 -95
  256. package/dist/recorder.cjs.map +1 -1
  257. package/dist/recorder.d.cts.map +1 -1
  258. package/dist/recorder.d.ts.map +1 -1
  259. package/dist/recorder.js +176 -95
  260. package/dist/recorder.js.map +1 -1
  261. package/dist/rerank.cjs +13 -11
  262. package/dist/rerank.cjs.map +1 -1
  263. package/dist/rerank.js +12 -10
  264. package/dist/rerank.js.map +1 -1
  265. package/dist/response/catalog/anthropic-page.cjs +39 -0
  266. package/dist/response/catalog/anthropic-page.cjs.map +1 -0
  267. package/dist/response/catalog/anthropic-page.js +38 -0
  268. package/dist/response/catalog/anthropic-page.js.map +1 -0
  269. package/dist/response/catalog/collect.cjs +61 -0
  270. package/dist/response/catalog/collect.cjs.map +1 -0
  271. package/dist/response/catalog/collect.js +59 -0
  272. package/dist/response/catalog/collect.js.map +1 -0
  273. package/dist/response/catalog/gemini-page.cjs +25 -0
  274. package/dist/response/catalog/gemini-page.cjs.map +1 -0
  275. package/dist/response/catalog/gemini-page.js +24 -0
  276. package/dist/response/catalog/gemini-page.js.map +1 -0
  277. package/dist/response/catalog/render.cjs +182 -0
  278. package/dist/response/catalog/render.cjs.map +1 -0
  279. package/dist/response/catalog/render.js +171 -0
  280. package/dist/response/catalog/render.js.map +1 -0
  281. package/dist/response/deliver.cjs +144 -0
  282. package/dist/response/deliver.cjs.map +1 -0
  283. package/dist/response/deliver.js +143 -0
  284. package/dist/response/deliver.js.map +1 -0
  285. package/dist/response/errors.cjs +225 -0
  286. package/dist/response/errors.cjs.map +1 -0
  287. package/dist/response/errors.js +224 -0
  288. package/dist/response/errors.js.map +1 -0
  289. package/dist/response/finish.cjs +39 -0
  290. package/dist/response/finish.cjs.map +1 -0
  291. package/dist/response/finish.js +35 -0
  292. package/dist/response/finish.js.map +1 -0
  293. package/dist/response/frame-loop.cjs +102 -0
  294. package/dist/response/frame-loop.cjs.map +1 -0
  295. package/dist/response/frame-loop.js +100 -0
  296. package/dist/response/frame-loop.js.map +1 -0
  297. package/dist/response/lifecycle.cjs +223 -0
  298. package/dist/response/lifecycle.cjs.map +1 -0
  299. package/dist/response/lifecycle.js +211 -0
  300. package/dist/response/lifecycle.js.map +1 -0
  301. package/dist/response/policy.cjs +126 -0
  302. package/dist/response/policy.cjs.map +1 -0
  303. package/dist/response/policy.js +120 -0
  304. package/dist/response/policy.js.map +1 -0
  305. package/dist/response/profiles.cjs +69 -0
  306. package/dist/response/profiles.cjs.map +1 -0
  307. package/dist/response/profiles.js +65 -0
  308. package/dist/response/profiles.js.map +1 -0
  309. package/dist/response/proxy.cjs +71 -0
  310. package/dist/response/proxy.cjs.map +1 -0
  311. package/dist/response/proxy.js +70 -0
  312. package/dist/response/proxy.js.map +1 -0
  313. package/dist/response/raw.cjs +228 -0
  314. package/dist/response/raw.cjs.map +1 -0
  315. package/dist/response/raw.js +227 -0
  316. package/dist/response/raw.js.map +1 -0
  317. package/dist/response/timing.cjs +157 -0
  318. package/dist/response/timing.cjs.map +1 -0
  319. package/dist/response/timing.js +150 -0
  320. package/dist/response/timing.js.map +1 -0
  321. package/dist/response/usage/codecs/anthropic.cjs +52 -0
  322. package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
  323. package/dist/response/usage/codecs/anthropic.js +50 -0
  324. package/dist/response/usage/codecs/anthropic.js.map +1 -0
  325. package/dist/response/usage/codecs/cohere.cjs +36 -0
  326. package/dist/response/usage/codecs/cohere.cjs.map +1 -0
  327. package/dist/response/usage/codecs/cohere.js +35 -0
  328. package/dist/response/usage/codecs/cohere.js.map +1 -0
  329. package/dist/response/usage/codecs/converse.cjs +16 -0
  330. package/dist/response/usage/codecs/converse.cjs.map +1 -0
  331. package/dist/response/usage/codecs/converse.js +15 -0
  332. package/dist/response/usage/codecs/converse.js.map +1 -0
  333. package/dist/response/usage/codecs/gemini.cjs +31 -0
  334. package/dist/response/usage/codecs/gemini.cjs.map +1 -0
  335. package/dist/response/usage/codecs/gemini.js +31 -0
  336. package/dist/response/usage/codecs/gemini.js.map +1 -0
  337. package/dist/response/usage/codecs/interactions.cjs +22 -0
  338. package/dist/response/usage/codecs/interactions.cjs.map +1 -0
  339. package/dist/response/usage/codecs/interactions.js +21 -0
  340. package/dist/response/usage/codecs/interactions.js.map +1 -0
  341. package/dist/response/usage/codecs/ollama.cjs +17 -0
  342. package/dist/response/usage/codecs/ollama.cjs.map +1 -0
  343. package/dist/response/usage/codecs/ollama.js +17 -0
  344. package/dist/response/usage/codecs/ollama.js.map +1 -0
  345. package/dist/response/usage/codecs/openai.cjs +52 -0
  346. package/dist/response/usage/codecs/openai.cjs.map +1 -0
  347. package/dist/response/usage/codecs/openai.js +50 -0
  348. package/dist/response/usage/codecs/openai.js.map +1 -0
  349. package/dist/response/usage/codecs/openrouter.cjs +68 -0
  350. package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
  351. package/dist/response/usage/codecs/openrouter.js +67 -0
  352. package/dist/response/usage/codecs/openrouter.js.map +1 -0
  353. package/dist/response/usage/codecs/realtime.cjs +19 -0
  354. package/dist/response/usage/codecs/realtime.cjs.map +1 -0
  355. package/dist/response/usage/codecs/realtime.js +18 -0
  356. package/dist/response/usage/codecs/realtime.js.map +1 -0
  357. package/dist/response/usage/codecs/responses.cjs +21 -0
  358. package/dist/response/usage/codecs/responses.cjs.map +1 -0
  359. package/dist/response/usage/codecs/responses.js +20 -0
  360. package/dist/response/usage/codecs/responses.js.map +1 -0
  361. package/dist/response/usage/estimate.cjs +23 -0
  362. package/dist/response/usage/estimate.cjs.map +1 -0
  363. package/dist/response/usage/estimate.js +21 -0
  364. package/dist/response/usage/estimate.js.map +1 -0
  365. package/dist/response/usage/fields.cjs +72 -0
  366. package/dist/response/usage/fields.cjs.map +1 -0
  367. package/dist/response/usage/fields.js +70 -0
  368. package/dist/response/usage/fields.js.map +1 -0
  369. package/dist/response/usage/recording.cjs +52 -0
  370. package/dist/response/usage/recording.cjs.map +1 -0
  371. package/dist/response/usage/recording.js +52 -0
  372. package/dist/response/usage/recording.js.map +1 -0
  373. package/dist/responses.cjs +304 -196
  374. package/dist/responses.cjs.map +1 -1
  375. package/dist/responses.d.cts.map +1 -1
  376. package/dist/responses.d.ts.map +1 -1
  377. package/dist/responses.js +303 -195
  378. package/dist/responses.js.map +1 -1
  379. package/dist/route-registry.cjs +45 -0
  380. package/dist/route-registry.cjs.map +1 -1
  381. package/dist/route-registry.d.cts.map +1 -1
  382. package/dist/route-registry.d.ts.map +1 -1
  383. package/dist/route-registry.js +41 -1
  384. package/dist/route-registry.js.map +1 -1
  385. package/dist/router.cjs +24 -4
  386. package/dist/router.cjs.map +1 -1
  387. package/dist/router.d.cts +7 -1
  388. package/dist/router.d.cts.map +1 -1
  389. package/dist/router.d.ts +7 -1
  390. package/dist/router.d.ts.map +1 -1
  391. package/dist/router.js +24 -5
  392. package/dist/router.js.map +1 -1
  393. package/dist/search.cjs +13 -11
  394. package/dist/search.cjs.map +1 -1
  395. package/dist/search.js +12 -10
  396. package/dist/search.js.map +1 -1
  397. package/dist/server.cjs +293 -248
  398. package/dist/server.cjs.map +1 -1
  399. package/dist/server.d.cts.map +1 -1
  400. package/dist/server.d.ts.map +1 -1
  401. package/dist/server.js +294 -249
  402. package/dist/server.js.map +1 -1
  403. package/dist/speech.cjs +20 -18
  404. package/dist/speech.cjs.map +1 -1
  405. package/dist/speech.d.cts.map +1 -1
  406. package/dist/speech.d.ts.map +1 -1
  407. package/dist/speech.js +19 -17
  408. package/dist/speech.js.map +1 -1
  409. package/dist/sse-writer.cjs +56 -30
  410. package/dist/sse-writer.cjs.map +1 -1
  411. package/dist/sse-writer.d.cts +0 -1
  412. package/dist/sse-writer.d.cts.map +1 -1
  413. package/dist/sse-writer.d.ts +0 -1
  414. package/dist/sse-writer.d.ts.map +1 -1
  415. package/dist/sse-writer.js +55 -31
  416. package/dist/sse-writer.js.map +1 -1
  417. package/dist/stream-collapse.cjs +118 -11
  418. package/dist/stream-collapse.cjs.map +1 -1
  419. package/dist/stream-collapse.d.cts +10 -9
  420. package/dist/stream-collapse.d.cts.map +1 -1
  421. package/dist/stream-collapse.d.ts +10 -9
  422. package/dist/stream-collapse.d.ts.map +1 -1
  423. package/dist/stream-collapse.js +118 -11
  424. package/dist/stream-collapse.js.map +1 -1
  425. package/dist/transcription.cjs +31 -43
  426. package/dist/transcription.cjs.map +1 -1
  427. package/dist/transcription.d.cts.map +1 -1
  428. package/dist/transcription.d.ts.map +1 -1
  429. package/dist/transcription.js +31 -43
  430. package/dist/transcription.js.map +1 -1
  431. package/dist/types.cjs.map +1 -1
  432. package/dist/types.d.cts +231 -9
  433. package/dist/types.d.cts.map +1 -1
  434. package/dist/types.d.ts +231 -9
  435. package/dist/types.d.ts.map +1 -1
  436. package/dist/types.js.map +1 -1
  437. package/dist/vector-mock.cjs +3 -8
  438. package/dist/vector-mock.cjs.map +1 -1
  439. package/dist/vector-mock.d.cts.map +1 -1
  440. package/dist/vector-mock.d.ts.map +1 -1
  441. package/dist/vector-mock.js +3 -8
  442. package/dist/vector-mock.js.map +1 -1
  443. package/dist/vector-stores.cjs +2 -4
  444. package/dist/vector-stores.cjs.map +1 -1
  445. package/dist/vector-stores.d.cts.map +1 -1
  446. package/dist/vector-stores.d.ts.map +1 -1
  447. package/dist/vector-stores.js +2 -4
  448. package/dist/vector-stores.js.map +1 -1
  449. package/dist/veo-video.cjs +16 -14
  450. package/dist/veo-video.cjs.map +1 -1
  451. package/dist/veo-video.js +10 -8
  452. package/dist/veo-video.js.map +1 -1
  453. package/dist/video.cjs +21 -19
  454. package/dist/video.cjs.map +1 -1
  455. package/dist/video.d.cts.map +1 -1
  456. package/dist/video.d.ts.map +1 -1
  457. package/dist/video.js +20 -18
  458. package/dist/video.js.map +1 -1
  459. package/dist/ws-framing.cjs +12 -6
  460. package/dist/ws-framing.cjs.map +1 -1
  461. package/dist/ws-framing.d.cts +2 -0
  462. package/dist/ws-framing.d.cts.map +1 -1
  463. package/dist/ws-framing.d.ts +2 -0
  464. package/dist/ws-framing.d.ts.map +1 -1
  465. package/dist/ws-framing.js +12 -6
  466. package/dist/ws-framing.js.map +1 -1
  467. package/dist/ws-gemini-live.cjs +131 -96
  468. package/dist/ws-gemini-live.cjs.map +1 -1
  469. package/dist/ws-gemini-live.d.cts +0 -1
  470. package/dist/ws-gemini-live.d.cts.map +1 -1
  471. package/dist/ws-gemini-live.d.ts +0 -1
  472. package/dist/ws-gemini-live.d.ts.map +1 -1
  473. package/dist/ws-gemini-live.js +130 -95
  474. package/dist/ws-gemini-live.js.map +1 -1
  475. package/dist/ws-live.cjs +6 -5
  476. package/dist/ws-live.cjs.map +1 -1
  477. package/dist/ws-live.js +5 -4
  478. package/dist/ws-live.js.map +1 -1
  479. package/dist/ws-realtime.cjs +108 -124
  480. package/dist/ws-realtime.cjs.map +1 -1
  481. package/dist/ws-realtime.d.cts.map +1 -1
  482. package/dist/ws-realtime.d.ts.map +1 -1
  483. package/dist/ws-realtime.js +105 -121
  484. package/dist/ws-realtime.js.map +1 -1
  485. package/dist/ws-responses.cjs +80 -59
  486. package/dist/ws-responses.cjs.map +1 -1
  487. package/dist/ws-responses.d.cts.map +1 -1
  488. package/dist/ws-responses.d.ts.map +1 -1
  489. package/dist/ws-responses.js +79 -58
  490. package/dist/ws-responses.js.map +1 -1
  491. package/package.json +4 -1
  492. package/skills/write-fixtures/SKILL.md +57 -16
@@ -0,0 +1,211 @@
1
+ import { responseGoneReason } from "../chaos.js";
2
+ import { createInterruptionSignal } from "../interruption.js";
3
+
4
+ //#region src/response/lifecycle.ts
5
+ /** The journal `interruptReason` for a client that left before the reply finished. */
6
+ const CLIENT_ABORTED = "client aborted";
7
+ /**
8
+ * The journal `interruptReason` for a response aimock tore down itself: the
9
+ * chaos `disconnect` action or a server-side `destroy`. It is the name the
10
+ * chaos journal entry records (`chaosAction: "disconnect"`), so a teardown
11
+ * aimock caused is never blamed on the client.
12
+ */
13
+ const AIMOCK_DISCONNECTED = "disconnect";
14
+ /**
15
+ * Subscribe to `close`, guarded the same way as `awaitChaosLatency`: a partial
16
+ * test double without `once` is not subscribed, and only a listener that was
17
+ * added is ever removed, through a remover that exists.
18
+ */
19
+ function subscribeClose(source, onClose) {
20
+ if (typeof source.once !== "function") return void 0;
21
+ source.once("close", onClose);
22
+ if (typeof source.off === "function") return () => source.off?.("close", onClose);
23
+ if (typeof source.removeListener === "function") return () => source.removeListener?.("close", onClose);
24
+ }
25
+ function createLifecycle(interruption, goneAtStart, subscribe) {
26
+ const controller = new AbortController();
27
+ let firstCause;
28
+ const abortWith = (cause) => {
29
+ if (controller.signal.aborted) return;
30
+ firstCause = cause;
31
+ controller.abort();
32
+ };
33
+ const onFixtureAbort = () => abortWith("fixture");
34
+ interruption?.signal.addEventListener("abort", onFixtureAbort, { once: true });
35
+ let unsubscribe;
36
+ if (goneAtStart !== void 0) abortWith(goneAtStart);
37
+ else unsubscribe = subscribe(abortWith);
38
+ return {
39
+ signal: controller.signal,
40
+ cause: () => firstCause,
41
+ reason: () => {
42
+ if (firstCause === "fixture") return interruption?.reason();
43
+ if (firstCause === "client") return CLIENT_ABORTED;
44
+ if (firstCause === "server") return AIMOCK_DISCONNECTED;
45
+ },
46
+ tick: () => {
47
+ interruption?.tick();
48
+ },
49
+ dispose: () => {
50
+ unsubscribe?.();
51
+ unsubscribe = void 0;
52
+ interruption?.signal.removeEventListener("abort", onFixtureAbort);
53
+ interruption?.cleanup();
54
+ }
55
+ };
56
+ }
57
+ /** The request socket of `res`, or `undefined` when it carries none. */
58
+ function requestSocket(res) {
59
+ const owner = res;
60
+ return owner.req?.socket ?? owner.socket ?? void 0;
61
+ }
62
+ /**
63
+ * THE client hang-up rule, shared by the delivery lifecycle and the proxy
64
+ * relay: the client's socket closed or reset.
65
+ *
66
+ * - A reset, or a write into a socket the peer closed, leaves `errored` set
67
+ * (ECONNRESET / EPIPE).
68
+ * - aimock's server keeps Node's default `httpAllowHalfOpen: false`, so a
69
+ * client FIN makes Node end our write side: the socket is `writableEnded`
70
+ * while the response is not. That is the evidence the client closed.
71
+ *
72
+ * A FIN alone (`readableEnded` with neither of those) is not a hang-up: it is
73
+ * what the socket shows when aimock tore the response down before Node
74
+ * answered the FIN. A real client FIN that Node has not answered yet looks the
75
+ * same, so this rule also classifies it as not a hang-up. aimock's own
76
+ * `destroy()` leaves neither signal.
77
+ */
78
+ function closedByClient(socket) {
79
+ return (socket.errored ?? null) !== null || socket.writableEnded === true;
80
+ }
81
+ /**
82
+ * Who tore down a response that is gone without having ended. After the fact
83
+ * `res.destroyed` reads the same either way, so this reads the evidence:
84
+ *
85
+ * - a chaos `disconnect` marked the response before destroying it: aimock;
86
+ * - the request socket meets {@link closedByClient}: the client;
87
+ * - neither: aimock destroyed it server-side.
88
+ *
89
+ * `socket` defaults to the response's request socket. With no socket to read
90
+ * (a partial test double) the client attribution is kept.
91
+ */
92
+ function teardownCause(res, socket = requestSocket(res)) {
93
+ if (responseGoneReason(res) === "chaos-disconnected") return "server";
94
+ if (!socket) return "client";
95
+ return closedByClient(socket) ? "client" : "server";
96
+ }
97
+ /**
98
+ * The request socket's `bytesWritten` when each delivery started, so the
99
+ * status-line rule counts only this response's bytes on a keep-alive socket.
100
+ */
101
+ const deliveryStartBytes = /* @__PURE__ */ new WeakMap();
102
+ /**
103
+ * Record where this response's bytes start on its socket. A response whose
104
+ * head was already committed when the delivery started is not recorded:
105
+ * {@link statusLineSent} keeps the `headersSent` rule for it.
106
+ */
107
+ function noteDeliveryStart(res) {
108
+ if (deliveryStartBytes.has(res) || res.headersSent) return;
109
+ const bytes = requestSocket(res)?.bytesWritten;
110
+ if (typeof bytes === "number") deliveryStartBytes.set(res, bytes);
111
+ }
112
+ /**
113
+ * Whether this response's status line reached the client's socket: a byte of
114
+ * it was handed to the socket after {@link noteDeliveryStart}. `headersSent`
115
+ * is not that test: `writeHead` sets it before any byte is written. With no
116
+ * recorded start, or no socket to read, `headersSent` is the fallback.
117
+ */
118
+ function statusLineSent(res) {
119
+ const start = deliveryStartBytes.get(res);
120
+ const bytes = requestSocket(res)?.bytesWritten;
121
+ if (start === void 0 || typeof bytes !== "number") return res.headersSent;
122
+ return bytes > start;
123
+ }
124
+ /** The journal `interruptReason` for {@link teardownCause}. */
125
+ function teardownReason(res) {
126
+ return teardownCause(res) === "client" ? CLIENT_ABORTED : AIMOCK_DISCONNECTED;
127
+ }
128
+ /**
129
+ * HTTP delivery lifecycle. A `close` before `res.writableFinished` aborts it,
130
+ * and {@link teardownCause} decides whose teardown it was: the client
131
+ * (`"client"`) or aimock itself (`"server"`). A response that is already gone
132
+ * at construction aborts at once with the same attribution. A chaos
133
+ * `disconnect` destroys without ending, so no `writableEnded` check stops a
134
+ * writer for it. Only an already-ended response does not abort: the frame
135
+ * loop exits on its `writableEnded` check. The fixture interruption is
136
+ * created only when `streaming` is true (it is streaming-only).
137
+ */
138
+ function httpLifecycle(res, fixture, opts) {
139
+ const interruption = opts.streaming && fixture ? createInterruptionSignal(fixture) : null;
140
+ const gone = responseGoneReason(res);
141
+ return createLifecycle(interruption, gone === null || gone === "already-ended" ? void 0 : teardownCause(res), (onGone) => subscribeClose(res, () => {
142
+ if (!res.writableFinished) onGone(teardownCause(res));
143
+ }));
144
+ }
145
+ /**
146
+ * Per watched connection: whether its `close` event has fired. Only sockets
147
+ * watched while open are recorded, so an entry is evidence, not a guess.
148
+ */
149
+ const wsCloseEmitted = /* @__PURE__ */ new WeakMap();
150
+ /**
151
+ * Start recording when `ws` emits `close`, so {@link wsClosedCause} can tell
152
+ * aimock's own close from the client's. Each WS transport calls it when the
153
+ * connection opens. Idempotent, and a no-op once closed.
154
+ */
155
+ function watchWsClose(ws) {
156
+ if (ws.isClosed || wsCloseEmitted.has(ws)) return;
157
+ const source = ws;
158
+ if (typeof source.once !== "function") return;
159
+ wsCloseEmitted.set(ws, false);
160
+ source.once("close", () => wsCloseEmitted.set(ws, true));
161
+ }
162
+ /**
163
+ * Who closed a socket that reads `isClosed`, or `undefined` while it is open.
164
+ *
165
+ * `WebSocketConnection` marks itself closed and emits `close` in the same step
166
+ * for a client close frame, a lost connection, and `destroy()`. Only aimock's
167
+ * own `close()` (a write or buffer limit, a bad frame, a strict miss, shutdown)
168
+ * marks it closed at once and emits `close` about 100ms later. So a socket
169
+ * watched while open ({@link watchWsClose}) that is closed with no `close` yet
170
+ * was closed by aimock (`"server"`). Any other closed socket reads as the
171
+ * client leaving, as before.
172
+ */
173
+ function wsClosedCause(ws) {
174
+ if (!ws.isClosed) return void 0;
175
+ return wsCloseEmitted.get(ws) === false ? "server" : "client";
176
+ }
177
+ /**
178
+ * WebSocket turn lifecycle. A WS turn is always a stream, so it always owns
179
+ * the fixture interruption. A `close` means the client left. A socket already
180
+ * closed at construction aborts at once with {@link wsClosedCause}. Callers
181
+ * never create their own interruption object.
182
+ */
183
+ function wsLifecycle(ws, fixture) {
184
+ return createLifecycle(fixture ? createInterruptionSignal(fixture) : null, wsClosedCause(ws), (onGone) => subscribeClose(ws, () => onGone("client")));
185
+ }
186
+ /**
187
+ * Journal a WS turn that stopped because its lifecycle aborted or its socket
188
+ * closed. This is the closed-socket rule every WS transport uses: the
189
+ * lifecycle's reason when it has one, else the closed socket's cause
190
+ * ({@link wsClosedCause}): {@link AIMOCK_DISCONNECTED} for aimock's own close,
191
+ * {@link CLIENT_ABORTED} for the client. A turn with no lifecycle (it never
192
+ * waits) passes none.
193
+ */
194
+ function markWsTurnStopped(entry, ws, lifecycle) {
195
+ const cause = wsClosedCause(ws);
196
+ const socketReason = cause === "server" ? AIMOCK_DISCONNECTED : cause === "client" ? CLIENT_ABORTED : void 0;
197
+ markInterrupted(entry, lifecycle?.reason() ?? socketReason);
198
+ }
199
+ /**
200
+ * Mark a journal entry interrupted. The reason is set only when the entry has
201
+ * none, so a later `undefined` never erases an earlier reason. The status is
202
+ * never changed: the entry keeps the status it was journaled with.
203
+ */
204
+ function markInterrupted(entry, reason) {
205
+ entry.response.interrupted = true;
206
+ if (entry.response.interruptReason === void 0 && reason !== void 0) entry.response.interruptReason = reason;
207
+ }
208
+
209
+ //#endregion
210
+ export { AIMOCK_DISCONNECTED, CLIENT_ABORTED, closedByClient, httpLifecycle, markInterrupted, markWsTurnStopped, noteDeliveryStart, requestSocket, statusLineSent, teardownCause, teardownReason, watchWsClose, wsLifecycle };
211
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","names":[],"sources":["../../src/response/lifecycle.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { Fixture, JournalEntry } from \"../types.js\";\nimport type { WebSocketConnection } from \"../ws-framing.js\";\nimport { createInterruptionSignal, type InterruptionControl } from \"../interruption.js\";\nimport { responseGoneReason } from \"../chaos.js\";\n\n/** The journal `interruptReason` for a client that left before the reply finished. */\nexport const CLIENT_ABORTED = \"client aborted\";\n\n/**\n * The journal `interruptReason` for a response aimock tore down itself: the\n * chaos `disconnect` action or a server-side `destroy`. It is the name the\n * chaos journal entry records (`chaosAction: \"disconnect\"`), so a teardown\n * aimock caused is never blamed on the client.\n */\nexport const AIMOCK_DISCONNECTED = \"disconnect\";\n\n/**\n * Which side stopped a delivery first: the client, the fixture's own\n * interruption, or aimock tearing the response down (`\"server\"`).\n */\nexport type InterruptCause = \"client\" | \"fixture\" | \"server\";\n\n/**\n * One delivery's stop signal. It merges the fixture interruption\n * (`truncateAfterChunks` / `disconnectAfterMs`) with the client going away\n * and with aimock tearing the response down (`\"server\"`).\n * The first cause wins: a later cause never reclassifies it.\n */\nexport interface DeliveryLifecycle {\n /** Aborts on the first cause: fixture, client, or server. */\n readonly signal: AbortSignal;\n /** The cause that aborted `signal`, or `undefined` while it has not aborted. */\n cause(): InterruptCause | undefined;\n /**\n * The fixture's reason, {@link CLIENT_ABORTED}, {@link AIMOCK_DISCONNECTED},\n * or `undefined` while running.\n */\n reason(): string | undefined;\n /** Forwards a sent chunk to the fixture interruption (counts toward `truncateAfterChunks`). */\n tick(): void;\n /** Removes the close and fixture-abort listeners and clears the fixture's `disconnectAfterMs` timer. */\n dispose(): void;\n}\n\n/** The part of an EventEmitter a close source may expose. Test doubles may carry less. */\ninterface CloseSource {\n once?: (event: \"close\", listener: () => void) => unknown;\n off?: (event: \"close\", listener: () => void) => unknown;\n removeListener?: (event: \"close\", listener: () => void) => unknown;\n}\n\n/**\n * Subscribe to `close`, guarded the same way as `awaitChaosLatency`: a partial\n * test double without `once` is not subscribed, and only a listener that was\n * added is ever removed, through a remover that exists.\n */\nfunction subscribeClose(source: CloseSource, onClose: () => void): (() => void) | undefined {\n if (typeof source.once !== \"function\") return undefined;\n source.once(\"close\", onClose);\n if (typeof source.off === \"function\") return () => source.off?.(\"close\", onClose);\n if (typeof source.removeListener === \"function\")\n return () => source.removeListener?.(\"close\", onClose);\n return undefined;\n}\n\nfunction createLifecycle(\n interruption: InterruptionControl | null,\n goneAtStart: InterruptCause | undefined,\n subscribe: (onGone: (cause: InterruptCause) => void) => (() => void) | undefined,\n): DeliveryLifecycle {\n const controller = new AbortController();\n let firstCause: InterruptCause | undefined;\n const abortWith = (cause: InterruptCause): void => {\n if (controller.signal.aborted) return;\n firstCause = cause;\n controller.abort();\n };\n const onFixtureAbort = (): void => abortWith(\"fixture\");\n interruption?.signal.addEventListener(\"abort\", onFixtureAbort, { once: true });\n let unsubscribe: (() => void) | undefined;\n if (goneAtStart !== undefined) abortWith(goneAtStart);\n else unsubscribe = subscribe(abortWith);\n return {\n signal: controller.signal,\n cause: () => firstCause,\n reason: () => {\n if (firstCause === \"fixture\") return interruption?.reason();\n if (firstCause === \"client\") return CLIENT_ABORTED;\n if (firstCause === \"server\") return AIMOCK_DISCONNECTED;\n return undefined;\n },\n tick: () => {\n interruption?.tick();\n },\n dispose: () => {\n unsubscribe?.();\n unsubscribe = undefined;\n interruption?.signal.removeEventListener(\"abort\", onFixtureAbort);\n interruption?.cleanup();\n },\n };\n}\n\n/** The request socket fields the teardown rule and the status-line rule read. */\nexport interface PeerSocket {\n readonly errored?: Error | null;\n readonly writableEnded?: boolean;\n readonly bytesWritten?: number;\n}\n\n/** Where a response's request socket can be read from. Test doubles may carry neither. */\ninterface SocketOwner {\n readonly req?: { readonly socket?: PeerSocket | null };\n readonly socket?: PeerSocket | null;\n}\n\n/** The request socket of `res`, or `undefined` when it carries none. */\nexport function requestSocket(res: http.ServerResponse): PeerSocket | undefined {\n const owner: SocketOwner = res;\n return owner.req?.socket ?? owner.socket ?? undefined;\n}\n\n/**\n * THE client hang-up rule, shared by the delivery lifecycle and the proxy\n * relay: the client's socket closed or reset.\n *\n * - A reset, or a write into a socket the peer closed, leaves `errored` set\n * (ECONNRESET / EPIPE).\n * - aimock's server keeps Node's default `httpAllowHalfOpen: false`, so a\n * client FIN makes Node end our write side: the socket is `writableEnded`\n * while the response is not. That is the evidence the client closed.\n *\n * A FIN alone (`readableEnded` with neither of those) is not a hang-up: it is\n * what the socket shows when aimock tore the response down before Node\n * answered the FIN. A real client FIN that Node has not answered yet looks the\n * same, so this rule also classifies it as not a hang-up. aimock's own\n * `destroy()` leaves neither signal.\n */\nexport function closedByClient(socket: PeerSocket): boolean {\n return (socket.errored ?? null) !== null || socket.writableEnded === true;\n}\n\n/**\n * Who tore down a response that is gone without having ended. After the fact\n * `res.destroyed` reads the same either way, so this reads the evidence:\n *\n * - a chaos `disconnect` marked the response before destroying it: aimock;\n * - the request socket meets {@link closedByClient}: the client;\n * - neither: aimock destroyed it server-side.\n *\n * `socket` defaults to the response's request socket. With no socket to read\n * (a partial test double) the client attribution is kept.\n */\nexport function teardownCause(\n res: http.ServerResponse,\n socket: PeerSocket | null | undefined = requestSocket(res),\n): \"client\" | \"server\" {\n if (responseGoneReason(res) === \"chaos-disconnected\") return \"server\";\n if (!socket) return \"client\";\n return closedByClient(socket) ? \"client\" : \"server\";\n}\n\n/**\n * The request socket's `bytesWritten` when each delivery started, so the\n * status-line rule counts only this response's bytes on a keep-alive socket.\n */\nconst deliveryStartBytes = new WeakMap<http.ServerResponse, number>();\n\n/**\n * Record where this response's bytes start on its socket. A response whose\n * head was already committed when the delivery started is not recorded:\n * {@link statusLineSent} keeps the `headersSent` rule for it.\n */\nexport function noteDeliveryStart(res: http.ServerResponse): void {\n if (deliveryStartBytes.has(res) || res.headersSent) return;\n const bytes = requestSocket(res)?.bytesWritten;\n if (typeof bytes === \"number\") deliveryStartBytes.set(res, bytes);\n}\n\n/**\n * Whether this response's status line reached the client's socket: a byte of\n * it was handed to the socket after {@link noteDeliveryStart}. `headersSent`\n * is not that test: `writeHead` sets it before any byte is written. With no\n * recorded start, or no socket to read, `headersSent` is the fallback.\n */\nexport function statusLineSent(res: http.ServerResponse): boolean {\n const start = deliveryStartBytes.get(res);\n const bytes = requestSocket(res)?.bytesWritten;\n if (start === undefined || typeof bytes !== \"number\") return res.headersSent;\n return bytes > start;\n}\n\n/** The journal `interruptReason` for {@link teardownCause}. */\nexport function teardownReason(res: http.ServerResponse): string {\n return teardownCause(res) === \"client\" ? CLIENT_ABORTED : AIMOCK_DISCONNECTED;\n}\n\n/**\n * HTTP delivery lifecycle. A `close` before `res.writableFinished` aborts it,\n * and {@link teardownCause} decides whose teardown it was: the client\n * (`\"client\"`) or aimock itself (`\"server\"`). A response that is already gone\n * at construction aborts at once with the same attribution. A chaos\n * `disconnect` destroys without ending, so no `writableEnded` check stops a\n * writer for it. Only an already-ended response does not abort: the frame\n * loop exits on its `writableEnded` check. The fixture interruption is\n * created only when `streaming` is true (it is streaming-only).\n */\nexport function httpLifecycle(\n res: http.ServerResponse,\n fixture: Fixture | null,\n opts: { streaming: boolean },\n): DeliveryLifecycle {\n const interruption = opts.streaming && fixture ? createInterruptionSignal(fixture) : null;\n const gone = responseGoneReason(res);\n const goneAtStart = gone === null || gone === \"already-ended\" ? undefined : teardownCause(res);\n return createLifecycle(interruption, goneAtStart, (onGone) =>\n subscribeClose(res, () => {\n if (!res.writableFinished) onGone(teardownCause(res));\n }),\n );\n}\n\n/**\n * Per watched connection: whether its `close` event has fired. Only sockets\n * watched while open are recorded, so an entry is evidence, not a guess.\n */\nconst wsCloseEmitted = new WeakMap<WebSocketConnection, boolean>();\n\n/**\n * Start recording when `ws` emits `close`, so {@link wsClosedCause} can tell\n * aimock's own close from the client's. Each WS transport calls it when the\n * connection opens. Idempotent, and a no-op once closed.\n */\nexport function watchWsClose(ws: WebSocketConnection): void {\n if (ws.isClosed || wsCloseEmitted.has(ws)) return;\n const source: CloseSource = ws;\n if (typeof source.once !== \"function\") return;\n wsCloseEmitted.set(ws, false);\n source.once(\"close\", () => wsCloseEmitted.set(ws, true));\n}\n\n/**\n * Who closed a socket that reads `isClosed`, or `undefined` while it is open.\n *\n * `WebSocketConnection` marks itself closed and emits `close` in the same step\n * for a client close frame, a lost connection, and `destroy()`. Only aimock's\n * own `close()` (a write or buffer limit, a bad frame, a strict miss, shutdown)\n * marks it closed at once and emits `close` about 100ms later. So a socket\n * watched while open ({@link watchWsClose}) that is closed with no `close` yet\n * was closed by aimock (`\"server\"`). Any other closed socket reads as the\n * client leaving, as before.\n */\nexport function wsClosedCause(ws: WebSocketConnection): \"client\" | \"server\" | undefined {\n if (!ws.isClosed) return undefined;\n return wsCloseEmitted.get(ws) === false ? \"server\" : \"client\";\n}\n\n/**\n * WebSocket turn lifecycle. A WS turn is always a stream, so it always owns\n * the fixture interruption. A `close` means the client left. A socket already\n * closed at construction aborts at once with {@link wsClosedCause}. Callers\n * never create their own interruption object.\n */\nexport function wsLifecycle(ws: WebSocketConnection, fixture: Fixture | null): DeliveryLifecycle {\n const interruption = fixture ? createInterruptionSignal(fixture) : null;\n return createLifecycle(interruption, wsClosedCause(ws), (onGone) =>\n subscribeClose(ws, () => onGone(\"client\")),\n );\n}\n\n/**\n * Journal a WS turn that stopped because its lifecycle aborted or its socket\n * closed. This is the closed-socket rule every WS transport uses: the\n * lifecycle's reason when it has one, else the closed socket's cause\n * ({@link wsClosedCause}): {@link AIMOCK_DISCONNECTED} for aimock's own close,\n * {@link CLIENT_ABORTED} for the client. A turn with no lifecycle (it never\n * waits) passes none.\n */\nexport function markWsTurnStopped(\n entry: JournalEntry,\n ws: WebSocketConnection,\n lifecycle?: DeliveryLifecycle,\n): void {\n const cause = wsClosedCause(ws);\n const socketReason =\n cause === \"server\" ? AIMOCK_DISCONNECTED : cause === \"client\" ? CLIENT_ABORTED : undefined;\n markInterrupted(entry, lifecycle?.reason() ?? socketReason);\n}\n\n/**\n * Mark a journal entry interrupted. The reason is set only when the entry has\n * none, so a later `undefined` never erases an earlier reason. The status is\n * never changed: the entry keeps the status it was journaled with.\n */\nexport function markInterrupted(entry: JournalEntry, reason: string | undefined): void {\n entry.response.interrupted = true;\n if (entry.response.interruptReason === undefined && reason !== undefined) {\n entry.response.interruptReason = reason;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+
2
+ //#region src/response/policy.ts
3
+ /** Entry points choose whether a failed parse warns or becomes a CLI usage error. */
4
+ function parsePolicyEntry(key, value) {
5
+ if (key === "requiredHeaders") {
6
+ const normalized = typeof value === "string" ? value.toLowerCase() : void 0;
7
+ if (normalized === "off" || normalized === "enforce") return {
8
+ ok: true,
9
+ key,
10
+ value: normalized
11
+ };
12
+ return {
13
+ ok: false,
14
+ reason: "expected \"off\" or \"enforce\"."
15
+ };
16
+ }
17
+ if (key === "authErrors" || key === "modelCatalog") {
18
+ const normalized = typeof value === "string" ? value.toLowerCase() : void 0;
19
+ if (normalized === "legacy" || normalized === "native") return {
20
+ ok: true,
21
+ key,
22
+ value: normalized
23
+ };
24
+ return {
25
+ ok: false,
26
+ reason: "expected \"legacy\" or \"native\"."
27
+ };
28
+ }
29
+ return {
30
+ ok: false,
31
+ reason: "unknown policy key."
32
+ };
33
+ }
34
+ /** Store one parsed entry without widening either key's value type. */
35
+ function assignPolicyEntry(target, entry) {
36
+ if (entry.key === "requiredHeaders") target.requiredHeaders = entry.value;
37
+ else target[entry.key] = entry.value;
38
+ }
39
+ /** Parse each programmatic entry independently without changing the caller's object. */
40
+ function parsePolicy(value, warn) {
41
+ const result = {};
42
+ if (value === void 0) return result;
43
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
44
+ warn("Ignoring policy: expected an object.");
45
+ return result;
46
+ }
47
+ for (const [key, entry] of Object.entries(value)) {
48
+ const parsed = parsePolicyEntry(key, entry);
49
+ if (parsed.ok) assignPolicyEntry(result, parsed);
50
+ else warn(`Ignoring policy.${key}: ${parsed.reason}`);
51
+ }
52
+ return result;
53
+ }
54
+ /** A bad header entry does not erase a prior valid entry or other policy keys. */
55
+ function parsePolicyHeader(rawHeaders, warn) {
56
+ const result = {};
57
+ const header = rawHeaders?.["x-aimock-policy"];
58
+ if (header === void 0) return result;
59
+ const text = Array.isArray(header) ? header.join(",") : header;
60
+ for (const rawEntry of text.split(",")) {
61
+ const entry = rawEntry.trim();
62
+ const separator = entry.indexOf("=");
63
+ if (separator < 0) {
64
+ warn(`Ignoring policy entry ${JSON.stringify(entry)}: expected key=value.`);
65
+ continue;
66
+ }
67
+ const key = entry.slice(0, separator);
68
+ const parsed = parsePolicyEntry(key, entry.slice(separator + 1));
69
+ if (parsed.ok) assignPolicyEntry(result, parsed);
70
+ else warn(`Ignoring policy.${key}: ${parsed.reason}`);
71
+ }
72
+ return result;
73
+ }
74
+ /** Resolve from the direct-handler input, preserving the independent legacy strict bit. */
75
+ function resolvePolicy(defaults, rawHeaders, warn = (message) => defaults.logger.warn(message)) {
76
+ const strict = resolveStrictMode(defaults.strict, rawHeaders);
77
+ const server = parsePolicy(defaults.policy, warn);
78
+ const request = parsePolicyHeader(rawHeaders, warn);
79
+ return Object.freeze({
80
+ strict,
81
+ requiredHeaders: request.requiredHeaders ?? server.requiredHeaders ?? "off",
82
+ authErrors: request.authErrors ?? server.authErrors ?? "legacy",
83
+ modelCatalog: request.modelCatalog ?? server.modelCatalog ?? "legacy"
84
+ });
85
+ }
86
+ /** Preserve the legacy request strict header grammar and server fallback. */
87
+ function resolveStrictMode(serverDefault, rawHeaders) {
88
+ if (rawHeaders) {
89
+ const header = rawHeaders["x-aimock-strict"];
90
+ const rawVal = typeof header === "string" ? header : Array.isArray(header) ? header[0] : void 0;
91
+ if (typeof rawVal === "string") {
92
+ const val = rawVal.trim().toLowerCase();
93
+ if (val === "true" || val === "1") return true;
94
+ if (val === "false" || val === "0") return false;
95
+ }
96
+ }
97
+ return serverDefault ?? false;
98
+ }
99
+ /** Journal fields retain their existing omitted-versus-overridden shape. */
100
+ function strictOverrideField(serverDefault, rawHeaders) {
101
+ const effective = resolveStrictMode(serverDefault, rawHeaders);
102
+ if (effective !== (serverDefault ?? false)) return { strictOverride: effective };
103
+ return {};
104
+ }
105
+ /** Named legacy decisions share one header read at the caller's existing site. */
106
+ function resolveRequestDecisions(serverDefault, rawHeaders) {
107
+ const strict = resolveStrictMode(serverDefault, rawHeaders);
108
+ return Object.freeze({
109
+ strict,
110
+ fixtureMiss: strict,
111
+ upstream: !strict,
112
+ reasoningSuppression: strict,
113
+ thinkingRejection: strict,
114
+ mcpRecordingStrict: strict,
115
+ strictOverride: strict !== (serverDefault ?? false) ? strict : void 0
116
+ });
117
+ }
118
+
119
+ //#endregion
120
+ exports.assignPolicyEntry = assignPolicyEntry;
121
+ exports.parsePolicy = parsePolicy;
122
+ exports.parsePolicyEntry = parsePolicyEntry;
123
+ exports.resolvePolicy = resolvePolicy;
124
+ exports.resolveRequestDecisions = resolveRequestDecisions;
125
+ exports.strictOverrideField = strictOverrideField;
126
+ //# sourceMappingURL=policy.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.cjs","names":[],"sources":["../../src/response/policy.ts"],"sourcesContent":["import type { IncomingHttpHeaders } from \"node:http\";\nimport type { AimockPolicy, HandlerDefaults } from \"../types.js\";\n\n/** Entry points choose whether a failed parse warns or becomes a CLI usage error. */\nexport function parsePolicyEntry(\n key: string,\n value: unknown,\n):\n | { ok: true; key: \"requiredHeaders\"; value: \"off\" | \"enforce\" }\n | { ok: true; key: \"authErrors\" | \"modelCatalog\"; value: \"legacy\" | \"native\" }\n | { ok: false; reason: string } {\n if (key === \"requiredHeaders\") {\n const normalized = typeof value === \"string\" ? value.toLowerCase() : undefined;\n if (normalized === \"off\" || normalized === \"enforce\") {\n return { ok: true, key, value: normalized };\n }\n return { ok: false, reason: 'expected \"off\" or \"enforce\".' };\n }\n if (key === \"authErrors\" || key === \"modelCatalog\") {\n const normalized = typeof value === \"string\" ? value.toLowerCase() : undefined;\n if (normalized === \"legacy\" || normalized === \"native\") {\n return { ok: true, key, value: normalized };\n }\n return { ok: false, reason: 'expected \"legacy\" or \"native\".' };\n }\n return { ok: false, reason: \"unknown policy key.\" };\n}\n\n/** Store one parsed entry without widening either key's value type. */\nexport function assignPolicyEntry(\n target: AimockPolicy,\n entry: Exclude<ReturnType<typeof parsePolicyEntry>, { ok: false }>,\n): void {\n if (entry.key === \"requiredHeaders\") target.requiredHeaders = entry.value;\n else target[entry.key] = entry.value;\n}\n\n/** Parse each programmatic entry independently without changing the caller's object. */\nexport function parsePolicy(value: unknown, warn: (message: string) => void): AimockPolicy {\n const result: AimockPolicy = {};\n if (value === undefined) return result;\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n warn(\"Ignoring policy: expected an object.\");\n return result;\n }\n for (const [key, entry] of Object.entries(value)) {\n const parsed = parsePolicyEntry(key, entry);\n if (parsed.ok) assignPolicyEntry(result, parsed);\n else warn(`Ignoring policy.${key}: ${parsed.reason}`);\n }\n return result;\n}\n\n/** A bad header entry does not erase a prior valid entry or other policy keys. */\nexport function parsePolicyHeader(\n rawHeaders: IncomingHttpHeaders | undefined,\n warn: (message: string) => void,\n): AimockPolicy {\n const result: AimockPolicy = {};\n const header = rawHeaders?.[\"x-aimock-policy\"];\n if (header === undefined) return result;\n const text = Array.isArray(header) ? header.join(\",\") : header;\n for (const rawEntry of text.split(\",\")) {\n const entry = rawEntry.trim();\n const separator = entry.indexOf(\"=\");\n if (separator < 0) {\n warn(`Ignoring policy entry ${JSON.stringify(entry)}: expected key=value.`);\n continue;\n }\n const key = entry.slice(0, separator);\n const parsed = parsePolicyEntry(key, entry.slice(separator + 1));\n if (parsed.ok) assignPolicyEntry(result, parsed);\n else warn(`Ignoring policy.${key}: ${parsed.reason}`);\n }\n return result;\n}\n\n/** Resolve from the direct-handler input, preserving the independent legacy strict bit. */\nexport function resolvePolicy(\n defaults: HandlerDefaults,\n rawHeaders?: IncomingHttpHeaders,\n warn: (message: string) => void = (message) => defaults.logger.warn(message),\n) {\n const strict = resolveStrictMode(defaults.strict, rawHeaders);\n const server = parsePolicy(defaults.policy, warn);\n const request = parsePolicyHeader(rawHeaders, warn);\n return Object.freeze({\n strict,\n requiredHeaders: request.requiredHeaders ?? server.requiredHeaders ?? \"off\",\n authErrors: request.authErrors ?? server.authErrors ?? \"legacy\",\n modelCatalog: request.modelCatalog ?? server.modelCatalog ?? \"legacy\",\n });\n}\n\n/** Preserve the legacy request strict header grammar and server fallback. */\nexport function resolveStrictMode(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): boolean {\n if (rawHeaders) {\n const header = rawHeaders[\"x-aimock-strict\"];\n const rawVal =\n typeof header === \"string\" ? header : Array.isArray(header) ? header[0] : undefined;\n if (typeof rawVal === \"string\") {\n const val = rawVal.trim().toLowerCase();\n if (val === \"true\" || val === \"1\") return true;\n if (val === \"false\" || val === \"0\") return false;\n }\n }\n return serverDefault ?? false;\n}\n\n/** Journal fields retain their existing omitted-versus-overridden shape. */\nexport function strictOverrideField(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): { strictOverride?: boolean } {\n const effective = resolveStrictMode(serverDefault, rawHeaders);\n if (effective !== (serverDefault ?? false)) {\n return { strictOverride: effective };\n }\n return {};\n}\n\n/** Named legacy decisions share one header read at the caller's existing site. */\nexport function resolveRequestDecisions(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n) {\n const strict = resolveStrictMode(serverDefault, rawHeaders);\n return Object.freeze({\n strict,\n fixtureMiss: strict,\n upstream: !strict,\n reasoningSuppression: strict,\n thinkingRejection: strict,\n mcpRecordingStrict: strict,\n strictOverride: strict !== (serverDefault ?? false) ? strict : undefined,\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ //#region src/response/policy.ts
2
+ /** Entry points choose whether a failed parse warns or becomes a CLI usage error. */
3
+ function parsePolicyEntry(key, value) {
4
+ if (key === "requiredHeaders") {
5
+ const normalized = typeof value === "string" ? value.toLowerCase() : void 0;
6
+ if (normalized === "off" || normalized === "enforce") return {
7
+ ok: true,
8
+ key,
9
+ value: normalized
10
+ };
11
+ return {
12
+ ok: false,
13
+ reason: "expected \"off\" or \"enforce\"."
14
+ };
15
+ }
16
+ if (key === "authErrors" || key === "modelCatalog") {
17
+ const normalized = typeof value === "string" ? value.toLowerCase() : void 0;
18
+ if (normalized === "legacy" || normalized === "native") return {
19
+ ok: true,
20
+ key,
21
+ value: normalized
22
+ };
23
+ return {
24
+ ok: false,
25
+ reason: "expected \"legacy\" or \"native\"."
26
+ };
27
+ }
28
+ return {
29
+ ok: false,
30
+ reason: "unknown policy key."
31
+ };
32
+ }
33
+ /** Store one parsed entry without widening either key's value type. */
34
+ function assignPolicyEntry(target, entry) {
35
+ if (entry.key === "requiredHeaders") target.requiredHeaders = entry.value;
36
+ else target[entry.key] = entry.value;
37
+ }
38
+ /** Parse each programmatic entry independently without changing the caller's object. */
39
+ function parsePolicy(value, warn) {
40
+ const result = {};
41
+ if (value === void 0) return result;
42
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
43
+ warn("Ignoring policy: expected an object.");
44
+ return result;
45
+ }
46
+ for (const [key, entry] of Object.entries(value)) {
47
+ const parsed = parsePolicyEntry(key, entry);
48
+ if (parsed.ok) assignPolicyEntry(result, parsed);
49
+ else warn(`Ignoring policy.${key}: ${parsed.reason}`);
50
+ }
51
+ return result;
52
+ }
53
+ /** A bad header entry does not erase a prior valid entry or other policy keys. */
54
+ function parsePolicyHeader(rawHeaders, warn) {
55
+ const result = {};
56
+ const header = rawHeaders?.["x-aimock-policy"];
57
+ if (header === void 0) return result;
58
+ const text = Array.isArray(header) ? header.join(",") : header;
59
+ for (const rawEntry of text.split(",")) {
60
+ const entry = rawEntry.trim();
61
+ const separator = entry.indexOf("=");
62
+ if (separator < 0) {
63
+ warn(`Ignoring policy entry ${JSON.stringify(entry)}: expected key=value.`);
64
+ continue;
65
+ }
66
+ const key = entry.slice(0, separator);
67
+ const parsed = parsePolicyEntry(key, entry.slice(separator + 1));
68
+ if (parsed.ok) assignPolicyEntry(result, parsed);
69
+ else warn(`Ignoring policy.${key}: ${parsed.reason}`);
70
+ }
71
+ return result;
72
+ }
73
+ /** Resolve from the direct-handler input, preserving the independent legacy strict bit. */
74
+ function resolvePolicy(defaults, rawHeaders, warn = (message) => defaults.logger.warn(message)) {
75
+ const strict = resolveStrictMode(defaults.strict, rawHeaders);
76
+ const server = parsePolicy(defaults.policy, warn);
77
+ const request = parsePolicyHeader(rawHeaders, warn);
78
+ return Object.freeze({
79
+ strict,
80
+ requiredHeaders: request.requiredHeaders ?? server.requiredHeaders ?? "off",
81
+ authErrors: request.authErrors ?? server.authErrors ?? "legacy",
82
+ modelCatalog: request.modelCatalog ?? server.modelCatalog ?? "legacy"
83
+ });
84
+ }
85
+ /** Preserve the legacy request strict header grammar and server fallback. */
86
+ function resolveStrictMode(serverDefault, rawHeaders) {
87
+ if (rawHeaders) {
88
+ const header = rawHeaders["x-aimock-strict"];
89
+ const rawVal = typeof header === "string" ? header : Array.isArray(header) ? header[0] : void 0;
90
+ if (typeof rawVal === "string") {
91
+ const val = rawVal.trim().toLowerCase();
92
+ if (val === "true" || val === "1") return true;
93
+ if (val === "false" || val === "0") return false;
94
+ }
95
+ }
96
+ return serverDefault ?? false;
97
+ }
98
+ /** Journal fields retain their existing omitted-versus-overridden shape. */
99
+ function strictOverrideField(serverDefault, rawHeaders) {
100
+ const effective = resolveStrictMode(serverDefault, rawHeaders);
101
+ if (effective !== (serverDefault ?? false)) return { strictOverride: effective };
102
+ return {};
103
+ }
104
+ /** Named legacy decisions share one header read at the caller's existing site. */
105
+ function resolveRequestDecisions(serverDefault, rawHeaders) {
106
+ const strict = resolveStrictMode(serverDefault, rawHeaders);
107
+ return Object.freeze({
108
+ strict,
109
+ fixtureMiss: strict,
110
+ upstream: !strict,
111
+ reasoningSuppression: strict,
112
+ thinkingRejection: strict,
113
+ mcpRecordingStrict: strict,
114
+ strictOverride: strict !== (serverDefault ?? false) ? strict : void 0
115
+ });
116
+ }
117
+
118
+ //#endregion
119
+ export { assignPolicyEntry, parsePolicy, parsePolicyEntry, resolvePolicy, resolveRequestDecisions, strictOverrideField };
120
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","names":[],"sources":["../../src/response/policy.ts"],"sourcesContent":["import type { IncomingHttpHeaders } from \"node:http\";\nimport type { AimockPolicy, HandlerDefaults } from \"../types.js\";\n\n/** Entry points choose whether a failed parse warns or becomes a CLI usage error. */\nexport function parsePolicyEntry(\n key: string,\n value: unknown,\n):\n | { ok: true; key: \"requiredHeaders\"; value: \"off\" | \"enforce\" }\n | { ok: true; key: \"authErrors\" | \"modelCatalog\"; value: \"legacy\" | \"native\" }\n | { ok: false; reason: string } {\n if (key === \"requiredHeaders\") {\n const normalized = typeof value === \"string\" ? value.toLowerCase() : undefined;\n if (normalized === \"off\" || normalized === \"enforce\") {\n return { ok: true, key, value: normalized };\n }\n return { ok: false, reason: 'expected \"off\" or \"enforce\".' };\n }\n if (key === \"authErrors\" || key === \"modelCatalog\") {\n const normalized = typeof value === \"string\" ? value.toLowerCase() : undefined;\n if (normalized === \"legacy\" || normalized === \"native\") {\n return { ok: true, key, value: normalized };\n }\n return { ok: false, reason: 'expected \"legacy\" or \"native\".' };\n }\n return { ok: false, reason: \"unknown policy key.\" };\n}\n\n/** Store one parsed entry without widening either key's value type. */\nexport function assignPolicyEntry(\n target: AimockPolicy,\n entry: Exclude<ReturnType<typeof parsePolicyEntry>, { ok: false }>,\n): void {\n if (entry.key === \"requiredHeaders\") target.requiredHeaders = entry.value;\n else target[entry.key] = entry.value;\n}\n\n/** Parse each programmatic entry independently without changing the caller's object. */\nexport function parsePolicy(value: unknown, warn: (message: string) => void): AimockPolicy {\n const result: AimockPolicy = {};\n if (value === undefined) return result;\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n warn(\"Ignoring policy: expected an object.\");\n return result;\n }\n for (const [key, entry] of Object.entries(value)) {\n const parsed = parsePolicyEntry(key, entry);\n if (parsed.ok) assignPolicyEntry(result, parsed);\n else warn(`Ignoring policy.${key}: ${parsed.reason}`);\n }\n return result;\n}\n\n/** A bad header entry does not erase a prior valid entry or other policy keys. */\nexport function parsePolicyHeader(\n rawHeaders: IncomingHttpHeaders | undefined,\n warn: (message: string) => void,\n): AimockPolicy {\n const result: AimockPolicy = {};\n const header = rawHeaders?.[\"x-aimock-policy\"];\n if (header === undefined) return result;\n const text = Array.isArray(header) ? header.join(\",\") : header;\n for (const rawEntry of text.split(\",\")) {\n const entry = rawEntry.trim();\n const separator = entry.indexOf(\"=\");\n if (separator < 0) {\n warn(`Ignoring policy entry ${JSON.stringify(entry)}: expected key=value.`);\n continue;\n }\n const key = entry.slice(0, separator);\n const parsed = parsePolicyEntry(key, entry.slice(separator + 1));\n if (parsed.ok) assignPolicyEntry(result, parsed);\n else warn(`Ignoring policy.${key}: ${parsed.reason}`);\n }\n return result;\n}\n\n/** Resolve from the direct-handler input, preserving the independent legacy strict bit. */\nexport function resolvePolicy(\n defaults: HandlerDefaults,\n rawHeaders?: IncomingHttpHeaders,\n warn: (message: string) => void = (message) => defaults.logger.warn(message),\n) {\n const strict = resolveStrictMode(defaults.strict, rawHeaders);\n const server = parsePolicy(defaults.policy, warn);\n const request = parsePolicyHeader(rawHeaders, warn);\n return Object.freeze({\n strict,\n requiredHeaders: request.requiredHeaders ?? server.requiredHeaders ?? \"off\",\n authErrors: request.authErrors ?? server.authErrors ?? \"legacy\",\n modelCatalog: request.modelCatalog ?? server.modelCatalog ?? \"legacy\",\n });\n}\n\n/** Preserve the legacy request strict header grammar and server fallback. */\nexport function resolveStrictMode(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): boolean {\n if (rawHeaders) {\n const header = rawHeaders[\"x-aimock-strict\"];\n const rawVal =\n typeof header === \"string\" ? header : Array.isArray(header) ? header[0] : undefined;\n if (typeof rawVal === \"string\") {\n const val = rawVal.trim().toLowerCase();\n if (val === \"true\" || val === \"1\") return true;\n if (val === \"false\" || val === \"0\") return false;\n }\n }\n return serverDefault ?? false;\n}\n\n/** Journal fields retain their existing omitted-versus-overridden shape. */\nexport function strictOverrideField(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): { strictOverride?: boolean } {\n const effective = resolveStrictMode(serverDefault, rawHeaders);\n if (effective !== (serverDefault ?? false)) {\n return { strictOverride: effective };\n }\n return {};\n}\n\n/** Named legacy decisions share one header read at the caller's existing site. */\nexport function resolveRequestDecisions(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n) {\n const strict = resolveStrictMode(serverDefault, rawHeaders);\n return Object.freeze({\n strict,\n fixtureMiss: strict,\n upstream: !strict,\n reasoningSuppression: strict,\n thinkingRejection: strict,\n mcpRecordingStrict: strict,\n strictOverride: strict !== (serverDefault ?? false) ? strict : undefined,\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+
2
+ //#region src/response/profiles.ts
3
+ /** Inbound HTTP auth uses only provider-owned paths; shared routes stay legacy. */
4
+ function authErrorDialectForPath(pathname) {
5
+ if (pathname === "/v1/messages" || pathname === "/v1/messages/count_tokens") return "messages";
6
+ if (pathname.startsWith("/v1beta/") || /^\/v1\/projects\/[^/]+\/locations\/[^/]+\/publishers\/google\//.test(pathname)) return "gemini";
7
+ return "openai";
8
+ }
9
+ /**
10
+ * Shared catalog response dialect. The list route uses it only under
11
+ * `policy.modelCatalog: "native"`. Auth retains its pathname-selected dialect.
12
+ */
13
+ function isAnthropicModelRequest(headers) {
14
+ return headers["anthropic-version"] !== void 0 || headers["x-api-key"] !== void 0 && headers.authorization === void 0;
15
+ }
16
+ /** Static wire facts only. Status selection and transport behavior belong to callers. */
17
+ const errorProfiles = {
18
+ openai: {
19
+ fixtureType: "server_error",
20
+ validationType: "invalid_request_error"
21
+ },
22
+ messages: {
23
+ fixtureType: "api_error",
24
+ validationType: "invalid_request_error",
25
+ requiredHeaders: ["anthropic-version"]
26
+ },
27
+ bedrock: {
28
+ fixtureType: "api_error",
29
+ validationType: "invalid_request_error",
30
+ toolArgumentType: "InternalServerException"
31
+ },
32
+ "bedrock-converse": {
33
+ fixtureType: "api_error",
34
+ validationType: "invalid_request_error",
35
+ toolArgumentType: "InternalServerException"
36
+ },
37
+ ollama: {
38
+ fixtureType: "server_error",
39
+ validationType: "invalid_request_error"
40
+ },
41
+ "ollama-embed": {
42
+ fixtureType: "server_error",
43
+ validationType: "invalid_request_error"
44
+ },
45
+ cohere: {
46
+ fixtureType: "server_error",
47
+ validationType: "invalid_request_error"
48
+ },
49
+ interactions: {
50
+ fixtureType: "server_error",
51
+ validationType: "invalid_request_error"
52
+ },
53
+ gemini: {
54
+ fixtureType: "server_error",
55
+ validationType: "invalid_request_error"
56
+ }
57
+ };
58
+ /** Bedrock's body-level version and other wire families have no header requirement. */
59
+ function missingRequiredHeader(wire, headers) {
60
+ if (wire !== "messages") return;
61
+ return errorProfiles.messages.requiredHeaders.find((name) => headers[name] === void 0);
62
+ }
63
+
64
+ //#endregion
65
+ exports.authErrorDialectForPath = authErrorDialectForPath;
66
+ exports.errorProfiles = errorProfiles;
67
+ exports.isAnthropicModelRequest = isAnthropicModelRequest;
68
+ exports.missingRequiredHeader = missingRequiredHeader;
69
+ //# sourceMappingURL=profiles.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiles.cjs","names":[],"sources":["../../src/response/profiles.ts"],"sourcesContent":["import type { IncomingHttpHeaders } from \"node:http\";\n\n/** Inbound HTTP auth uses only provider-owned paths; shared routes stay legacy. */\nexport function authErrorDialectForPath(pathname: string): \"messages\" | \"gemini\" | \"openai\" {\n if (pathname === \"/v1/messages\" || pathname === \"/v1/messages/count_tokens\") return \"messages\";\n if (\n pathname.startsWith(\"/v1beta/\") ||\n /^\\/v1\\/projects\\/[^/]+\\/locations\\/[^/]+\\/publishers\\/google\\//.test(pathname)\n )\n return \"gemini\";\n return \"openai\";\n}\n\n/**\n * Shared catalog response dialect. The list route uses it only under\n * `policy.modelCatalog: \"native\"`. Auth retains its pathname-selected dialect.\n */\nexport function isAnthropicModelRequest(headers: IncomingHttpHeaders): boolean {\n return (\n headers[\"anthropic-version\"] !== undefined ||\n (headers[\"x-api-key\"] !== undefined && headers.authorization === undefined)\n );\n}\n\n/** Static wire facts only. Status selection and transport behavior belong to callers. */\nexport const errorProfiles = {\n openai: { fixtureType: \"server_error\", validationType: \"invalid_request_error\" },\n messages: {\n fixtureType: \"api_error\",\n validationType: \"invalid_request_error\",\n requiredHeaders: [\"anthropic-version\"],\n },\n bedrock: {\n fixtureType: \"api_error\",\n validationType: \"invalid_request_error\",\n toolArgumentType: \"InternalServerException\",\n },\n \"bedrock-converse\": {\n fixtureType: \"api_error\",\n validationType: \"invalid_request_error\",\n toolArgumentType: \"InternalServerException\",\n },\n ollama: { fixtureType: \"server_error\", validationType: \"invalid_request_error\" },\n \"ollama-embed\": { fixtureType: \"server_error\", validationType: \"invalid_request_error\" },\n cohere: { fixtureType: \"server_error\", validationType: \"invalid_request_error\" },\n interactions: { fixtureType: \"server_error\", validationType: \"invalid_request_error\" },\n gemini: { fixtureType: \"server_error\", validationType: \"invalid_request_error\" },\n} as const;\n\nexport type ErrorWire = keyof typeof errorProfiles;\n\n/** Bedrock's body-level version and other wire families have no header requirement. */\nexport function missingRequiredHeader(\n wire: ErrorWire,\n headers: IncomingHttpHeaders,\n): string | undefined {\n if (wire !== \"messages\") return;\n return errorProfiles.messages.requiredHeaders.find((name) => headers[name] === undefined);\n}\n"],"mappings":";;;AAGA,SAAgB,wBAAwB,UAAoD;AAC1F,KAAI,aAAa,kBAAkB,aAAa,4BAA6B,QAAO;AACpF,KACE,SAAS,WAAW,WAAW,IAC/B,iEAAiE,KAAK,SAAS,CAE/E,QAAO;AACT,QAAO;;;;;;AAOT,SAAgB,wBAAwB,SAAuC;AAC7E,QACE,QAAQ,yBAAyB,UAChC,QAAQ,iBAAiB,UAAa,QAAQ,kBAAkB;;;AAKrE,MAAa,gBAAgB;CAC3B,QAAQ;EAAE,aAAa;EAAgB,gBAAgB;EAAyB;CAChF,UAAU;EACR,aAAa;EACb,gBAAgB;EAChB,iBAAiB,CAAC,oBAAoB;EACvC;CACD,SAAS;EACP,aAAa;EACb,gBAAgB;EAChB,kBAAkB;EACnB;CACD,oBAAoB;EAClB,aAAa;EACb,gBAAgB;EAChB,kBAAkB;EACnB;CACD,QAAQ;EAAE,aAAa;EAAgB,gBAAgB;EAAyB;CAChF,gBAAgB;EAAE,aAAa;EAAgB,gBAAgB;EAAyB;CACxF,QAAQ;EAAE,aAAa;EAAgB,gBAAgB;EAAyB;CAChF,cAAc;EAAE,aAAa;EAAgB,gBAAgB;EAAyB;CACtF,QAAQ;EAAE,aAAa;EAAgB,gBAAgB;EAAyB;CACjF;;AAKD,SAAgB,sBACd,MACA,SACoB;AACpB,KAAI,SAAS,WAAY;AACzB,QAAO,cAAc,SAAS,gBAAgB,MAAM,SAAS,QAAQ,UAAU,OAAU"}