@copilotkit/aimock 1.41.0 → 1.42.1

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 (218) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +18 -1
  4. package/README.md +2 -1
  5. package/dist/a2a-mock.cjs +18 -11
  6. package/dist/a2a-mock.cjs.map +1 -1
  7. package/dist/a2a-mock.d.cts +1 -0
  8. package/dist/a2a-mock.d.cts.map +1 -1
  9. package/dist/a2a-mock.d.ts +1 -0
  10. package/dist/a2a-mock.d.ts.map +1 -1
  11. package/dist/a2a-mock.js +18 -11
  12. package/dist/a2a-mock.js.map +1 -1
  13. package/dist/agui-handler.d.ts.map +1 -1
  14. package/dist/agui-mock.cjs +2 -0
  15. package/dist/agui-mock.cjs.map +1 -1
  16. package/dist/agui-mock.d.cts.map +1 -1
  17. package/dist/agui-mock.d.ts.map +1 -1
  18. package/dist/agui-mock.js +2 -0
  19. package/dist/agui-mock.js.map +1 -1
  20. package/dist/bedrock-converse.cjs +36 -2
  21. package/dist/bedrock-converse.cjs.map +1 -1
  22. package/dist/bedrock-converse.d.cts.map +1 -1
  23. package/dist/bedrock-converse.d.ts.map +1 -1
  24. package/dist/bedrock-converse.js +37 -3
  25. package/dist/bedrock-converse.js.map +1 -1
  26. package/dist/bedrock.cjs +50 -13
  27. package/dist/bedrock.cjs.map +1 -1
  28. package/dist/bedrock.d.cts.map +1 -1
  29. package/dist/bedrock.d.ts.map +1 -1
  30. package/dist/bedrock.js +51 -14
  31. package/dist/bedrock.js.map +1 -1
  32. package/dist/byteplus-video.cjs +2 -2
  33. package/dist/byteplus-video.cjs.map +1 -1
  34. package/dist/byteplus-video.d.cts.map +1 -1
  35. package/dist/byteplus-video.d.ts.map +1 -1
  36. package/dist/byteplus-video.js +2 -2
  37. package/dist/byteplus-video.js.map +1 -1
  38. package/dist/chaos.cjs +36 -7
  39. package/dist/chaos.cjs.map +1 -1
  40. package/dist/chaos.d.cts +8 -3
  41. package/dist/chaos.d.cts.map +1 -1
  42. package/dist/chaos.d.ts +8 -3
  43. package/dist/chaos.d.ts.map +1 -1
  44. package/dist/chaos.js +36 -8
  45. package/dist/chaos.js.map +1 -1
  46. package/dist/cohere.cjs +58 -5
  47. package/dist/cohere.cjs.map +1 -1
  48. package/dist/cohere.d.cts.map +1 -1
  49. package/dist/cohere.d.ts.map +1 -1
  50. package/dist/cohere.js +59 -6
  51. package/dist/cohere.js.map +1 -1
  52. package/dist/config-loader.d.cts.map +1 -1
  53. package/dist/config-loader.d.ts.map +1 -1
  54. package/dist/elevenlabs-audio.cjs +36 -2
  55. package/dist/elevenlabs-audio.cjs.map +1 -1
  56. package/dist/elevenlabs-audio.d.cts.map +1 -1
  57. package/dist/elevenlabs-audio.d.ts.map +1 -1
  58. package/dist/elevenlabs-audio.js +37 -3
  59. package/dist/elevenlabs-audio.js.map +1 -1
  60. package/dist/embeddings.cjs +58 -3
  61. package/dist/embeddings.cjs.map +1 -1
  62. package/dist/embeddings.d.cts.map +1 -1
  63. package/dist/embeddings.d.ts.map +1 -1
  64. package/dist/embeddings.js +59 -4
  65. package/dist/embeddings.js.map +1 -1
  66. package/dist/fal-audio.cjs +38 -2
  67. package/dist/fal-audio.cjs.map +1 -1
  68. package/dist/fal-audio.d.cts.map +1 -1
  69. package/dist/fal-audio.d.ts.map +1 -1
  70. package/dist/fal-audio.js +39 -3
  71. package/dist/fal-audio.js.map +1 -1
  72. package/dist/fixture-loader.cjs +0 -0
  73. package/dist/fixture-loader.cjs.map +1 -1
  74. package/dist/fixture-loader.d.cts +9 -1
  75. package/dist/fixture-loader.d.cts.map +1 -1
  76. package/dist/fixture-loader.d.ts +9 -1
  77. package/dist/fixture-loader.d.ts.map +1 -1
  78. package/dist/fixture-loader.js +0 -0
  79. package/dist/fixture-loader.js.map +1 -1
  80. package/dist/gemini-embeddings.cjs +19 -1
  81. package/dist/gemini-embeddings.cjs.map +1 -1
  82. package/dist/gemini-embeddings.js +20 -2
  83. package/dist/gemini-embeddings.js.map +1 -1
  84. package/dist/gemini-interactions.cjs +15 -1
  85. package/dist/gemini-interactions.cjs.map +1 -1
  86. package/dist/gemini-interactions.d.cts.map +1 -1
  87. package/dist/gemini-interactions.d.ts.map +1 -1
  88. package/dist/gemini-interactions.js +16 -2
  89. package/dist/gemini-interactions.js.map +1 -1
  90. package/dist/gemini.cjs +70 -6
  91. package/dist/gemini.cjs.map +1 -1
  92. package/dist/gemini.d.cts.map +1 -1
  93. package/dist/gemini.d.ts.map +1 -1
  94. package/dist/gemini.js +71 -7
  95. package/dist/gemini.js.map +1 -1
  96. package/dist/grok-video.cjs +2 -2
  97. package/dist/grok-video.cjs.map +1 -1
  98. package/dist/grok-video.d.cts.map +1 -1
  99. package/dist/grok-video.d.ts.map +1 -1
  100. package/dist/grok-video.js +2 -2
  101. package/dist/grok-video.js.map +1 -1
  102. package/dist/helpers.cjs +168 -7
  103. package/dist/helpers.cjs.map +1 -1
  104. package/dist/helpers.d.cts.map +1 -1
  105. package/dist/helpers.d.ts.map +1 -1
  106. package/dist/helpers.js +160 -8
  107. package/dist/helpers.js.map +1 -1
  108. package/dist/images.cjs +22 -4
  109. package/dist/images.cjs.map +1 -1
  110. package/dist/images.d.cts.map +1 -1
  111. package/dist/images.d.ts.map +1 -1
  112. package/dist/images.js +23 -5
  113. package/dist/images.js.map +1 -1
  114. package/dist/llmock.cjs +15 -0
  115. package/dist/llmock.cjs.map +1 -1
  116. package/dist/llmock.d.cts +12 -0
  117. package/dist/llmock.d.cts.map +1 -1
  118. package/dist/llmock.d.ts +12 -0
  119. package/dist/llmock.d.ts.map +1 -1
  120. package/dist/llmock.js +16 -1
  121. package/dist/llmock.js.map +1 -1
  122. package/dist/mcp-mock.cjs +11 -0
  123. package/dist/mcp-mock.cjs.map +1 -1
  124. package/dist/mcp-mock.d.cts.map +1 -1
  125. package/dist/mcp-mock.d.ts.map +1 -1
  126. package/dist/mcp-mock.js +11 -0
  127. package/dist/mcp-mock.js.map +1 -1
  128. package/dist/messages.cjs +18 -1
  129. package/dist/messages.cjs.map +1 -1
  130. package/dist/messages.d.cts.map +1 -1
  131. package/dist/messages.d.ts.map +1 -1
  132. package/dist/messages.js +19 -2
  133. package/dist/messages.js.map +1 -1
  134. package/dist/moderation.cjs +67 -3
  135. package/dist/moderation.cjs.map +1 -1
  136. package/dist/moderation.d.cts +1 -0
  137. package/dist/moderation.d.cts.map +1 -1
  138. package/dist/moderation.d.ts +1 -0
  139. package/dist/moderation.d.ts.map +1 -1
  140. package/dist/moderation.js +68 -4
  141. package/dist/moderation.js.map +1 -1
  142. package/dist/ollama.cjs +75 -5
  143. package/dist/ollama.cjs.map +1 -1
  144. package/dist/ollama.d.cts.map +1 -1
  145. package/dist/ollama.d.ts.map +1 -1
  146. package/dist/ollama.js +76 -6
  147. package/dist/ollama.js.map +1 -1
  148. package/dist/openrouter-video.cjs +4 -4
  149. package/dist/openrouter-video.cjs.map +1 -1
  150. package/dist/openrouter-video.d.cts.map +1 -1
  151. package/dist/openrouter-video.d.ts.map +1 -1
  152. package/dist/openrouter-video.js +4 -4
  153. package/dist/openrouter-video.js.map +1 -1
  154. package/dist/rerank.cjs +65 -2
  155. package/dist/rerank.cjs.map +1 -1
  156. package/dist/rerank.d.cts +1 -1
  157. package/dist/rerank.d.cts.map +1 -1
  158. package/dist/rerank.d.ts +1 -1
  159. package/dist/rerank.d.ts.map +1 -1
  160. package/dist/rerank.js +66 -3
  161. package/dist/rerank.js.map +1 -1
  162. package/dist/responses.cjs +18 -1
  163. package/dist/responses.cjs.map +1 -1
  164. package/dist/responses.d.cts.map +1 -1
  165. package/dist/responses.d.ts.map +1 -1
  166. package/dist/responses.js +19 -2
  167. package/dist/responses.js.map +1 -1
  168. package/dist/search.cjs +67 -3
  169. package/dist/search.cjs.map +1 -1
  170. package/dist/search.d.cts +1 -0
  171. package/dist/search.d.cts.map +1 -1
  172. package/dist/search.d.ts +1 -0
  173. package/dist/search.d.ts.map +1 -1
  174. package/dist/search.js +68 -4
  175. package/dist/search.js.map +1 -1
  176. package/dist/server.cjs +317 -13
  177. package/dist/server.cjs.map +1 -1
  178. package/dist/server.d.cts.map +1 -1
  179. package/dist/server.d.ts.map +1 -1
  180. package/dist/server.js +320 -16
  181. package/dist/server.js.map +1 -1
  182. package/dist/speech.cjs +18 -1
  183. package/dist/speech.cjs.map +1 -1
  184. package/dist/speech.d.cts.map +1 -1
  185. package/dist/speech.d.ts.map +1 -1
  186. package/dist/speech.js +19 -2
  187. package/dist/speech.js.map +1 -1
  188. package/dist/transcription.cjs +21 -2
  189. package/dist/transcription.cjs.map +1 -1
  190. package/dist/transcription.d.cts.map +1 -1
  191. package/dist/transcription.d.ts.map +1 -1
  192. package/dist/transcription.js +21 -2
  193. package/dist/transcription.js.map +1 -1
  194. package/dist/types.d.cts +39 -2
  195. package/dist/types.d.cts.map +1 -1
  196. package/dist/types.d.ts +39 -2
  197. package/dist/types.d.ts.map +1 -1
  198. package/dist/vector-mock.cjs +88 -0
  199. package/dist/vector-mock.cjs.map +1 -1
  200. package/dist/vector-mock.d.cts.map +1 -1
  201. package/dist/vector-mock.d.ts.map +1 -1
  202. package/dist/vector-mock.js +89 -1
  203. package/dist/vector-mock.js.map +1 -1
  204. package/dist/vector-types.d.cts.map +1 -1
  205. package/dist/vector-types.d.ts.map +1 -1
  206. package/dist/veo-video.cjs +2 -2
  207. package/dist/veo-video.cjs.map +1 -1
  208. package/dist/veo-video.d.cts.map +1 -1
  209. package/dist/veo-video.d.ts.map +1 -1
  210. package/dist/veo-video.js +2 -2
  211. package/dist/veo-video.js.map +1 -1
  212. package/dist/video.cjs +19 -2
  213. package/dist/video.cjs.map +1 -1
  214. package/dist/video.d.cts.map +1 -1
  215. package/dist/video.d.ts.map +1 -1
  216. package/dist/video.js +20 -3
  217. package/dist/video.js.map +1 -1
  218. package/package.json +1 -1
@@ -9,7 +9,7 @@
9
9
  "source": {
10
10
  "source": "npm",
11
11
  "package": "@copilotkit/aimock",
12
- "version": "^1.41.0"
12
+ "version": "^1.42.1"
13
13
  },
14
14
  "description": "Fixture authoring skill for @copilotkit/aimock — LLM, multimedia (image/TTS/transcription/video), MCP, A2A, AG-UI, vector, embeddings, structured output, sequential responses, streaming physics, record/replay, agent loop patterns, and debugging"
15
15
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aimock",
3
- "version": "1.41.0",
3
+ "version": "1.42.1",
4
4
  "description": "Fixture authoring guidance for @copilotkit/aimock — LLM, multimedia, MCP, A2A, AG-UI, vector, and service mocking",
5
5
  "author": {
6
6
  "name": "CopilotKit"
package/CHANGELOG.md CHANGED
@@ -1,6 +1,23 @@
1
1
  # @copilotkit/aimock
2
2
 
3
- ## [Unreleased]
3
+ ## [1.42.1] - 2026-09-18
4
+
5
+ ### Fixed
6
+
7
+ - Identify reasoning-first tool streams as assistant messages for MCP Apps (#470).
8
+
9
+ ## [1.42.0] - 2026-09-13
10
+
11
+ ### Added
12
+
13
+ - Filtering and pagination on `GET /__aimock/journal`, with an `X-Total-Count` header (#435)
14
+ - Runtime chaos control at `/__aimock/chaos`, and a `?include=fixtures` fixture dump (#435)
15
+
16
+ ### Fixed
17
+
18
+ - Every wrapper path journals now — exact-length or `X-Total-Count` asserts see more (#441)
19
+ - Moderation, rerank and search honour strict mode; non-object JSON bodies now `400` (#440, #438)
20
+ - `POST /__aimock/error` validates its payload; quoted multipart boundaries parse (#434)
4
21
 
5
22
  ## [1.41.0] - 2026-09-10
6
23
 
package/README.md CHANGED
@@ -75,7 +75,8 @@ Run them all on one port with `npx @copilotkit/aimock --config aimock.json`, or
75
75
  - **[14 providers across 16 API surfaces](https://aimock.copilotkit.dev/docs)** — OpenAI Chat, OpenAI Responses, OpenAI Realtime (GA + Beta shim), Claude, Gemini REST, Gemini embedContent, Gemini Live, Gemini Interactions, Azure, Bedrock, Vertex AI, Ollama (chat + embeddings), Cohere (chat + embed), OpenRouter (chat + router failover), BytePlus Ark (OpenAI-compatible chat + images under `/api/v3`), ElevenLabs TTS — full streaming support
76
76
  - **Multimedia APIs** — [image generation](https://aimock.copilotkit.dev/images) (DALL-E, Imagen), [image editing](https://aimock.copilotkit.dev/images) (/v1/images/edits), [text-to-speech](https://aimock.copilotkit.dev/speech) (OpenAI + ElevenLabs), [audio transcription](https://aimock.copilotkit.dev/transcription), [audio translation](https://aimock.copilotkit.dev/transcription) (/v1/audio/translations), [video generation](https://aimock.copilotkit.dev/video), [OpenRouter video generation](https://aimock.copilotkit.dev/openrouter-video) (/api/v1/videos with async job lifecycle), [Google Veo video generation](https://aimock.copilotkit.dev/veo-video) (:predictLongRunning + /v1beta/operations async lifecycle), [Grok Imagine video generation](https://aimock.copilotkit.dev/grok-video) (/v1/videos/generations with async job lifecycle), [BytePlus Ark video generation](https://aimock.copilotkit.dev/byteplus-video) (Seedance, /api/v3/contents/generations/tasks with async task lifecycle), [fal.ai](https://aimock.copilotkit.dev/fal-ai) (image / video / audio with queue lifecycle)
77
77
  - **[MCP](https://aimock.copilotkit.dev/mcp-mock) / [A2A](https://aimock.copilotkit.dev/a2a-mock) / [AG-UI](https://aimock.copilotkit.dev/agui-mock) / [Vector](https://aimock.copilotkit.dev/vector-mock)** — Mock every protocol your AI agents use
78
- - **[Chaos Testing](https://aimock.copilotkit.dev/chaos-testing)** — 500 errors, malformed JSON, mid-stream disconnects at any probability
78
+ - **[Chaos Testing](https://aimock.copilotkit.dev/chaos-testing)** — 500 errors, malformed JSON, mid-stream disconnects at any probability; read, set and clear the rates at runtime via `GET` / `POST` / `DELETE /__aimock/chaos`, scoped to the caller's `X-Test-Id` so one test cannot destabilise the suite running beside it
79
+ - **Request journal** — `GET /__aimock/journal` returns every recorded request, filterable by `path`, `method`, `status`, `service` and `testId` with `limit` / `offset` paging and an `X-Total-Count` header carrying the pre-pagination total
79
80
  - **Per-Request Strict Mode** — `X-AIMock-Strict` header overrides the server-level `--strict` flag per request (`true`/`1` = strict, `false`/`0` = lenient)
80
81
  - **Context-Based Fixture Routing** — `X-AIMock-Context` header scopes fixtures per integration; fixtures with `match.context` only match requests carrying that context, fixtures without it remain shared
81
82
  - **[Drift Detection](https://aimock.copilotkit.dev/drift-detection)** — Daily CI validation against real APIs
package/dist/a2a-mock.cjs CHANGED
@@ -63,6 +63,19 @@ var A2AMock = class {
63
63
  });
64
64
  return this;
65
65
  }
66
+ journalRequest(req, pathname, status) {
67
+ if (this.journal) this.journal.add({
68
+ method: req.method ?? "POST",
69
+ path: req.url ?? pathname,
70
+ headers: require_helpers.flattenHeaders(req.headers),
71
+ body: null,
72
+ service: "a2a",
73
+ response: {
74
+ status,
75
+ fixture: null
76
+ }
77
+ });
78
+ }
66
79
  async handleRequest(req, res, pathname) {
67
80
  if (req.method === "GET" && pathname === "/.well-known/agent-card.json") {
68
81
  if (this.registry) this.registry.incrementCounter("aimock_a2a_requests_total", { method: "GetAgentCard" });
@@ -72,6 +85,7 @@ var A2AMock = class {
72
85
  "A2A-Version": "1.0"
73
86
  });
74
87
  res.end(JSON.stringify(card));
88
+ this.journalRequest(req, pathname, 200);
75
89
  return true;
76
90
  }
77
91
  if (req.method === "POST" && (pathname === "/" || pathname === "")) {
@@ -92,6 +106,7 @@ var A2AMock = class {
92
106
  message: "Parse error"
93
107
  }
94
108
  }));
109
+ this.journalRequest(req, pathname, 200);
95
110
  return true;
96
111
  }
97
112
  if (this.registry) {
@@ -104,17 +119,7 @@ var A2AMock = class {
104
119
  }
105
120
  res.setHeader("A2A-Version", "1.0");
106
121
  await this.dispatcher(req, res, body);
107
- if (this.journal) this.journal.add({
108
- method: req.method ?? "POST",
109
- path: pathname,
110
- headers: require_helpers.flattenHeaders(req.headers),
111
- body: null,
112
- service: "a2a",
113
- response: {
114
- status: res.statusCode,
115
- fixture: null
116
- }
117
- });
122
+ this.journalRequest(req, pathname, res.statusCode);
118
123
  return true;
119
124
  }
120
125
  return false;
@@ -145,6 +150,7 @@ var A2AMock = class {
145
150
  res.writeHead(500);
146
151
  res.end("Internal server error");
147
152
  } else if (!res.writableEnded) res.end();
153
+ this.journalRequest(req, url.pathname, res.statusCode);
148
154
  });
149
155
  });
150
156
  srv.on("error", reject);
@@ -194,6 +200,7 @@ var A2AMock = class {
194
200
  message: "No matching pattern for message"
195
201
  }
196
202
  }));
203
+ this.journalRequest(req, "/", res.statusCode);
197
204
  return;
198
205
  }
199
206
  const taskId = require_helpers.generateId("task");
@@ -1 +1 @@
1
- {"version":3,"file":"a2a-mock.cjs","names":["createJsonRpcDispatcher","createA2AMethods","buildAgentCard","readBody","flattenHeaders","http","extractText","findStreamingMatch","generateId","TERMINAL_STATES"],"sources":["../src/a2a-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n A2AAgentDefinition,\n A2AArtifact,\n A2AMockOptions,\n A2APart,\n A2AStreamEvent,\n A2ATask,\n} from \"./a2a-types.js\";\nimport type { PatternEntry } from \"./a2a-handler.js\";\nimport {\n buildAgentCard,\n createA2AMethods,\n extractText,\n findStreamingMatch,\n TERMINAL_STATES,\n} from \"./a2a-handler.js\";\nimport { createJsonRpcDispatcher } from \"./jsonrpc.js\";\nimport { generateId, flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class A2AMock implements Mountable {\n private agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }> = new Map();\n private tasks: Map<string, A2ATask> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: A2AMockOptions;\n private baseUrl = \"\";\n private dispatcher: ReturnType<typeof createJsonRpcDispatcher>;\n\n constructor(options?: A2AMockOptions) {\n this.options = options ?? {};\n this.dispatcher = this.buildDispatcher();\n }\n\n private buildDispatcher() {\n const methods = createA2AMethods(this.agents, this.tasks);\n return createJsonRpcDispatcher({ methods });\n }\n\n // ---- Agent registration ----\n\n registerAgent(def: A2AAgentDefinition): this {\n this.agents.set(def.name, { def, patterns: [] });\n return this;\n }\n\n // ---- Pattern registration ----\n\n onMessage(agentName: string, pattern: string | RegExp, parts: A2APart[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"message\", pattern, agentName, parts });\n return this;\n }\n\n onTask(agentName: string, pattern: string | RegExp, artifacts: A2AArtifact[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"task\", pattern, agentName, artifacts });\n return this;\n }\n\n onStreamingTask(\n agentName: string,\n pattern: string | RegExp,\n events: A2AStreamEvent[],\n delayMs?: number,\n ): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"streamingTask\", pattern, agentName, events, delayMs });\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Agent card endpoint\n if (req.method === \"GET\" && pathname === \"/.well-known/agent-card.json\") {\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: \"GetAgentCard\" });\n }\n const card = buildAgentCard(this.agents, this.baseUrl);\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(JSON.stringify(card));\n return true;\n }\n\n // JSON-RPC endpoint\n if (req.method === \"POST\" && (pathname === \"/\" || pathname === \"\")) {\n const body = await readBody(req);\n\n // Check for SendStreamingMessage before dispatching\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32700, message: \"Parse error\" },\n }),\n );\n return true;\n }\n\n // Record A2A method metric\n if (this.registry) {\n const rpcMethod =\n typeof parsed === \"object\" && parsed !== null && \"method\" in parsed\n ? String((parsed as Record<string, unknown>).method)\n : \"unknown\";\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: rpcMethod });\n }\n\n if (isStreamingRequest(parsed)) {\n await this.handleStreamingMessage(parsed as Record<string, unknown>, req, res);\n return true;\n }\n\n // Regular JSON-RPC dispatch\n // Add A2A-Version header before dispatching\n res.setHeader(\"A2A-Version\", \"1.0\");\n\n await this.dispatcher(req, res, body);\n\n // Journal the request after the handler completes\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n\n return true;\n }\n\n return false;\n }\n\n health(): { status: string; agents: number; tasks: number } {\n return {\n status: \"ok\",\n agents: this.agents.size,\n tasks: this.tasks.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"A2AMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n await this.handleRequest(req, res, url.pathname).catch((err) => {\n console.error(\"A2AMock request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n });\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Reset ----\n\n reset(): this {\n this.agents.clear();\n this.tasks.clear();\n return this;\n }\n\n // ---- Internal: set base URL when mounted ----\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n // ---- Private: streaming handler ----\n\n private async handleStreamingMessage(\n parsed: Record<string, unknown>,\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n const params = parsed.params as Record<string, unknown> | undefined;\n const id = parsed.id as string | number;\n const text = extractText(params);\n const entry = findStreamingMatch(text, this.agents);\n\n if (!entry) {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: { code: -32000, message: \"No matching pattern for message\" },\n }),\n );\n return;\n }\n\n // Create task for the streaming response\n const taskId = generateId(\"task\");\n const contextId = generateId(\"ctx\");\n const userParts: A2APart[] = params?.message\n ? (((params.message as Record<string, unknown>).parts as A2APart[]) ?? [{ text }])\n : [{ text }];\n\n const task: A2ATask = {\n id: taskId,\n contextId,\n status: { state: \"TASK_STATE_WORKING\", timestamp: new Date().toISOString() },\n artifacts: [],\n history: [\n {\n messageId: generateId(\"msg\"),\n role: \"ROLE_USER\",\n parts: userParts,\n },\n ],\n };\n this.tasks.set(taskId, task);\n\n // Write SSE response\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"A2A-Version\": \"1.0\",\n });\n\n const delayMs = entry.delayMs ?? 0;\n\n for (const event of entry.events) {\n if (delayMs > 0) {\n await delay(delayMs);\n }\n\n let resultPayload: Record<string, unknown>;\n\n if (event.type === \"status\") {\n task.status = { state: event.state, timestamp: new Date().toISOString() };\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n };\n } else {\n // artifact event\n const artifact = {\n parts: event.parts,\n name: event.name,\n append: event.append,\n lastChunk: event.lastChunk,\n };\n task.artifacts.push({ parts: event.parts, name: event.name });\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n artifact,\n };\n }\n\n const envelope = JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n result: resultPayload,\n });\n\n res.write(`data: ${envelope}\\n\\n`);\n }\n\n // Final completion — only set COMPLETED if the task is not already in a terminal state\n if (!TERMINAL_STATES.has(task.status.state)) {\n task.status = { state: \"TASK_STATE_COMPLETED\", timestamp: new Date().toISOString() };\n }\n\n res.end();\n\n // Journal\n if (this.journal) {\n this.journal.add({\n method: \"POST\",\n path: \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n }\n}\n\n// ---- Helpers ----\n\nfunction isStreamingRequest(parsed: unknown): boolean {\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return false;\n const obj = parsed as Record<string, unknown>;\n return obj.method === \"SendStreamingMessage\";\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"a2a-mock.cjs","names":["createJsonRpcDispatcher","createA2AMethods","flattenHeaders","buildAgentCard","readBody","http","extractText","findStreamingMatch","generateId","TERMINAL_STATES"],"sources":["../src/a2a-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n A2AAgentDefinition,\n A2AArtifact,\n A2AMockOptions,\n A2APart,\n A2AStreamEvent,\n A2ATask,\n} from \"./a2a-types.js\";\nimport type { PatternEntry } from \"./a2a-handler.js\";\nimport {\n buildAgentCard,\n createA2AMethods,\n extractText,\n findStreamingMatch,\n TERMINAL_STATES,\n} from \"./a2a-handler.js\";\nimport { createJsonRpcDispatcher } from \"./jsonrpc.js\";\nimport { generateId, flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class A2AMock implements Mountable {\n private agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }> = new Map();\n private tasks: Map<string, A2ATask> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: A2AMockOptions;\n private baseUrl = \"\";\n private dispatcher: ReturnType<typeof createJsonRpcDispatcher>;\n\n constructor(options?: A2AMockOptions) {\n this.options = options ?? {};\n this.dispatcher = this.buildDispatcher();\n }\n\n private buildDispatcher() {\n const methods = createA2AMethods(this.agents, this.tasks);\n return createJsonRpcDispatcher({ methods });\n }\n\n // ---- Agent registration ----\n\n registerAgent(def: A2AAgentDefinition): this {\n this.agents.set(def.name, { def, patterns: [] });\n return this;\n }\n\n // ---- Pattern registration ----\n\n onMessage(agentName: string, pattern: string | RegExp, parts: A2APart[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"message\", pattern, agentName, parts });\n return this;\n }\n\n onTask(agentName: string, pattern: string | RegExp, artifacts: A2AArtifact[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"task\", pattern, agentName, artifacts });\n return this;\n }\n\n onStreamingTask(\n agentName: string,\n pattern: string | RegExp,\n events: A2AStreamEvent[],\n delayMs?: number,\n ): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"streamingTask\", pattern, agentName, events, delayMs });\n return this;\n }\n\n // ---- Mountable interface ----\n\n private journalRequest(req: http.IncomingMessage, pathname: string, status: number): void {\n if (this.journal) {\n this.journal.add({\n // req.url keeps the mount prefix and the query string, which pathname\n // (the mount-relative sub-path) drops; the journal's ?path= and\n // ?testId= filters both read this field.\n method: req.method ?? \"POST\",\n path: req.url ?? pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status, fixture: null },\n });\n }\n }\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Agent card endpoint\n if (req.method === \"GET\" && pathname === \"/.well-known/agent-card.json\") {\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: \"GetAgentCard\" });\n }\n const card = buildAgentCard(this.agents, this.baseUrl);\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(JSON.stringify(card));\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // JSON-RPC endpoint\n if (req.method === \"POST\" && (pathname === \"/\" || pathname === \"\")) {\n const body = await readBody(req);\n\n // Check for SendStreamingMessage before dispatching\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32700, message: \"Parse error\" },\n }),\n );\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // Record A2A method metric\n if (this.registry) {\n const rpcMethod =\n typeof parsed === \"object\" && parsed !== null && \"method\" in parsed\n ? String((parsed as Record<string, unknown>).method)\n : \"unknown\";\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: rpcMethod });\n }\n\n if (isStreamingRequest(parsed)) {\n await this.handleStreamingMessage(parsed as Record<string, unknown>, req, res);\n return true;\n }\n\n // Regular JSON-RPC dispatch\n // Add A2A-Version header before dispatching\n res.setHeader(\"A2A-Version\", \"1.0\");\n\n await this.dispatcher(req, res, body);\n\n // Journal the request after the handler completes\n this.journalRequest(req, pathname, res.statusCode);\n\n return true;\n }\n\n return false;\n }\n\n health(): { status: string; agents: number; tasks: number } {\n return {\n status: \"ok\",\n agents: this.agents.size,\n tasks: this.tasks.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"A2AMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n await this.handleRequest(req, res, url.pathname).catch((err) => {\n console.error(\"A2AMock request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n this.journalRequest(req, url.pathname, res.statusCode);\n });\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Reset ----\n\n reset(): this {\n this.agents.clear();\n this.tasks.clear();\n return this;\n }\n\n // ---- Internal: set base URL when mounted ----\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n // ---- Private: streaming handler ----\n\n private async handleStreamingMessage(\n parsed: Record<string, unknown>,\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n const params = parsed.params as Record<string, unknown> | undefined;\n const id = parsed.id as string | number;\n const text = extractText(params);\n const entry = findStreamingMatch(text, this.agents);\n\n if (!entry) {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: { code: -32000, message: \"No matching pattern for message\" },\n }),\n );\n this.journalRequest(req, \"/\", res.statusCode);\n return;\n }\n\n // Create task for the streaming response\n const taskId = generateId(\"task\");\n const contextId = generateId(\"ctx\");\n const userParts: A2APart[] = params?.message\n ? (((params.message as Record<string, unknown>).parts as A2APart[]) ?? [{ text }])\n : [{ text }];\n\n const task: A2ATask = {\n id: taskId,\n contextId,\n status: { state: \"TASK_STATE_WORKING\", timestamp: new Date().toISOString() },\n artifacts: [],\n history: [\n {\n messageId: generateId(\"msg\"),\n role: \"ROLE_USER\",\n parts: userParts,\n },\n ],\n };\n this.tasks.set(taskId, task);\n\n // Write SSE response\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"A2A-Version\": \"1.0\",\n });\n\n const delayMs = entry.delayMs ?? 0;\n\n for (const event of entry.events) {\n if (delayMs > 0) {\n await delay(delayMs);\n }\n\n let resultPayload: Record<string, unknown>;\n\n if (event.type === \"status\") {\n task.status = { state: event.state, timestamp: new Date().toISOString() };\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n };\n } else {\n // artifact event\n const artifact = {\n parts: event.parts,\n name: event.name,\n append: event.append,\n lastChunk: event.lastChunk,\n };\n task.artifacts.push({ parts: event.parts, name: event.name });\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n artifact,\n };\n }\n\n const envelope = JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n result: resultPayload,\n });\n\n res.write(`data: ${envelope}\\n\\n`);\n }\n\n // Final completion — only set COMPLETED if the task is not already in a terminal state\n if (!TERMINAL_STATES.has(task.status.state)) {\n task.status = { state: \"TASK_STATE_COMPLETED\", timestamp: new Date().toISOString() };\n }\n\n res.end();\n\n // Journal\n if (this.journal) {\n this.journal.add({\n method: \"POST\",\n path: \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n }\n}\n\n// ---- Helpers ----\n\nfunction isStreamingRequest(parsed: unknown): boolean {\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return false;\n const obj = parsed as Record<string, unknown>;\n return obj.method === \"SendStreamingMessage\";\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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"}
@@ -20,6 +20,7 @@ declare class A2AMock implements Mountable {
20
20
  onMessage(agentName: string, pattern: string | RegExp, parts: A2APart[]): this;
21
21
  onTask(agentName: string, pattern: string | RegExp, artifacts: A2AArtifact[]): this;
22
22
  onStreamingTask(agentName: string, pattern: string | RegExp, events: A2AStreamEvent[], delayMs?: number): this;
23
+ private journalRequest;
23
24
  handleRequest(req: http$1.IncomingMessage, res: http$1.ServerResponse, pathname: string): Promise<boolean>;
24
25
  health(): {
25
26
  status: string;
@@ -1 +1 @@
1
- {"version":3,"file":"a2a-mock.d.cts","names":[],"sources":["../src/a2a-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,OAAA,YAAmB;;EAAnB,QAAA,KAAQ;EAAA,QAAA,MAAA;UAUG,OAAA;UAYH,QAAA;UAO4B,OAAA;UAAe,OAAA;UASlB,UAAA;aAAmB,CAAA,OAAA,CAAA,EA5BzC,cA4ByC;UAW3C,eAAA;eACV,CAAA,GAAA,EA5BS,kBA4BT,CAAA,EAAA,IAAA;WAcH,CAAK,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAnCmC,MAmCnC,EAAA,KAAA,EAnCkD,OAmClD,EAAA,CAAA,EAAA,IAAA;QACL,CAAA,SAAK,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GA3BgC,MA2BhC,EAAA,SAAA,EA3BmD,WA2BnD,EAAA,CAAA,EAAA,IAAA;iBAET,CAAA,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAlBiB,MAkBjB,EAAA,MAAA,EAjBO,cAiBP,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;eAoFiB,CAAA,GAAA,EAvFb,MAAA,CAAK,eAuFQ,EAAA,GAAA,EAtFb,MAAA,CAAK,cAsFQ,EAAA,QAAA,EAAA,MAAA,CAAA,EApFjB,OAoFiB,CAAA,OAAA,CAAA;QAIE,CAAA,CAAA,EAAA;IAMP,MAAA,EAAA,MAAA;IAmCD,MAAA,EAAA,MAAA;IApMgB,KAAA,EAAA,MAAA;EAAS,CAAA;sBAuJnB;wBAIE;WAMP;UAmCD"}
1
+ {"version":3,"file":"a2a-mock.d.cts","names":[],"sources":["../src/a2a-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,OAAA,YAAmB;;EAAnB,QAAA,KAAQ;EAAA,QAAA,MAAA;UAUG,OAAA;UAYH,QAAA;UAO4B,OAAA;UAAe,OAAA;UASlB,UAAA;aAAmB,CAAA,OAAA,CAAA,EA5BzC,cA4ByC;UAW3C,eAAA;eACV,CAAA,GAAA,EA5BS,kBA4BT,CAAA,EAAA,IAAA;WA8BH,CAAK,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAnDmC,MAmDnC,EAAA,KAAA,EAnDkD,OAmDlD,EAAA,CAAA,EAAA,IAAA;QACL,CAAA,SAAK,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GA3CgC,MA2ChC,EAAA,SAAA,EA3CmD,WA2CnD,EAAA,CAAA,EAAA,IAAA;iBAET,CAAA,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAlCiB,MAkCjB,EAAA,MAAA,EAjCO,cAiCP,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;UA6EiB,cAAA;eAIE,CAAA,GAAA,EApFf,MAAA,CAAK,eAoFU,EAAA,GAAA,EAnFf,MAAA,CAAK,cAmFU,EAAA,QAAA,EAAA,MAAA,CAAA,EAjFnB,OAiFmB,CAAA,OAAA,CAAA;QAMP,CAAA,CAAA,EAAA;IAoCD,MAAA,EAAA,MAAA;IA9MgB,MAAA,EAAA,MAAA;IAAS,KAAA,EAAA,MAAA;;sBAgKnB;wBAIE;WAMP;UAoCD"}
@@ -20,6 +20,7 @@ declare class A2AMock implements Mountable {
20
20
  onMessage(agentName: string, pattern: string | RegExp, parts: A2APart[]): this;
21
21
  onTask(agentName: string, pattern: string | RegExp, artifacts: A2AArtifact[]): this;
22
22
  onStreamingTask(agentName: string, pattern: string | RegExp, events: A2AStreamEvent[], delayMs?: number): this;
23
+ private journalRequest;
23
24
  handleRequest(req: http$1.IncomingMessage, res: http$1.ServerResponse, pathname: string): Promise<boolean>;
24
25
  health(): {
25
26
  status: string;
@@ -1 +1 @@
1
- {"version":3,"file":"a2a-mock.d.ts","names":[],"sources":["../src/a2a-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,OAAA,YAAmB;;EAAnB,QAAA,KAAQ;EAAA,QAAA,MAAA;UAUG,OAAA;UAYH,QAAA;UAO4B,OAAA;UAAe,OAAA;UASlB,UAAA;aAAmB,CAAA,OAAA,CAAA,EA5BzC,cA4ByC;UAW3C,eAAA;eACV,CAAA,GAAA,EA5BS,kBA4BT,CAAA,EAAA,IAAA;WAcH,CAAK,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAnCmC,MAmCnC,EAAA,KAAA,EAnCkD,OAmClD,EAAA,CAAA,EAAA,IAAA;QACL,CAAA,SAAK,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GA3BgC,MA2BhC,EAAA,SAAA,EA3BmD,WA2BnD,EAAA,CAAA,EAAA,IAAA;iBAET,CAAA,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAlBiB,MAkBjB,EAAA,MAAA,EAjBO,cAiBP,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;eAoFiB,CAAA,GAAA,EAvFb,MAAA,CAAK,eAuFQ,EAAA,GAAA,EAtFb,MAAA,CAAK,cAsFQ,EAAA,QAAA,EAAA,MAAA,CAAA,EApFjB,OAoFiB,CAAA,OAAA,CAAA;QAIE,CAAA,CAAA,EAAA;IAMP,MAAA,EAAA,MAAA;IAmCD,MAAA,EAAA,MAAA;IApMgB,KAAA,EAAA,MAAA;EAAS,CAAA;sBAuJnB;wBAIE;WAMP;UAmCD"}
1
+ {"version":3,"file":"a2a-mock.d.ts","names":[],"sources":["../src/a2a-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,OAAA,YAAmB;;EAAnB,QAAA,KAAQ;EAAA,QAAA,MAAA;UAUG,OAAA;UAYH,QAAA;UAO4B,OAAA;UAAe,OAAA;UASlB,UAAA;aAAmB,CAAA,OAAA,CAAA,EA5BzC,cA4ByC;UAW3C,eAAA;eACV,CAAA,GAAA,EA5BS,kBA4BT,CAAA,EAAA,IAAA;WA8BH,CAAK,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAnDmC,MAmDnC,EAAA,KAAA,EAnDkD,OAmDlD,EAAA,CAAA,EAAA,IAAA;QACL,CAAA,SAAK,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GA3CgC,MA2ChC,EAAA,SAAA,EA3CmD,WA2CnD,EAAA,CAAA,EAAA,IAAA;iBAET,CAAA,SAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,GAlCiB,MAkCjB,EAAA,MAAA,EAjCO,cAiCP,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;UA6EiB,cAAA;eAIE,CAAA,GAAA,EApFf,MAAA,CAAK,eAoFU,EAAA,GAAA,EAnFf,MAAA,CAAK,cAmFU,EAAA,QAAA,EAAA,MAAA,CAAA,EAjFnB,OAiFmB,CAAA,OAAA,CAAA;QAMP,CAAA,CAAA,EAAA;IAoCD,MAAA,EAAA,MAAA;IA9MgB,MAAA,EAAA,MAAA;IAAS,KAAA,EAAA,MAAA;;sBAgKnB;wBAIE;WAMP;UAoCD"}
package/dist/a2a-mock.js CHANGED
@@ -61,6 +61,19 @@ var A2AMock = class {
61
61
  });
62
62
  return this;
63
63
  }
64
+ journalRequest(req, pathname, status) {
65
+ if (this.journal) this.journal.add({
66
+ method: req.method ?? "POST",
67
+ path: req.url ?? pathname,
68
+ headers: flattenHeaders(req.headers),
69
+ body: null,
70
+ service: "a2a",
71
+ response: {
72
+ status,
73
+ fixture: null
74
+ }
75
+ });
76
+ }
64
77
  async handleRequest(req, res, pathname) {
65
78
  if (req.method === "GET" && pathname === "/.well-known/agent-card.json") {
66
79
  if (this.registry) this.registry.incrementCounter("aimock_a2a_requests_total", { method: "GetAgentCard" });
@@ -70,6 +83,7 @@ var A2AMock = class {
70
83
  "A2A-Version": "1.0"
71
84
  });
72
85
  res.end(JSON.stringify(card));
86
+ this.journalRequest(req, pathname, 200);
73
87
  return true;
74
88
  }
75
89
  if (req.method === "POST" && (pathname === "/" || pathname === "")) {
@@ -90,6 +104,7 @@ var A2AMock = class {
90
104
  message: "Parse error"
91
105
  }
92
106
  }));
107
+ this.journalRequest(req, pathname, 200);
93
108
  return true;
94
109
  }
95
110
  if (this.registry) {
@@ -102,17 +117,7 @@ var A2AMock = class {
102
117
  }
103
118
  res.setHeader("A2A-Version", "1.0");
104
119
  await this.dispatcher(req, res, body);
105
- if (this.journal) this.journal.add({
106
- method: req.method ?? "POST",
107
- path: pathname,
108
- headers: flattenHeaders(req.headers),
109
- body: null,
110
- service: "a2a",
111
- response: {
112
- status: res.statusCode,
113
- fixture: null
114
- }
115
- });
120
+ this.journalRequest(req, pathname, res.statusCode);
116
121
  return true;
117
122
  }
118
123
  return false;
@@ -143,6 +148,7 @@ var A2AMock = class {
143
148
  res.writeHead(500);
144
149
  res.end("Internal server error");
145
150
  } else if (!res.writableEnded) res.end();
151
+ this.journalRequest(req, url.pathname, res.statusCode);
146
152
  });
147
153
  });
148
154
  srv.on("error", reject);
@@ -192,6 +198,7 @@ var A2AMock = class {
192
198
  message: "No matching pattern for message"
193
199
  }
194
200
  }));
201
+ this.journalRequest(req, "/", res.statusCode);
195
202
  return;
196
203
  }
197
204
  const taskId = generateId("task");
@@ -1 +1 @@
1
- {"version":3,"file":"a2a-mock.js","names":["http"],"sources":["../src/a2a-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n A2AAgentDefinition,\n A2AArtifact,\n A2AMockOptions,\n A2APart,\n A2AStreamEvent,\n A2ATask,\n} from \"./a2a-types.js\";\nimport type { PatternEntry } from \"./a2a-handler.js\";\nimport {\n buildAgentCard,\n createA2AMethods,\n extractText,\n findStreamingMatch,\n TERMINAL_STATES,\n} from \"./a2a-handler.js\";\nimport { createJsonRpcDispatcher } from \"./jsonrpc.js\";\nimport { generateId, flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class A2AMock implements Mountable {\n private agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }> = new Map();\n private tasks: Map<string, A2ATask> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: A2AMockOptions;\n private baseUrl = \"\";\n private dispatcher: ReturnType<typeof createJsonRpcDispatcher>;\n\n constructor(options?: A2AMockOptions) {\n this.options = options ?? {};\n this.dispatcher = this.buildDispatcher();\n }\n\n private buildDispatcher() {\n const methods = createA2AMethods(this.agents, this.tasks);\n return createJsonRpcDispatcher({ methods });\n }\n\n // ---- Agent registration ----\n\n registerAgent(def: A2AAgentDefinition): this {\n this.agents.set(def.name, { def, patterns: [] });\n return this;\n }\n\n // ---- Pattern registration ----\n\n onMessage(agentName: string, pattern: string | RegExp, parts: A2APart[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"message\", pattern, agentName, parts });\n return this;\n }\n\n onTask(agentName: string, pattern: string | RegExp, artifacts: A2AArtifact[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"task\", pattern, agentName, artifacts });\n return this;\n }\n\n onStreamingTask(\n agentName: string,\n pattern: string | RegExp,\n events: A2AStreamEvent[],\n delayMs?: number,\n ): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"streamingTask\", pattern, agentName, events, delayMs });\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Agent card endpoint\n if (req.method === \"GET\" && pathname === \"/.well-known/agent-card.json\") {\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: \"GetAgentCard\" });\n }\n const card = buildAgentCard(this.agents, this.baseUrl);\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(JSON.stringify(card));\n return true;\n }\n\n // JSON-RPC endpoint\n if (req.method === \"POST\" && (pathname === \"/\" || pathname === \"\")) {\n const body = await readBody(req);\n\n // Check for SendStreamingMessage before dispatching\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32700, message: \"Parse error\" },\n }),\n );\n return true;\n }\n\n // Record A2A method metric\n if (this.registry) {\n const rpcMethod =\n typeof parsed === \"object\" && parsed !== null && \"method\" in parsed\n ? String((parsed as Record<string, unknown>).method)\n : \"unknown\";\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: rpcMethod });\n }\n\n if (isStreamingRequest(parsed)) {\n await this.handleStreamingMessage(parsed as Record<string, unknown>, req, res);\n return true;\n }\n\n // Regular JSON-RPC dispatch\n // Add A2A-Version header before dispatching\n res.setHeader(\"A2A-Version\", \"1.0\");\n\n await this.dispatcher(req, res, body);\n\n // Journal the request after the handler completes\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n\n return true;\n }\n\n return false;\n }\n\n health(): { status: string; agents: number; tasks: number } {\n return {\n status: \"ok\",\n agents: this.agents.size,\n tasks: this.tasks.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"A2AMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n await this.handleRequest(req, res, url.pathname).catch((err) => {\n console.error(\"A2AMock request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n });\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Reset ----\n\n reset(): this {\n this.agents.clear();\n this.tasks.clear();\n return this;\n }\n\n // ---- Internal: set base URL when mounted ----\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n // ---- Private: streaming handler ----\n\n private async handleStreamingMessage(\n parsed: Record<string, unknown>,\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n const params = parsed.params as Record<string, unknown> | undefined;\n const id = parsed.id as string | number;\n const text = extractText(params);\n const entry = findStreamingMatch(text, this.agents);\n\n if (!entry) {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: { code: -32000, message: \"No matching pattern for message\" },\n }),\n );\n return;\n }\n\n // Create task for the streaming response\n const taskId = generateId(\"task\");\n const contextId = generateId(\"ctx\");\n const userParts: A2APart[] = params?.message\n ? (((params.message as Record<string, unknown>).parts as A2APart[]) ?? [{ text }])\n : [{ text }];\n\n const task: A2ATask = {\n id: taskId,\n contextId,\n status: { state: \"TASK_STATE_WORKING\", timestamp: new Date().toISOString() },\n artifacts: [],\n history: [\n {\n messageId: generateId(\"msg\"),\n role: \"ROLE_USER\",\n parts: userParts,\n },\n ],\n };\n this.tasks.set(taskId, task);\n\n // Write SSE response\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"A2A-Version\": \"1.0\",\n });\n\n const delayMs = entry.delayMs ?? 0;\n\n for (const event of entry.events) {\n if (delayMs > 0) {\n await delay(delayMs);\n }\n\n let resultPayload: Record<string, unknown>;\n\n if (event.type === \"status\") {\n task.status = { state: event.state, timestamp: new Date().toISOString() };\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n };\n } else {\n // artifact event\n const artifact = {\n parts: event.parts,\n name: event.name,\n append: event.append,\n lastChunk: event.lastChunk,\n };\n task.artifacts.push({ parts: event.parts, name: event.name });\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n artifact,\n };\n }\n\n const envelope = JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n result: resultPayload,\n });\n\n res.write(`data: ${envelope}\\n\\n`);\n }\n\n // Final completion — only set COMPLETED if the task is not already in a terminal state\n if (!TERMINAL_STATES.has(task.status.state)) {\n task.status = { state: \"TASK_STATE_COMPLETED\", timestamp: new Date().toISOString() };\n }\n\n res.end();\n\n // Journal\n if (this.journal) {\n this.journal.add({\n method: \"POST\",\n path: \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n }\n}\n\n// ---- Helpers ----\n\nfunction isStreamingRequest(parsed: unknown): boolean {\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return false;\n const obj = parsed as Record<string, unknown>;\n return obj.method === \"SendStreamingMessage\";\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"a2a-mock.js","names":["http"],"sources":["../src/a2a-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n A2AAgentDefinition,\n A2AArtifact,\n A2AMockOptions,\n A2APart,\n A2AStreamEvent,\n A2ATask,\n} from \"./a2a-types.js\";\nimport type { PatternEntry } from \"./a2a-handler.js\";\nimport {\n buildAgentCard,\n createA2AMethods,\n extractText,\n findStreamingMatch,\n TERMINAL_STATES,\n} from \"./a2a-handler.js\";\nimport { createJsonRpcDispatcher } from \"./jsonrpc.js\";\nimport { generateId, flattenHeaders, readBody } from \"./helpers.js\";\n\nexport class A2AMock implements Mountable {\n private agents: Map<string, { def: A2AAgentDefinition; patterns: PatternEntry[] }> = new Map();\n private tasks: Map<string, A2ATask> = new Map();\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: A2AMockOptions;\n private baseUrl = \"\";\n private dispatcher: ReturnType<typeof createJsonRpcDispatcher>;\n\n constructor(options?: A2AMockOptions) {\n this.options = options ?? {};\n this.dispatcher = this.buildDispatcher();\n }\n\n private buildDispatcher() {\n const methods = createA2AMethods(this.agents, this.tasks);\n return createJsonRpcDispatcher({ methods });\n }\n\n // ---- Agent registration ----\n\n registerAgent(def: A2AAgentDefinition): this {\n this.agents.set(def.name, { def, patterns: [] });\n return this;\n }\n\n // ---- Pattern registration ----\n\n onMessage(agentName: string, pattern: string | RegExp, parts: A2APart[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"message\", pattern, agentName, parts });\n return this;\n }\n\n onTask(agentName: string, pattern: string | RegExp, artifacts: A2AArtifact[]): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"task\", pattern, agentName, artifacts });\n return this;\n }\n\n onStreamingTask(\n agentName: string,\n pattern: string | RegExp,\n events: A2AStreamEvent[],\n delayMs?: number,\n ): this {\n const agent = this.agents.get(agentName);\n if (!agent) {\n throw new Error(`Agent \"${agentName}\" not registered`);\n }\n agent.patterns.push({ kind: \"streamingTask\", pattern, agentName, events, delayMs });\n return this;\n }\n\n // ---- Mountable interface ----\n\n private journalRequest(req: http.IncomingMessage, pathname: string, status: number): void {\n if (this.journal) {\n this.journal.add({\n // req.url keeps the mount prefix and the query string, which pathname\n // (the mount-relative sub-path) drops; the journal's ?path= and\n // ?testId= filters both read this field.\n method: req.method ?? \"POST\",\n path: req.url ?? pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status, fixture: null },\n });\n }\n }\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n // Agent card endpoint\n if (req.method === \"GET\" && pathname === \"/.well-known/agent-card.json\") {\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: \"GetAgentCard\" });\n }\n const card = buildAgentCard(this.agents, this.baseUrl);\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(JSON.stringify(card));\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // JSON-RPC endpoint\n if (req.method === \"POST\" && (pathname === \"/\" || pathname === \"\")) {\n const body = await readBody(req);\n\n // Check for SendStreamingMessage before dispatching\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id: null,\n error: { code: -32700, message: \"Parse error\" },\n }),\n );\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // Record A2A method metric\n if (this.registry) {\n const rpcMethod =\n typeof parsed === \"object\" && parsed !== null && \"method\" in parsed\n ? String((parsed as Record<string, unknown>).method)\n : \"unknown\";\n this.registry.incrementCounter(\"aimock_a2a_requests_total\", { method: rpcMethod });\n }\n\n if (isStreamingRequest(parsed)) {\n await this.handleStreamingMessage(parsed as Record<string, unknown>, req, res);\n return true;\n }\n\n // Regular JSON-RPC dispatch\n // Add A2A-Version header before dispatching\n res.setHeader(\"A2A-Version\", \"1.0\");\n\n await this.dispatcher(req, res, body);\n\n // Journal the request after the handler completes\n this.journalRequest(req, pathname, res.statusCode);\n\n return true;\n }\n\n return false;\n }\n\n health(): { status: string; agents: number; tasks: number } {\n return {\n status: \"ok\",\n agents: this.agents.size,\n tasks: this.tasks.size,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"A2AMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n await this.handleRequest(req, res, url.pathname).catch((err) => {\n console.error(\"A2AMock request error:\", err);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n this.journalRequest(req, url.pathname, res.statusCode);\n });\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"A2AMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Reset ----\n\n reset(): this {\n this.agents.clear();\n this.tasks.clear();\n return this;\n }\n\n // ---- Internal: set base URL when mounted ----\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n // ---- Private: streaming handler ----\n\n private async handleStreamingMessage(\n parsed: Record<string, unknown>,\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n const params = parsed.params as Record<string, unknown> | undefined;\n const id = parsed.id as string | number;\n const text = extractText(params);\n const entry = findStreamingMatch(text, this.agents);\n\n if (!entry) {\n res.writeHead(200, {\n \"Content-Type\": \"application/json\",\n \"A2A-Version\": \"1.0\",\n });\n res.end(\n JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: { code: -32000, message: \"No matching pattern for message\" },\n }),\n );\n this.journalRequest(req, \"/\", res.statusCode);\n return;\n }\n\n // Create task for the streaming response\n const taskId = generateId(\"task\");\n const contextId = generateId(\"ctx\");\n const userParts: A2APart[] = params?.message\n ? (((params.message as Record<string, unknown>).parts as A2APart[]) ?? [{ text }])\n : [{ text }];\n\n const task: A2ATask = {\n id: taskId,\n contextId,\n status: { state: \"TASK_STATE_WORKING\", timestamp: new Date().toISOString() },\n artifacts: [],\n history: [\n {\n messageId: generateId(\"msg\"),\n role: \"ROLE_USER\",\n parts: userParts,\n },\n ],\n };\n this.tasks.set(taskId, task);\n\n // Write SSE response\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"A2A-Version\": \"1.0\",\n });\n\n const delayMs = entry.delayMs ?? 0;\n\n for (const event of entry.events) {\n if (delayMs > 0) {\n await delay(delayMs);\n }\n\n let resultPayload: Record<string, unknown>;\n\n if (event.type === \"status\") {\n task.status = { state: event.state, timestamp: new Date().toISOString() };\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n };\n } else {\n // artifact event\n const artifact = {\n parts: event.parts,\n name: event.name,\n append: event.append,\n lastChunk: event.lastChunk,\n };\n task.artifacts.push({ parts: event.parts, name: event.name });\n resultPayload = {\n task: {\n id: task.id,\n contextId: task.contextId,\n status: task.status,\n },\n artifact,\n };\n }\n\n const envelope = JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n result: resultPayload,\n });\n\n res.write(`data: ${envelope}\\n\\n`);\n }\n\n // Final completion — only set COMPLETED if the task is not already in a terminal state\n if (!TERMINAL_STATES.has(task.status.state)) {\n task.status = { state: \"TASK_STATE_COMPLETED\", timestamp: new Date().toISOString() };\n }\n\n res.end();\n\n // Journal\n if (this.journal) {\n this.journal.add({\n method: \"POST\",\n path: \"/\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"a2a\",\n response: { status: res.statusCode, fixture: null },\n });\n }\n }\n}\n\n// ---- Helpers ----\n\nfunction isStreamingRequest(parsed: unknown): boolean {\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) return false;\n const obj = parsed as Record<string, unknown>;\n return obj.method === \"SendStreamingMessage\";\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"agui-handler.d.ts","names":[],"sources":["../src/agui-handler.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"agui-handler.d.ts","names":[],"sources":["../src/agui-handler.ts"],"sourcesContent":[],"mappings":"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"}
@@ -168,10 +168,12 @@ var AGUIMock = class {
168
168
  res.writeHead(500);
169
169
  res.end("Internal server error");
170
170
  } else if (!res.writableEnded) res.end();
171
+ this.journalRequest(req, url.pathname, res.statusCode);
171
172
  return true;
172
173
  }) && !res.headersSent) {
173
174
  res.writeHead(404, { "Content-Type": "application/json" });
174
175
  res.end(JSON.stringify({ error: "Not found" }));
176
+ this.journalRequest(req, url.pathname, 404);
175
177
  }
176
178
  });
177
179
  srv.on("error", reject);
@@ -1 +1 @@
1
- {"version":3,"file":"agui-mock.cjs","names":["Logger","buildTextResponse","buildToolCallResponse","buildStateUpdate","buildReasoningResponse","readBody","findFixture","writeAGUIEventStream","proxyAndRecordAGUI","http","flattenHeaders"],"sources":["../src/agui-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n AGUIFixture,\n AGUIMockOptions,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport {\n findFixture,\n buildTextResponse,\n buildToolCallResponse,\n buildStateUpdate,\n buildReasoningResponse,\n writeAGUIEventStream,\n} from \"./agui-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\nimport { proxyAndRecordAGUI } from \"./agui-recorder.js\";\nimport { Logger, type LogLevel } from \"./logger.js\";\n\nexport class AGUIMock implements Mountable {\n private fixtures: AGUIFixture[] = [];\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: AGUIMockOptions;\n private baseUrl = \"\";\n private recordConfig: AGUIRecordConfig | undefined;\n private logger: Logger;\n\n constructor(options?: AGUIMockOptions) {\n this.options = options ?? {};\n this.logger = new Logger((options?.logLevel as LogLevel) ?? \"warn\");\n }\n\n // ---- Fluent registration API ----\n\n addFixture(fixture: AGUIFixture): this {\n this.fixtures.push(fixture);\n return this;\n }\n\n onMessage(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildTextResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onRun(pattern: string | RegExp, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolCall(\n pattern: string | RegExp,\n toolName: string,\n args: string,\n opts?: { result?: string; delayMs?: number },\n ): this {\n const events = buildToolCallResponse(toolName, args, {\n result: opts?.result,\n });\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onStateKey(key: string, snapshot: Record<string, unknown>, delayMs?: number): this {\n const events = buildStateUpdate(snapshot);\n this.fixtures.push({\n match: { stateKey: key },\n events,\n delayMs,\n });\n return this;\n }\n\n onReasoning(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildReasoningResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onPredicate(\n predicate: (input: AGUIRunAgentInput) => boolean,\n events: AGUIEvent[],\n delayMs?: number,\n ): this {\n this.fixtures.push({\n match: { predicate },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolResult(toolCallId: string, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { toolCallId },\n events,\n delayMs,\n });\n return this;\n }\n\n enableRecording(config: AGUIRecordConfig): this {\n this.recordConfig = config;\n return this;\n }\n\n reset(): this {\n this.fixtures = [];\n this.recordConfig = undefined;\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n if (req.method !== \"POST\" || (pathname !== \"/\" && pathname !== \"\")) {\n return false;\n }\n\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_agui_requests_total\", { method: \"POST\" });\n }\n\n let body: string;\n try {\n body = await readBody(req);\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"body read failed\";\n res.end(JSON.stringify({ error: `Failed to read request body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n let input: AGUIRunAgentInput;\n try {\n input = JSON.parse(body) as AGUIRunAgentInput;\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"unknown parse error\";\n res.end(JSON.stringify({ error: `Invalid JSON body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n if (input.messages !== undefined && !Array.isArray(input.messages)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"Invalid input: 'messages' must be an array when provided\",\n }),\n );\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n const fixture = findFixture(input, this.fixtures);\n\n if (fixture) {\n await writeAGUIEventStream(res, fixture.events, { delayMs: fixture.delayMs });\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // No match — if recording is enabled, proxy to upstream\n if (this.recordConfig) {\n const result = await proxyAndRecordAGUI(\n req,\n res,\n input,\n this.fixtures,\n this.recordConfig,\n this.logger,\n );\n if (result !== false) {\n this.journalRequest(req, pathname, result);\n return true;\n }\n }\n\n // No match, no recorder — 404\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No matching AG-UI fixture\" }));\n this.journalRequest(req, pathname, 404);\n return true;\n }\n\n health(): { status: string; fixtures: number } {\n return {\n status: \"ok\",\n fixtures: this.fixtures.length,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"AGUIMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const handled = await this.handleRequest(req, res, url.pathname).catch((err) => {\n this.logger.error(`AGUIMock request error: ${err instanceof Error ? err.message : err}`);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n return true;\n });\n if (!handled && !res.headersSent) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Private helpers ----\n\n private journalRequest(req: http.IncomingMessage, pathname: string, status: number): void {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"agui\",\n response: { status, fixture: null },\n });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agui-mock.cjs","names":["Logger","buildTextResponse","buildToolCallResponse","buildStateUpdate","buildReasoningResponse","readBody","findFixture","writeAGUIEventStream","proxyAndRecordAGUI","http","flattenHeaders"],"sources":["../src/agui-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n AGUIFixture,\n AGUIMockOptions,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport {\n findFixture,\n buildTextResponse,\n buildToolCallResponse,\n buildStateUpdate,\n buildReasoningResponse,\n writeAGUIEventStream,\n} from \"./agui-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\nimport { proxyAndRecordAGUI } from \"./agui-recorder.js\";\nimport { Logger, type LogLevel } from \"./logger.js\";\n\nexport class AGUIMock implements Mountable {\n private fixtures: AGUIFixture[] = [];\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: AGUIMockOptions;\n private baseUrl = \"\";\n private recordConfig: AGUIRecordConfig | undefined;\n private logger: Logger;\n\n constructor(options?: AGUIMockOptions) {\n this.options = options ?? {};\n this.logger = new Logger((options?.logLevel as LogLevel) ?? \"warn\");\n }\n\n // ---- Fluent registration API ----\n\n addFixture(fixture: AGUIFixture): this {\n this.fixtures.push(fixture);\n return this;\n }\n\n onMessage(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildTextResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onRun(pattern: string | RegExp, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolCall(\n pattern: string | RegExp,\n toolName: string,\n args: string,\n opts?: { result?: string; delayMs?: number },\n ): this {\n const events = buildToolCallResponse(toolName, args, {\n result: opts?.result,\n });\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onStateKey(key: string, snapshot: Record<string, unknown>, delayMs?: number): this {\n const events = buildStateUpdate(snapshot);\n this.fixtures.push({\n match: { stateKey: key },\n events,\n delayMs,\n });\n return this;\n }\n\n onReasoning(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildReasoningResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onPredicate(\n predicate: (input: AGUIRunAgentInput) => boolean,\n events: AGUIEvent[],\n delayMs?: number,\n ): this {\n this.fixtures.push({\n match: { predicate },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolResult(toolCallId: string, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { toolCallId },\n events,\n delayMs,\n });\n return this;\n }\n\n enableRecording(config: AGUIRecordConfig): this {\n this.recordConfig = config;\n return this;\n }\n\n reset(): this {\n this.fixtures = [];\n this.recordConfig = undefined;\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n if (req.method !== \"POST\" || (pathname !== \"/\" && pathname !== \"\")) {\n return false;\n }\n\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_agui_requests_total\", { method: \"POST\" });\n }\n\n let body: string;\n try {\n body = await readBody(req);\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"body read failed\";\n res.end(JSON.stringify({ error: `Failed to read request body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n let input: AGUIRunAgentInput;\n try {\n input = JSON.parse(body) as AGUIRunAgentInput;\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"unknown parse error\";\n res.end(JSON.stringify({ error: `Invalid JSON body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n if (input.messages !== undefined && !Array.isArray(input.messages)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"Invalid input: 'messages' must be an array when provided\",\n }),\n );\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n const fixture = findFixture(input, this.fixtures);\n\n if (fixture) {\n await writeAGUIEventStream(res, fixture.events, { delayMs: fixture.delayMs });\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // No match — if recording is enabled, proxy to upstream\n if (this.recordConfig) {\n const result = await proxyAndRecordAGUI(\n req,\n res,\n input,\n this.fixtures,\n this.recordConfig,\n this.logger,\n );\n if (result !== false) {\n this.journalRequest(req, pathname, result);\n return true;\n }\n }\n\n // No match, no recorder — 404\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No matching AG-UI fixture\" }));\n this.journalRequest(req, pathname, 404);\n return true;\n }\n\n health(): { status: string; fixtures: number } {\n return {\n status: \"ok\",\n fixtures: this.fixtures.length,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"AGUIMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const handled = await this.handleRequest(req, res, url.pathname).catch((err) => {\n this.logger.error(`AGUIMock request error: ${err instanceof Error ? err.message : err}`);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n this.journalRequest(req, url.pathname, res.statusCode);\n return true;\n });\n if (!handled && !res.headersSent) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n this.journalRequest(req, url.pathname, 404);\n }\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Private helpers ----\n\n private journalRequest(req: http.IncomingMessage, pathname: string, status: number): void {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"agui\",\n response: { status, fixture: null },\n });\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"agui-mock.d.cts","names":[],"sources":["../src/agui-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,QAAA,YAAoB;;EAApB,QAAA,MAAS;EAAA,QAAA,OAAA;UAUE,QAAA;UAOF,OAAA;UAKQ,OAAA;UAUJ,YAAA;UAAgB,MAAA;aAUpB,CAAA,OAAA,CAAA,EAhCE,eAgCF;YAgBc,CAAA,OAAA,EAzCd,WAyCc,CAAA,EAAA,IAAA;WAUJ,CAAA,OAAA,EAAA,MAAA,GA9CF,MA8CE,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAWT,OAAA,CAAA,EAAA,MAAA;MACX,IAAA;OAW+B,CAAA,OAAA,EAAA,MAAA,GA3DjB,MA2DiB,EAAA,MAAA,EA3DD,SA2DC,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;YASjB,CAAA,OAAA,EAAA,MAAA,GA1DJ,MA0DI,EAAA,QAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAcjB,MAAK,CAAA,EAAA,MAAA;IACL,OAAK,CAAA,EAAA,MAAA;MAET,IAAA;YAgFiB,CAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EA3Ic,MA2Id,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;aAQE,CAAA,OAAA,EAAA,MAAA,GAzIQ,MAyIR,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAMP,OAAA,CAAA,EAAA,MAAA;MAwCD,IAAA;aA3PiB,CAAA,SAAA,EAAA,CAAA,KAAA,EA+EV,iBA/EU,EAAA,GAAA,OAAA,EAAA,MAAA,EAgFrB,SAhFqB,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAS,YAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EA2FC,SA3FD,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;0BAoGhB;;qBAcjB,MAAA,CAAK,sBACL,MAAA,CAAK,mCAET;;;;;sBAgFiB;;wBAQE;WAMP;UAwCD"}
1
+ {"version":3,"file":"agui-mock.d.cts","names":[],"sources":["../src/agui-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,QAAA,YAAoB;;EAApB,QAAA,MAAS;EAAA,QAAA,OAAA;UAUE,QAAA;UAOF,OAAA;UAKQ,OAAA;UAUJ,YAAA;UAAgB,MAAA;aAUpB,CAAA,OAAA,CAAA,EAhCE,eAgCF;YAgBc,CAAA,OAAA,EAzCd,WAyCc,CAAA,EAAA,IAAA;WAUJ,CAAA,OAAA,EAAA,MAAA,GA9CF,MA8CE,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAWT,OAAA,CAAA,EAAA,MAAA;MACX,IAAA;OAW+B,CAAA,OAAA,EAAA,MAAA,GA3DjB,MA2DiB,EAAA,MAAA,EA3DD,SA2DC,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;YASjB,CAAA,OAAA,EAAA,MAAA,GA1DJ,MA0DI,EAAA,QAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAcjB,MAAK,CAAA,EAAA,MAAA;IACL,OAAK,CAAA,EAAA,MAAA;MAET,IAAA;YAgFiB,CAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EA3Ic,MA2Id,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;aAQE,CAAA,OAAA,EAAA,MAAA,GAzIQ,MAyIR,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAMP,OAAA,CAAA,EAAA,MAAA;MA0CD,IAAA;aA7PiB,CAAA,SAAA,EAAA,CAAA,KAAA,EA+EV,iBA/EU,EAAA,GAAA,OAAA,EAAA,MAAA,EAgFrB,SAhFqB,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAS,YAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EA2FC,SA3FD,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;0BAoGhB;;qBAcjB,MAAA,CAAK,sBACL,MAAA,CAAK,mCAET;;;;;sBAgFiB;;wBAQE;WAMP;UA0CD"}
@@ -1 +1 @@
1
- {"version":3,"file":"agui-mock.d.ts","names":[],"sources":["../src/agui-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,QAAA,YAAoB;;EAApB,QAAA,MAAS;EAAA,QAAA,OAAA;UAUE,QAAA;UAOF,OAAA;UAKQ,OAAA;UAUJ,YAAA;UAAgB,MAAA;aAUpB,CAAA,OAAA,CAAA,EAhCE,eAgCF;YAgBc,CAAA,OAAA,EAzCd,WAyCc,CAAA,EAAA,IAAA;WAUJ,CAAA,OAAA,EAAA,MAAA,GA9CF,MA8CE,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAWT,OAAA,CAAA,EAAA,MAAA;MACX,IAAA;OAW+B,CAAA,OAAA,EAAA,MAAA,GA3DjB,MA2DiB,EAAA,MAAA,EA3DD,SA2DC,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;YASjB,CAAA,OAAA,EAAA,MAAA,GA1DJ,MA0DI,EAAA,QAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAcjB,MAAK,CAAA,EAAA,MAAA;IACL,OAAK,CAAA,EAAA,MAAA;MAET,IAAA;YAgFiB,CAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EA3Ic,MA2Id,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;aAQE,CAAA,OAAA,EAAA,MAAA,GAzIQ,MAyIR,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAMP,OAAA,CAAA,EAAA,MAAA;MAwCD,IAAA;aA3PiB,CAAA,SAAA,EAAA,CAAA,KAAA,EA+EV,iBA/EU,EAAA,GAAA,OAAA,EAAA,MAAA,EAgFrB,SAhFqB,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAS,YAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EA2FC,SA3FD,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;0BAoGhB;;qBAcjB,MAAA,CAAK,sBACL,MAAA,CAAK,mCAET;;;;;sBAgFiB;;wBAQE;WAMP;UAwCD"}
1
+ {"version":3,"file":"agui-mock.d.ts","names":[],"sources":["../src/agui-mock.ts"],"sourcesContent":[],"mappings":";;;;;;;cAuBa,QAAA,YAAoB;;EAApB,QAAA,MAAS;EAAA,QAAA,OAAA;UAUE,QAAA;UAOF,OAAA;UAKQ,OAAA;UAUJ,YAAA;UAAgB,MAAA;aAUpB,CAAA,OAAA,CAAA,EAhCE,eAgCF;YAgBc,CAAA,OAAA,EAzCd,WAyCc,CAAA,EAAA,IAAA;WAUJ,CAAA,OAAA,EAAA,MAAA,GA9CF,MA8CE,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAWT,OAAA,CAAA,EAAA,MAAA;MACX,IAAA;OAW+B,CAAA,OAAA,EAAA,MAAA,GA3DjB,MA2DiB,EAAA,MAAA,EA3DD,SA2DC,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;YASjB,CAAA,OAAA,EAAA,MAAA,GA1DJ,MA0DI,EAAA,QAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAcjB,MAAK,CAAA,EAAA,MAAA;IACL,OAAK,CAAA,EAAA,MAAA;MAET,IAAA;YAgFiB,CAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EA3Ic,MA2Id,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;aAQE,CAAA,OAAA,EAAA,MAAA,GAzIQ,MAyIR,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAMP,OAAA,CAAA,EAAA,MAAA;MA0CD,IAAA;aA7PiB,CAAA,SAAA,EAAA,CAAA,KAAA,EA+EV,iBA/EU,EAAA,GAAA,OAAA,EAAA,MAAA,EAgFrB,SAhFqB,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAS,YAAA,CAAA,UAAA,EAAA,MAAA,EAAA,MAAA,EA2FC,SA3FD,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;0BAoGhB;;qBAcjB,MAAA,CAAK,sBACL,MAAA,CAAK,mCAET;;;;;sBAgFiB;;wBAQE;WAMP;UA0CD"}
package/dist/agui-mock.js CHANGED
@@ -166,10 +166,12 @@ var AGUIMock = class {
166
166
  res.writeHead(500);
167
167
  res.end("Internal server error");
168
168
  } else if (!res.writableEnded) res.end();
169
+ this.journalRequest(req, url.pathname, res.statusCode);
169
170
  return true;
170
171
  }) && !res.headersSent) {
171
172
  res.writeHead(404, { "Content-Type": "application/json" });
172
173
  res.end(JSON.stringify({ error: "Not found" }));
174
+ this.journalRequest(req, url.pathname, 404);
173
175
  }
174
176
  });
175
177
  srv.on("error", reject);
@@ -1 +1 @@
1
- {"version":3,"file":"agui-mock.js","names":["http"],"sources":["../src/agui-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n AGUIFixture,\n AGUIMockOptions,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport {\n findFixture,\n buildTextResponse,\n buildToolCallResponse,\n buildStateUpdate,\n buildReasoningResponse,\n writeAGUIEventStream,\n} from \"./agui-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\nimport { proxyAndRecordAGUI } from \"./agui-recorder.js\";\nimport { Logger, type LogLevel } from \"./logger.js\";\n\nexport class AGUIMock implements Mountable {\n private fixtures: AGUIFixture[] = [];\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: AGUIMockOptions;\n private baseUrl = \"\";\n private recordConfig: AGUIRecordConfig | undefined;\n private logger: Logger;\n\n constructor(options?: AGUIMockOptions) {\n this.options = options ?? {};\n this.logger = new Logger((options?.logLevel as LogLevel) ?? \"warn\");\n }\n\n // ---- Fluent registration API ----\n\n addFixture(fixture: AGUIFixture): this {\n this.fixtures.push(fixture);\n return this;\n }\n\n onMessage(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildTextResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onRun(pattern: string | RegExp, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolCall(\n pattern: string | RegExp,\n toolName: string,\n args: string,\n opts?: { result?: string; delayMs?: number },\n ): this {\n const events = buildToolCallResponse(toolName, args, {\n result: opts?.result,\n });\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onStateKey(key: string, snapshot: Record<string, unknown>, delayMs?: number): this {\n const events = buildStateUpdate(snapshot);\n this.fixtures.push({\n match: { stateKey: key },\n events,\n delayMs,\n });\n return this;\n }\n\n onReasoning(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildReasoningResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onPredicate(\n predicate: (input: AGUIRunAgentInput) => boolean,\n events: AGUIEvent[],\n delayMs?: number,\n ): this {\n this.fixtures.push({\n match: { predicate },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolResult(toolCallId: string, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { toolCallId },\n events,\n delayMs,\n });\n return this;\n }\n\n enableRecording(config: AGUIRecordConfig): this {\n this.recordConfig = config;\n return this;\n }\n\n reset(): this {\n this.fixtures = [];\n this.recordConfig = undefined;\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n if (req.method !== \"POST\" || (pathname !== \"/\" && pathname !== \"\")) {\n return false;\n }\n\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_agui_requests_total\", { method: \"POST\" });\n }\n\n let body: string;\n try {\n body = await readBody(req);\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"body read failed\";\n res.end(JSON.stringify({ error: `Failed to read request body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n let input: AGUIRunAgentInput;\n try {\n input = JSON.parse(body) as AGUIRunAgentInput;\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"unknown parse error\";\n res.end(JSON.stringify({ error: `Invalid JSON body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n if (input.messages !== undefined && !Array.isArray(input.messages)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"Invalid input: 'messages' must be an array when provided\",\n }),\n );\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n const fixture = findFixture(input, this.fixtures);\n\n if (fixture) {\n await writeAGUIEventStream(res, fixture.events, { delayMs: fixture.delayMs });\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // No match — if recording is enabled, proxy to upstream\n if (this.recordConfig) {\n const result = await proxyAndRecordAGUI(\n req,\n res,\n input,\n this.fixtures,\n this.recordConfig,\n this.logger,\n );\n if (result !== false) {\n this.journalRequest(req, pathname, result);\n return true;\n }\n }\n\n // No match, no recorder — 404\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No matching AG-UI fixture\" }));\n this.journalRequest(req, pathname, 404);\n return true;\n }\n\n health(): { status: string; fixtures: number } {\n return {\n status: \"ok\",\n fixtures: this.fixtures.length,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"AGUIMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const handled = await this.handleRequest(req, res, url.pathname).catch((err) => {\n this.logger.error(`AGUIMock request error: ${err instanceof Error ? err.message : err}`);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n return true;\n });\n if (!handled && !res.headersSent) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n }\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Private helpers ----\n\n private journalRequest(req: http.IncomingMessage, pathname: string, status: number): void {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"agui\",\n response: { status, fixture: null },\n });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agui-mock.js","names":["http"],"sources":["../src/agui-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n AGUIFixture,\n AGUIMockOptions,\n AGUIRecordConfig,\n AGUIEvent,\n AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport {\n findFixture,\n buildTextResponse,\n buildToolCallResponse,\n buildStateUpdate,\n buildReasoningResponse,\n writeAGUIEventStream,\n} from \"./agui-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\nimport { proxyAndRecordAGUI } from \"./agui-recorder.js\";\nimport { Logger, type LogLevel } from \"./logger.js\";\n\nexport class AGUIMock implements Mountable {\n private fixtures: AGUIFixture[] = [];\n private server: http.Server | null = null;\n private journal: Journal | null = null;\n private registry: MetricsRegistry | null = null;\n private options: AGUIMockOptions;\n private baseUrl = \"\";\n private recordConfig: AGUIRecordConfig | undefined;\n private logger: Logger;\n\n constructor(options?: AGUIMockOptions) {\n this.options = options ?? {};\n this.logger = new Logger((options?.logLevel as LogLevel) ?? \"warn\");\n }\n\n // ---- Fluent registration API ----\n\n addFixture(fixture: AGUIFixture): this {\n this.fixtures.push(fixture);\n return this;\n }\n\n onMessage(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildTextResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onRun(pattern: string | RegExp, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolCall(\n pattern: string | RegExp,\n toolName: string,\n args: string,\n opts?: { result?: string; delayMs?: number },\n ): this {\n const events = buildToolCallResponse(toolName, args, {\n result: opts?.result,\n });\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onStateKey(key: string, snapshot: Record<string, unknown>, delayMs?: number): this {\n const events = buildStateUpdate(snapshot);\n this.fixtures.push({\n match: { stateKey: key },\n events,\n delayMs,\n });\n return this;\n }\n\n onReasoning(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n const events = buildReasoningResponse(text);\n this.fixtures.push({\n match: { message: pattern },\n events,\n delayMs: opts?.delayMs,\n });\n return this;\n }\n\n onPredicate(\n predicate: (input: AGUIRunAgentInput) => boolean,\n events: AGUIEvent[],\n delayMs?: number,\n ): this {\n this.fixtures.push({\n match: { predicate },\n events,\n delayMs,\n });\n return this;\n }\n\n onToolResult(toolCallId: string, events: AGUIEvent[], delayMs?: number): this {\n this.fixtures.push({\n match: { toolCallId },\n events,\n delayMs,\n });\n return this;\n }\n\n enableRecording(config: AGUIRecordConfig): this {\n this.recordConfig = config;\n return this;\n }\n\n reset(): this {\n this.fixtures = [];\n this.recordConfig = undefined;\n return this;\n }\n\n // ---- Mountable interface ----\n\n async handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n pathname: string,\n ): Promise<boolean> {\n if (req.method !== \"POST\" || (pathname !== \"/\" && pathname !== \"\")) {\n return false;\n }\n\n if (this.registry) {\n this.registry.incrementCounter(\"aimock_agui_requests_total\", { method: \"POST\" });\n }\n\n let body: string;\n try {\n body = await readBody(req);\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"body read failed\";\n res.end(JSON.stringify({ error: `Failed to read request body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n let input: AGUIRunAgentInput;\n try {\n input = JSON.parse(body) as AGUIRunAgentInput;\n } catch (err) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n const detail = err instanceof Error ? err.message : \"unknown parse error\";\n res.end(JSON.stringify({ error: `Invalid JSON body: ${detail}` }));\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n if (input.messages !== undefined && !Array.isArray(input.messages)) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"Invalid input: 'messages' must be an array when provided\",\n }),\n );\n this.journalRequest(req, pathname, 400);\n return true;\n }\n\n const fixture = findFixture(input, this.fixtures);\n\n if (fixture) {\n await writeAGUIEventStream(res, fixture.events, { delayMs: fixture.delayMs });\n this.journalRequest(req, pathname, 200);\n return true;\n }\n\n // No match — if recording is enabled, proxy to upstream\n if (this.recordConfig) {\n const result = await proxyAndRecordAGUI(\n req,\n res,\n input,\n this.fixtures,\n this.recordConfig,\n this.logger,\n );\n if (result !== false) {\n this.journalRequest(req, pathname, result);\n return true;\n }\n }\n\n // No match, no recorder — 404\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"No matching AG-UI fixture\" }));\n this.journalRequest(req, pathname, 404);\n return true;\n }\n\n health(): { status: string; fixtures: number } {\n return {\n status: \"ok\",\n fixtures: this.fixtures.length,\n };\n }\n\n setJournal(journal: Journal): void {\n this.journal = journal;\n }\n\n setBaseUrl(url: string): void {\n this.baseUrl = url;\n }\n\n setRegistry(registry: MetricsRegistry): void {\n this.registry = registry;\n }\n\n // ---- Standalone mode ----\n\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(\"AGUIMock server already started\");\n }\n\n const host = this.options.host ?? \"127.0.0.1\";\n const port = this.options.port ?? 0;\n\n return new Promise<string>((resolve, reject) => {\n const srv = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n const handled = await this.handleRequest(req, res, url.pathname).catch((err) => {\n this.logger.error(`AGUIMock request error: ${err instanceof Error ? err.message : err}`);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal server error\");\n } else if (!res.writableEnded) {\n res.end();\n }\n this.journalRequest(req, url.pathname, res.statusCode);\n return true;\n });\n if (!handled && !res.headersSent) {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not found\" }));\n this.journalRequest(req, url.pathname, 404);\n }\n });\n\n srv.on(\"error\", reject);\n\n srv.listen(port, host, () => {\n const addr = srv.address();\n if (typeof addr === \"object\" && addr !== null) {\n this.baseUrl = `http://${host}:${addr.port}`;\n }\n this.server = srv;\n resolve(this.baseUrl);\n });\n });\n }\n\n async stop(): Promise<void> {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n const srv = this.server;\n await new Promise<void>((resolve, reject) => {\n srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n });\n this.server = null;\n }\n\n get url(): string {\n if (!this.server) {\n throw new Error(\"AGUIMock server not started\");\n }\n return this.baseUrl;\n }\n\n // ---- Private helpers ----\n\n private journalRequest(req: http.IncomingMessage, pathname: string, status: number): void {\n if (this.journal) {\n this.journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? pathname,\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"agui\",\n response: { status, fixture: null },\n });\n }\n }\n}\n"],"mappings":"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"}