@copilotkit/aimock 1.42.1 → 1.44.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 (442) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +65 -0
  4. package/README.md +108 -16
  5. package/dist/agui-handler.d.ts.map +1 -1
  6. package/dist/agui-mock.cjs +1 -1
  7. package/dist/agui-mock.cjs.map +1 -1
  8. package/dist/agui-mock.d.cts.map +1 -1
  9. package/dist/agui-mock.d.ts.map +1 -1
  10. package/dist/agui-mock.js +1 -1
  11. package/dist/agui-mock.js.map +1 -1
  12. package/dist/agui-recorder.cjs +33 -11
  13. package/dist/agui-recorder.cjs.map +1 -1
  14. package/dist/agui-recorder.d.cts +10 -1
  15. package/dist/agui-recorder.d.cts.map +1 -1
  16. package/dist/agui-recorder.d.ts +10 -1
  17. package/dist/agui-recorder.d.ts.map +1 -1
  18. package/dist/agui-recorder.js +33 -11
  19. package/dist/agui-recorder.js.map +1 -1
  20. package/dist/agui-types.d.cts +1 -0
  21. package/dist/agui-types.d.cts.map +1 -1
  22. package/dist/agui-types.d.ts +1 -0
  23. package/dist/agui-types.d.ts.map +1 -1
  24. package/dist/aimock-cli.cjs +38 -10
  25. package/dist/aimock-cli.cjs.map +1 -1
  26. package/dist/aimock-cli.d.cts +5 -0
  27. package/dist/aimock-cli.d.cts.map +1 -1
  28. package/dist/aimock-cli.d.ts +5 -0
  29. package/dist/aimock-cli.d.ts.map +1 -1
  30. package/dist/aimock-cli.js +38 -10
  31. package/dist/aimock-cli.js.map +1 -1
  32. package/dist/batches.cjs +458 -0
  33. package/dist/batches.cjs.map +1 -0
  34. package/dist/batches.d.cts +57 -0
  35. package/dist/batches.d.cts.map +1 -0
  36. package/dist/batches.d.ts +57 -0
  37. package/dist/batches.d.ts.map +1 -0
  38. package/dist/batches.js +453 -0
  39. package/dist/batches.js.map +1 -0
  40. package/dist/bedrock-converse.cjs +44 -12
  41. package/dist/bedrock-converse.cjs.map +1 -1
  42. package/dist/bedrock-converse.d.cts.map +1 -1
  43. package/dist/bedrock-converse.d.ts.map +1 -1
  44. package/dist/bedrock-converse.js +46 -14
  45. package/dist/bedrock-converse.js.map +1 -1
  46. package/dist/bedrock.cjs +10 -8
  47. package/dist/bedrock.cjs.map +1 -1
  48. package/dist/bedrock.d.cts.map +1 -1
  49. package/dist/bedrock.d.ts.map +1 -1
  50. package/dist/bedrock.js +12 -10
  51. package/dist/bedrock.js.map +1 -1
  52. package/dist/byteplus-video.cjs +3 -3
  53. package/dist/byteplus-video.cjs.map +1 -1
  54. package/dist/byteplus-video.js +4 -4
  55. package/dist/byteplus-video.js.map +1 -1
  56. package/dist/chaos.cjs +625 -72
  57. package/dist/chaos.cjs.map +1 -1
  58. package/dist/chaos.d.cts +164 -7
  59. package/dist/chaos.d.cts.map +1 -1
  60. package/dist/chaos.d.ts +164 -7
  61. package/dist/chaos.d.ts.map +1 -1
  62. package/dist/chaos.js +616 -74
  63. package/dist/chaos.js.map +1 -1
  64. package/dist/cli.cjs +131 -30
  65. package/dist/cli.cjs.map +1 -1
  66. package/dist/cli.js +132 -31
  67. package/dist/cli.js.map +1 -1
  68. package/dist/cohere.cjs +37 -9
  69. package/dist/cohere.cjs.map +1 -1
  70. package/dist/cohere.d.cts.map +1 -1
  71. package/dist/cohere.d.ts.map +1 -1
  72. package/dist/cohere.js +39 -11
  73. package/dist/cohere.js.map +1 -1
  74. package/dist/config-loader.cjs +1 -1
  75. package/dist/config-loader.js +1 -1
  76. package/dist/constants.cjs +8 -0
  77. package/dist/constants.cjs.map +1 -1
  78. package/dist/constants.d.cts +6 -2
  79. package/dist/constants.d.ts +6 -2
  80. package/dist/constants.js +8 -1
  81. package/dist/constants.js.map +1 -1
  82. package/dist/convert.cjs +1 -1
  83. package/dist/convert.cjs.map +1 -1
  84. package/dist/convert.js +1 -1
  85. package/dist/convert.js.map +1 -1
  86. package/dist/echo-text.cjs +65 -0
  87. package/dist/echo-text.cjs.map +1 -0
  88. package/dist/echo-text.d.cts +22 -0
  89. package/dist/echo-text.d.cts.map +1 -0
  90. package/dist/echo-text.d.ts +22 -0
  91. package/dist/echo-text.d.ts.map +1 -0
  92. package/dist/echo-text.js +65 -0
  93. package/dist/echo-text.js.map +1 -0
  94. package/dist/elevenlabs-audio.cjs +3 -3
  95. package/dist/elevenlabs-audio.cjs.map +1 -1
  96. package/dist/elevenlabs-audio.js +4 -4
  97. package/dist/elevenlabs-audio.js.map +1 -1
  98. package/dist/elevenlabs-voice.cjs +1053 -0
  99. package/dist/elevenlabs-voice.cjs.map +1 -0
  100. package/dist/elevenlabs-voice.d.cts +72 -0
  101. package/dist/elevenlabs-voice.d.cts.map +1 -0
  102. package/dist/elevenlabs-voice.d.ts +72 -0
  103. package/dist/elevenlabs-voice.d.ts.map +1 -0
  104. package/dist/elevenlabs-voice.js +1048 -0
  105. package/dist/elevenlabs-voice.js.map +1 -0
  106. package/dist/embeddings.cjs +26 -5
  107. package/dist/embeddings.cjs.map +1 -1
  108. package/dist/embeddings.d.cts +1 -1
  109. package/dist/embeddings.d.cts.map +1 -1
  110. package/dist/embeddings.d.ts +1 -1
  111. package/dist/embeddings.d.ts.map +1 -1
  112. package/dist/embeddings.js +27 -6
  113. package/dist/embeddings.js.map +1 -1
  114. package/dist/fal-audio.cjs +3 -3
  115. package/dist/fal-audio.cjs.map +1 -1
  116. package/dist/fal-audio.js +4 -4
  117. package/dist/fal-audio.js.map +1 -1
  118. package/dist/fal.cjs +28 -4
  119. package/dist/fal.cjs.map +1 -1
  120. package/dist/fal.d.cts +16 -1
  121. package/dist/fal.d.cts.map +1 -1
  122. package/dist/fal.d.ts +16 -1
  123. package/dist/fal.d.ts.map +1 -1
  124. package/dist/fal.js +28 -5
  125. package/dist/fal.js.map +1 -1
  126. package/dist/files.cjs +1069 -0
  127. package/dist/files.cjs.map +1 -0
  128. package/dist/files.d.cts +68 -0
  129. package/dist/files.d.cts.map +1 -0
  130. package/dist/files.d.ts +68 -0
  131. package/dist/files.d.ts.map +1 -0
  132. package/dist/files.js +1056 -0
  133. package/dist/files.js.map +1 -0
  134. package/dist/fine-tuning.cjs +1033 -0
  135. package/dist/fine-tuning.cjs.map +1 -0
  136. package/dist/fine-tuning.d.cts +96 -0
  137. package/dist/fine-tuning.d.cts.map +1 -0
  138. package/dist/fine-tuning.d.ts +96 -0
  139. package/dist/fine-tuning.d.ts.map +1 -0
  140. package/dist/fine-tuning.js +1026 -0
  141. package/dist/fine-tuning.js.map +1 -0
  142. package/dist/fixture-loader-services.cjs +116 -0
  143. package/dist/fixture-loader-services.cjs.map +1 -0
  144. package/dist/fixture-loader-services.d.cts +48 -0
  145. package/dist/fixture-loader-services.d.cts.map +1 -0
  146. package/dist/fixture-loader-services.d.ts +48 -0
  147. package/dist/fixture-loader-services.d.ts.map +1 -0
  148. package/dist/fixture-loader-services.js +114 -0
  149. package/dist/fixture-loader-services.js.map +1 -0
  150. package/dist/fixture-loader.cjs +0 -0
  151. package/dist/fixture-loader.cjs.map +1 -1
  152. package/dist/fixture-loader.d.cts +131 -5
  153. package/dist/fixture-loader.d.cts.map +1 -1
  154. package/dist/fixture-loader.d.ts +131 -5
  155. package/dist/fixture-loader.d.ts.map +1 -1
  156. package/dist/fixture-loader.js +0 -0
  157. package/dist/fixture-loader.js.map +1 -1
  158. package/dist/gemini-embeddings.cjs +44 -4
  159. package/dist/gemini-embeddings.cjs.map +1 -1
  160. package/dist/gemini-embeddings.js +46 -6
  161. package/dist/gemini-embeddings.js.map +1 -1
  162. package/dist/gemini-interactions.cjs +51 -7
  163. package/dist/gemini-interactions.cjs.map +1 -1
  164. package/dist/gemini-interactions.d.cts.map +1 -1
  165. package/dist/gemini-interactions.d.ts.map +1 -1
  166. package/dist/gemini-interactions.js +53 -9
  167. package/dist/gemini-interactions.js.map +1 -1
  168. package/dist/gemini.cjs +19 -8
  169. package/dist/gemini.cjs.map +1 -1
  170. package/dist/gemini.d.cts.map +1 -1
  171. package/dist/gemini.d.ts.map +1 -1
  172. package/dist/gemini.js +21 -10
  173. package/dist/gemini.js.map +1 -1
  174. package/dist/grok-video.cjs +14 -14
  175. package/dist/grok-video.cjs.map +1 -1
  176. package/dist/grok-video.js +15 -15
  177. package/dist/grok-video.js.map +1 -1
  178. package/dist/helpers.cjs +497 -10
  179. package/dist/helpers.cjs.map +1 -1
  180. package/dist/helpers.d.cts +38 -2
  181. package/dist/helpers.d.cts.map +1 -1
  182. package/dist/helpers.d.ts +38 -2
  183. package/dist/helpers.d.ts.map +1 -1
  184. package/dist/helpers.js +487 -12
  185. package/dist/helpers.js.map +1 -1
  186. package/dist/images.cjs +57 -125
  187. package/dist/images.cjs.map +1 -1
  188. package/dist/images.d.cts.map +1 -1
  189. package/dist/images.d.ts.map +1 -1
  190. package/dist/images.js +58 -126
  191. package/dist/images.js.map +1 -1
  192. package/dist/index.cjs +71 -9
  193. package/dist/index.d.cts +17 -7
  194. package/dist/index.d.ts +17 -7
  195. package/dist/index.js +21 -11
  196. package/dist/jest.cjs +24 -12
  197. package/dist/jest.cjs.map +1 -1
  198. package/dist/jest.d.cts +6 -3
  199. package/dist/jest.d.cts.map +1 -1
  200. package/dist/jest.d.ts +6 -3
  201. package/dist/jest.d.ts.map +1 -1
  202. package/dist/jest.js +25 -13
  203. package/dist/jest.js.map +1 -1
  204. package/dist/journal.cjs +61 -4
  205. package/dist/journal.cjs.map +1 -1
  206. package/dist/journal.d.cts +60 -3
  207. package/dist/journal.d.cts.map +1 -1
  208. package/dist/journal.d.ts +60 -3
  209. package/dist/journal.d.ts.map +1 -1
  210. package/dist/journal.js +61 -5
  211. package/dist/journal.js.map +1 -1
  212. package/dist/live-fixture.cjs +1192 -0
  213. package/dist/live-fixture.cjs.map +1 -0
  214. package/dist/live-fixture.js +1187 -0
  215. package/dist/live-fixture.js.map +1 -0
  216. package/dist/live-recorder.cjs +256 -0
  217. package/dist/live-recorder.cjs.map +1 -0
  218. package/dist/live-recorder.js +254 -0
  219. package/dist/live-recorder.js.map +1 -0
  220. package/dist/live-replay.cjs +286 -0
  221. package/dist/live-replay.cjs.map +1 -0
  222. package/dist/live-replay.js +285 -0
  223. package/dist/live-replay.js.map +1 -0
  224. package/dist/live-sanitize.cjs +196 -0
  225. package/dist/live-sanitize.cjs.map +1 -0
  226. package/dist/live-sanitize.js +196 -0
  227. package/dist/live-sanitize.js.map +1 -0
  228. package/dist/live-types.d.cts +71 -0
  229. package/dist/live-types.d.cts.map +1 -0
  230. package/dist/live-types.d.ts +71 -0
  231. package/dist/live-types.d.ts.map +1 -0
  232. package/dist/llmock.cjs +132 -49
  233. package/dist/llmock.cjs.map +1 -1
  234. package/dist/llmock.d.cts +51 -5
  235. package/dist/llmock.d.cts.map +1 -1
  236. package/dist/llmock.d.ts +51 -5
  237. package/dist/llmock.d.ts.map +1 -1
  238. package/dist/llmock.js +133 -50
  239. package/dist/llmock.js.map +1 -1
  240. package/dist/mcp-fakes-mount.cjs +216 -0
  241. package/dist/mcp-fakes-mount.cjs.map +1 -0
  242. package/dist/mcp-fakes-mount.js +210 -0
  243. package/dist/mcp-fakes-mount.js.map +1 -0
  244. package/dist/mcp-fakes.cjs +1619 -0
  245. package/dist/mcp-fakes.cjs.map +1 -0
  246. package/dist/mcp-fakes.d.cts +101 -0
  247. package/dist/mcp-fakes.d.cts.map +1 -0
  248. package/dist/mcp-fakes.d.ts +101 -0
  249. package/dist/mcp-fakes.d.ts.map +1 -0
  250. package/dist/mcp-fakes.js +1614 -0
  251. package/dist/mcp-fakes.js.map +1 -0
  252. package/dist/mcp-handler.cjs +331 -17
  253. package/dist/mcp-handler.cjs.map +1 -1
  254. package/dist/mcp-handler.js +330 -18
  255. package/dist/mcp-handler.js.map +1 -1
  256. package/dist/mcp-mock.cjs +217 -49
  257. package/dist/mcp-mock.cjs.map +1 -1
  258. package/dist/mcp-mock.d.cts +66 -1
  259. package/dist/mcp-mock.d.cts.map +1 -1
  260. package/dist/mcp-mock.d.ts +66 -1
  261. package/dist/mcp-mock.d.ts.map +1 -1
  262. package/dist/mcp-mock.js +218 -50
  263. package/dist/mcp-mock.js.map +1 -1
  264. package/dist/mcp-stub.cjs +5 -1
  265. package/dist/mcp-stub.d.cts +3 -1
  266. package/dist/mcp-stub.d.ts +3 -1
  267. package/dist/mcp-stub.js +3 -1
  268. package/dist/mcp-types.d.cts +6 -0
  269. package/dist/mcp-types.d.ts +6 -0
  270. package/dist/message-text.cjs +192 -0
  271. package/dist/message-text.cjs.map +1 -0
  272. package/dist/message-text.d.cts +31 -0
  273. package/dist/message-text.d.cts.map +1 -0
  274. package/dist/message-text.d.ts +31 -0
  275. package/dist/message-text.d.ts.map +1 -0
  276. package/dist/message-text.js +184 -0
  277. package/dist/message-text.js.map +1 -0
  278. package/dist/messages.cjs +49 -7
  279. package/dist/messages.cjs.map +1 -1
  280. package/dist/messages.d.cts.map +1 -1
  281. package/dist/messages.d.ts.map +1 -1
  282. package/dist/messages.js +51 -9
  283. package/dist/messages.js.map +1 -1
  284. package/dist/metrics.cjs +278 -45
  285. package/dist/metrics.cjs.map +1 -1
  286. package/dist/metrics.d.cts +17 -7
  287. package/dist/metrics.d.cts.map +1 -1
  288. package/dist/metrics.d.ts +17 -7
  289. package/dist/metrics.d.ts.map +1 -1
  290. package/dist/metrics.js +270 -29
  291. package/dist/metrics.js.map +1 -1
  292. package/dist/moderation.cjs +10 -1
  293. package/dist/moderation.cjs.map +1 -1
  294. package/dist/moderation.d.cts.map +1 -1
  295. package/dist/moderation.d.ts.map +1 -1
  296. package/dist/moderation.js +10 -1
  297. package/dist/moderation.js.map +1 -1
  298. package/dist/ollama.cjs +10 -6
  299. package/dist/ollama.cjs.map +1 -1
  300. package/dist/ollama.d.cts.map +1 -1
  301. package/dist/ollama.d.ts.map +1 -1
  302. package/dist/ollama.js +11 -7
  303. package/dist/ollama.js.map +1 -1
  304. package/dist/openapi.cjs +3405 -0
  305. package/dist/openapi.cjs.map +1 -0
  306. package/dist/openapi.d.cts +30 -0
  307. package/dist/openapi.d.cts.map +1 -0
  308. package/dist/openapi.d.ts +30 -0
  309. package/dist/openapi.d.ts.map +1 -0
  310. package/dist/openapi.js +3404 -0
  311. package/dist/openapi.js.map +1 -0
  312. package/dist/openrouter-video.cjs +9 -9
  313. package/dist/openrouter-video.cjs.map +1 -1
  314. package/dist/openrouter-video.d.cts.map +1 -1
  315. package/dist/openrouter-video.d.ts.map +1 -1
  316. package/dist/openrouter-video.js +10 -10
  317. package/dist/openrouter-video.js.map +1 -1
  318. package/dist/recorder.cjs +54 -10
  319. package/dist/recorder.cjs.map +1 -1
  320. package/dist/recorder.d.cts.map +1 -1
  321. package/dist/recorder.d.ts.map +1 -1
  322. package/dist/recorder.js +52 -12
  323. package/dist/recorder.js.map +1 -1
  324. package/dist/responses.cjs +47 -7
  325. package/dist/responses.cjs.map +1 -1
  326. package/dist/responses.d.cts.map +1 -1
  327. package/dist/responses.d.ts.map +1 -1
  328. package/dist/responses.js +48 -9
  329. package/dist/responses.js.map +1 -1
  330. package/dist/route-registry.cjs +951 -0
  331. package/dist/route-registry.cjs.map +1 -0
  332. package/dist/route-registry.d.cts +33 -0
  333. package/dist/route-registry.d.cts.map +1 -0
  334. package/dist/route-registry.d.ts +33 -0
  335. package/dist/route-registry.d.ts.map +1 -0
  336. package/dist/route-registry.js +866 -0
  337. package/dist/route-registry.js.map +1 -0
  338. package/dist/router.cjs +21 -34
  339. package/dist/router.cjs.map +1 -1
  340. package/dist/router.d.cts +4 -1
  341. package/dist/router.d.cts.map +1 -1
  342. package/dist/router.d.ts +4 -1
  343. package/dist/router.d.ts.map +1 -1
  344. package/dist/router.js +21 -35
  345. package/dist/router.js.map +1 -1
  346. package/dist/server.cjs +1252 -777
  347. package/dist/server.cjs.map +1 -1
  348. package/dist/server.d.cts +5 -1
  349. package/dist/server.d.cts.map +1 -1
  350. package/dist/server.d.ts +5 -1
  351. package/dist/server.d.ts.map +1 -1
  352. package/dist/server.js +1185 -710
  353. package/dist/server.js.map +1 -1
  354. package/dist/speech.cjs +6 -4
  355. package/dist/speech.cjs.map +1 -1
  356. package/dist/speech.js +7 -5
  357. package/dist/speech.js.map +1 -1
  358. package/dist/suite.cjs +1 -1
  359. package/dist/suite.js +1 -1
  360. package/dist/transcription.cjs +6 -6
  361. package/dist/transcription.cjs.map +1 -1
  362. package/dist/transcription.d.cts.map +1 -1
  363. package/dist/transcription.d.ts.map +1 -1
  364. package/dist/transcription.js +7 -7
  365. package/dist/transcription.js.map +1 -1
  366. package/dist/types.cjs +18 -0
  367. package/dist/types.cjs.map +1 -0
  368. package/dist/types.d.cts +320 -17
  369. package/dist/types.d.cts.map +1 -1
  370. package/dist/types.d.ts +320 -17
  371. package/dist/types.d.ts.map +1 -1
  372. package/dist/types.js +17 -0
  373. package/dist/types.js.map +1 -0
  374. package/dist/validate-cli.cjs +983 -0
  375. package/dist/validate-cli.cjs.map +1 -0
  376. package/dist/validate-cli.js +982 -0
  377. package/dist/validate-cli.js.map +1 -0
  378. package/dist/vector-stores.cjs +1180 -0
  379. package/dist/vector-stores.cjs.map +1 -0
  380. package/dist/vector-stores.d.cts +106 -0
  381. package/dist/vector-stores.d.cts.map +1 -0
  382. package/dist/vector-stores.d.ts +106 -0
  383. package/dist/vector-stores.d.ts.map +1 -0
  384. package/dist/vector-stores.js +1164 -0
  385. package/dist/vector-stores.js.map +1 -0
  386. package/dist/vector-types.d.cts.map +1 -1
  387. package/dist/vector-types.d.ts.map +1 -1
  388. package/dist/veo-video.cjs +13 -13
  389. package/dist/veo-video.cjs.map +1 -1
  390. package/dist/veo-video.js +14 -14
  391. package/dist/veo-video.js.map +1 -1
  392. package/dist/video.cjs +23 -4
  393. package/dist/video.cjs.map +1 -1
  394. package/dist/video.d.cts +1 -1
  395. package/dist/video.d.cts.map +1 -1
  396. package/dist/video.d.ts +1 -1
  397. package/dist/video.d.ts.map +1 -1
  398. package/dist/video.js +24 -5
  399. package/dist/video.js.map +1 -1
  400. package/dist/vitest.cjs +23 -11
  401. package/dist/vitest.cjs.map +1 -1
  402. package/dist/vitest.d.cts +5 -2
  403. package/dist/vitest.d.cts.map +1 -1
  404. package/dist/vitest.d.ts +5 -2
  405. package/dist/vitest.d.ts.map +1 -1
  406. package/dist/vitest.js +25 -13
  407. package/dist/vitest.js.map +1 -1
  408. package/dist/watcher.cjs +2 -1
  409. package/dist/watcher.cjs.map +1 -1
  410. package/dist/watcher.js +2 -1
  411. package/dist/watcher.js.map +1 -1
  412. package/dist/ws-framing.cjs +150 -35
  413. package/dist/ws-framing.cjs.map +1 -1
  414. package/dist/ws-framing.d.cts +18 -3
  415. package/dist/ws-framing.d.cts.map +1 -1
  416. package/dist/ws-framing.d.ts +18 -3
  417. package/dist/ws-framing.d.ts.map +1 -1
  418. package/dist/ws-framing.js +151 -36
  419. package/dist/ws-framing.js.map +1 -1
  420. package/dist/ws-gemini-live.cjs +2 -2
  421. package/dist/ws-gemini-live.js +2 -2
  422. package/dist/ws-live.cjs +401 -0
  423. package/dist/ws-live.cjs.map +1 -0
  424. package/dist/ws-live.js +400 -0
  425. package/dist/ws-live.js.map +1 -0
  426. package/dist/ws-realtime.cjs +46 -2
  427. package/dist/ws-realtime.cjs.map +1 -1
  428. package/dist/ws-realtime.d.cts.map +1 -1
  429. package/dist/ws-realtime.d.ts.map +1 -1
  430. package/dist/ws-realtime.js +46 -2
  431. package/dist/ws-realtime.js.map +1 -1
  432. package/dist/ws-responses.cjs +16 -4
  433. package/dist/ws-responses.cjs.map +1 -1
  434. package/dist/ws-responses.js +16 -4
  435. package/dist/ws-responses.js.map +1 -1
  436. package/dist/ws-upstream.cjs +118 -0
  437. package/dist/ws-upstream.cjs.map +1 -0
  438. package/dist/ws-upstream.js +115 -0
  439. package/dist/ws-upstream.js.map +1 -0
  440. package/fixtures/openai-live-client.json +390 -0
  441. package/fixtures/openai-live-managed.json +709 -0
  442. package/package.json +4 -1
package/dist/helpers.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { LiveFixtureResponse } from "./live-types.js";
1
2
  import { AudioResponse, ChatCompletion, ChatCompletionRequest, ContentWithToolCallsResponse, EmbeddingResponse, ErrorResponse, FixtureBlock, FixtureFileBlock, FixtureResponse, ImageResponse, ResponseOverrides, SSEChunk, TextResponse, ToolCall, ToolCallResponse, TranscriptionResponse, VideoResponse } from "./types.js";
2
3
  import * as http$1 from "node:http";
3
4
 
@@ -44,7 +45,32 @@ declare function strictNoMatchLogLine(method: string, url: string, skippedBySequ
44
45
  */
45
46
 
46
47
  declare function flattenHeaders(headers: http$1.IncomingHttpHeaders): Record<string, string>;
48
+ /** Structural discriminator; registration performs full transcript validation. */
49
+ declare function isLiveResponse(value: unknown): value is LiveFixtureResponse;
47
50
  declare function generateId(prefix?: string): string;
51
+ /**
52
+ * Resolve the request id for this HTTP request.
53
+ *
54
+ * - When the caller sends a well-formed `X-Request-Id` (1-128 chars of
55
+ * `A-Za-z0-9-_.:`), it is echoed verbatim so distributed traces correlate.
56
+ * - Otherwise (absent, empty, too long, or illegal characters) a fresh
57
+ * `req-…` id is generated — never trust an attacker-controlled correlation
58
+ * value to be a valid log key.
59
+ *
60
+ * Returns `{ id, generated }`. The server normalizes
61
+ * `req.headers["x-request-id"]` to the resolved value, so every downstream
62
+ * `flattenHeaders` journal snapshot carries it with zero per-handler edits —
63
+ * which also means a MINTED id would otherwise ride the egress header set to
64
+ * a real provider. The server therefore passes `generated` to
65
+ * `markMintedRequestId` so `buildForwardHeaders` can drop it; a caller's own
66
+ * id still forwards verbatim.
67
+ */
68
+ declare function resolveRequestId(rawHeaders: http$1.IncomingHttpHeaders): {
69
+ id: string;
70
+ generated: boolean;
71
+ };
72
+ /** @internal Record that this request's `x-request-id` is aimock's own. */
73
+
48
74
  declare function generateToolCallId(): string;
49
75
  declare function generateMessageId(): string;
50
76
  declare function generateToolUseId(): string;
@@ -81,6 +107,8 @@ declare function isContentWithToolCallsResponse(r: FixtureResponse): r is Conten
81
107
  * guard at {@link resolveResponse}).
82
108
  */
83
109
  declare function resolveFixtureBlocks(blocks: FixtureFileBlock[]): FixtureBlock[];
110
+ /** Project normalized ordered blocks without changing the fixture or generating ids. */
111
+
84
112
  declare function isErrorResponse(r: FixtureResponse): r is ErrorResponse;
85
113
  /**
86
114
  * Serialize an ErrorResponse to JSON, stripping the internal-only `status`
@@ -115,6 +143,15 @@ declare function buildTextCompletion(content: string, model: string, reasoning?:
115
143
  declare function buildToolCallCompletion(toolCalls: ToolCall[], model: string, reasoning?: string, overrides?: ResponseOverrides, requestMessages?: ChatCompletionRequest["messages"]): ChatCompletion;
116
144
  declare function buildContentWithToolCallsChunks(content: string, toolCalls: ToolCall[], model: string, chunkSize: number, reasoning?: string, overrides?: ResponseOverrides, blocks?: FixtureBlock[]): SSEChunk[];
117
145
  declare function buildContentWithToolCallsCompletion(content: string, toolCalls: ToolCall[], model: string, reasoning?: string, overrides?: ResponseOverrides, requestMessages?: ChatCompletionRequest["messages"]): ChatCompletion;
146
+ /**
147
+ * A body read that stopped because the CLIENT sent more bytes than the route
148
+ * allows. The socket is already destroyed when this rejects, so no status line
149
+ * ever reaches the caller — but the fault is the caller's, and error arms that
150
+ * journal and log the failure need to say so. Carrying the classification on
151
+ * the error type (rather than re-matching the message text) is what lets them:
152
+ * the message is unchanged from the plain `Error` this replaced.
153
+ */
154
+
118
155
  /**
119
156
  * Generate a deterministic embedding vector from input text.
120
157
  * Hashes the input with SHA-256 and spreads the hash bytes across
@@ -141,7 +178,6 @@ declare function buildEmbeddingResponse(embeddings: number[][], model: string, u
141
178
  prompt_tokens?: number;
142
179
  total_tokens?: number;
143
180
  }): EmbeddingAPIResponse;
144
- //# sourceMappingURL=helpers.d.ts.map
145
181
  //#endregion
146
- export { EmbeddingAPIResponse, FORMAT_TO_CONTENT_TYPE, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, resolveFixtureBlocks, strictNoMatchLogLine, strictNoMatchMessage };
182
+ export { EmbeddingAPIResponse, FORMAT_TO_CONTENT_TYPE, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isLiveResponse, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, resolveFixtureBlocks, resolveRequestId, strictNoMatchLogLine, strictNoMatchMessage };
147
183
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":";;;;;;AAqSA;;;;;AAqDA;;;;;AAyDA;;;;AAAuE,iBA1TvD,oBAAA,CA0TuD,uBAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AA0BvE;;;;AAA+E,iBAzU/D,oBAAA,CAyU+D,MAAA,EAAA,MAAA,EAAA,GAAA,EAAA,MAAA,EAAA,uBAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAI/E;;;;;AAOA;;;;;AAUA;AAaA;AAIA;;;;;AAQA;;AAuXc,iBA5oBE,cAAA,CA4oBF,OAAA,EA5oB0B,MAAA,CAAK,mBA4oB/B,CAAA,EA5oBqD,MA4oBrD,CAAA,MAAA,EAAA,MAAA,CAAA;AAEG,iBA3kBD,UAAA,CA2kBC,MAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AA2CD,iBAlnBA,kBAAA,CAAA,CAknB+B,EAAA,MAAA;AAAA,iBA9mB/B,iBAAA,CAAA,CA8mB+B,EAAA,MAAA;AAElC,iBA5mBG,iBAAA,CAAA,CA4mBH,EAAA,MAAA;AAIC,iBA5mBE,cAAA,CA4mBF,CAAA,EA5mBoB,eA4mBpB,CAAA,EAAA,CAAA,IA5mB2C,YA4mB3C;AACH,iBAzmBK,kBAAA,CAymBL,CAAA,EAzmB2B,eAymB3B,CAAA,EAAA,CAAA,IAzmBkD,gBAymBlD;AACR,iBAlmBa,8BAAA,CAkmBb,CAAA,EAjmBE,eAimBF,CAAA,EAAA,CAAA,IAhmBK,4BAgmBL;;AAkSH;;;;;;;AA0XA;AAgBA;AAUA;;;;;;;;;;;;;;;;;;;iBAnuCgB,oBAAA,SAA6B,qBAAqB;iBAyDlD,eAAA,IAAmB,uBAAuB;;;;;;;iBA0B1C,mBAAA,IAAuB,uBAAuB;iBAI9C,eAAA,IAAmB,uBAAuB;iBAO1C,eAAA,IAAmB,uBAAuB;;;;;cAU7C,wBAAwB;;;;;iBAarB,YAAA;iBAIA,uBAAA,IAA2B,uBAAuB;iBAQlD,eAAA,IAAmB,uBAAuB;iBAY1C,gBAAA,WACJ,eAAe,mBAAmB,+BAC3C;;;;;;iBAmHa,eAAA,oFAKF,oBACX;iBAqEa,mBAAA,YACH,8EAIC,oBACX;iBAsHa,mBAAA,iEAIF,qCACM,oCACjB;iBAqCa,uBAAA,YACH,2DAGC,qCACM,oCACjB;iBA2Ca,+BAAA,6BAEH,8EAIC,4BACH,iBACR;iBAkSa,mCAAA,6BAEH,2DAGC,qCACM,oCACjB;;;;;;iBAmXa,8BAAA;UAgBC,oBAAA;;;;;;;;;;;;;;;;iBAUD,sBAAA;;;IAIb"}
1
+ {"version":3,"file":"helpers.d.ts","names":[],"sources":["../src/helpers.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAmjBA;;;;;AAUA;AAaA;AAIA;;;;AAAuF,iBAtcvE,oBAAA,CAscuE,uBAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAQvF;;;;AAAuE,iBAncvD,oBAAA,CAmcuD,MAAA,EAAA,MAAA,EAAA,GAAA,EAAA,MAAA,EAAA,uBAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAYvE;;;;;;;AAqHA;;;;;AA2EA;;;;;;;iBA7iBgB,cAAA,UAAwB,MAAA,CAAK,sBAAsB;;iBAcnD,cAAA,2BAAyC;iBA0DzC,UAAA;;;;;;;;;;;;;;;;;;iBAqBA,gBAAA,aAA6B,MAAA,CAAK;;;;;;iBAqClC,kBAAA,CAAA;iBAIA,iBAAA,CAAA;iBAIA,iBAAA,CAAA;iBAIA,cAAA,IAAkB,uBAAuB;iBAIzC,kBAAA,IAAsB,uBAAuB;iBAQ7C,8BAAA,IACX,uBACG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmDQ,oBAAA,SAA6B,qBAAqB;;;iBA0ElD,eAAA,IAAmB,uBAAuB;;;;;;;iBA0B1C,mBAAA,IAAuB,uBAAuB;iBAI9C,eAAA,IAAmB,uBAAuB;iBAO1C,eAAA,IAAmB,uBAAuB;;;;;cAU7C,wBAAwB;;;;;iBAarB,YAAA;iBAIA,uBAAA,IAA2B,uBAAuB;iBAQlD,eAAA,IAAmB,uBAAuB;iBAY1C,gBAAA,WACJ,eAAe,mBAAmB,+BAC3C;;;;;;iBAmHa,eAAA,oFAKF,oBACX;iBAqEa,mBAAA,YACH,8EAIC,oBACX;iBAsHa,mBAAA,iEAIF,qCACM,oCACjB;iBAqCa,uBAAA,YACH,2DAGC,qCACM,oCACjB;iBA2Ca,+BAAA,6BAEH,8EAIC,4BACH,iBACR;iBAmSa,mCAAA,6BAEH,2DAGC,qCACM,oCACjB;;;;;;;;;;;;;;;iBA+wBa,8BAAA;UAgBC,oBAAA;;;;;;;;;;;;;;;;iBAUD,sBAAA;;;IAIb"}
package/dist/helpers.js CHANGED
@@ -1,4 +1,6 @@
1
- import { DEFAULT_TEST_ID } from "./constants.js";
1
+ import { DEFAULT_TEST_ID, MCP_FAKES_ECHO_LIMIT } from "./constants.js";
2
+ import { cutText } from "./echo-text.js";
3
+ import { build, fixed, msg, quote } from "./message-text.js";
2
4
  import { isReasoningModel } from "./model-utils.js";
3
5
  import { isRecognizedApiKeyHeader } from "./api-key-auth.js";
4
6
  import { createHash, randomBytes } from "node:crypto";
@@ -25,6 +27,37 @@ function resolveStrictMode(serverDefault, rawHeaders) {
25
27
  }
26
28
  return serverDefault ?? false;
27
29
  }
30
+ const STRICT_INTEGER_RE = /^\d+$/;
31
+ /**
32
+ * Strict decimal-integer grammar shared by the query/list parsers that grew
33
+ * their own local copy on separate branches (files `limit`, fine-tuning
34
+ * `limit`, chaos integer fields): an unsigned digit run and nothing else — no
35
+ * sign, no exponent, no radix prefix, no surrounding whitespace (callers that
36
+ * accept padded input trim before calling).
37
+ *
38
+ * Deliberately narrower than `Number()`, which reads `0x10` as 16, `1e3` as
39
+ * 1000 and strips whitespace and a leading `+`. Returns the value, or `null`
40
+ * when the text is not a safe integer literal. Range checks and error
41
+ * messages stay with the callers — this is the grammar gate only, so sharing
42
+ * it cannot change any surface's accepted range or its 400 text.
43
+ */
44
+ function parseStrictIntegerText(text) {
45
+ if (!STRICT_INTEGER_RE.test(text)) return null;
46
+ const n = Number(text);
47
+ return Number.isSafeInteger(n) ? n : null;
48
+ }
49
+ /**
50
+ * Would a fixture miss on this request have been forwarded upstream? This is
51
+ * the ONE rule behind the no-fixture chaos journal `source` label: "proxy"
52
+ * only when record mode has an upstream for THIS provider and strict mode is
53
+ * not refusing the miss first. `effectiveStrict` is the header-resolved value
54
+ * from {@link resolveStrictMode}. `record.providers` is read guarded — a JS
55
+ * caller can hand over `record: {}`.
56
+ */
57
+ function wouldProxyMiss(effectiveStrict, record, providerKey) {
58
+ if (effectiveStrict || providerKey === void 0) return false;
59
+ return Boolean(record?.providers?.[providerKey]);
60
+ }
28
61
  /**
29
62
  * Returns `true` or `false` when the X-AIMock-Strict header overrides the
30
63
  * server default, or `undefined` when it doesn't. Designed to be spread
@@ -142,6 +175,10 @@ function flattenHeaders(headers) {
142
175
  }
143
176
  return flat;
144
177
  }
178
+ /** Structural discriminator; registration performs full transcript validation. */
179
+ function isLiveResponse(value) {
180
+ return value !== null && typeof value === "object" && !Array.isArray(value) && "live" in value;
181
+ }
145
182
  function isResponseFactory(r) {
146
183
  return typeof r === "function";
147
184
  }
@@ -176,6 +213,57 @@ function normalizeFactoryResponse(raw) {
176
213
  function generateId(prefix = "chatcmpl") {
177
214
  return `${prefix}-${randomBytes(12).toString("base64url")}`;
178
215
  }
216
+ /**
217
+ * Resolve the request id for this HTTP request.
218
+ *
219
+ * - When the caller sends a well-formed `X-Request-Id` (1-128 chars of
220
+ * `A-Za-z0-9-_.:`), it is echoed verbatim so distributed traces correlate.
221
+ * - Otherwise (absent, empty, too long, or illegal characters) a fresh
222
+ * `req-…` id is generated — never trust an attacker-controlled correlation
223
+ * value to be a valid log key.
224
+ *
225
+ * Returns `{ id, generated }`. The server normalizes
226
+ * `req.headers["x-request-id"]` to the resolved value, so every downstream
227
+ * `flattenHeaders` journal snapshot carries it with zero per-handler edits —
228
+ * which also means a MINTED id would otherwise ride the egress header set to
229
+ * a real provider. The server therefore passes `generated` to
230
+ * `markMintedRequestId` so `buildForwardHeaders` can drop it; a caller's own
231
+ * id still forwards verbatim.
232
+ */
233
+ function resolveRequestId(rawHeaders) {
234
+ const raw = rawHeaders["x-request-id"];
235
+ const first = Array.isArray(raw) ? raw[0] : raw;
236
+ if (typeof first === "string") {
237
+ const trimmed = first.trim();
238
+ if (trimmed.length >= 1 && trimmed.length <= 128 && /^[A-Za-z0-9\-_.:]+$/.test(trimmed)) return {
239
+ id: trimmed,
240
+ generated: false
241
+ };
242
+ }
243
+ return {
244
+ id: generateId("req"),
245
+ generated: true
246
+ };
247
+ }
248
+ /**
249
+ * Requests whose `x-request-id` aimock MINTED (the caller sent none, or sent
250
+ * an unusable one). The normalized header is indistinguishable from a
251
+ * caller-supplied one by inspection, so the provenance is tracked out-of-band
252
+ * and keyed on the request object, exactly like the egress auth marker.
253
+ */
254
+ const mintedRequestIds = /* @__PURE__ */ new WeakSet();
255
+ /** @internal Record that this request's `x-request-id` is aimock's own. */
256
+ function markMintedRequestId(req, generated) {
257
+ if (generated) mintedRequestIds.add(req);
258
+ }
259
+ /**
260
+ * @internal True when `x-request-id` on this request was minted by aimock.
261
+ * Egress paths use it to avoid transmitting an invented correlation id to a
262
+ * real provider.
263
+ */
264
+ function hasMintedRequestId(req) {
265
+ return mintedRequestIds.has(req);
266
+ }
179
267
  function generateToolCallId() {
180
268
  return `call_${randomBytes(12).toString("base64url")}`;
181
269
  }
@@ -254,6 +342,24 @@ function resolveFixtureBlocks(blocks) {
254
342
  } else throw new Error(`Invalid fixture block at index ${i}: unknown type ${JSON.stringify(b.type)} (expected "text" or "toolCall")`);
255
343
  });
256
344
  }
345
+ /** Project normalized ordered blocks without changing the fixture or generating ids. */
346
+ function resolveFixtureBlockOutcome(blocks) {
347
+ const ordered = resolveFixtureBlocks(blocks);
348
+ let content = "";
349
+ const toolCalls = [];
350
+ for (const block of ordered) if (block.type === "text") content += block.text;
351
+ else toolCalls.push({
352
+ name: block.name,
353
+ arguments: block.arguments,
354
+ ...block.id !== void 0 ? { id: block.id } : {}
355
+ });
356
+ return {
357
+ ordered,
358
+ content,
359
+ toolCalls,
360
+ hasToolCalls: toolCalls.length > 0
361
+ };
362
+ }
257
363
  function isErrorResponse(r) {
258
364
  return "error" in r && r.error !== null && typeof r.error === "object" && "message" in r.error && typeof r.error.message === "string";
259
365
  }
@@ -603,7 +709,8 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize, r
603
709
  const chunks = [];
604
710
  const fingerprint = overrides?.systemFingerprint;
605
711
  if (blocks && blocks.length > 0) {
606
- const ordered = resolveFixtureBlocks(blocks);
712
+ const outcome = resolveFixtureBlockOutcome(blocks);
713
+ const ordered = outcome.ordered;
607
714
  if (reasoning) for (let i = 0; i < reasoning.length; i += chunkSize) {
608
715
  const slice = reasoning.slice(i, i + chunkSize);
609
716
  chunks.push({
@@ -710,7 +817,7 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize, r
710
817
  index: 0,
711
818
  delta: {},
712
819
  logprobs: null,
713
- finish_reason: overrides?.finishReason ?? "tool_calls"
820
+ finish_reason: overrides?.finishReason ?? (outcome.hasToolCalls ? "tool_calls" : "stop")
714
821
  }],
715
822
  ...fingerprint !== void 0 && { system_fingerprint: fingerprint }
716
823
  });
@@ -859,7 +966,31 @@ function buildContentWithToolCallsCompletion(content, toolCalls, model, reasonin
859
966
  };
860
967
  }
861
968
  const DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024;
862
- function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
969
+ /**
970
+ * A body read that stopped because the CLIENT sent more bytes than the route
971
+ * allows. The socket is already destroyed when this rejects, so no status line
972
+ * ever reaches the caller — but the fault is the caller's, and error arms that
973
+ * journal and log the failure need to say so. Carrying the classification on
974
+ * the error type (rather than re-matching the message text) is what lets them:
975
+ * the message is unchanged from the plain `Error` this replaced.
976
+ */
977
+ var RequestBodyTooLargeError = class extends Error {
978
+ constructor(maxBytes) {
979
+ super(`Request body exceeded size limit of ${maxBytes} bytes`);
980
+ this.maxBytes = maxBytes;
981
+ this.name = "RequestBodyTooLargeError";
982
+ }
983
+ };
984
+ /**
985
+ * Read a request body as raw bytes, preserving every octet.
986
+ *
987
+ * This is the byte-level primitive {@link readBody} is built on: routes that
988
+ * must not lose bytes (binary file uploads) take the Buffer, everything else
989
+ * keeps taking the decoded string. Splitting it this way leaves the text path
990
+ * byte-identical — `readBody` performs exactly the same `Buffer.concat(...)`
991
+ * + default (utf8) `toString()` it always did, just one call later.
992
+ */
993
+ function readBodyBuffer(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
863
994
  return new Promise((resolve, reject) => {
864
995
  const chunks = [];
865
996
  let totalBytes = 0;
@@ -870,7 +1001,7 @@ function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
870
1001
  if (totalBytes > maxBytes) {
871
1002
  settled = true;
872
1003
  req.destroy();
873
- reject(/* @__PURE__ */ new Error(`Request body exceeded size limit of ${maxBytes} bytes`));
1004
+ reject(new RequestBodyTooLargeError(maxBytes));
874
1005
  return;
875
1006
  }
876
1007
  chunks.push(chunk);
@@ -878,8 +1009,58 @@ function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
878
1009
  req.on("end", () => {
879
1010
  if (!settled) {
880
1011
  settled = true;
881
- resolve(Buffer.concat(chunks).toString());
1012
+ resolve(Buffer.concat(chunks));
1013
+ }
1014
+ });
1015
+ req.on("error", (err) => {
1016
+ if (!settled) {
1017
+ settled = true;
1018
+ reject(err);
1019
+ }
1020
+ });
1021
+ });
1022
+ }
1023
+ /**
1024
+ * Read a request body that may legitimately be over-size, without either
1025
+ * buffering it all or dropping the socket.
1026
+ *
1027
+ * {@link readBodyBuffer} answers an over-size body with `req.destroy()`: no
1028
+ * status line, no body, no CORS headers. That is the right default for routes
1029
+ * whose limit is a pure DoS bound, but it is the wrong answer for a route that
1030
+ * owes the caller a `400` describing what was wrong — the caller cannot read an
1031
+ * error it never receives. So this variant splits the two concerns:
1032
+ *
1033
+ * - `maxBytes` bounds MEMORY. Past it the buffered chunks are dropped and the
1034
+ * body is only counted, so peak retention never exceeds `maxBytes` no matter
1035
+ * how large the body is. The caller gets `buffer: null` and answers itself.
1036
+ * - `drainMaxBytes` bounds WORK, and is the only socket drop here: a body that
1037
+ * keeps coming past it is not worth draining and rejects as `readBodyBuffer`
1038
+ * would.
1039
+ */
1040
+ function readBodyBufferBounded(req, maxBytes, drainMaxBytes) {
1041
+ return new Promise((resolve, reject) => {
1042
+ let chunks = [];
1043
+ let totalBytes = 0;
1044
+ let settled = false;
1045
+ req.on("data", (chunk) => {
1046
+ if (settled) return;
1047
+ totalBytes += chunk.length;
1048
+ if (chunks !== null && totalBytes > maxBytes) chunks = null;
1049
+ if (totalBytes > drainMaxBytes) {
1050
+ settled = true;
1051
+ req.destroy();
1052
+ reject(new RequestBodyTooLargeError(drainMaxBytes));
1053
+ return;
882
1054
  }
1055
+ if (chunks !== null) chunks.push(chunk);
1056
+ });
1057
+ req.on("end", () => {
1058
+ if (settled) return;
1059
+ settled = true;
1060
+ resolve({
1061
+ buffer: chunks === null ? null : Buffer.concat(chunks),
1062
+ bytesRead: totalBytes
1063
+ });
883
1064
  });
884
1065
  req.on("error", (err) => {
885
1066
  if (!settled) {
@@ -889,6 +1070,9 @@ function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
889
1070
  });
890
1071
  });
891
1072
  }
1073
+ function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
1074
+ return readBodyBuffer(req, maxBytes).then((buf) => buf.toString());
1075
+ }
892
1076
  /**
893
1077
  * Case-insensitive substring/regex match used for search, rerank, and
894
1078
  * moderation endpoints where exact casing rarely matters. String patterns
@@ -907,11 +1091,14 @@ function matchesPattern(text, pattern) {
907
1091
  return result;
908
1092
  }
909
1093
  /**
910
- * The ONE definition of "which test does this traffic belong to": the
911
- * `X-Test-Id` header wins, then `?testId=` in the query string, then
912
- * `DEFAULT_TEST_ID`. Every per-test axis (fixture match-counts, the journal
913
- * filter, chaos scoping) resolves through this, so a caller that tags one way
914
- * can never land in two different scopes.
1094
+ * Which test LLM and other HTTP-route traffic belongs to: the `X-Test-Id`
1095
+ * header (raw, not percent-decoded) wins, then `?testId=` in the query string
1096
+ * (read through `URLSearchParams`, so percent-decoded and with `+` as a
1097
+ * space), then `DEFAULT_TEST_ID`. Every per-test axis of those routes
1098
+ * (fixture match-counts, the journal filter, chaos scoping) resolves through
1099
+ * this. MCP mounts resolve the test id with {@link resolveMcpIdentity}
1100
+ * instead, which percent-decodes the header, so `X-Test-Id: a%20b` is `a%20b`
1101
+ * here and `a b` there. Both read a `+` in the query as a space.
915
1102
  */
916
1103
  function resolveTestId(headers, url) {
917
1104
  const headerValue = headers["x-test-id"];
@@ -935,6 +1122,237 @@ function getContext(req) {
935
1122
  } else if (typeof headerValue === "string" && headerValue) return headerValue;
936
1123
  }
937
1124
  /**
1125
+ * Decode one MCP identity value: an `X-Test-Id` / `X-AIMock-Context` header
1126
+ * value, and also each query name and (through {@link decodeMcpQueryValue})
1127
+ * each `?testId=` / `?context=` / `?undeclared=` query value. Clients send `encodeURIComponent(id)` because
1128
+ * `fetch` rejects characters such as `›` in a header. A value that is not
1129
+ * valid percent-encoding (a raw `applies 50% discount`) is used as-is, with
1130
+ * `fellBack: true` so the caller can log the fallback. Only that `URIError`
1131
+ * is caught; any other throw propagates. MCP requests only: LLM routes read
1132
+ * the header raw and the query through `URLSearchParams` (see
1133
+ * {@link resolveTestId}).
1134
+ */
1135
+ function decodeMcpHeaderValue(raw) {
1136
+ try {
1137
+ return {
1138
+ value: decodeURIComponent(raw),
1139
+ fellBack: false
1140
+ };
1141
+ } catch (err) {
1142
+ if (!(err instanceof URIError)) throw err;
1143
+ return {
1144
+ value: raw,
1145
+ fellBack: true
1146
+ };
1147
+ }
1148
+ }
1149
+ /**
1150
+ * Decode one MCP query value (`?testId=`, `?context=`, `?undeclared=`) the way
1151
+ * `URLSearchParams` reads it (spec I1): a `+` is a space, then the value is
1152
+ * percent-decoded by the {@link decodeMcpHeaderValue} rule. So a query built
1153
+ * with `URL.searchParams.set(...)` (a space sent as `+`) and one built with
1154
+ * `encodeURIComponent` both resolve to the id itself. A value that is not
1155
+ * valid percent-encoding is used as-is (its `+` still a space), with
1156
+ * `fellBack: true`.
1157
+ */
1158
+ function decodeMcpQueryValue(raw) {
1159
+ return decodeMcpHeaderValue(raw.replace(/\+/g, " "));
1160
+ }
1161
+ /**
1162
+ * Every value of the header `name`, one per header line: `[]` when absent.
1163
+ * Node joins a repeated header into one comma-joined `req.headers` string, so
1164
+ * the values come from `req.headersDistinct` (one element per line) when the
1165
+ * request has it. A caller without it (a hand-built request) is read from
1166
+ * `req.headers`: one element for a string, each element for an array.
1167
+ */
1168
+ function headerValues(req, name) {
1169
+ const distinct = req.headersDistinct?.[name];
1170
+ if (distinct !== void 0) return distinct;
1171
+ const value = req.headers[name];
1172
+ if (value === void 0) return [];
1173
+ return Array.isArray(value) ? value : [value];
1174
+ }
1175
+ /**
1176
+ * Every pair of a query string, in order, each name decoded by the
1177
+ * `decodeMcpHeaderValue` rule. A bare `name` with no `=` counts as an
1178
+ * occurrence with an empty value, so `?testId&testId=t1` has two. Each value
1179
+ * is left raw for the caller to decode with {@link decodeMcpQueryValue}.
1180
+ * A name that is not valid percent-encoding (`test%Id`) gets `name: null`: it
1181
+ * matches no field, and the caller counts it.
1182
+ */
1183
+ function queryPairs(query) {
1184
+ if (query === "") return [];
1185
+ return query.split("&").map((pair) => {
1186
+ const eq = pair.indexOf("=");
1187
+ const key = decodeMcpHeaderValue(eq === -1 ? pair : pair.slice(0, eq));
1188
+ return {
1189
+ name: key.fellBack ? null : key.value,
1190
+ value: eq === -1 ? "" : pair.slice(eq + 1)
1191
+ };
1192
+ });
1193
+ }
1194
+ /**
1195
+ * A request value as a message part, the way the message builder quotes every
1196
+ * user string: JSON-quoted (quotes and control characters escaped), then cut
1197
+ * once as JSON text, so the whole echo, quotes, escapes and the
1198
+ * `… (<n> more chars)` count included, fits in `MCP_FAKES_ECHO_LIMIT` and the
1199
+ * cut never splits a JSON escape or a surrogate pair.
1200
+ */
1201
+ function echoIdentityValue(value) {
1202
+ return quote(value, MCP_FAKES_ECHO_LIMIT);
1203
+ }
1204
+ const MCP_IDENTITY_INPUTS = {
1205
+ testId: {
1206
+ header: "x-test-id",
1207
+ query: "testId",
1208
+ label: "X-Test-Id"
1209
+ },
1210
+ context: {
1211
+ header: "x-aimock-context",
1212
+ query: "context",
1213
+ label: "X-AIMock-Context"
1214
+ },
1215
+ undeclared: {
1216
+ header: "x-aimock-mcp-undeclared",
1217
+ query: "undeclared",
1218
+ label: "X-AIMock-MCP-Undeclared"
1219
+ }
1220
+ };
1221
+ /**
1222
+ * Resolve the identity (test id, context, undeclared override) of one MCP request.
1223
+ * Each field resolves on its own, first of: the header, the query parameter,
1224
+ * the session value bound at `initialize`. A field sent more than once on
1225
+ * either path (more than one header line, read from `headersDistinct` because
1226
+ * Node joins repeated lines into one `headers` string; a header array from a
1227
+ * hand-built request; a repeated query name) is an error
1228
+ * (`MCP_DUPLICATE_IDENTITY`), even when the other path also carries it. For
1229
+ * the test id and the context, a header value is decoded with
1230
+ * `decodeURIComponent` (raw value on failure, with `fellBack` set) and a query
1231
+ * value the way `URLSearchParams` reads it (spec I1): a `+` is a space, then
1232
+ * the same `decodeURIComponent` rule. So `encodeURIComponent` output (what
1233
+ * clients send) and a query built with `URL.searchParams` both decode to the
1234
+ * id itself, while a raw `+` is a `+` in the header and a space in the query.
1235
+ * When both paths carry a test id or a context, the header is used and the
1236
+ * query value is ignored, even if the two differ, but an ignored query value
1237
+ * that does not decode still sets `fellBack`. The undeclared header is not
1238
+ * percent-decoded; its query value is, by the same query rule. Both are
1239
+ * trimmed and compared case-insensitively. `supplied` says whether the
1240
+ * request itself carried a non-empty header or query value for the field,
1241
+ * never `resolveTestId`'s result (which returns `"__default__"` when nothing
1242
+ * is sent). Blank values: an empty value is not supplied and falls through;
1243
+ * it is never rejected. A whitespace-only value is not supplied for the
1244
+ * undeclared override (it is trimmed), but is a real value for the test id
1245
+ * and the context, which match exactly and are never trimmed (Node trims a
1246
+ * header value, so only the query or a hand-built request can carry one). A
1247
+ * lone query name with no `=` carries no value. An empty session value counts
1248
+ * as none, and a missing `req.url` as `/`. A field with no value anywhere is
1249
+ * `null`. An unrecognized undeclared override on the header or the query is
1250
+ * an error (`ok: false`), not a fall-through: both are checked before the
1251
+ * header one is used, so a valid header does not hide a mistyped query value.
1252
+ * Bad percent-encoding is never silent: a test id or context value that does
1253
+ * not decode sets `fellBack`, on either path, used or ignored; an undeclared
1254
+ * query value that does not decode is that error, with the message saying
1255
+ * so; and a query name that does not decode matches no field and is counted
1256
+ * in `undecodedQueryNames`. `resolveTestId` / `getContext` stay unchanged for
1257
+ * LLM routes.
1258
+ */
1259
+ function resolveMcpIdentity(req, session) {
1260
+ const url = req.url ?? "/";
1261
+ const qIdx = url.indexOf("?");
1262
+ const pairs = queryPairs(qIdx === -1 ? "" : url.slice(qIdx + 1));
1263
+ const raw = {};
1264
+ for (const field of [
1265
+ "testId",
1266
+ "context",
1267
+ "undeclared"
1268
+ ]) {
1269
+ const input = MCP_IDENTITY_INPUTS[field];
1270
+ const sent = [["header", headerValues(req, input.header)], ["query", pairs.filter((p) => p.name === input.query).map((p) => p.value)]];
1271
+ for (const [source, values] of sent) if (values.length > 1) {
1272
+ const label = source === "header" ? msg`${fixed(input.label)} header` : msg`?${fixed(input.query)}= query parameter`;
1273
+ return {
1274
+ ok: false,
1275
+ error: {
1276
+ code: "MCP_DUPLICATE_IDENTITY",
1277
+ status: 400,
1278
+ field,
1279
+ source,
1280
+ count: values.length,
1281
+ message: build(msg`Duplicate ${label}: ${values.length} values sent, expected one`)
1282
+ }
1283
+ };
1284
+ }
1285
+ raw[field] = {
1286
+ header: sent[0][1][0],
1287
+ query: sent[1][1][0]
1288
+ };
1289
+ }
1290
+ const fromRequest = (field) => {
1291
+ const { header, query: q } = raw[field];
1292
+ const fromQuery = q ? decodeMcpQueryValue(q) : null;
1293
+ if (header) {
1294
+ const used = decodeMcpHeaderValue(header);
1295
+ return {
1296
+ value: used.value,
1297
+ fellBack: used.fellBack || (fromQuery?.fellBack ?? false)
1298
+ };
1299
+ }
1300
+ return fromQuery ?? {
1301
+ value: null,
1302
+ fellBack: false
1303
+ };
1304
+ };
1305
+ const testId = fromRequest("testId");
1306
+ const context = fromRequest("context");
1307
+ let undeclared = null;
1308
+ for (const source of ["header", "query"]) {
1309
+ const sent = raw.undeclared[source];
1310
+ if (sent === void 0) continue;
1311
+ const decoded = source === "header" ? {
1312
+ value: sent,
1313
+ fellBack: false
1314
+ } : decodeMcpQueryValue(sent);
1315
+ if (decoded.value.trim() === "") continue;
1316
+ const parsed = parseMcpUndeclared(decoded.value);
1317
+ if (parsed === null) {
1318
+ const echoed = echoIdentityValue(sent);
1319
+ const label = source === "header" ? msg`X-AIMock-MCP-Undeclared header` : msg`?undeclared= query parameter`;
1320
+ const note = decoded.fellBack ? msg` (not valid percent-encoding)` : msg``;
1321
+ return {
1322
+ ok: false,
1323
+ error: {
1324
+ code: "MCP_INVALID_UNDECLARED",
1325
+ status: 400,
1326
+ field: "undeclared",
1327
+ source,
1328
+ raw: cutText(sent),
1329
+ message: build(msg`Invalid ${label} value ${echoed}${note}: expected allow or deny`)
1330
+ }
1331
+ };
1332
+ }
1333
+ undeclared ??= parsed;
1334
+ }
1335
+ const undecodedQueryNames = pairs.filter((p) => p.name === null).length;
1336
+ return {
1337
+ ok: true,
1338
+ identity: {
1339
+ testId: testId.value ?? (session?.testId || null),
1340
+ context: context.value ?? (session?.context || null),
1341
+ undeclared: undeclared ?? session?.undeclared ?? null
1342
+ },
1343
+ supplied: {
1344
+ testId: testId.value !== null,
1345
+ context: context.value !== null,
1346
+ undeclared: undeclared !== null
1347
+ },
1348
+ fellBack: {
1349
+ testId: testId.fellBack,
1350
+ context: context.fellBack
1351
+ },
1352
+ ...undecodedQueryNames > 0 ? { undecodedQueryNames } : {}
1353
+ };
1354
+ }
1355
+ /**
938
1356
  * Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug
939
1357
  * suitable for use as a directory name in snapshot-style recording.
940
1358
  */
@@ -1121,7 +1539,64 @@ function buildEmbeddingResponse(embeddings, model, usage) {
1121
1539
  }
1122
1540
  };
1123
1541
  }
1542
+ /**
1543
+ * Build a stable, human-readable identifier for a fixture's match shape, for
1544
+ * any log line that has to name WHICH fixture it means. The `Fixture` type
1545
+ * carries no `id`/`name`, so the matchers are the only handle a reader has.
1546
+ *
1547
+ * Used by the relaxed-turnIndex warning (`router.ts`) and by the chaos
1548
+ * rejected-value warning (`chaos.ts`); one implementation so the two name the
1549
+ * same fixture the same way. The obvious `JSON.stringify(match)` is unfit: it
1550
+ * DROPS `predicate` functions (non-serialisable) and serialises any RegExp
1551
+ * matcher to `{}`, so a predicate- or regex-gated fixture's warning collapsed to
1552
+ * an uninformative "served fixture {}" / `{"userMessage":{}}` blob.
1553
+ *
1554
+ * Instead we list the PRESENT matcher keys in declaration order, annotating each
1555
+ * by VALUE KIND so predicates and regexes survive: `predicate(fn)`,
1556
+ * `userMessage(regex)`, `userMessage("hello")`, `turnIndex=0`, etc. The
1557
+ * fixture's array `index` is prefixed as a stable positional identifier when
1558
+ * the caller knows it (i.e. `>= 0`); callers holding only the fixture object
1559
+ * pass `-1` and get the matcher summary alone. String/number values are shown
1560
+ * inline (truncated) so a content match remains recognisable; the whole string
1561
+ * is capped to keep the log line bounded.
1562
+ */
1563
+ const DESCRIBE_MATCH_MAX = 160;
1564
+ function describeMatch(match, index) {
1565
+ const parts = [];
1566
+ for (const [key, value] of Object.entries(match)) {
1567
+ if (value === void 0) continue;
1568
+ if (typeof value === "function") parts.push(key === "predicate" ? "[predicate]" : `${key}[fn]`);
1569
+ else if (value instanceof RegExp) parts.push(`${key}(${value})`);
1570
+ else if (typeof value === "string") {
1571
+ const v = value.length > 40 ? `${value.slice(0, 40)}…` : value;
1572
+ parts.push(`${key}(${JSON.stringify(v)})`);
1573
+ } else if (Array.isArray(value)) parts.push(`${key}(${value.length} item${value.length === 1 ? "" : "s"})`);
1574
+ else if (typeof value === "object" && value !== null) {
1575
+ const json = JSON.stringify(value);
1576
+ parts.push(`${key}(${json.length > 40 ? `${json.slice(0, 40)}…` : json})`);
1577
+ } else parts.push(`${key}=${String(value)}`);
1578
+ }
1579
+ const keys = parts.length > 0 ? parts.join(", ") : "no matchers";
1580
+ const full = `${index >= 0 ? `#${index} ` : ""}{ ${keys} }`;
1581
+ if (full.length <= DESCRIBE_MATCH_MAX) return full;
1582
+ const head = full.slice(0, DESCRIBE_MATCH_MAX - 4);
1583
+ const cut = head.lastIndexOf(", ");
1584
+ return `${cut > 0 ? head.slice(0, cut) : head}, … }`;
1585
+ }
1586
+ /**
1587
+ * Parse an `X-AIMock-MCP-Undeclared` header or `?undeclared=` value.
1588
+ * Modeled on {@link resolveStrictMode}: case-insensitive, surrounding
1589
+ * whitespace trimmed. Returns `null` when unrecognised; unlike the strict
1590
+ * header, {@link resolveMcpIdentity} turns that into an error rather than a
1591
+ * fall-through, because falling through would turn a mistyped `deny` into
1592
+ * the permissive default.
1593
+ */
1594
+ function parseMcpUndeclared(raw) {
1595
+ const val = raw.trim().toLowerCase();
1596
+ if (val === "allow" || val === "deny") return val;
1597
+ return null;
1598
+ }
1124
1599
 
1125
1600
  //#endregion
1126
- export { FORMAT_TO_CONTENT_TYPE, MAX_EMBEDDING_DIMENSIONS, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getContext, getTestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isJsonObject, isResponseFactory, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, matchesPattern, normalizeEmbeddingInput, normalizeTextInput, readBody, resolveFixtureBlocks, resolveReasoningArtifactsForModel, resolveReasoningForModel, resolveResponse, resolveStrictMode, resolveTestId, resolveUsage, serializeErrorResponse, slugifyContext, slugifyTestId, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, validateChatMessages, validateEmbeddingDimensions, validateToolsField };
1601
+ export { FORMAT_TO_CONTENT_TYPE, MAX_EMBEDDING_DIMENSIONS, RequestBodyTooLargeError, buildContentWithToolCallsChunks, buildContentWithToolCallsCompletion, buildEmbeddingResponse, buildTextChunks, buildTextCompletion, buildToolCallChunks, buildToolCallCompletion, buildUsageChunk, decodeMcpHeaderValue, describeMatch, extractOverrides, flattenHeaders, formatToMime, generateDeterministicEmbedding, generateId, generateMessageId, generateToolCallId, generateToolUseId, getContext, getTestId, hasMintedRequestId, isAudioResponse, isContentWithToolCallsResponse, isEmbeddingResponse, isErrorResponse, isImageResponse, isJSONResponse, isJsonObject, isLiveResponse, isResponseFactory, isTextResponse, isToolCallResponse, isTranscriptionResponse, isVideoResponse, markMintedRequestId, matchesPattern, normalizeEmbeddingInput, normalizeTextInput, parseStrictIntegerText, readBody, readBodyBufferBounded, resolveFixtureBlockOutcome, resolveFixtureBlocks, resolveMcpIdentity, resolveReasoningArtifactsForModel, resolveReasoningForModel, resolveRequestId, resolveResponse, resolveStrictMode, resolveTestId, resolveUsage, serializeErrorResponse, slugifyContext, slugifyTestId, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, validateChatMessages, validateEmbeddingDimensions, validateToolsField, wouldProxyMiss };
1127
1602
  //# sourceMappingURL=helpers.js.map