@copilotkit/aimock 1.42.0 → 1.43.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 (341) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +56 -0
  4. package/README.md +102 -16
  5. package/dist/agui-mock.cjs +1 -1
  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 +1 -1
  10. package/dist/agui-mock.js.map +1 -1
  11. package/dist/agui-recorder.cjs +33 -11
  12. package/dist/agui-recorder.cjs.map +1 -1
  13. package/dist/agui-recorder.d.cts +10 -1
  14. package/dist/agui-recorder.d.cts.map +1 -1
  15. package/dist/agui-recorder.d.ts +10 -1
  16. package/dist/agui-recorder.d.ts.map +1 -1
  17. package/dist/agui-recorder.js +33 -11
  18. package/dist/agui-recorder.js.map +1 -1
  19. package/dist/agui-types.d.cts +1 -0
  20. package/dist/agui-types.d.cts.map +1 -1
  21. package/dist/agui-types.d.ts +1 -0
  22. package/dist/agui-types.d.ts.map +1 -1
  23. package/dist/aimock-cli.cjs +38 -10
  24. package/dist/aimock-cli.cjs.map +1 -1
  25. package/dist/aimock-cli.d.cts +5 -0
  26. package/dist/aimock-cli.d.cts.map +1 -1
  27. package/dist/aimock-cli.d.ts +5 -0
  28. package/dist/aimock-cli.d.ts.map +1 -1
  29. package/dist/aimock-cli.js +38 -10
  30. package/dist/aimock-cli.js.map +1 -1
  31. package/dist/batches.cjs +458 -0
  32. package/dist/batches.cjs.map +1 -0
  33. package/dist/batches.d.cts +57 -0
  34. package/dist/batches.d.cts.map +1 -0
  35. package/dist/batches.d.ts +57 -0
  36. package/dist/batches.d.ts.map +1 -0
  37. package/dist/batches.js +453 -0
  38. package/dist/batches.js.map +1 -0
  39. package/dist/bedrock-converse.cjs +12 -8
  40. package/dist/bedrock-converse.cjs.map +1 -1
  41. package/dist/bedrock-converse.d.cts.map +1 -1
  42. package/dist/bedrock-converse.d.ts.map +1 -1
  43. package/dist/bedrock-converse.js +14 -10
  44. package/dist/bedrock-converse.js.map +1 -1
  45. package/dist/bedrock.cjs +10 -8
  46. package/dist/bedrock.cjs.map +1 -1
  47. package/dist/bedrock.d.cts.map +1 -1
  48. package/dist/bedrock.d.ts.map +1 -1
  49. package/dist/bedrock.js +12 -10
  50. package/dist/bedrock.js.map +1 -1
  51. package/dist/byteplus-video.cjs +3 -3
  52. package/dist/byteplus-video.cjs.map +1 -1
  53. package/dist/byteplus-video.js +4 -4
  54. package/dist/byteplus-video.js.map +1 -1
  55. package/dist/chaos.cjs +622 -72
  56. package/dist/chaos.cjs.map +1 -1
  57. package/dist/chaos.d.cts +164 -7
  58. package/dist/chaos.d.cts.map +1 -1
  59. package/dist/chaos.d.ts +164 -7
  60. package/dist/chaos.d.ts.map +1 -1
  61. package/dist/chaos.js +613 -74
  62. package/dist/chaos.js.map +1 -1
  63. package/dist/cli.cjs +42 -23
  64. package/dist/cli.cjs.map +1 -1
  65. package/dist/cli.js +42 -23
  66. package/dist/cli.js.map +1 -1
  67. package/dist/cohere.cjs +10 -8
  68. package/dist/cohere.cjs.map +1 -1
  69. package/dist/cohere.d.cts.map +1 -1
  70. package/dist/cohere.d.ts.map +1 -1
  71. package/dist/cohere.js +12 -10
  72. package/dist/cohere.js.map +1 -1
  73. package/dist/config-loader.d.cts.map +1 -1
  74. package/dist/config-loader.d.ts.map +1 -1
  75. package/dist/convert.cjs +1 -1
  76. package/dist/convert.cjs.map +1 -1
  77. package/dist/convert.js +1 -1
  78. package/dist/convert.js.map +1 -1
  79. package/dist/elevenlabs-audio.cjs +3 -3
  80. package/dist/elevenlabs-audio.cjs.map +1 -1
  81. package/dist/elevenlabs-audio.js +4 -4
  82. package/dist/elevenlabs-audio.js.map +1 -1
  83. package/dist/elevenlabs-voice.cjs +1053 -0
  84. package/dist/elevenlabs-voice.cjs.map +1 -0
  85. package/dist/elevenlabs-voice.d.cts +72 -0
  86. package/dist/elevenlabs-voice.d.cts.map +1 -0
  87. package/dist/elevenlabs-voice.d.ts +72 -0
  88. package/dist/elevenlabs-voice.d.ts.map +1 -0
  89. package/dist/elevenlabs-voice.js +1048 -0
  90. package/dist/elevenlabs-voice.js.map +1 -0
  91. package/dist/embeddings.cjs +8 -4
  92. package/dist/embeddings.cjs.map +1 -1
  93. package/dist/embeddings.d.cts.map +1 -1
  94. package/dist/embeddings.d.ts.map +1 -1
  95. package/dist/embeddings.js +9 -5
  96. package/dist/embeddings.js.map +1 -1
  97. package/dist/fal-audio.cjs +3 -3
  98. package/dist/fal-audio.cjs.map +1 -1
  99. package/dist/fal-audio.js +4 -4
  100. package/dist/fal-audio.js.map +1 -1
  101. package/dist/fal.cjs +28 -4
  102. package/dist/fal.cjs.map +1 -1
  103. package/dist/fal.d.cts +16 -1
  104. package/dist/fal.d.cts.map +1 -1
  105. package/dist/fal.d.ts +16 -1
  106. package/dist/fal.d.ts.map +1 -1
  107. package/dist/fal.js +28 -5
  108. package/dist/fal.js.map +1 -1
  109. package/dist/files.cjs +1071 -0
  110. package/dist/files.cjs.map +1 -0
  111. package/dist/files.d.cts +68 -0
  112. package/dist/files.d.cts.map +1 -0
  113. package/dist/files.d.ts +68 -0
  114. package/dist/files.d.ts.map +1 -0
  115. package/dist/files.js +1059 -0
  116. package/dist/files.js.map +1 -0
  117. package/dist/fine-tuning.cjs +1051 -0
  118. package/dist/fine-tuning.cjs.map +1 -0
  119. package/dist/fine-tuning.d.cts +96 -0
  120. package/dist/fine-tuning.d.cts.map +1 -0
  121. package/dist/fine-tuning.d.ts +96 -0
  122. package/dist/fine-tuning.d.ts.map +1 -0
  123. package/dist/fine-tuning.js +1044 -0
  124. package/dist/fine-tuning.js.map +1 -0
  125. package/dist/fixture-loader.cjs +0 -0
  126. package/dist/fixture-loader.cjs.map +1 -1
  127. package/dist/fixture-loader.d.cts +37 -5
  128. package/dist/fixture-loader.d.cts.map +1 -1
  129. package/dist/fixture-loader.d.ts +37 -5
  130. package/dist/fixture-loader.d.ts.map +1 -1
  131. package/dist/fixture-loader.js +0 -0
  132. package/dist/fixture-loader.js.map +1 -1
  133. package/dist/gemini-embeddings.cjs +2 -2
  134. package/dist/gemini-embeddings.cjs.map +1 -1
  135. package/dist/gemini-embeddings.js +3 -3
  136. package/dist/gemini-embeddings.js.map +1 -1
  137. package/dist/gemini-interactions.cjs +13 -7
  138. package/dist/gemini-interactions.cjs.map +1 -1
  139. package/dist/gemini-interactions.d.cts.map +1 -1
  140. package/dist/gemini-interactions.d.ts.map +1 -1
  141. package/dist/gemini-interactions.js +15 -9
  142. package/dist/gemini-interactions.js.map +1 -1
  143. package/dist/gemini.cjs +11 -7
  144. package/dist/gemini.cjs.map +1 -1
  145. package/dist/gemini.d.cts.map +1 -1
  146. package/dist/gemini.d.ts.map +1 -1
  147. package/dist/gemini.js +13 -9
  148. package/dist/gemini.js.map +1 -1
  149. package/dist/grok-video.cjs +14 -14
  150. package/dist/grok-video.cjs.map +1 -1
  151. package/dist/grok-video.js +15 -15
  152. package/dist/grok-video.js.map +1 -1
  153. package/dist/helpers.cjs +229 -7
  154. package/dist/helpers.cjs.map +1 -1
  155. package/dist/helpers.d.cts +38 -2
  156. package/dist/helpers.d.cts.map +1 -1
  157. package/dist/helpers.d.ts +38 -2
  158. package/dist/helpers.d.ts.map +1 -1
  159. package/dist/helpers.js +221 -8
  160. package/dist/helpers.js.map +1 -1
  161. package/dist/images.cjs +54 -123
  162. package/dist/images.cjs.map +1 -1
  163. package/dist/images.d.cts.map +1 -1
  164. package/dist/images.d.ts.map +1 -1
  165. package/dist/images.js +55 -124
  166. package/dist/images.js.map +1 -1
  167. package/dist/index.cjs +41 -8
  168. package/dist/index.d.cts +12 -7
  169. package/dist/index.d.ts +12 -7
  170. package/dist/index.js +14 -10
  171. package/dist/jest.cjs +14 -7
  172. package/dist/jest.cjs.map +1 -1
  173. package/dist/jest.d.cts +3 -2
  174. package/dist/jest.d.cts.map +1 -1
  175. package/dist/jest.d.ts +3 -2
  176. package/dist/jest.d.ts.map +1 -1
  177. package/dist/jest.js +14 -7
  178. package/dist/jest.js.map +1 -1
  179. package/dist/journal.cjs +41 -4
  180. package/dist/journal.cjs.map +1 -1
  181. package/dist/journal.d.cts +44 -3
  182. package/dist/journal.d.cts.map +1 -1
  183. package/dist/journal.d.ts +44 -3
  184. package/dist/journal.d.ts.map +1 -1
  185. package/dist/journal.js +41 -5
  186. package/dist/journal.js.map +1 -1
  187. package/dist/live-fixture.cjs +1192 -0
  188. package/dist/live-fixture.cjs.map +1 -0
  189. package/dist/live-fixture.js +1187 -0
  190. package/dist/live-fixture.js.map +1 -0
  191. package/dist/live-recorder.cjs +256 -0
  192. package/dist/live-recorder.cjs.map +1 -0
  193. package/dist/live-recorder.js +254 -0
  194. package/dist/live-recorder.js.map +1 -0
  195. package/dist/live-replay.cjs +286 -0
  196. package/dist/live-replay.cjs.map +1 -0
  197. package/dist/live-replay.js +285 -0
  198. package/dist/live-replay.js.map +1 -0
  199. package/dist/live-sanitize.cjs +196 -0
  200. package/dist/live-sanitize.cjs.map +1 -0
  201. package/dist/live-sanitize.js +196 -0
  202. package/dist/live-sanitize.js.map +1 -0
  203. package/dist/live-types.d.cts +71 -0
  204. package/dist/live-types.d.cts.map +1 -0
  205. package/dist/live-types.d.ts +71 -0
  206. package/dist/live-types.d.ts.map +1 -0
  207. package/dist/llmock.cjs +52 -41
  208. package/dist/llmock.cjs.map +1 -1
  209. package/dist/llmock.d.cts +16 -5
  210. package/dist/llmock.d.cts.map +1 -1
  211. package/dist/llmock.d.ts +16 -5
  212. package/dist/llmock.d.ts.map +1 -1
  213. package/dist/llmock.js +53 -42
  214. package/dist/llmock.js.map +1 -1
  215. package/dist/messages.cjs +11 -7
  216. package/dist/messages.cjs.map +1 -1
  217. package/dist/messages.d.cts.map +1 -1
  218. package/dist/messages.d.ts.map +1 -1
  219. package/dist/messages.js +13 -9
  220. package/dist/messages.js.map +1 -1
  221. package/dist/metrics.cjs +258 -5
  222. package/dist/metrics.cjs.map +1 -1
  223. package/dist/metrics.d.cts +3 -1
  224. package/dist/metrics.d.cts.map +1 -1
  225. package/dist/metrics.d.ts +3 -1
  226. package/dist/metrics.d.ts.map +1 -1
  227. package/dist/metrics.js +255 -6
  228. package/dist/metrics.js.map +1 -1
  229. package/dist/moderation.cjs +10 -1
  230. package/dist/moderation.cjs.map +1 -1
  231. package/dist/moderation.d.cts.map +1 -1
  232. package/dist/moderation.d.ts.map +1 -1
  233. package/dist/moderation.js +10 -1
  234. package/dist/moderation.js.map +1 -1
  235. package/dist/ollama.cjs +5 -5
  236. package/dist/ollama.cjs.map +1 -1
  237. package/dist/ollama.js +6 -6
  238. package/dist/ollama.js.map +1 -1
  239. package/dist/openrouter-video.cjs +5 -5
  240. package/dist/openrouter-video.cjs.map +1 -1
  241. package/dist/openrouter-video.js +6 -6
  242. package/dist/openrouter-video.js.map +1 -1
  243. package/dist/recorder.cjs +45 -9
  244. package/dist/recorder.cjs.map +1 -1
  245. package/dist/recorder.d.cts.map +1 -1
  246. package/dist/recorder.d.ts.map +1 -1
  247. package/dist/recorder.js +43 -11
  248. package/dist/recorder.js.map +1 -1
  249. package/dist/responses.cjs +4 -3
  250. package/dist/responses.cjs.map +1 -1
  251. package/dist/responses.js +5 -5
  252. package/dist/responses.js.map +1 -1
  253. package/dist/router.cjs +20 -33
  254. package/dist/router.cjs.map +1 -1
  255. package/dist/router.d.cts +4 -1
  256. package/dist/router.d.cts.map +1 -1
  257. package/dist/router.d.ts +4 -1
  258. package/dist/router.d.ts.map +1 -1
  259. package/dist/router.js +20 -34
  260. package/dist/router.js.map +1 -1
  261. package/dist/server.cjs +701 -625
  262. package/dist/server.cjs.map +1 -1
  263. package/dist/server.d.cts +1 -0
  264. package/dist/server.d.cts.map +1 -1
  265. package/dist/server.d.ts +1 -0
  266. package/dist/server.d.ts.map +1 -1
  267. package/dist/server.js +704 -628
  268. package/dist/server.js.map +1 -1
  269. package/dist/speech.cjs +2 -2
  270. package/dist/speech.cjs.map +1 -1
  271. package/dist/speech.js +3 -3
  272. package/dist/speech.js.map +1 -1
  273. package/dist/transcription.cjs +4 -4
  274. package/dist/transcription.cjs.map +1 -1
  275. package/dist/transcription.js +5 -5
  276. package/dist/transcription.js.map +1 -1
  277. package/dist/types.d.cts +119 -16
  278. package/dist/types.d.cts.map +1 -1
  279. package/dist/types.d.ts +119 -16
  280. package/dist/types.d.ts.map +1 -1
  281. package/dist/validate-cli.cjs +925 -0
  282. package/dist/validate-cli.cjs.map +1 -0
  283. package/dist/validate-cli.js +924 -0
  284. package/dist/validate-cli.js.map +1 -0
  285. package/dist/vector-types.d.cts.map +1 -1
  286. package/dist/vector-types.d.ts.map +1 -1
  287. package/dist/veo-video.cjs +13 -13
  288. package/dist/veo-video.cjs.map +1 -1
  289. package/dist/veo-video.js +14 -14
  290. package/dist/veo-video.js.map +1 -1
  291. package/dist/video.cjs +4 -4
  292. package/dist/video.cjs.map +1 -1
  293. package/dist/video.d.cts +1 -1
  294. package/dist/video.d.cts.map +1 -1
  295. package/dist/video.d.ts +1 -1
  296. package/dist/video.d.ts.map +1 -1
  297. package/dist/video.js +5 -5
  298. package/dist/video.js.map +1 -1
  299. package/dist/vitest.cjs +13 -6
  300. package/dist/vitest.cjs.map +1 -1
  301. package/dist/vitest.d.cts +2 -1
  302. package/dist/vitest.d.cts.map +1 -1
  303. package/dist/vitest.d.ts +2 -1
  304. package/dist/vitest.d.ts.map +1 -1
  305. package/dist/vitest.js +14 -7
  306. package/dist/vitest.js.map +1 -1
  307. package/dist/watcher.cjs +2 -1
  308. package/dist/watcher.cjs.map +1 -1
  309. package/dist/watcher.js +2 -1
  310. package/dist/watcher.js.map +1 -1
  311. package/dist/ws-framing.cjs +150 -35
  312. package/dist/ws-framing.cjs.map +1 -1
  313. package/dist/ws-framing.d.cts +18 -3
  314. package/dist/ws-framing.d.cts.map +1 -1
  315. package/dist/ws-framing.d.ts +18 -3
  316. package/dist/ws-framing.d.ts.map +1 -1
  317. package/dist/ws-framing.js +151 -36
  318. package/dist/ws-framing.js.map +1 -1
  319. package/dist/ws-gemini-live.cjs +2 -2
  320. package/dist/ws-gemini-live.d.ts +2 -2
  321. package/dist/ws-gemini-live.js +2 -2
  322. package/dist/ws-live.cjs +401 -0
  323. package/dist/ws-live.cjs.map +1 -0
  324. package/dist/ws-live.js +400 -0
  325. package/dist/ws-live.js.map +1 -0
  326. package/dist/ws-realtime.cjs +46 -2
  327. package/dist/ws-realtime.cjs.map +1 -1
  328. package/dist/ws-realtime.d.cts.map +1 -1
  329. package/dist/ws-realtime.d.ts +2 -2
  330. package/dist/ws-realtime.d.ts.map +1 -1
  331. package/dist/ws-realtime.js +46 -2
  332. package/dist/ws-realtime.js.map +1 -1
  333. package/dist/ws-responses.cjs +2 -2
  334. package/dist/ws-responses.js +2 -2
  335. package/dist/ws-upstream.cjs +118 -0
  336. package/dist/ws-upstream.cjs.map +1 -0
  337. package/dist/ws-upstream.js +115 -0
  338. package/dist/ws-upstream.js.map +1 -0
  339. package/fixtures/openai-live-client.json +390 -0
  340. package/fixtures/openai-live-managed.json +709 -0
  341. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"agui-recorder.cjs","names":["isAuthenticatedRequest","https","http","getLastMessageIfToolResult","extractLastUserMessage","crypto","path"],"sources":["../src/agui-recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n AGUIFixture,\n AGUIFixtureMatch,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport { extractLastUserMessage, getLastMessageIfToolResult } from \"./agui-handler.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isAuthenticatedRequest } from \"./api-key-auth.js\";\n\n/**\n * Sentinel `match.message` value written to disk when the request had no\n * extractable user text. Keeps the on-disk fixture serializable (predicate\n * matchers aren't) but won't match any real user input on replay.\n */\nexport const NO_USER_MESSAGE_SENTINEL = \"__NO_USER_MESSAGE__\";\n\n/**\n * Default ceiling (bytes) for the in-memory AG-UI record buffer. The recorder\n * tees every upstream SSE chunk straight to the client AND buffers a copy so it\n * can `Buffer.concat(chunks).toString()` + parse the SSE events into a fixture\n * once the stream ends. With no cap, a large upstream response (amplified since\n * the real-key fixture-miss passthrough landed) builds a string past V8's\n * ~512 MiB max string length and throws `RangeError: Invalid string length`.\n * 64 MiB mirrors the generic proxy path's `DEFAULT_MAX_PROXY_BUFFER_BYTES` and\n * is generous for any real agent turn. Overridable via\n * `AGUIRecordConfig.maxRecordBufferBytes`.\n */\nexport const DEFAULT_AGUI_RECORD_BUFFER_BYTES = 64 * 1024 * 1024; // 64 MiB\n\n/**\n * Absolute hard ceiling (bytes) for the AG-UI record buffer, independent of the\n * configurable `maxRecordBufferBytes`, mirroring the generic proxy path's\n * `PROXY_BUFFER_HARD_CEILING`. 256 MiB of bytes can never decode to more than\n * 256 Mi UTF-16 code units, comfortably below V8's 2^29 - 1 string-length limit,\n * so the eventual `Buffer.concat(chunks).toString()` can never throw.\n */\nexport const AGUI_RECORD_BUFFER_HARD_CEILING = 256 * 1024 * 1024; // 256 MiB\n\n/**\n * Test-only override of the effective AG-UI record-buffer ceiling. Lets the cap\n * suite exercise the over-cap truncation path with a small body instead of\n * streaming hundreds of MB. `undefined` (the default) uses the configured /\n * default cap. NEVER set from production code.\n */\nlet aguiRecordBufferCeilingOverride: number | undefined;\n\n/** @internal test-only — see {@link aguiRecordBufferCeilingOverride}. */\nexport function setAGUIRecordBufferCeilingForTests(value: number | undefined): void {\n aguiRecordBufferCeilingOverride = value;\n}\n\n/**\n * Resolve the effective AG-UI record-buffer byte cap: honor a test-only\n * override, else the configured `maxRecordBufferBytes` (clamped to the hard\n * ceiling), else the default. Non-finite / non-positive configured values fall\n * back to the default.\n */\nfunction resolveAGUIRecordBufferCap(configured: number | undefined): number {\n if (aguiRecordBufferCeilingOverride !== undefined) return aguiRecordBufferCeilingOverride;\n if (configured == null || !Number.isFinite(configured) || configured <= 0) {\n return DEFAULT_AGUI_RECORD_BUFFER_BYTES;\n }\n return Math.min(configured, AGUI_RECORD_BUFFER_HARD_CEILING);\n}\n\n/**\n * Proxy an unmatched AG-UI request to a real upstream agent, record the\n * SSE event stream as a fixture on disk and in memory, and relay the\n * response back to the original client in real time.\n *\n * Returns the HTTP status code written to the client if the request was proxied,\n * or `false` if no upstream is configured.\n */\nexport async function proxyAndRecordAGUI(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n): Promise<number | false> {\n // AG-UI has no provider-specific static credential contract. An inbound\n // test key must never become an implicit upstream credential, so fail before\n // opening an upstream connection.\n if (isAuthenticatedRequest(req)) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No configured provider credential\" }));\n return 502;\n }\n if (!config.upstream) {\n logger.warn(\"No upstream URL configured for AG-UI recording — cannot proxy\");\n return false;\n }\n\n let target: URL;\n try {\n target = new URL(config.upstream);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n logger.error(`Invalid upstream AG-UI URL: ${config.upstream} — ${detail}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid upstream AG-UI URL\" }));\n return 502;\n }\n\n logger.warn(`NO AG-UI FIXTURE MATCH — proxying to ${config.upstream}`);\n\n // Build upstream request headers\n const forwardHeaders: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n };\n // Forward auth headers if present\n const authorization = req.headers[\"authorization\"];\n if (authorization) {\n forwardHeaders[\"Authorization\"] = Array.isArray(authorization)\n ? authorization.join(\", \")\n : authorization;\n }\n const apiKey = req.headers[\"x-api-key\"];\n if (apiKey) {\n forwardHeaders[\"x-api-key\"] = Array.isArray(apiKey) ? apiKey.join(\", \") : apiKey;\n }\n\n const requestBody = JSON.stringify(input);\n\n let status: number;\n try {\n status = await teeUpstreamStream(\n target,\n forwardHeaders,\n requestBody,\n res,\n input,\n fixtures,\n config,\n logger,\n );\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n logger.error(`AG-UI proxy request failed: ${msg}`);\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!res.writableEnded) {\n res.end();\n }\n status = 502;\n }\n\n return status;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: tee the upstream SSE stream to the client and buffer for recording\n// ---------------------------------------------------------------------------\n\nfunction teeUpstreamStream(\n target: URL,\n headers: Record<string, string>,\n body: string,\n clientRes: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n\n const upstreamReq = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (upstreamRes) => {\n const upstreamStatus = upstreamRes.statusCode ?? 200;\n\n // Normalize status codes: aimock acts as a gateway, so upstream\n // provider details (429, 503, etc.) should not leak.\n // Successes → 200, errors → 502 (Bad Gateway).\n const clientStatus = upstreamStatus >= 200 && upstreamStatus < 300 ? 200 : 502;\n\n // Set appropriate headers on the client response.\n if (!clientRes.headersSent) {\n if (clientStatus === 200) {\n clientRes.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n } else {\n const ct = upstreamRes.headers[\"content-type\"] || \"application/json\";\n clientRes.writeHead(502, { \"Content-Type\": ct });\n }\n }\n\n const chunks: Buffer[] = [];\n let clientWriteFailed = false;\n // Bound the in-memory record buffer so a single huge upstream response\n // cannot build a string past V8's ~512 MiB limit\n // (RangeError: Invalid string length) when we `Buffer.concat`+stringify\n // it below. The client relay above is INDEPENDENT of this buffer, so\n // capping it never truncates what the client receives — the cap means\n // \"don't journal\", not \"don't answer\" (mirrors the generic proxy path).\n const recordBufferCap = resolveAGUIRecordBufferCap(config.maxRecordBufferBytes);\n let bufferedBytes = 0;\n let recordTruncated = false;\n\n upstreamRes.on(\"data\", (chunk: Buffer) => {\n // Relay to client in real time\n try {\n clientRes.write(chunk);\n } catch (err) {\n if (!clientWriteFailed) {\n clientWriteFailed = true;\n logger?.warn(\n \"Client write failed during proxy relay:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n // Buffer for fixture construction, bounded by the record-buffer cap.\n // Once the cap is crossed we stop accumulating and free the buffer so\n // heap growth is bounded and the end-handler skips concat/stringify.\n if (recordTruncated) return;\n if (bufferedBytes + chunk.length > recordBufferCap) {\n recordTruncated = true;\n chunks.length = 0;\n bufferedBytes = 0;\n logger?.warn(\n `AG-UI upstream response exceeded the ${recordBufferCap}-byte record buffer cap — relaying full body to client, but skipping fixture recording to bound memory`,\n );\n return;\n }\n chunks.push(chunk);\n bufferedBytes += chunk.length;\n });\n\n let settled = false;\n\n upstreamRes.on(\"error\", (err) => {\n if (settled) return;\n settled = true;\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamRes.on(\"end\", () => {\n if (settled) return;\n settled = true;\n\n // Don't record fixtures for non-2xx upstream responses\n if (clientStatus !== 200) {\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n resolve(clientStatus);\n return;\n }\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n\n // Record buffer cap tripped: the buffered copy is partial (and was\n // freed), so we cannot faithfully build a fixture. The client already\n // received every byte via the live tee above, so just skip recording\n // — never stringify the (now-empty) buffer, never persist a truncated\n // fixture. Mirrors the generic proxy path's over-cap \"skip recording,\n // still answer the client\" behavior.\n if (recordTruncated) {\n logger.warn(\n \"AG-UI record buffer cap exceeded — response relayed to client, recording skipped\",\n );\n resolve(clientStatus);\n return;\n }\n\n // Parse buffered SSE events\n const buffered = Buffer.concat(chunks).toString();\n const events = parseSSEEvents(buffered, logger);\n\n // Build fixture — three-way match priority:\n // 1. Tool-result continuation (HITL): match by toolCallId\n // 2. User message: match by last user message content\n // 3. Fallback predicate: no user message present\n let match: AGUIFixtureMatch;\n const lastToolResult = getLastMessageIfToolResult(input);\n if (lastToolResult?.toolCallId) {\n match = { toolCallId: lastToolResult.toolCallId };\n logger.info(`Recorded AG-UI fixture keyed on toolCallId=${lastToolResult.toolCallId}`);\n } else {\n const message = extractLastUserMessage(input);\n if (message) {\n match = { message };\n } else {\n match = {\n predicate: (inp: AGUIRunAgentInput) =>\n !inp.messages?.length || !inp.messages.some((m) => m.role === \"user\"),\n };\n logger.warn(\n \"Recorded AG-UI fixture has no user message — available in-memory only (predicate fixtures cannot be persisted to disk)\",\n );\n }\n }\n const fixture: AGUIFixture = { match, events };\n\n if (!config.proxyOnly) {\n // Register in memory first (always available even if disk write fails)\n fixtures.push(fixture);\n\n // Predicate fixtures (no user message, no toolCallId) cannot be\n // meaningfully serialized — the sentinel becomes a literal string\n // match that never matches real requests. Keep in-memory only.\n if (fixture.match.predicate) {\n logger.warn(\n \"Skipping disk write for predicate fixture — in-memory only (cannot be persisted)\",\n );\n } else {\n const serializableFixture = {\n match: fixture.match,\n events: fixture.events,\n ...(fixture.delayMs !== undefined ? { delayMs: fixture.delayMs } : {}),\n };\n\n const fixturePath = config.fixturePath ?? \"./fixtures/agui-recorded\";\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `agui-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n try {\n fs.mkdirSync(fixturePath, { recursive: true });\n fs.writeFileSync(\n filepath,\n JSON.stringify({ fixtures: [serializableFixture] }, null, 2),\n \"utf-8\",\n );\n logger.warn(`AG-UI response recorded → ${filepath}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n logger.error(\n `Failed to save AG-UI fixture to disk: ${msg} (fixture retained in memory)`,\n );\n }\n }\n } else {\n logger.info(\"Proxied AG-UI request (proxy-only mode)\");\n }\n\n resolve(clientStatus);\n });\n },\n );\n\n upstreamReq.on(\"timeout\", () => {\n upstreamReq.destroy(\n new Error(`Upstream AG-UI request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s`),\n );\n });\n\n upstreamReq.on(\"error\", (err) => {\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamReq.write(body);\n upstreamReq.end();\n });\n}\n\n/**\n * Parse SSE data lines from buffered stream text.\n */\nfunction parseSSEEvents(text: string, logger?: Logger): AGUIEvent[] {\n const events: AGUIEvent[] = [];\n const blocks = text.split(\"\\n\\n\");\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n for (const line of lines) {\n if (line.startsWith(\"data:\")) {\n const payload = line.startsWith(\"data: \") ? line.slice(6) : line.slice(5);\n try {\n const parsed = JSON.parse(payload) as AGUIEvent;\n events.push(parsed);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const warning = `Skipping unparseable SSE data line (${msg}): ${payload.slice(0, 200)}`;\n if (logger) logger.warn(warning);\n else console.warn(warning);\n }\n }\n }\n }\n return events;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agui-recorder.cjs","names":["isAuthenticatedRequest","buildForwardHeaders","https","http","getLastMessageIfToolResult","extractLastUserMessage","crypto","path"],"sources":["../src/agui-recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n AGUIFixture,\n AGUIFixtureMatch,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport { extractLastUserMessage, getLastMessageIfToolResult } from \"./agui-handler.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isAuthenticatedRequest } from \"./api-key-auth.js\";\nimport { buildForwardHeaders, removeForwardHeader, setForwardHeaderDefault } from \"./recorder.js\";\n\n/**\n * Sentinel `match.message` value written to disk when the request had no\n * extractable user text. Keeps the on-disk fixture serializable (predicate\n * matchers aren't) but won't match any real user input on replay.\n */\nexport const NO_USER_MESSAGE_SENTINEL = \"__NO_USER_MESSAGE__\";\n\n/**\n * Default ceiling (bytes) for the in-memory AG-UI record buffer. The recorder\n * tees every upstream SSE chunk straight to the client AND buffers a copy so it\n * can `Buffer.concat(chunks).toString()` + parse the SSE events into a fixture\n * once the stream ends. With no cap, a large upstream response (amplified since\n * the real-key fixture-miss passthrough landed) builds a string past V8's\n * ~512 MiB max string length and throws `RangeError: Invalid string length`.\n * 64 MiB mirrors the generic proxy path's `DEFAULT_MAX_PROXY_BUFFER_BYTES` and\n * is generous for any real agent turn. Overridable via\n * `AGUIRecordConfig.maxRecordBufferBytes`.\n */\nexport const DEFAULT_AGUI_RECORD_BUFFER_BYTES = 64 * 1024 * 1024; // 64 MiB\n\n/**\n * Absolute hard ceiling (bytes) for the AG-UI record buffer, independent of the\n * configurable `maxRecordBufferBytes`, mirroring the generic proxy path's\n * `PROXY_BUFFER_HARD_CEILING`. 256 MiB of bytes can never decode to more than\n * 256 Mi UTF-16 code units, comfortably below V8's 2^29 - 1 string-length limit,\n * so the eventual `Buffer.concat(chunks).toString()` can never throw.\n */\nexport const AGUI_RECORD_BUFFER_HARD_CEILING = 256 * 1024 * 1024; // 256 MiB\n\n/**\n * Test-only override of the effective AG-UI record-buffer ceiling. Lets the cap\n * suite exercise the over-cap truncation path with a small body instead of\n * streaming hundreds of MB. `undefined` (the default) uses the configured /\n * default cap. NEVER set from production code.\n */\nlet aguiRecordBufferCeilingOverride: number | undefined;\n\n/** @internal test-only — see {@link aguiRecordBufferCeilingOverride}. */\nexport function setAGUIRecordBufferCeilingForTests(value: number | undefined): void {\n aguiRecordBufferCeilingOverride = value;\n}\n\n/**\n * Resolve the effective AG-UI record-buffer byte cap: honor a test-only\n * override, else the configured `maxRecordBufferBytes` (clamped to the hard\n * ceiling), else the default. Non-finite / non-positive configured values fall\n * back to the default.\n */\nfunction resolveAGUIRecordBufferCap(configured: number | undefined): number {\n if (aguiRecordBufferCeilingOverride !== undefined) return aguiRecordBufferCeilingOverride;\n if (configured == null || !Number.isFinite(configured) || configured <= 0) {\n return DEFAULT_AGUI_RECORD_BUFFER_BYTES;\n }\n return Math.min(configured, AGUI_RECORD_BUFFER_HARD_CEILING);\n}\n\n/**\n * Proxy an unmatched AG-UI request to a real upstream agent, record the\n * SSE event stream as a fixture on disk and in memory, and relay the\n * response back to the original client in real time.\n *\n * `rawBody` is the exact request payload as it arrived on the wire. It is\n * forwarded byte-for-byte rather than re-serialized from the parsed `input`,\n * because re-serialization changes key order and whitespace and therefore\n * invalidates any signature the caller computed over the body (AWS SigV4 signs\n * a hash of the payload). `input` remains the parsed view used for fixture\n * matching only. It is the LAST parameter so that a pre-existing JavaScript\n * caller omitting it fails loudly here rather than silently shifting an\n * earlier argument into the request body.\n *\n * Returns the HTTP status code written to the client if the request was proxied,\n * or `false` if no upstream is configured.\n */\nexport async function proxyAndRecordAGUI(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n rawBody: string,\n): Promise<number | false> {\n // A caller that predates the raw-body parameter would otherwise proxy\n // `undefined` as the payload. Refuse before opening an upstream connection.\n if (typeof rawBody !== \"string\") {\n logger.error(\"proxyAndRecordAGUI called without the raw request body — cannot proxy\");\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal proxy misconfiguration\" }));\n return 500;\n }\n // AG-UI has no provider-specific static credential contract. An inbound\n // test key must never become an implicit upstream credential, so fail before\n // opening an upstream connection.\n if (isAuthenticatedRequest(req)) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No configured provider credential\" }));\n return 502;\n }\n if (!config.upstream) {\n logger.warn(\"No upstream URL configured for AG-UI recording — cannot proxy\");\n return false;\n }\n\n let target: URL;\n try {\n target = new URL(config.upstream);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n logger.error(`Invalid upstream AG-UI URL: ${config.upstream} — ${detail}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid upstream AG-UI URL\" }));\n return 502;\n }\n\n logger.warn(`NO AG-UI FIXTURE MATCH — proxying to ${config.upstream}`);\n\n // Forward the inbound headers to the upstream agent, minus hop-by-hop,\n // client-set, and mock-internal ones — the same egress rule the generic\n // proxy path applies. An agent runtime's contract frequently lives in\n // headers (session affinity, per-request agent configuration, request\n // signatures), so the proxy strips a known-unsafe set rather than keeping a\n // hand-picked allowlist that silently drops everything it has not heard of.\n const forwardHeaders = buildForwardHeaders(req);\n // `Content-Type` describes the bytes the CALLER sent, so the caller owns it\n // and this only fills in a default. Signed requests depend on that: replacing\n // a caller-sent Content-Type invalidates a SigV4 signature computed over it.\n setForwardHeaderDefault(forwardHeaders, \"Content-Type\", \"application/json\");\n // `Accept` is the opposite: it describes what AIMOCK needs back. AG-UI is an\n // SSE protocol and the recorder can only parse an event stream, so a caller's\n // generic `*/*` or `application/json, text/plain, */*` must not be forwarded\n // to an upstream that content-negotiates — it would answer with JSON, which\n // relays to the client mislabelled and records as a zero-event fixture.\n // Overriding is safe here where it is not for Content-Type: AWS signs `host`,\n // `content-type` and the `x-amz-*` family, and `accept` is not in\n // `SignedHeaders`.\n removeForwardHeader(forwardHeaders, \"Accept\");\n forwardHeaders[\"Accept\"] = \"text/event-stream\";\n\n let status: number;\n try {\n status = await teeUpstreamStream(\n target,\n forwardHeaders,\n rawBody,\n res,\n input,\n fixtures,\n config,\n logger,\n );\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n logger.error(`AG-UI proxy request failed: ${msg}`);\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!res.writableEnded) {\n res.end();\n }\n status = 502;\n }\n\n return status;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: tee the upstream SSE stream to the client and buffer for recording\n// ---------------------------------------------------------------------------\n\nfunction teeUpstreamStream(\n target: URL,\n headers: Record<string, string>,\n body: string,\n clientRes: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n\n const upstreamReq = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (upstreamRes) => {\n const upstreamStatus = upstreamRes.statusCode ?? 200;\n\n // Normalize status codes: aimock acts as a gateway, so upstream\n // provider details (429, 503, etc.) should not leak.\n // Successes → 200, errors → 502 (Bad Gateway).\n const clientStatus = upstreamStatus >= 200 && upstreamStatus < 300 ? 200 : 502;\n\n // Set appropriate headers on the client response.\n if (!clientRes.headersSent) {\n if (clientStatus === 200) {\n clientRes.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n } else {\n const ct = upstreamRes.headers[\"content-type\"] || \"application/json\";\n clientRes.writeHead(502, { \"Content-Type\": ct });\n }\n }\n\n const chunks: Buffer[] = [];\n let clientWriteFailed = false;\n // Bound the in-memory record buffer so a single huge upstream response\n // cannot build a string past V8's ~512 MiB limit\n // (RangeError: Invalid string length) when we `Buffer.concat`+stringify\n // it below. The client relay above is INDEPENDENT of this buffer, so\n // capping it never truncates what the client receives — the cap means\n // \"don't journal\", not \"don't answer\" (mirrors the generic proxy path).\n const recordBufferCap = resolveAGUIRecordBufferCap(config.maxRecordBufferBytes);\n let bufferedBytes = 0;\n let recordTruncated = false;\n\n upstreamRes.on(\"data\", (chunk: Buffer) => {\n // Relay to client in real time\n try {\n clientRes.write(chunk);\n } catch (err) {\n if (!clientWriteFailed) {\n clientWriteFailed = true;\n logger?.warn(\n \"Client write failed during proxy relay:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n // Buffer for fixture construction, bounded by the record-buffer cap.\n // Once the cap is crossed we stop accumulating and free the buffer so\n // heap growth is bounded and the end-handler skips concat/stringify.\n if (recordTruncated) return;\n if (bufferedBytes + chunk.length > recordBufferCap) {\n recordTruncated = true;\n chunks.length = 0;\n bufferedBytes = 0;\n logger?.warn(\n `AG-UI upstream response exceeded the ${recordBufferCap}-byte record buffer cap — relaying full body to client, but skipping fixture recording to bound memory`,\n );\n return;\n }\n chunks.push(chunk);\n bufferedBytes += chunk.length;\n });\n\n let settled = false;\n\n upstreamRes.on(\"error\", (err) => {\n if (settled) return;\n settled = true;\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamRes.on(\"end\", () => {\n if (settled) return;\n settled = true;\n\n // Don't record fixtures for non-2xx upstream responses\n if (clientStatus !== 200) {\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n resolve(clientStatus);\n return;\n }\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n\n // Record buffer cap tripped: the buffered copy is partial (and was\n // freed), so we cannot faithfully build a fixture. The client already\n // received every byte via the live tee above, so just skip recording\n // — never stringify the (now-empty) buffer, never persist a truncated\n // fixture. Mirrors the generic proxy path's over-cap \"skip recording,\n // still answer the client\" behavior.\n if (recordTruncated) {\n logger.warn(\n \"AG-UI record buffer cap exceeded — response relayed to client, recording skipped\",\n );\n resolve(clientStatus);\n return;\n }\n\n // A 2xx upstream that did not answer with an event stream cannot be\n // recorded. `parseSSEEvents` finds no `data:` lines in a JSON body and\n // returns [], which would persist a zero-event fixture — and that\n // fixture then MATCHES on replay and streams nothing, with no\n // \"NO AG-UI FIXTURE MATCH\" and no parse warning to chase. Refuse the\n // recording and say why, rather than writing a tape that looks valid.\n // The client has already received every byte via the live tee above.\n const upstreamContentType = upstreamRes.headers[\"content-type\"] ?? \"\";\n if (!/text\\/event-stream/i.test(upstreamContentType)) {\n logger.error(\n `Upstream answered ${upstreamStatus} with Content-Type \"${upstreamContentType || \"(none)\"}\", not text/event-stream — response relayed to the client, recording refused`,\n );\n resolve(clientStatus);\n return;\n }\n\n // Parse buffered SSE events\n const buffered = Buffer.concat(chunks).toString();\n const events = parseSSEEvents(buffered, logger);\n\n // Same reasoning as the Content-Type refusal: an event stream that\n // yielded nothing parseable must not become a fixture.\n if (events.length === 0) {\n logger.error(\n \"Upstream event stream contained no parseable AG-UI events — response relayed to the client, recording refused\",\n );\n resolve(clientStatus);\n return;\n }\n\n // Build fixture — three-way match priority:\n // 1. Tool-result continuation (HITL): match by toolCallId\n // 2. User message: match by last user message content\n // 3. Fallback predicate: no user message present\n let match: AGUIFixtureMatch;\n const lastToolResult = getLastMessageIfToolResult(input);\n if (lastToolResult?.toolCallId) {\n match = { toolCallId: lastToolResult.toolCallId };\n logger.info(`Recorded AG-UI fixture keyed on toolCallId=${lastToolResult.toolCallId}`);\n } else {\n const message = extractLastUserMessage(input);\n if (message) {\n match = { message };\n } else {\n match = {\n predicate: (inp: AGUIRunAgentInput) =>\n !inp.messages?.length || !inp.messages.some((m) => m.role === \"user\"),\n };\n logger.warn(\n \"Recorded AG-UI fixture has no user message — available in-memory only (predicate fixtures cannot be persisted to disk)\",\n );\n }\n }\n const fixture: AGUIFixture = { match, events };\n\n if (!config.proxyOnly) {\n // Register in memory first (always available even if disk write fails)\n fixtures.push(fixture);\n\n // Predicate fixtures (no user message, no toolCallId) cannot be\n // meaningfully serialized — the sentinel becomes a literal string\n // match that never matches real requests. Keep in-memory only.\n if (fixture.match.predicate) {\n logger.warn(\n \"Skipping disk write for predicate fixture — in-memory only (cannot be persisted)\",\n );\n } else {\n const serializableFixture = {\n match: fixture.match,\n events: fixture.events,\n ...(fixture.delayMs !== undefined ? { delayMs: fixture.delayMs } : {}),\n };\n\n const fixturePath = config.fixturePath ?? \"./fixtures/agui-recorded\";\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `agui-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n try {\n fs.mkdirSync(fixturePath, { recursive: true });\n fs.writeFileSync(\n filepath,\n JSON.stringify({ fixtures: [serializableFixture] }, null, 2),\n \"utf-8\",\n );\n logger.warn(`AG-UI response recorded → ${filepath}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n logger.error(\n `Failed to save AG-UI fixture to disk: ${msg} (fixture retained in memory)`,\n );\n }\n }\n } else {\n logger.info(\"Proxied AG-UI request (proxy-only mode)\");\n }\n\n resolve(clientStatus);\n });\n },\n );\n\n upstreamReq.on(\"timeout\", () => {\n upstreamReq.destroy(\n new Error(`Upstream AG-UI request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s`),\n );\n });\n\n upstreamReq.on(\"error\", (err) => {\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamReq.write(body);\n upstreamReq.end();\n });\n}\n\n/**\n * Parse SSE data lines from buffered stream text.\n */\nfunction parseSSEEvents(text: string, logger?: Logger): AGUIEvent[] {\n const events: AGUIEvent[] = [];\n const blocks = text.split(\"\\n\\n\");\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n for (const line of lines) {\n if (line.startsWith(\"data:\")) {\n const payload = line.startsWith(\"data: \") ? line.slice(6) : line.slice(5);\n try {\n const parsed = JSON.parse(payload) as AGUIEvent;\n events.push(parsed);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const warning = `Skipping unparseable SSE data line (${msg}): ${payload.slice(0, 200)}`;\n if (logger) logger.warn(warning);\n else console.warn(warning);\n }\n }\n }\n }\n return events;\n}\n"],"mappings":"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"}
@@ -31,10 +31,19 @@ declare function setAGUIRecordBufferCeilingForTests(value: number | undefined):
31
31
  * SSE event stream as a fixture on disk and in memory, and relay the
32
32
  * response back to the original client in real time.
33
33
  *
34
+ * `rawBody` is the exact request payload as it arrived on the wire. It is
35
+ * forwarded byte-for-byte rather than re-serialized from the parsed `input`,
36
+ * because re-serialization changes key order and whitespace and therefore
37
+ * invalidates any signature the caller computed over the body (AWS SigV4 signs
38
+ * a hash of the payload). `input` remains the parsed view used for fixture
39
+ * matching only. It is the LAST parameter so that a pre-existing JavaScript
40
+ * caller omitting it fails loudly here rather than silently shifting an
41
+ * earlier argument into the request body.
42
+ *
34
43
  * Returns the HTTP status code written to the client if the request was proxied,
35
44
  * or `false` if no upstream is configured.
36
45
  */
37
- declare function proxyAndRecordAGUI(req: http$1.IncomingMessage, res: http$1.ServerResponse, input: AGUIRunAgentInput, fixtures: AGUIFixture[], config: AGUIRecordConfig, logger: Logger): Promise<number | false>;
46
+ declare function proxyAndRecordAGUI(req: http$1.IncomingMessage, res: http$1.ServerResponse, input: AGUIRunAgentInput, fixtures: AGUIFixture[], config: AGUIRecordConfig, logger: Logger, rawBody: string): Promise<number | false>;
38
47
  //# sourceMappingURL=agui-recorder.d.ts.map
39
48
  //#endregion
40
49
  export { AGUI_RECORD_BUFFER_HARD_CEILING, DEFAULT_AGUI_RECORD_BUFFER_BYTES, proxyAndRecordAGUI, setAGUIRecordBufferCeilingForTests };
@@ -1 +1 @@
1
- {"version":3,"file":"agui-recorder.d.cts","names":[],"sources":["../src/agui-recorder.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;cAkCa;;;;;;;;cASA;;iBAWG,kCAAA;;;;;;;;;iBA0BM,kBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uBACH,6BACG,uBACF,0BACA,SACP"}
1
+ {"version":3,"file":"agui-recorder.d.cts","names":[],"sources":["../src/agui-recorder.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;cAmCa;;;;;;;;cASA;;iBAWG,kCAAA;;;;;;;;;;;;;;;;;;iBAmCM,kBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uBACH,6BACG,uBACF,0BACA,0BAEP"}
@@ -31,10 +31,19 @@ declare function setAGUIRecordBufferCeilingForTests(value: number | undefined):
31
31
  * SSE event stream as a fixture on disk and in memory, and relay the
32
32
  * response back to the original client in real time.
33
33
  *
34
+ * `rawBody` is the exact request payload as it arrived on the wire. It is
35
+ * forwarded byte-for-byte rather than re-serialized from the parsed `input`,
36
+ * because re-serialization changes key order and whitespace and therefore
37
+ * invalidates any signature the caller computed over the body (AWS SigV4 signs
38
+ * a hash of the payload). `input` remains the parsed view used for fixture
39
+ * matching only. It is the LAST parameter so that a pre-existing JavaScript
40
+ * caller omitting it fails loudly here rather than silently shifting an
41
+ * earlier argument into the request body.
42
+ *
34
43
  * Returns the HTTP status code written to the client if the request was proxied,
35
44
  * or `false` if no upstream is configured.
36
45
  */
37
- declare function proxyAndRecordAGUI(req: http$1.IncomingMessage, res: http$1.ServerResponse, input: AGUIRunAgentInput, fixtures: AGUIFixture[], config: AGUIRecordConfig, logger: Logger): Promise<number | false>;
46
+ declare function proxyAndRecordAGUI(req: http$1.IncomingMessage, res: http$1.ServerResponse, input: AGUIRunAgentInput, fixtures: AGUIFixture[], config: AGUIRecordConfig, logger: Logger, rawBody: string): Promise<number | false>;
38
47
  //# sourceMappingURL=agui-recorder.d.ts.map
39
48
  //#endregion
40
49
  export { AGUI_RECORD_BUFFER_HARD_CEILING, DEFAULT_AGUI_RECORD_BUFFER_BYTES, proxyAndRecordAGUI, setAGUIRecordBufferCeilingForTests };
@@ -1 +1 @@
1
- {"version":3,"file":"agui-recorder.d.ts","names":[],"sources":["../src/agui-recorder.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;cAkCa;;;;;;;;cASA;;iBAWG,kCAAA;;;;;;;;;iBA0BM,kBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uBACH,6BACG,uBACF,0BACA,SACP"}
1
+ {"version":3,"file":"agui-recorder.d.ts","names":[],"sources":["../src/agui-recorder.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;cAmCa;;;;;;;;cASA;;iBAWG,kCAAA;;;;;;;;;;;;;;;;;;iBAmCM,kBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uBACH,6BACG,uBACF,0BACA,0BAEP"}
@@ -1,5 +1,6 @@
1
1
  import { isAuthenticatedRequest } from "./api-key-auth.js";
2
2
  import { extractLastUserMessage, getLastMessageIfToolResult } from "./agui-handler.js";
3
+ import { buildForwardHeaders, removeForwardHeader, setForwardHeaderDefault } from "./recorder.js";
3
4
  import * as http$1 from "node:http";
4
5
  import * as crypto$1 from "node:crypto";
5
6
  import * as https from "node:https";
@@ -54,10 +55,25 @@ function resolveAGUIRecordBufferCap(configured) {
54
55
  * SSE event stream as a fixture on disk and in memory, and relay the
55
56
  * response back to the original client in real time.
56
57
  *
58
+ * `rawBody` is the exact request payload as it arrived on the wire. It is
59
+ * forwarded byte-for-byte rather than re-serialized from the parsed `input`,
60
+ * because re-serialization changes key order and whitespace and therefore
61
+ * invalidates any signature the caller computed over the body (AWS SigV4 signs
62
+ * a hash of the payload). `input` remains the parsed view used for fixture
63
+ * matching only. It is the LAST parameter so that a pre-existing JavaScript
64
+ * caller omitting it fails loudly here rather than silently shifting an
65
+ * earlier argument into the request body.
66
+ *
57
67
  * Returns the HTTP status code written to the client if the request was proxied,
58
68
  * or `false` if no upstream is configured.
59
69
  */
60
- async function proxyAndRecordAGUI(req, res, input, fixtures, config, logger) {
70
+ async function proxyAndRecordAGUI(req, res, input, fixtures, config, logger, rawBody) {
71
+ if (typeof rawBody !== "string") {
72
+ logger.error("proxyAndRecordAGUI called without the raw request body — cannot proxy");
73
+ res.writeHead(500, { "Content-Type": "application/json" });
74
+ res.end(JSON.stringify({ error: "Internal proxy misconfiguration" }));
75
+ return 500;
76
+ }
61
77
  if (isAuthenticatedRequest(req)) {
62
78
  res.writeHead(502, { "Content-Type": "application/json" });
63
79
  res.end(JSON.stringify({ error: "No configured provider credential" }));
@@ -78,18 +94,13 @@ async function proxyAndRecordAGUI(req, res, input, fixtures, config, logger) {
78
94
  return 502;
79
95
  }
80
96
  logger.warn(`NO AG-UI FIXTURE MATCH — proxying to ${config.upstream}`);
81
- const forwardHeaders = {
82
- "Content-Type": "application/json",
83
- Accept: "text/event-stream"
84
- };
85
- const authorization = req.headers["authorization"];
86
- if (authorization) forwardHeaders["Authorization"] = Array.isArray(authorization) ? authorization.join(", ") : authorization;
87
- const apiKey = req.headers["x-api-key"];
88
- if (apiKey) forwardHeaders["x-api-key"] = Array.isArray(apiKey) ? apiKey.join(", ") : apiKey;
89
- const requestBody = JSON.stringify(input);
97
+ const forwardHeaders = buildForwardHeaders(req);
98
+ setForwardHeaderDefault(forwardHeaders, "Content-Type", "application/json");
99
+ removeForwardHeader(forwardHeaders, "Accept");
100
+ forwardHeaders["Accept"] = "text/event-stream";
90
101
  let status;
91
102
  try {
92
- status = await teeUpstreamStream(target, forwardHeaders, requestBody, res, input, fixtures, config, logger);
103
+ status = await teeUpstreamStream(target, forwardHeaders, rawBody, res, input, fixtures, config, logger);
93
104
  } catch (err) {
94
105
  const msg = err instanceof Error ? err.message : "Unknown proxy error";
95
106
  logger.error(`AG-UI proxy request failed: ${msg}`);
@@ -185,7 +196,18 @@ function teeUpstreamStream(target, headers, body, clientRes, input, fixtures, co
185
196
  resolve(clientStatus);
186
197
  return;
187
198
  }
199
+ const upstreamContentType = upstreamRes.headers["content-type"] ?? "";
200
+ if (!/text\/event-stream/i.test(upstreamContentType)) {
201
+ logger.error(`Upstream answered ${upstreamStatus} with Content-Type "${upstreamContentType || "(none)"}", not text/event-stream — response relayed to the client, recording refused`);
202
+ resolve(clientStatus);
203
+ return;
204
+ }
188
205
  const events = parseSSEEvents(Buffer.concat(chunks).toString(), logger);
206
+ if (events.length === 0) {
207
+ logger.error("Upstream event stream contained no parseable AG-UI events — response relayed to the client, recording refused");
208
+ resolve(clientStatus);
209
+ return;
210
+ }
189
211
  let match;
190
212
  const lastToolResult = getLastMessageIfToolResult(input);
191
213
  if (lastToolResult?.toolCallId) {
@@ -1 +1 @@
1
- {"version":3,"file":"agui-recorder.js","names":["http","crypto"],"sources":["../src/agui-recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n AGUIFixture,\n AGUIFixtureMatch,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport { extractLastUserMessage, getLastMessageIfToolResult } from \"./agui-handler.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isAuthenticatedRequest } from \"./api-key-auth.js\";\n\n/**\n * Sentinel `match.message` value written to disk when the request had no\n * extractable user text. Keeps the on-disk fixture serializable (predicate\n * matchers aren't) but won't match any real user input on replay.\n */\nexport const NO_USER_MESSAGE_SENTINEL = \"__NO_USER_MESSAGE__\";\n\n/**\n * Default ceiling (bytes) for the in-memory AG-UI record buffer. The recorder\n * tees every upstream SSE chunk straight to the client AND buffers a copy so it\n * can `Buffer.concat(chunks).toString()` + parse the SSE events into a fixture\n * once the stream ends. With no cap, a large upstream response (amplified since\n * the real-key fixture-miss passthrough landed) builds a string past V8's\n * ~512 MiB max string length and throws `RangeError: Invalid string length`.\n * 64 MiB mirrors the generic proxy path's `DEFAULT_MAX_PROXY_BUFFER_BYTES` and\n * is generous for any real agent turn. Overridable via\n * `AGUIRecordConfig.maxRecordBufferBytes`.\n */\nexport const DEFAULT_AGUI_RECORD_BUFFER_BYTES = 64 * 1024 * 1024; // 64 MiB\n\n/**\n * Absolute hard ceiling (bytes) for the AG-UI record buffer, independent of the\n * configurable `maxRecordBufferBytes`, mirroring the generic proxy path's\n * `PROXY_BUFFER_HARD_CEILING`. 256 MiB of bytes can never decode to more than\n * 256 Mi UTF-16 code units, comfortably below V8's 2^29 - 1 string-length limit,\n * so the eventual `Buffer.concat(chunks).toString()` can never throw.\n */\nexport const AGUI_RECORD_BUFFER_HARD_CEILING = 256 * 1024 * 1024; // 256 MiB\n\n/**\n * Test-only override of the effective AG-UI record-buffer ceiling. Lets the cap\n * suite exercise the over-cap truncation path with a small body instead of\n * streaming hundreds of MB. `undefined` (the default) uses the configured /\n * default cap. NEVER set from production code.\n */\nlet aguiRecordBufferCeilingOverride: number | undefined;\n\n/** @internal test-only — see {@link aguiRecordBufferCeilingOverride}. */\nexport function setAGUIRecordBufferCeilingForTests(value: number | undefined): void {\n aguiRecordBufferCeilingOverride = value;\n}\n\n/**\n * Resolve the effective AG-UI record-buffer byte cap: honor a test-only\n * override, else the configured `maxRecordBufferBytes` (clamped to the hard\n * ceiling), else the default. Non-finite / non-positive configured values fall\n * back to the default.\n */\nfunction resolveAGUIRecordBufferCap(configured: number | undefined): number {\n if (aguiRecordBufferCeilingOverride !== undefined) return aguiRecordBufferCeilingOverride;\n if (configured == null || !Number.isFinite(configured) || configured <= 0) {\n return DEFAULT_AGUI_RECORD_BUFFER_BYTES;\n }\n return Math.min(configured, AGUI_RECORD_BUFFER_HARD_CEILING);\n}\n\n/**\n * Proxy an unmatched AG-UI request to a real upstream agent, record the\n * SSE event stream as a fixture on disk and in memory, and relay the\n * response back to the original client in real time.\n *\n * Returns the HTTP status code written to the client if the request was proxied,\n * or `false` if no upstream is configured.\n */\nexport async function proxyAndRecordAGUI(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n): Promise<number | false> {\n // AG-UI has no provider-specific static credential contract. An inbound\n // test key must never become an implicit upstream credential, so fail before\n // opening an upstream connection.\n if (isAuthenticatedRequest(req)) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No configured provider credential\" }));\n return 502;\n }\n if (!config.upstream) {\n logger.warn(\"No upstream URL configured for AG-UI recording — cannot proxy\");\n return false;\n }\n\n let target: URL;\n try {\n target = new URL(config.upstream);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n logger.error(`Invalid upstream AG-UI URL: ${config.upstream} — ${detail}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid upstream AG-UI URL\" }));\n return 502;\n }\n\n logger.warn(`NO AG-UI FIXTURE MATCH — proxying to ${config.upstream}`);\n\n // Build upstream request headers\n const forwardHeaders: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n };\n // Forward auth headers if present\n const authorization = req.headers[\"authorization\"];\n if (authorization) {\n forwardHeaders[\"Authorization\"] = Array.isArray(authorization)\n ? authorization.join(\", \")\n : authorization;\n }\n const apiKey = req.headers[\"x-api-key\"];\n if (apiKey) {\n forwardHeaders[\"x-api-key\"] = Array.isArray(apiKey) ? apiKey.join(\", \") : apiKey;\n }\n\n const requestBody = JSON.stringify(input);\n\n let status: number;\n try {\n status = await teeUpstreamStream(\n target,\n forwardHeaders,\n requestBody,\n res,\n input,\n fixtures,\n config,\n logger,\n );\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n logger.error(`AG-UI proxy request failed: ${msg}`);\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!res.writableEnded) {\n res.end();\n }\n status = 502;\n }\n\n return status;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: tee the upstream SSE stream to the client and buffer for recording\n// ---------------------------------------------------------------------------\n\nfunction teeUpstreamStream(\n target: URL,\n headers: Record<string, string>,\n body: string,\n clientRes: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n\n const upstreamReq = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (upstreamRes) => {\n const upstreamStatus = upstreamRes.statusCode ?? 200;\n\n // Normalize status codes: aimock acts as a gateway, so upstream\n // provider details (429, 503, etc.) should not leak.\n // Successes → 200, errors → 502 (Bad Gateway).\n const clientStatus = upstreamStatus >= 200 && upstreamStatus < 300 ? 200 : 502;\n\n // Set appropriate headers on the client response.\n if (!clientRes.headersSent) {\n if (clientStatus === 200) {\n clientRes.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n } else {\n const ct = upstreamRes.headers[\"content-type\"] || \"application/json\";\n clientRes.writeHead(502, { \"Content-Type\": ct });\n }\n }\n\n const chunks: Buffer[] = [];\n let clientWriteFailed = false;\n // Bound the in-memory record buffer so a single huge upstream response\n // cannot build a string past V8's ~512 MiB limit\n // (RangeError: Invalid string length) when we `Buffer.concat`+stringify\n // it below. The client relay above is INDEPENDENT of this buffer, so\n // capping it never truncates what the client receives — the cap means\n // \"don't journal\", not \"don't answer\" (mirrors the generic proxy path).\n const recordBufferCap = resolveAGUIRecordBufferCap(config.maxRecordBufferBytes);\n let bufferedBytes = 0;\n let recordTruncated = false;\n\n upstreamRes.on(\"data\", (chunk: Buffer) => {\n // Relay to client in real time\n try {\n clientRes.write(chunk);\n } catch (err) {\n if (!clientWriteFailed) {\n clientWriteFailed = true;\n logger?.warn(\n \"Client write failed during proxy relay:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n // Buffer for fixture construction, bounded by the record-buffer cap.\n // Once the cap is crossed we stop accumulating and free the buffer so\n // heap growth is bounded and the end-handler skips concat/stringify.\n if (recordTruncated) return;\n if (bufferedBytes + chunk.length > recordBufferCap) {\n recordTruncated = true;\n chunks.length = 0;\n bufferedBytes = 0;\n logger?.warn(\n `AG-UI upstream response exceeded the ${recordBufferCap}-byte record buffer cap — relaying full body to client, but skipping fixture recording to bound memory`,\n );\n return;\n }\n chunks.push(chunk);\n bufferedBytes += chunk.length;\n });\n\n let settled = false;\n\n upstreamRes.on(\"error\", (err) => {\n if (settled) return;\n settled = true;\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamRes.on(\"end\", () => {\n if (settled) return;\n settled = true;\n\n // Don't record fixtures for non-2xx upstream responses\n if (clientStatus !== 200) {\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n resolve(clientStatus);\n return;\n }\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n\n // Record buffer cap tripped: the buffered copy is partial (and was\n // freed), so we cannot faithfully build a fixture. The client already\n // received every byte via the live tee above, so just skip recording\n // — never stringify the (now-empty) buffer, never persist a truncated\n // fixture. Mirrors the generic proxy path's over-cap \"skip recording,\n // still answer the client\" behavior.\n if (recordTruncated) {\n logger.warn(\n \"AG-UI record buffer cap exceeded — response relayed to client, recording skipped\",\n );\n resolve(clientStatus);\n return;\n }\n\n // Parse buffered SSE events\n const buffered = Buffer.concat(chunks).toString();\n const events = parseSSEEvents(buffered, logger);\n\n // Build fixture — three-way match priority:\n // 1. Tool-result continuation (HITL): match by toolCallId\n // 2. User message: match by last user message content\n // 3. Fallback predicate: no user message present\n let match: AGUIFixtureMatch;\n const lastToolResult = getLastMessageIfToolResult(input);\n if (lastToolResult?.toolCallId) {\n match = { toolCallId: lastToolResult.toolCallId };\n logger.info(`Recorded AG-UI fixture keyed on toolCallId=${lastToolResult.toolCallId}`);\n } else {\n const message = extractLastUserMessage(input);\n if (message) {\n match = { message };\n } else {\n match = {\n predicate: (inp: AGUIRunAgentInput) =>\n !inp.messages?.length || !inp.messages.some((m) => m.role === \"user\"),\n };\n logger.warn(\n \"Recorded AG-UI fixture has no user message — available in-memory only (predicate fixtures cannot be persisted to disk)\",\n );\n }\n }\n const fixture: AGUIFixture = { match, events };\n\n if (!config.proxyOnly) {\n // Register in memory first (always available even if disk write fails)\n fixtures.push(fixture);\n\n // Predicate fixtures (no user message, no toolCallId) cannot be\n // meaningfully serialized — the sentinel becomes a literal string\n // match that never matches real requests. Keep in-memory only.\n if (fixture.match.predicate) {\n logger.warn(\n \"Skipping disk write for predicate fixture — in-memory only (cannot be persisted)\",\n );\n } else {\n const serializableFixture = {\n match: fixture.match,\n events: fixture.events,\n ...(fixture.delayMs !== undefined ? { delayMs: fixture.delayMs } : {}),\n };\n\n const fixturePath = config.fixturePath ?? \"./fixtures/agui-recorded\";\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `agui-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n try {\n fs.mkdirSync(fixturePath, { recursive: true });\n fs.writeFileSync(\n filepath,\n JSON.stringify({ fixtures: [serializableFixture] }, null, 2),\n \"utf-8\",\n );\n logger.warn(`AG-UI response recorded → ${filepath}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n logger.error(\n `Failed to save AG-UI fixture to disk: ${msg} (fixture retained in memory)`,\n );\n }\n }\n } else {\n logger.info(\"Proxied AG-UI request (proxy-only mode)\");\n }\n\n resolve(clientStatus);\n });\n },\n );\n\n upstreamReq.on(\"timeout\", () => {\n upstreamReq.destroy(\n new Error(`Upstream AG-UI request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s`),\n );\n });\n\n upstreamReq.on(\"error\", (err) => {\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamReq.write(body);\n upstreamReq.end();\n });\n}\n\n/**\n * Parse SSE data lines from buffered stream text.\n */\nfunction parseSSEEvents(text: string, logger?: Logger): AGUIEvent[] {\n const events: AGUIEvent[] = [];\n const blocks = text.split(\"\\n\\n\");\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n for (const line of lines) {\n if (line.startsWith(\"data:\")) {\n const payload = line.startsWith(\"data: \") ? line.slice(6) : line.slice(5);\n try {\n const parsed = JSON.parse(payload) as AGUIEvent;\n events.push(parsed);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const warning = `Skipping unparseable SSE data line (${msg}): ${payload.slice(0, 200)}`;\n if (logger) logger.warn(warning);\n else console.warn(warning);\n }\n }\n }\n }\n return events;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agui-recorder.js","names":["http","crypto"],"sources":["../src/agui-recorder.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as crypto from \"node:crypto\";\nimport type {\n AGUIFixture,\n AGUIFixtureMatch,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport { extractLastUserMessage, getLastMessageIfToolResult } from \"./agui-handler.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isAuthenticatedRequest } from \"./api-key-auth.js\";\nimport { buildForwardHeaders, removeForwardHeader, setForwardHeaderDefault } from \"./recorder.js\";\n\n/**\n * Sentinel `match.message` value written to disk when the request had no\n * extractable user text. Keeps the on-disk fixture serializable (predicate\n * matchers aren't) but won't match any real user input on replay.\n */\nexport const NO_USER_MESSAGE_SENTINEL = \"__NO_USER_MESSAGE__\";\n\n/**\n * Default ceiling (bytes) for the in-memory AG-UI record buffer. The recorder\n * tees every upstream SSE chunk straight to the client AND buffers a copy so it\n * can `Buffer.concat(chunks).toString()` + parse the SSE events into a fixture\n * once the stream ends. With no cap, a large upstream response (amplified since\n * the real-key fixture-miss passthrough landed) builds a string past V8's\n * ~512 MiB max string length and throws `RangeError: Invalid string length`.\n * 64 MiB mirrors the generic proxy path's `DEFAULT_MAX_PROXY_BUFFER_BYTES` and\n * is generous for any real agent turn. Overridable via\n * `AGUIRecordConfig.maxRecordBufferBytes`.\n */\nexport const DEFAULT_AGUI_RECORD_BUFFER_BYTES = 64 * 1024 * 1024; // 64 MiB\n\n/**\n * Absolute hard ceiling (bytes) for the AG-UI record buffer, independent of the\n * configurable `maxRecordBufferBytes`, mirroring the generic proxy path's\n * `PROXY_BUFFER_HARD_CEILING`. 256 MiB of bytes can never decode to more than\n * 256 Mi UTF-16 code units, comfortably below V8's 2^29 - 1 string-length limit,\n * so the eventual `Buffer.concat(chunks).toString()` can never throw.\n */\nexport const AGUI_RECORD_BUFFER_HARD_CEILING = 256 * 1024 * 1024; // 256 MiB\n\n/**\n * Test-only override of the effective AG-UI record-buffer ceiling. Lets the cap\n * suite exercise the over-cap truncation path with a small body instead of\n * streaming hundreds of MB. `undefined` (the default) uses the configured /\n * default cap. NEVER set from production code.\n */\nlet aguiRecordBufferCeilingOverride: number | undefined;\n\n/** @internal test-only — see {@link aguiRecordBufferCeilingOverride}. */\nexport function setAGUIRecordBufferCeilingForTests(value: number | undefined): void {\n aguiRecordBufferCeilingOverride = value;\n}\n\n/**\n * Resolve the effective AG-UI record-buffer byte cap: honor a test-only\n * override, else the configured `maxRecordBufferBytes` (clamped to the hard\n * ceiling), else the default. Non-finite / non-positive configured values fall\n * back to the default.\n */\nfunction resolveAGUIRecordBufferCap(configured: number | undefined): number {\n if (aguiRecordBufferCeilingOverride !== undefined) return aguiRecordBufferCeilingOverride;\n if (configured == null || !Number.isFinite(configured) || configured <= 0) {\n return DEFAULT_AGUI_RECORD_BUFFER_BYTES;\n }\n return Math.min(configured, AGUI_RECORD_BUFFER_HARD_CEILING);\n}\n\n/**\n * Proxy an unmatched AG-UI request to a real upstream agent, record the\n * SSE event stream as a fixture on disk and in memory, and relay the\n * response back to the original client in real time.\n *\n * `rawBody` is the exact request payload as it arrived on the wire. It is\n * forwarded byte-for-byte rather than re-serialized from the parsed `input`,\n * because re-serialization changes key order and whitespace and therefore\n * invalidates any signature the caller computed over the body (AWS SigV4 signs\n * a hash of the payload). `input` remains the parsed view used for fixture\n * matching only. It is the LAST parameter so that a pre-existing JavaScript\n * caller omitting it fails loudly here rather than silently shifting an\n * earlier argument into the request body.\n *\n * Returns the HTTP status code written to the client if the request was proxied,\n * or `false` if no upstream is configured.\n */\nexport async function proxyAndRecordAGUI(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n rawBody: string,\n): Promise<number | false> {\n // A caller that predates the raw-body parameter would otherwise proxy\n // `undefined` as the payload. Refuse before opening an upstream connection.\n if (typeof rawBody !== \"string\") {\n logger.error(\"proxyAndRecordAGUI called without the raw request body — cannot proxy\");\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal proxy misconfiguration\" }));\n return 500;\n }\n // AG-UI has no provider-specific static credential contract. An inbound\n // test key must never become an implicit upstream credential, so fail before\n // opening an upstream connection.\n if (isAuthenticatedRequest(req)) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No configured provider credential\" }));\n return 502;\n }\n if (!config.upstream) {\n logger.warn(\"No upstream URL configured for AG-UI recording — cannot proxy\");\n return false;\n }\n\n let target: URL;\n try {\n target = new URL(config.upstream);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n logger.error(`Invalid upstream AG-UI URL: ${config.upstream} — ${detail}`);\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid upstream AG-UI URL\" }));\n return 502;\n }\n\n logger.warn(`NO AG-UI FIXTURE MATCH — proxying to ${config.upstream}`);\n\n // Forward the inbound headers to the upstream agent, minus hop-by-hop,\n // client-set, and mock-internal ones — the same egress rule the generic\n // proxy path applies. An agent runtime's contract frequently lives in\n // headers (session affinity, per-request agent configuration, request\n // signatures), so the proxy strips a known-unsafe set rather than keeping a\n // hand-picked allowlist that silently drops everything it has not heard of.\n const forwardHeaders = buildForwardHeaders(req);\n // `Content-Type` describes the bytes the CALLER sent, so the caller owns it\n // and this only fills in a default. Signed requests depend on that: replacing\n // a caller-sent Content-Type invalidates a SigV4 signature computed over it.\n setForwardHeaderDefault(forwardHeaders, \"Content-Type\", \"application/json\");\n // `Accept` is the opposite: it describes what AIMOCK needs back. AG-UI is an\n // SSE protocol and the recorder can only parse an event stream, so a caller's\n // generic `*/*` or `application/json, text/plain, */*` must not be forwarded\n // to an upstream that content-negotiates — it would answer with JSON, which\n // relays to the client mislabelled and records as a zero-event fixture.\n // Overriding is safe here where it is not for Content-Type: AWS signs `host`,\n // `content-type` and the `x-amz-*` family, and `accept` is not in\n // `SignedHeaders`.\n removeForwardHeader(forwardHeaders, \"Accept\");\n forwardHeaders[\"Accept\"] = \"text/event-stream\";\n\n let status: number;\n try {\n status = await teeUpstreamStream(\n target,\n forwardHeaders,\n rawBody,\n res,\n input,\n fixtures,\n config,\n logger,\n );\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n logger.error(`AG-UI proxy request failed: ${msg}`);\n if (!res.headersSent) {\n res.writeHead(502, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!res.writableEnded) {\n res.end();\n }\n status = 502;\n }\n\n return status;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: tee the upstream SSE stream to the client and buffer for recording\n// ---------------------------------------------------------------------------\n\nfunction teeUpstreamStream(\n target: URL,\n headers: Record<string, string>,\n body: string,\n clientRes: http.ServerResponse,\n input: AGUIRunAgentInput,\n fixtures: AGUIFixture[],\n config: AGUIRecordConfig,\n logger: Logger,\n): Promise<number> {\n return new Promise((resolve, reject) => {\n const transport = target.protocol === \"https:\" ? https : http;\n const UPSTREAM_TIMEOUT_MS = 30_000;\n\n const upstreamReq = transport.request(\n target,\n {\n method: \"POST\",\n timeout: UPSTREAM_TIMEOUT_MS,\n headers: {\n ...headers,\n \"Content-Length\": Buffer.byteLength(body).toString(),\n },\n },\n (upstreamRes) => {\n const upstreamStatus = upstreamRes.statusCode ?? 200;\n\n // Normalize status codes: aimock acts as a gateway, so upstream\n // provider details (429, 503, etc.) should not leak.\n // Successes → 200, errors → 502 (Bad Gateway).\n const clientStatus = upstreamStatus >= 200 && upstreamStatus < 300 ? 200 : 502;\n\n // Set appropriate headers on the client response.\n if (!clientRes.headersSent) {\n if (clientStatus === 200) {\n clientRes.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n } else {\n const ct = upstreamRes.headers[\"content-type\"] || \"application/json\";\n clientRes.writeHead(502, { \"Content-Type\": ct });\n }\n }\n\n const chunks: Buffer[] = [];\n let clientWriteFailed = false;\n // Bound the in-memory record buffer so a single huge upstream response\n // cannot build a string past V8's ~512 MiB limit\n // (RangeError: Invalid string length) when we `Buffer.concat`+stringify\n // it below. The client relay above is INDEPENDENT of this buffer, so\n // capping it never truncates what the client receives — the cap means\n // \"don't journal\", not \"don't answer\" (mirrors the generic proxy path).\n const recordBufferCap = resolveAGUIRecordBufferCap(config.maxRecordBufferBytes);\n let bufferedBytes = 0;\n let recordTruncated = false;\n\n upstreamRes.on(\"data\", (chunk: Buffer) => {\n // Relay to client in real time\n try {\n clientRes.write(chunk);\n } catch (err) {\n if (!clientWriteFailed) {\n clientWriteFailed = true;\n logger?.warn(\n \"Client write failed during proxy relay:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n // Buffer for fixture construction, bounded by the record-buffer cap.\n // Once the cap is crossed we stop accumulating and free the buffer so\n // heap growth is bounded and the end-handler skips concat/stringify.\n if (recordTruncated) return;\n if (bufferedBytes + chunk.length > recordBufferCap) {\n recordTruncated = true;\n chunks.length = 0;\n bufferedBytes = 0;\n logger?.warn(\n `AG-UI upstream response exceeded the ${recordBufferCap}-byte record buffer cap — relaying full body to client, but skipping fixture recording to bound memory`,\n );\n return;\n }\n chunks.push(chunk);\n bufferedBytes += chunk.length;\n });\n\n let settled = false;\n\n upstreamRes.on(\"error\", (err) => {\n if (settled) return;\n settled = true;\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamRes.on(\"end\", () => {\n if (settled) return;\n settled = true;\n\n // Don't record fixtures for non-2xx upstream responses\n if (clientStatus !== 200) {\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n resolve(clientStatus);\n return;\n }\n try {\n if (!clientRes.writableEnded) clientRes.end();\n } catch (writeErr) {\n logger.warn(\n \"Failed to end client response:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n\n // Record buffer cap tripped: the buffered copy is partial (and was\n // freed), so we cannot faithfully build a fixture. The client already\n // received every byte via the live tee above, so just skip recording\n // — never stringify the (now-empty) buffer, never persist a truncated\n // fixture. Mirrors the generic proxy path's over-cap \"skip recording,\n // still answer the client\" behavior.\n if (recordTruncated) {\n logger.warn(\n \"AG-UI record buffer cap exceeded — response relayed to client, recording skipped\",\n );\n resolve(clientStatus);\n return;\n }\n\n // A 2xx upstream that did not answer with an event stream cannot be\n // recorded. `parseSSEEvents` finds no `data:` lines in a JSON body and\n // returns [], which would persist a zero-event fixture — and that\n // fixture then MATCHES on replay and streams nothing, with no\n // \"NO AG-UI FIXTURE MATCH\" and no parse warning to chase. Refuse the\n // recording and say why, rather than writing a tape that looks valid.\n // The client has already received every byte via the live tee above.\n const upstreamContentType = upstreamRes.headers[\"content-type\"] ?? \"\";\n if (!/text\\/event-stream/i.test(upstreamContentType)) {\n logger.error(\n `Upstream answered ${upstreamStatus} with Content-Type \"${upstreamContentType || \"(none)\"}\", not text/event-stream — response relayed to the client, recording refused`,\n );\n resolve(clientStatus);\n return;\n }\n\n // Parse buffered SSE events\n const buffered = Buffer.concat(chunks).toString();\n const events = parseSSEEvents(buffered, logger);\n\n // Same reasoning as the Content-Type refusal: an event stream that\n // yielded nothing parseable must not become a fixture.\n if (events.length === 0) {\n logger.error(\n \"Upstream event stream contained no parseable AG-UI events — response relayed to the client, recording refused\",\n );\n resolve(clientStatus);\n return;\n }\n\n // Build fixture — three-way match priority:\n // 1. Tool-result continuation (HITL): match by toolCallId\n // 2. User message: match by last user message content\n // 3. Fallback predicate: no user message present\n let match: AGUIFixtureMatch;\n const lastToolResult = getLastMessageIfToolResult(input);\n if (lastToolResult?.toolCallId) {\n match = { toolCallId: lastToolResult.toolCallId };\n logger.info(`Recorded AG-UI fixture keyed on toolCallId=${lastToolResult.toolCallId}`);\n } else {\n const message = extractLastUserMessage(input);\n if (message) {\n match = { message };\n } else {\n match = {\n predicate: (inp: AGUIRunAgentInput) =>\n !inp.messages?.length || !inp.messages.some((m) => m.role === \"user\"),\n };\n logger.warn(\n \"Recorded AG-UI fixture has no user message — available in-memory only (predicate fixtures cannot be persisted to disk)\",\n );\n }\n }\n const fixture: AGUIFixture = { match, events };\n\n if (!config.proxyOnly) {\n // Register in memory first (always available even if disk write fails)\n fixtures.push(fixture);\n\n // Predicate fixtures (no user message, no toolCallId) cannot be\n // meaningfully serialized — the sentinel becomes a literal string\n // match that never matches real requests. Keep in-memory only.\n if (fixture.match.predicate) {\n logger.warn(\n \"Skipping disk write for predicate fixture — in-memory only (cannot be persisted)\",\n );\n } else {\n const serializableFixture = {\n match: fixture.match,\n events: fixture.events,\n ...(fixture.delayMs !== undefined ? { delayMs: fixture.delayMs } : {}),\n };\n\n const fixturePath = config.fixturePath ?? \"./fixtures/agui-recorded\";\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filename = `agui-${timestamp}-${crypto.randomUUID().slice(0, 8)}.json`;\n const filepath = path.join(fixturePath, filename);\n\n try {\n fs.mkdirSync(fixturePath, { recursive: true });\n fs.writeFileSync(\n filepath,\n JSON.stringify({ fixtures: [serializableFixture] }, null, 2),\n \"utf-8\",\n );\n logger.warn(`AG-UI response recorded → ${filepath}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : \"Unknown filesystem error\";\n logger.error(\n `Failed to save AG-UI fixture to disk: ${msg} (fixture retained in memory)`,\n );\n }\n }\n } else {\n logger.info(\"Proxied AG-UI request (proxy-only mode)\");\n }\n\n resolve(clientStatus);\n });\n },\n );\n\n upstreamReq.on(\"timeout\", () => {\n upstreamReq.destroy(\n new Error(`Upstream AG-UI request timed out after ${UPSTREAM_TIMEOUT_MS / 1000}s`),\n );\n });\n\n upstreamReq.on(\"error\", (err) => {\n try {\n if (!clientRes.headersSent) {\n clientRes.writeHead(502, { \"Content-Type\": \"application/json\" });\n clientRes.end(JSON.stringify({ error: \"Upstream AG-UI agent unreachable\" }));\n } else if (!clientRes.writableEnded) {\n clientRes.end();\n }\n } catch (writeErr) {\n logger.warn(\n \"Failed to write error response to client:\",\n writeErr instanceof Error ? writeErr.message : String(writeErr),\n );\n }\n reject(err);\n });\n\n upstreamReq.write(body);\n upstreamReq.end();\n });\n}\n\n/**\n * Parse SSE data lines from buffered stream text.\n */\nfunction parseSSEEvents(text: string, logger?: Logger): AGUIEvent[] {\n const events: AGUIEvent[] = [];\n const blocks = text.split(\"\\n\\n\");\n for (const block of blocks) {\n const lines = block.split(\"\\n\");\n for (const line of lines) {\n if (line.startsWith(\"data:\")) {\n const payload = line.startsWith(\"data: \") ? line.slice(6) : line.slice(5);\n try {\n const parsed = JSON.parse(payload) as AGUIEvent;\n events.push(parsed);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const warning = `Skipping unparseable SSE data line (${msg}): ${payload.slice(0, 200)}`;\n if (logger) logger.warn(warning);\n else console.warn(warning);\n }\n }\n }\n }\n return events;\n}\n"],"mappings":"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"}
@@ -10,6 +10,7 @@ interface AGUIRunStartedEvent extends AGUIBaseEvent {
10
10
  type: "RUN_STARTED";
11
11
  threadId: string;
12
12
  runId: string;
13
+ protocolVersion?: string;
13
14
  parentRunId?: string;
14
15
  input?: AGUIRunAgentInput;
15
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agui-types.d.cts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"agui-types.d.cts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":"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"}
@@ -10,6 +10,7 @@ interface AGUIRunStartedEvent extends AGUIBaseEvent {
10
10
  type: "RUN_STARTED";
11
11
  threadId: string;
12
12
  runId: string;
13
+ protocolVersion?: string;
13
14
  parentRunId?: string;
14
15
  input?: AGUIRunAgentInput;
15
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agui-types.d.ts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"agui-types.d.ts","names":[],"sources":["../src/agui-types.ts"],"sourcesContent":[],"mappings":"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"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3
3
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
4
4
  const require_config_loader = require('./config-loader.cjs');
5
5
  const require_convert = require('./convert.cjs');
6
+ const require_validate_cli = require('./validate-cli.cjs');
6
7
  let node_path = require("node:path");
7
8
  let node_util = require("node:util");
8
9
 
@@ -10,16 +11,19 @@ let node_util = require("node:util");
10
11
  const HELP = `
11
12
  Usage: aimock [options]
12
13
  aimock convert <format> <input> [output]
14
+ aimock validate [--strict] [--json] [--] <path> [more paths ...]
13
15
 
14
16
  Options:
15
17
  -c, --config <path> Path to aimock config JSON file (required)
16
18
  -p, --port <number> Port override (default: from config or 0)
17
- -h, --host <string> Host override (default: from config or 127.0.0.1)
18
- --help Show this help message
19
+ --host <string> Host override (default: from config or 127.0.0.1)
20
+ -h, --help Show this help message
19
21
 
20
22
  Subcommands:
21
23
  convert Convert third-party mock configs to aimock format
22
24
  Run "aimock convert --help" for details
25
+ validate Validate fixture files or directories offline
26
+ Run "aimock validate --help" for details
23
27
  `.trim();
24
28
  function runAimockCli(deps = {}) {
25
29
  /* v8 ignore next 6 -- defaults used only when called from CLI entry point */
@@ -39,6 +43,15 @@ function runAimockCli(deps = {}) {
39
43
  });
40
44
  return;
41
45
  }
46
+ if (argv[0] === "validate") {
47
+ require_validate_cli.runValidateCli({
48
+ argv: argv.slice(1),
49
+ log: deps.validateDeps?.log ?? log,
50
+ logError: deps.validateDeps?.logError ?? logError,
51
+ exit: deps.validateDeps?.exit ?? exit
52
+ });
53
+ return;
54
+ }
42
55
  let values;
43
56
  try {
44
57
  ({values} = (0, node_util.parseArgs)({
@@ -52,12 +65,10 @@ function runAimockCli(deps = {}) {
52
65
  type: "string",
53
66
  short: "p"
54
67
  },
55
- host: {
56
- type: "string",
57
- short: "h"
58
- },
68
+ host: { type: "string" },
59
69
  help: {
60
70
  type: "boolean",
71
+ short: "h",
61
72
  default: false
62
73
  }
63
74
  },
@@ -73,11 +84,21 @@ function runAimockCli(deps = {}) {
73
84
  exit(0);
74
85
  return;
75
86
  }
76
- if (!values.config) {
87
+ if (values.config === void 0) {
77
88
  logError("Error: --config is required.\n\n" + HELP);
78
89
  exit(1);
79
90
  return;
80
91
  }
92
+ const blank = [
93
+ ["config", values.config],
94
+ ["port", values.port],
95
+ ["host", values.host]
96
+ ].find(([, value]) => value !== void 0 && value.trim() === "");
97
+ if (blank !== void 0) {
98
+ logError(`Error: --${blank[0]} requires a non-empty value.\n\n${HELP}`);
99
+ exit(1);
100
+ return;
101
+ }
81
102
  const configPath = (0, node_path.resolve)(values.config);
82
103
  let config;
83
104
  try {
@@ -87,7 +108,7 @@ function runAimockCli(deps = {}) {
87
108
  exit(1);
88
109
  return;
89
110
  }
90
- const port = values.port ? Number(values.port) : void 0;
111
+ const port = values.port === void 0 ? void 0 : Number(values.port);
91
112
  if (port !== void 0 && (Number.isNaN(port) || !Number.isInteger(port) || port < 0 || port > 65535)) {
92
113
  logError(`Error: invalid port "${values.port}".\n\n${HELP}`);
93
114
  exit(1);
@@ -118,8 +139,15 @@ function runAimockCli(deps = {}) {
118
139
  exit(1);
119
140
  });
120
141
  }
121
- const base = (0, node_path.basename)(process.argv[1] ?? "");
122
- if (base === "aimock" || base === "aimock-cli.js" || base === "aimock-cli.ts") runAimockCli();
142
+ /* v8 ignore start -- entry-point guard, exercised by integration tests */
143
+ const ENTRY_BASENAMES = new Set([
144
+ "aimock",
145
+ "aimock-cli.js",
146
+ "aimock-cli.cjs",
147
+ "aimock-cli.ts"
148
+ ]);
149
+ const scriptName = process.argv[1] ?? "";
150
+ if (ENTRY_BASENAMES.has((0, node_path.basename)(scriptName))) runAimockCli();
123
151
  /* v8 ignore stop */
124
152
 
125
153
  //#endregion