@copilotkit/aimock 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/agui-handler.cjs +340 -0
  4. package/dist/agui-handler.cjs.map +1 -0
  5. package/dist/agui-handler.d.cts +96 -0
  6. package/dist/agui-handler.d.cts.map +1 -0
  7. package/dist/agui-handler.d.ts +96 -0
  8. package/dist/agui-handler.d.ts.map +1 -0
  9. package/dist/agui-handler.js +326 -0
  10. package/dist/agui-handler.js.map +1 -0
  11. package/dist/agui-mock.cjs +190 -0
  12. package/dist/agui-mock.cjs.map +1 -0
  13. package/dist/agui-mock.d.cts +50 -0
  14. package/dist/agui-mock.d.cts.map +1 -0
  15. package/dist/agui-mock.d.ts +50 -0
  16. package/dist/agui-mock.d.ts.map +1 -0
  17. package/dist/agui-mock.js +188 -0
  18. package/dist/agui-mock.js.map +1 -0
  19. package/dist/agui-recorder.cjs +153 -0
  20. package/dist/agui-recorder.cjs.map +1 -0
  21. package/dist/agui-recorder.d.cts +19 -0
  22. package/dist/agui-recorder.d.cts.map +1 -0
  23. package/dist/agui-recorder.d.ts +19 -0
  24. package/dist/agui-recorder.d.ts.map +1 -0
  25. package/dist/agui-recorder.js +147 -0
  26. package/dist/agui-recorder.js.map +1 -0
  27. package/dist/agui-types.d.cts +231 -0
  28. package/dist/agui-types.d.cts.map +1 -0
  29. package/dist/agui-types.d.ts +231 -0
  30. package/dist/agui-types.d.ts.map +1 -0
  31. package/dist/cli.cjs +32 -1
  32. package/dist/cli.cjs.map +1 -1
  33. package/dist/cli.js +32 -1
  34. package/dist/cli.js.map +1 -1
  35. package/dist/config-loader.cjs +19 -0
  36. package/dist/config-loader.cjs.map +1 -1
  37. package/dist/config-loader.d.cts +16 -0
  38. package/dist/config-loader.d.cts.map +1 -1
  39. package/dist/config-loader.d.ts +16 -0
  40. package/dist/config-loader.d.ts.map +1 -1
  41. package/dist/config-loader.js +19 -0
  42. package/dist/config-loader.js.map +1 -1
  43. package/dist/index.cjs +19 -0
  44. package/dist/index.d.cts +5 -1
  45. package/dist/index.d.ts +5 -1
  46. package/dist/index.js +4 -1
  47. package/dist/suite.cjs +8 -0
  48. package/dist/suite.cjs.map +1 -1
  49. package/dist/suite.d.cts +4 -0
  50. package/dist/suite.d.cts.map +1 -1
  51. package/dist/suite.d.ts +4 -0
  52. package/dist/suite.d.ts.map +1 -1
  53. package/dist/suite.js +8 -0
  54. package/dist/suite.js.map +1 -1
  55. package/dist/vector-types.d.ts.map +1 -1
  56. package/package.json +1 -1
@@ -0,0 +1,326 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ //#region src/agui-handler.ts
4
+ /**
5
+ * Extract the content of the last message with role "user" from the input.
6
+ */
7
+ function extractLastUserMessage(input) {
8
+ if (!input.messages || input.messages.length === 0) return "";
9
+ for (let i = input.messages.length - 1; i >= 0; i--) {
10
+ const msg = input.messages[i];
11
+ if (msg.role === "user" && typeof msg.content === "string") return msg.content;
12
+ }
13
+ return "";
14
+ }
15
+ /**
16
+ * Check whether an input matches a fixture's match criteria.
17
+ * All specified criteria must pass (AND logic).
18
+ */
19
+ function matchesFixture(input, match) {
20
+ if (match.message !== void 0) {
21
+ const text = extractLastUserMessage(input);
22
+ if (typeof match.message === "string") {
23
+ if (!text.includes(match.message)) return false;
24
+ } else if (!match.message.test(text)) return false;
25
+ }
26
+ if (match.toolName !== void 0) {
27
+ if (!(input.tools ?? []).some((t) => t.name === match.toolName)) return false;
28
+ }
29
+ if (match.stateKey !== void 0) {
30
+ if (input.state === null || input.state === void 0 || typeof input.state !== "object" || !(match.stateKey in input.state)) return false;
31
+ }
32
+ if (match.predicate !== void 0) {
33
+ if (!match.predicate(input)) return false;
34
+ }
35
+ return true;
36
+ }
37
+ /**
38
+ * Find the first fixture whose match criteria pass for the given input.
39
+ */
40
+ function findFixture(input, fixtures) {
41
+ for (const fixture of fixtures) if (matchesFixture(input, fixture.match)) return fixture;
42
+ return null;
43
+ }
44
+ function makeRunStarted(opts) {
45
+ return {
46
+ type: "RUN_STARTED",
47
+ threadId: opts?.threadId ?? randomUUID(),
48
+ runId: opts?.runId ?? randomUUID(),
49
+ ...opts?.parentRunId ? { parentRunId: opts.parentRunId } : {}
50
+ };
51
+ }
52
+ function makeRunFinished(started) {
53
+ return {
54
+ type: "RUN_FINISHED",
55
+ threadId: started.threadId,
56
+ runId: started.runId
57
+ };
58
+ }
59
+ /**
60
+ * Build a complete text message response sequence.
61
+ * [RUN_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_END, RUN_FINISHED]
62
+ */
63
+ function buildTextResponse(text, opts) {
64
+ const started = makeRunStarted(opts);
65
+ const messageId = randomUUID();
66
+ return [
67
+ started,
68
+ {
69
+ type: "TEXT_MESSAGE_START",
70
+ messageId,
71
+ role: "assistant"
72
+ },
73
+ {
74
+ type: "TEXT_MESSAGE_CONTENT",
75
+ messageId,
76
+ delta: text
77
+ },
78
+ {
79
+ type: "TEXT_MESSAGE_END",
80
+ messageId
81
+ },
82
+ makeRunFinished(started)
83
+ ];
84
+ }
85
+ /**
86
+ * Build a text chunk response (single chunk, no start/end envelope).
87
+ * [RUN_STARTED, TEXT_MESSAGE_CHUNK, RUN_FINISHED]
88
+ */
89
+ function buildTextChunkResponse(text, opts) {
90
+ const started = makeRunStarted(opts);
91
+ return [
92
+ started,
93
+ {
94
+ type: "TEXT_MESSAGE_CHUNK",
95
+ messageId: randomUUID(),
96
+ role: "assistant",
97
+ delta: text
98
+ },
99
+ makeRunFinished(started)
100
+ ];
101
+ }
102
+ /**
103
+ * Build a tool call response sequence.
104
+ * [RUN_STARTED, TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, (TOOL_CALL_RESULT)?, RUN_FINISHED]
105
+ */
106
+ function buildToolCallResponse(toolName, args, opts) {
107
+ const started = makeRunStarted(opts);
108
+ const toolCallId = randomUUID();
109
+ const events = [
110
+ started,
111
+ {
112
+ type: "TOOL_CALL_START",
113
+ toolCallId,
114
+ toolCallName: toolName
115
+ },
116
+ {
117
+ type: "TOOL_CALL_ARGS",
118
+ toolCallId,
119
+ delta: args
120
+ },
121
+ {
122
+ type: "TOOL_CALL_END",
123
+ toolCallId
124
+ }
125
+ ];
126
+ if (opts?.result !== void 0) events.push({
127
+ type: "TOOL_CALL_RESULT",
128
+ messageId: randomUUID(),
129
+ toolCallId,
130
+ content: opts.result,
131
+ role: "tool"
132
+ });
133
+ events.push(makeRunFinished(started));
134
+ return events;
135
+ }
136
+ /**
137
+ * Build a state snapshot response.
138
+ * [RUN_STARTED, STATE_SNAPSHOT, RUN_FINISHED]
139
+ */
140
+ function buildStateUpdate(snapshot, opts) {
141
+ const started = makeRunStarted(opts);
142
+ return [
143
+ started,
144
+ {
145
+ type: "STATE_SNAPSHOT",
146
+ snapshot
147
+ },
148
+ makeRunFinished(started)
149
+ ];
150
+ }
151
+ /**
152
+ * Build a state delta response (JSON Patch).
153
+ * [RUN_STARTED, STATE_DELTA, RUN_FINISHED]
154
+ */
155
+ function buildStateDelta(patches, opts) {
156
+ const started = makeRunStarted(opts);
157
+ return [
158
+ started,
159
+ {
160
+ type: "STATE_DELTA",
161
+ delta: patches
162
+ },
163
+ makeRunFinished(started)
164
+ ];
165
+ }
166
+ /**
167
+ * Build a messages snapshot response.
168
+ * [RUN_STARTED, MESSAGES_SNAPSHOT, RUN_FINISHED]
169
+ */
170
+ function buildMessagesSnapshot(messages, opts) {
171
+ const started = makeRunStarted(opts);
172
+ return [
173
+ started,
174
+ {
175
+ type: "MESSAGES_SNAPSHOT",
176
+ messages
177
+ },
178
+ makeRunFinished(started)
179
+ ];
180
+ }
181
+ /**
182
+ * Build a reasoning response sequence.
183
+ * [RUN_STARTED, REASONING_START, REASONING_MESSAGE_START, REASONING_MESSAGE_CONTENT,
184
+ * REASONING_MESSAGE_END, REASONING_END, RUN_FINISHED]
185
+ */
186
+ function buildReasoningResponse(text, opts) {
187
+ const started = makeRunStarted(opts);
188
+ const messageId = randomUUID();
189
+ return [
190
+ started,
191
+ {
192
+ type: "REASONING_START",
193
+ messageId
194
+ },
195
+ {
196
+ type: "REASONING_MESSAGE_START",
197
+ messageId,
198
+ role: "reasoning"
199
+ },
200
+ {
201
+ type: "REASONING_MESSAGE_CONTENT",
202
+ messageId,
203
+ delta: text
204
+ },
205
+ {
206
+ type: "REASONING_MESSAGE_END",
207
+ messageId
208
+ },
209
+ {
210
+ type: "REASONING_END",
211
+ messageId
212
+ },
213
+ makeRunFinished(started)
214
+ ];
215
+ }
216
+ /**
217
+ * Build an activity snapshot response.
218
+ * [RUN_STARTED, ACTIVITY_SNAPSHOT, RUN_FINISHED]
219
+ */
220
+ function buildActivityResponse(messageId, activityType, content, opts) {
221
+ const started = makeRunStarted(opts);
222
+ return [
223
+ started,
224
+ {
225
+ type: "ACTIVITY_SNAPSHOT",
226
+ messageId,
227
+ activityType,
228
+ content,
229
+ replace: true
230
+ },
231
+ makeRunFinished(started)
232
+ ];
233
+ }
234
+ /**
235
+ * Build an error response.
236
+ * [RUN_STARTED, RUN_ERROR] (no RUN_FINISHED — the run errored)
237
+ */
238
+ function buildErrorResponse(message, code, opts) {
239
+ return [makeRunStarted(opts), {
240
+ type: "RUN_ERROR",
241
+ message,
242
+ ...code !== void 0 ? { code } : {}
243
+ }];
244
+ }
245
+ /**
246
+ * Build a step-wrapped text response.
247
+ * [RUN_STARTED, STEP_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT,
248
+ * TEXT_MESSAGE_END, STEP_FINISHED, RUN_FINISHED]
249
+ */
250
+ function buildStepWithText(stepName, text, opts) {
251
+ const started = makeRunStarted(opts);
252
+ const messageId = randomUUID();
253
+ return [
254
+ started,
255
+ {
256
+ type: "STEP_STARTED",
257
+ stepName
258
+ },
259
+ {
260
+ type: "TEXT_MESSAGE_START",
261
+ messageId,
262
+ role: "assistant"
263
+ },
264
+ {
265
+ type: "TEXT_MESSAGE_CONTENT",
266
+ messageId,
267
+ delta: text
268
+ },
269
+ {
270
+ type: "TEXT_MESSAGE_END",
271
+ messageId
272
+ },
273
+ {
274
+ type: "STEP_FINISHED",
275
+ stepName
276
+ },
277
+ makeRunFinished(started)
278
+ ];
279
+ }
280
+ /**
281
+ * Combine multiple builder outputs into a single run.
282
+ * Strips RUN_STARTED/RUN_FINISHED from each input, wraps all inner events
283
+ * in one RUN_STARTED...RUN_FINISHED pair.
284
+ */
285
+ function buildCompositeResponse(builderOutputs, opts) {
286
+ const started = makeRunStarted(opts);
287
+ const inner = [];
288
+ for (const events of builderOutputs) for (const event of events) if (event.type !== "RUN_STARTED" && event.type !== "RUN_FINISHED") inner.push(event);
289
+ return [
290
+ started,
291
+ ...inner,
292
+ makeRunFinished(started)
293
+ ];
294
+ }
295
+ /**
296
+ * Write AG-UI events as an SSE stream to an HTTP response.
297
+ * Sets appropriate headers, serializes each event as `data: {...}\n\n`,
298
+ * and optionally delays between events.
299
+ */
300
+ async function writeAGUIEventStream(res, events, opts) {
301
+ const delayMs = opts?.delayMs ?? 0;
302
+ res.writeHead(200, {
303
+ "Content-Type": "text/event-stream",
304
+ "Cache-Control": "no-cache",
305
+ Connection: "keep-alive"
306
+ });
307
+ for (const event of events) {
308
+ if (opts?.signal?.aborted) break;
309
+ if (res.socket?.destroyed) break;
310
+ const stamped = {
311
+ ...event,
312
+ timestamp: Date.now()
313
+ };
314
+ try {
315
+ res.write(`data: ${JSON.stringify(stamped)}\n\n`);
316
+ } catch {
317
+ break;
318
+ }
319
+ if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
320
+ }
321
+ if (!res.writableEnded) res.end();
322
+ }
323
+
324
+ //#endregion
325
+ export { buildActivityResponse, buildCompositeResponse, buildErrorResponse, buildMessagesSnapshot, buildReasoningResponse, buildStateDelta, buildStateUpdate, buildStepWithText, buildTextChunkResponse, buildTextResponse, buildToolCallResponse, extractLastUserMessage, findFixture, writeAGUIEventStream };
326
+ //# sourceMappingURL=agui-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agui-handler.js","names":[],"sources":["../src/agui-handler.ts"],"sourcesContent":["// ─── AG-UI Handler ───────────────────────────────────────────────────────────\n//\n// Matching functions, event builders, and SSE writer for AG-UI protocol.\n\nimport * as http from \"node:http\";\nimport { randomUUID } from \"node:crypto\";\n\nimport type {\n AGUIRunAgentInput,\n AGUIFixtureMatch,\n AGUIFixture,\n AGUIEvent,\n AGUIMessage,\n AGUIRunStartedEvent,\n AGUIRunFinishedEvent,\n AGUIRunErrorEvent,\n AGUITextMessageStartEvent,\n AGUITextMessageContentEvent,\n AGUITextMessageEndEvent,\n AGUITextMessageChunkEvent,\n AGUIToolCallStartEvent,\n AGUIToolCallArgsEvent,\n AGUIToolCallEndEvent,\n AGUIToolCallResultEvent,\n AGUIStateSnapshotEvent,\n AGUIStateDeltaEvent,\n AGUIMessagesSnapshotEvent,\n AGUIStepStartedEvent,\n AGUIStepFinishedEvent,\n AGUIReasoningStartEvent,\n AGUIReasoningMessageStartEvent,\n AGUIReasoningMessageContentEvent,\n AGUIReasoningMessageEndEvent,\n AGUIReasoningEndEvent,\n AGUIActivitySnapshotEvent,\n} from \"./agui-types.js\";\n\n// ─── Matching functions ──────────────────────────────────────────────────────\n\n/**\n * Extract the content of the last message with role \"user\" from the input.\n */\nexport function extractLastUserMessage(input: AGUIRunAgentInput): string {\n if (!input.messages || input.messages.length === 0) return \"\";\n for (let i = input.messages.length - 1; i >= 0; i--) {\n const msg = input.messages[i];\n if (msg.role === \"user\" && typeof msg.content === \"string\") {\n return msg.content;\n }\n }\n return \"\";\n}\n\n/**\n * Check whether an input matches a fixture's match criteria.\n * All specified criteria must pass (AND logic).\n */\nexport function matchesFixture(input: AGUIRunAgentInput, match: AGUIFixtureMatch): boolean {\n if (match.message !== undefined) {\n const text = extractLastUserMessage(input);\n if (typeof match.message === \"string\") {\n if (!text.includes(match.message)) return false;\n } else {\n if (!match.message.test(text)) return false;\n }\n }\n\n if (match.toolName !== undefined) {\n const tools = input.tools ?? [];\n if (!tools.some((t) => t.name === match.toolName)) return false;\n }\n\n if (match.stateKey !== undefined) {\n if (\n input.state === null ||\n input.state === undefined ||\n typeof input.state !== \"object\" ||\n !(match.stateKey in (input.state as Record<string, unknown>))\n ) {\n return false;\n }\n }\n\n if (match.predicate !== undefined) {\n if (!match.predicate(input)) return false;\n }\n\n return true;\n}\n\n/**\n * Find the first fixture whose match criteria pass for the given input.\n */\nexport function findFixture(input: AGUIRunAgentInput, fixtures: AGUIFixture[]): AGUIFixture | null {\n for (const fixture of fixtures) {\n if (matchesFixture(input, fixture.match)) {\n return fixture;\n }\n }\n return null;\n}\n\n// ─── Builder options ─────────────────────────────────────────────────────────\n\nexport interface AGUIBuildOpts {\n threadId?: string;\n runId?: string;\n parentRunId?: string;\n /** For tool call builder: include a result event */\n result?: string;\n}\n\n// ─── Event builders ──────────────────────────────────────────────────────────\n\nfunction makeRunStarted(opts?: AGUIBuildOpts): AGUIRunStartedEvent {\n return {\n type: \"RUN_STARTED\",\n threadId: opts?.threadId ?? randomUUID(),\n runId: opts?.runId ?? randomUUID(),\n ...(opts?.parentRunId ? { parentRunId: opts.parentRunId } : {}),\n };\n}\n\nfunction makeRunFinished(started: AGUIRunStartedEvent): AGUIRunFinishedEvent {\n return {\n type: \"RUN_FINISHED\",\n threadId: started.threadId,\n runId: started.runId,\n };\n}\n\n/**\n * Build a complete text message response sequence.\n * [RUN_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TEXT_MESSAGE_END, RUN_FINISHED]\n */\nexport function buildTextResponse(text: string, opts?: AGUIBuildOpts): AGUIEvent[] {\n const started = makeRunStarted(opts);\n const messageId = randomUUID();\n return [\n started,\n {\n type: \"TEXT_MESSAGE_START\",\n messageId,\n role: \"assistant\",\n } as AGUITextMessageStartEvent,\n {\n type: \"TEXT_MESSAGE_CONTENT\",\n messageId,\n delta: text,\n } as AGUITextMessageContentEvent,\n {\n type: \"TEXT_MESSAGE_END\",\n messageId,\n } as AGUITextMessageEndEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Build a text chunk response (single chunk, no start/end envelope).\n * [RUN_STARTED, TEXT_MESSAGE_CHUNK, RUN_FINISHED]\n */\nexport function buildTextChunkResponse(text: string, opts?: AGUIBuildOpts): AGUIEvent[] {\n const started = makeRunStarted(opts);\n return [\n started,\n {\n type: \"TEXT_MESSAGE_CHUNK\",\n messageId: randomUUID(),\n role: \"assistant\",\n delta: text,\n } as AGUITextMessageChunkEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Build a tool call response sequence.\n * [RUN_STARTED, TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, (TOOL_CALL_RESULT)?, RUN_FINISHED]\n */\nexport function buildToolCallResponse(\n toolName: string,\n args: string,\n opts?: AGUIBuildOpts,\n): AGUIEvent[] {\n const started = makeRunStarted(opts);\n const toolCallId = randomUUID();\n const events: AGUIEvent[] = [\n started,\n {\n type: \"TOOL_CALL_START\",\n toolCallId,\n toolCallName: toolName,\n } as AGUIToolCallStartEvent,\n {\n type: \"TOOL_CALL_ARGS\",\n toolCallId,\n delta: args,\n } as AGUIToolCallArgsEvent,\n {\n type: \"TOOL_CALL_END\",\n toolCallId,\n } as AGUIToolCallEndEvent,\n ];\n\n if (opts?.result !== undefined) {\n events.push({\n type: \"TOOL_CALL_RESULT\",\n messageId: randomUUID(),\n toolCallId,\n content: opts.result,\n role: \"tool\",\n } as AGUIToolCallResultEvent);\n }\n\n events.push(makeRunFinished(started));\n return events;\n}\n\n/**\n * Build a state snapshot response.\n * [RUN_STARTED, STATE_SNAPSHOT, RUN_FINISHED]\n */\nexport function buildStateUpdate(snapshot: unknown, opts?: AGUIBuildOpts): AGUIEvent[] {\n const started = makeRunStarted(opts);\n return [\n started,\n {\n type: \"STATE_SNAPSHOT\",\n snapshot,\n } as AGUIStateSnapshotEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Build a state delta response (JSON Patch).\n * [RUN_STARTED, STATE_DELTA, RUN_FINISHED]\n */\nexport function buildStateDelta(patches: unknown[], opts?: AGUIBuildOpts): AGUIEvent[] {\n const started = makeRunStarted(opts);\n return [\n started,\n {\n type: \"STATE_DELTA\",\n delta: patches,\n } as AGUIStateDeltaEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Build a messages snapshot response.\n * [RUN_STARTED, MESSAGES_SNAPSHOT, RUN_FINISHED]\n */\nexport function buildMessagesSnapshot(messages: AGUIMessage[], opts?: AGUIBuildOpts): AGUIEvent[] {\n const started = makeRunStarted(opts);\n return [\n started,\n {\n type: \"MESSAGES_SNAPSHOT\",\n messages,\n } as AGUIMessagesSnapshotEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Build a reasoning response sequence.\n * [RUN_STARTED, REASONING_START, REASONING_MESSAGE_START, REASONING_MESSAGE_CONTENT,\n * REASONING_MESSAGE_END, REASONING_END, RUN_FINISHED]\n */\nexport function buildReasoningResponse(text: string, opts?: AGUIBuildOpts): AGUIEvent[] {\n const started = makeRunStarted(opts);\n const messageId = randomUUID();\n return [\n started,\n {\n type: \"REASONING_START\",\n messageId,\n } as AGUIReasoningStartEvent,\n {\n type: \"REASONING_MESSAGE_START\",\n messageId,\n role: \"reasoning\",\n } as AGUIReasoningMessageStartEvent,\n {\n type: \"REASONING_MESSAGE_CONTENT\",\n messageId,\n delta: text,\n } as AGUIReasoningMessageContentEvent,\n {\n type: \"REASONING_MESSAGE_END\",\n messageId,\n } as AGUIReasoningMessageEndEvent,\n {\n type: \"REASONING_END\",\n messageId,\n } as AGUIReasoningEndEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Build an activity snapshot response.\n * [RUN_STARTED, ACTIVITY_SNAPSHOT, RUN_FINISHED]\n */\nexport function buildActivityResponse(\n messageId: string,\n activityType: string,\n content: Record<string, unknown>,\n opts?: AGUIBuildOpts,\n): AGUIEvent[] {\n const started = makeRunStarted(opts);\n return [\n started,\n {\n type: \"ACTIVITY_SNAPSHOT\",\n messageId,\n activityType,\n content,\n replace: true,\n } as AGUIActivitySnapshotEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Build an error response.\n * [RUN_STARTED, RUN_ERROR] (no RUN_FINISHED — the run errored)\n */\nexport function buildErrorResponse(\n message: string,\n code?: string,\n opts?: AGUIBuildOpts,\n): AGUIEvent[] {\n const started = makeRunStarted(opts);\n return [\n started,\n {\n type: \"RUN_ERROR\",\n message,\n ...(code !== undefined ? { code } : {}),\n } as AGUIRunErrorEvent,\n ];\n}\n\n/**\n * Build a step-wrapped text response.\n * [RUN_STARTED, STEP_STARTED, TEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT,\n * TEXT_MESSAGE_END, STEP_FINISHED, RUN_FINISHED]\n */\nexport function buildStepWithText(\n stepName: string,\n text: string,\n opts?: AGUIBuildOpts,\n): AGUIEvent[] {\n const started = makeRunStarted(opts);\n const messageId = randomUUID();\n return [\n started,\n {\n type: \"STEP_STARTED\",\n stepName,\n } as AGUIStepStartedEvent,\n {\n type: \"TEXT_MESSAGE_START\",\n messageId,\n role: \"assistant\",\n } as AGUITextMessageStartEvent,\n {\n type: \"TEXT_MESSAGE_CONTENT\",\n messageId,\n delta: text,\n } as AGUITextMessageContentEvent,\n {\n type: \"TEXT_MESSAGE_END\",\n messageId,\n } as AGUITextMessageEndEvent,\n {\n type: \"STEP_FINISHED\",\n stepName,\n } as AGUIStepFinishedEvent,\n makeRunFinished(started),\n ];\n}\n\n/**\n * Combine multiple builder outputs into a single run.\n * Strips RUN_STARTED/RUN_FINISHED from each input, wraps all inner events\n * in one RUN_STARTED...RUN_FINISHED pair.\n */\nexport function buildCompositeResponse(\n builderOutputs: AGUIEvent[][],\n opts?: AGUIBuildOpts,\n): AGUIEvent[] {\n const started = makeRunStarted(opts);\n const inner: AGUIEvent[] = [];\n\n for (const events of builderOutputs) {\n for (const event of events) {\n if (event.type !== \"RUN_STARTED\" && event.type !== \"RUN_FINISHED\") {\n inner.push(event);\n }\n }\n }\n\n return [started, ...inner, makeRunFinished(started)];\n}\n\n// ─── SSE writer ──────────────────────────────────────────────────────────────\n\n/**\n * Write AG-UI events as an SSE stream to an HTTP response.\n * Sets appropriate headers, serializes each event as `data: {...}\\n\\n`,\n * and optionally delays between events.\n */\nexport async function writeAGUIEventStream(\n res: http.ServerResponse,\n events: AGUIEvent[],\n opts?: { delayMs?: number; signal?: AbortSignal },\n): Promise<void> {\n const delayMs = opts?.delayMs ?? 0;\n\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n });\n\n for (const event of events) {\n if (opts?.signal?.aborted) break;\n if (res.socket?.destroyed) break;\n\n const stamped = { ...event, timestamp: Date.now() };\n try {\n res.write(`data: ${JSON.stringify(stamped)}\\n\\n`);\n } catch {\n break; // client disconnected or stream error — stop writing\n }\n\n if (delayMs > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, delayMs));\n }\n }\n\n if (!res.writableEnded) res.end();\n}\n"],"mappings":";;;;;;AA0CA,SAAgB,uBAAuB,OAAkC;AACvE,KAAI,CAAC,MAAM,YAAY,MAAM,SAAS,WAAW,EAAG,QAAO;AAC3D,MAAK,IAAI,IAAI,MAAM,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;EACnD,MAAM,MAAM,MAAM,SAAS;AAC3B,MAAI,IAAI,SAAS,UAAU,OAAO,IAAI,YAAY,SAChD,QAAO,IAAI;;AAGf,QAAO;;;;;;AAOT,SAAgB,eAAe,OAA0B,OAAkC;AACzF,KAAI,MAAM,YAAY,QAAW;EAC/B,MAAM,OAAO,uBAAuB,MAAM;AAC1C,MAAI,OAAO,MAAM,YAAY,UAC3B;OAAI,CAAC,KAAK,SAAS,MAAM,QAAQ,CAAE,QAAO;aAEtC,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAE,QAAO;;AAI1C,KAAI,MAAM,aAAa,QAErB;MAAI,EADU,MAAM,SAAS,EAAE,EACpB,MAAM,MAAM,EAAE,SAAS,MAAM,SAAS,CAAE,QAAO;;AAG5D,KAAI,MAAM,aAAa,QACrB;MACE,MAAM,UAAU,QAChB,MAAM,UAAU,UAChB,OAAO,MAAM,UAAU,YACvB,EAAE,MAAM,YAAa,MAAM,OAE3B,QAAO;;AAIX,KAAI,MAAM,cAAc,QACtB;MAAI,CAAC,MAAM,UAAU,MAAM,CAAE,QAAO;;AAGtC,QAAO;;;;;AAMT,SAAgB,YAAY,OAA0B,UAA6C;AACjG,MAAK,MAAM,WAAW,SACpB,KAAI,eAAe,OAAO,QAAQ,MAAM,CACtC,QAAO;AAGX,QAAO;;AAeT,SAAS,eAAe,MAA2C;AACjE,QAAO;EACL,MAAM;EACN,UAAU,MAAM,YAAY,YAAY;EACxC,OAAO,MAAM,SAAS,YAAY;EAClC,GAAI,MAAM,cAAc,EAAE,aAAa,KAAK,aAAa,GAAG,EAAE;EAC/D;;AAGH,SAAS,gBAAgB,SAAoD;AAC3E,QAAO;EACL,MAAM;EACN,UAAU,QAAQ;EAClB,OAAO,QAAQ;EAChB;;;;;;AAOH,SAAgB,kBAAkB,MAAc,MAAmC;CACjF,MAAM,UAAU,eAAe,KAAK;CACpC,MAAM,YAAY,YAAY;AAC9B,QAAO;EACL;EACA;GACE,MAAM;GACN;GACA,MAAM;GACP;EACD;GACE,MAAM;GACN;GACA,OAAO;GACR;EACD;GACE,MAAM;GACN;GACD;EACD,gBAAgB,QAAQ;EACzB;;;;;;AAOH,SAAgB,uBAAuB,MAAc,MAAmC;CACtF,MAAM,UAAU,eAAe,KAAK;AACpC,QAAO;EACL;EACA;GACE,MAAM;GACN,WAAW,YAAY;GACvB,MAAM;GACN,OAAO;GACR;EACD,gBAAgB,QAAQ;EACzB;;;;;;AAOH,SAAgB,sBACd,UACA,MACA,MACa;CACb,MAAM,UAAU,eAAe,KAAK;CACpC,MAAM,aAAa,YAAY;CAC/B,MAAM,SAAsB;EAC1B;EACA;GACE,MAAM;GACN;GACA,cAAc;GACf;EACD;GACE,MAAM;GACN;GACA,OAAO;GACR;EACD;GACE,MAAM;GACN;GACD;EACF;AAED,KAAI,MAAM,WAAW,OACnB,QAAO,KAAK;EACV,MAAM;EACN,WAAW,YAAY;EACvB;EACA,SAAS,KAAK;EACd,MAAM;EACP,CAA4B;AAG/B,QAAO,KAAK,gBAAgB,QAAQ,CAAC;AACrC,QAAO;;;;;;AAOT,SAAgB,iBAAiB,UAAmB,MAAmC;CACrF,MAAM,UAAU,eAAe,KAAK;AACpC,QAAO;EACL;EACA;GACE,MAAM;GACN;GACD;EACD,gBAAgB,QAAQ;EACzB;;;;;;AAOH,SAAgB,gBAAgB,SAAoB,MAAmC;CACrF,MAAM,UAAU,eAAe,KAAK;AACpC,QAAO;EACL;EACA;GACE,MAAM;GACN,OAAO;GACR;EACD,gBAAgB,QAAQ;EACzB;;;;;;AAOH,SAAgB,sBAAsB,UAAyB,MAAmC;CAChG,MAAM,UAAU,eAAe,KAAK;AACpC,QAAO;EACL;EACA;GACE,MAAM;GACN;GACD;EACD,gBAAgB,QAAQ;EACzB;;;;;;;AAQH,SAAgB,uBAAuB,MAAc,MAAmC;CACtF,MAAM,UAAU,eAAe,KAAK;CACpC,MAAM,YAAY,YAAY;AAC9B,QAAO;EACL;EACA;GACE,MAAM;GACN;GACD;EACD;GACE,MAAM;GACN;GACA,MAAM;GACP;EACD;GACE,MAAM;GACN;GACA,OAAO;GACR;EACD;GACE,MAAM;GACN;GACD;EACD;GACE,MAAM;GACN;GACD;EACD,gBAAgB,QAAQ;EACzB;;;;;;AAOH,SAAgB,sBACd,WACA,cACA,SACA,MACa;CACb,MAAM,UAAU,eAAe,KAAK;AACpC,QAAO;EACL;EACA;GACE,MAAM;GACN;GACA;GACA;GACA,SAAS;GACV;EACD,gBAAgB,QAAQ;EACzB;;;;;;AAOH,SAAgB,mBACd,SACA,MACA,MACa;AAEb,QAAO,CADS,eAAe,KAAK,EAGlC;EACE,MAAM;EACN;EACA,GAAI,SAAS,SAAY,EAAE,MAAM,GAAG,EAAE;EACvC,CACF;;;;;;;AAQH,SAAgB,kBACd,UACA,MACA,MACa;CACb,MAAM,UAAU,eAAe,KAAK;CACpC,MAAM,YAAY,YAAY;AAC9B,QAAO;EACL;EACA;GACE,MAAM;GACN;GACD;EACD;GACE,MAAM;GACN;GACA,MAAM;GACP;EACD;GACE,MAAM;GACN;GACA,OAAO;GACR;EACD;GACE,MAAM;GACN;GACD;EACD;GACE,MAAM;GACN;GACD;EACD,gBAAgB,QAAQ;EACzB;;;;;;;AAQH,SAAgB,uBACd,gBACA,MACa;CACb,MAAM,UAAU,eAAe,KAAK;CACpC,MAAM,QAAqB,EAAE;AAE7B,MAAK,MAAM,UAAU,eACnB,MAAK,MAAM,SAAS,OAClB,KAAI,MAAM,SAAS,iBAAiB,MAAM,SAAS,eACjD,OAAM,KAAK,MAAM;AAKvB,QAAO;EAAC;EAAS,GAAG;EAAO,gBAAgB,QAAQ;EAAC;;;;;;;AAUtD,eAAsB,qBACpB,KACA,QACA,MACe;CACf,MAAM,UAAU,MAAM,WAAW;AAEjC,KAAI,UAAU,KAAK;EACjB,gBAAgB;EAChB,iBAAiB;EACjB,YAAY;EACb,CAAC;AAEF,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,MAAM,QAAQ,QAAS;AAC3B,MAAI,IAAI,QAAQ,UAAW;EAE3B,MAAM,UAAU;GAAE,GAAG;GAAO,WAAW,KAAK,KAAK;GAAE;AACnD,MAAI;AACF,OAAI,MAAM,SAAS,KAAK,UAAU,QAAQ,CAAC,MAAM;UAC3C;AACN;;AAGF,MAAI,UAAU,EACZ,OAAM,IAAI,SAAe,YAAY,WAAW,SAAS,QAAQ,CAAC;;AAItE,KAAI,CAAC,IAAI,cAAe,KAAI,KAAK"}
@@ -0,0 +1,190 @@
1
+ const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ const require_helpers = require('./helpers.cjs');
3
+ const require_logger = require('./logger.cjs');
4
+ const require_agui_handler = require('./agui-handler.cjs');
5
+ const require_agui_recorder = require('./agui-recorder.cjs');
6
+ let node_http = require("node:http");
7
+ node_http = require_runtime.__toESM(node_http);
8
+
9
+ //#region src/agui-mock.ts
10
+ var AGUIMock = class {
11
+ fixtures = [];
12
+ server = null;
13
+ journal = null;
14
+ registry = null;
15
+ options;
16
+ baseUrl = "";
17
+ recordConfig;
18
+ logger;
19
+ constructor(options) {
20
+ this.options = options ?? {};
21
+ this.logger = new require_logger.Logger("silent");
22
+ }
23
+ addFixture(fixture) {
24
+ this.fixtures.push(fixture);
25
+ return this;
26
+ }
27
+ onMessage(pattern, text, opts) {
28
+ const events = require_agui_handler.buildTextResponse(text);
29
+ this.fixtures.push({
30
+ match: { message: pattern },
31
+ events,
32
+ delayMs: opts?.delayMs
33
+ });
34
+ return this;
35
+ }
36
+ onRun(pattern, events, delayMs) {
37
+ this.fixtures.push({
38
+ match: { message: pattern },
39
+ events,
40
+ delayMs
41
+ });
42
+ return this;
43
+ }
44
+ onToolCall(pattern, toolName, args, opts) {
45
+ const events = require_agui_handler.buildToolCallResponse(toolName, args, { result: opts?.result });
46
+ this.fixtures.push({
47
+ match: { message: pattern },
48
+ events,
49
+ delayMs: opts?.delayMs
50
+ });
51
+ return this;
52
+ }
53
+ onStateKey(key, snapshot, delayMs) {
54
+ const events = require_agui_handler.buildStateUpdate(snapshot);
55
+ this.fixtures.push({
56
+ match: { stateKey: key },
57
+ events,
58
+ delayMs
59
+ });
60
+ return this;
61
+ }
62
+ onReasoning(pattern, text, opts) {
63
+ const events = require_agui_handler.buildReasoningResponse(text);
64
+ this.fixtures.push({
65
+ match: { message: pattern },
66
+ events,
67
+ delayMs: opts?.delayMs
68
+ });
69
+ return this;
70
+ }
71
+ onPredicate(predicate, events, delayMs) {
72
+ this.fixtures.push({
73
+ match: { predicate },
74
+ events,
75
+ delayMs
76
+ });
77
+ return this;
78
+ }
79
+ enableRecording(config) {
80
+ this.recordConfig = config;
81
+ return this;
82
+ }
83
+ reset() {
84
+ this.fixtures = [];
85
+ this.recordConfig = void 0;
86
+ return this;
87
+ }
88
+ async handleRequest(req, res, pathname) {
89
+ if (req.method !== "POST" || pathname !== "/" && pathname !== "") return false;
90
+ if (this.registry) this.registry.incrementCounter("aimock_agui_requests_total", { method: "POST" });
91
+ const body = await require_helpers.readBody(req);
92
+ let input;
93
+ try {
94
+ input = JSON.parse(body);
95
+ } catch {
96
+ res.writeHead(400, { "Content-Type": "application/json" });
97
+ res.end(JSON.stringify({ error: "Invalid JSON body" }));
98
+ this.journalRequest(req, pathname, 400);
99
+ return true;
100
+ }
101
+ const fixture = require_agui_handler.findFixture(input, this.fixtures);
102
+ if (fixture) {
103
+ await require_agui_handler.writeAGUIEventStream(res, fixture.events, { delayMs: fixture.delayMs });
104
+ this.journalRequest(req, pathname, 200);
105
+ return true;
106
+ }
107
+ if (this.recordConfig) {
108
+ if (await require_agui_recorder.proxyAndRecordAGUI(req, res, input, this.fixtures, this.recordConfig, this.logger)) {
109
+ this.journalRequest(req, pathname, 200);
110
+ return true;
111
+ }
112
+ }
113
+ res.writeHead(404, { "Content-Type": "application/json" });
114
+ res.end(JSON.stringify({ error: "No matching AG-UI fixture" }));
115
+ this.journalRequest(req, pathname, 404);
116
+ return true;
117
+ }
118
+ health() {
119
+ return {
120
+ status: "ok",
121
+ fixtures: this.fixtures.length
122
+ };
123
+ }
124
+ setJournal(journal) {
125
+ this.journal = journal;
126
+ }
127
+ setBaseUrl(url) {
128
+ this.baseUrl = url;
129
+ }
130
+ setRegistry(registry) {
131
+ this.registry = registry;
132
+ }
133
+ async start() {
134
+ if (this.server) throw new Error("AGUIMock server already started");
135
+ const host = this.options.host ?? "127.0.0.1";
136
+ const port = this.options.port ?? 0;
137
+ return new Promise((resolve, reject) => {
138
+ const srv = node_http.createServer(async (req, res) => {
139
+ const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
140
+ if (!await this.handleRequest(req, res, url.pathname).catch((err) => {
141
+ this.logger.error(`AGUIMock request error: ${err instanceof Error ? err.message : err}`);
142
+ if (!res.headersSent) {
143
+ res.writeHead(500);
144
+ res.end("Internal server error");
145
+ } else if (!res.writableEnded) res.end();
146
+ return true;
147
+ }) && !res.headersSent) {
148
+ res.writeHead(404, { "Content-Type": "application/json" });
149
+ res.end(JSON.stringify({ error: "Not found" }));
150
+ }
151
+ });
152
+ srv.on("error", reject);
153
+ srv.listen(port, host, () => {
154
+ const addr = srv.address();
155
+ if (typeof addr === "object" && addr !== null) this.baseUrl = `http://${host}:${addr.port}`;
156
+ this.server = srv;
157
+ resolve(this.baseUrl);
158
+ });
159
+ });
160
+ }
161
+ async stop() {
162
+ if (!this.server) throw new Error("AGUIMock server not started");
163
+ const srv = this.server;
164
+ await new Promise((resolve, reject) => {
165
+ srv.close((err) => err ? reject(err) : resolve());
166
+ });
167
+ this.server = null;
168
+ }
169
+ get url() {
170
+ if (!this.server) throw new Error("AGUIMock server not started");
171
+ return this.baseUrl;
172
+ }
173
+ journalRequest(req, pathname, status) {
174
+ if (this.journal) this.journal.add({
175
+ method: req.method ?? "POST",
176
+ path: req.url ?? pathname,
177
+ headers: require_helpers.flattenHeaders(req.headers),
178
+ body: null,
179
+ service: "agui",
180
+ response: {
181
+ status,
182
+ fixture: null
183
+ }
184
+ });
185
+ }
186
+ };
187
+
188
+ //#endregion
189
+ exports.AGUIMock = AGUIMock;
190
+ //# sourceMappingURL=agui-mock.cjs.map
@@ -0,0 +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 } 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(\"silent\");\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 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 const body = await readBody(req);\n\n let input: AGUIRunAgentInput;\n try {\n input = JSON.parse(body) as AGUIRunAgentInput;\n } catch {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Invalid JSON body\" }));\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 proxied = await proxyAndRecordAGUI(\n req,\n res,\n input,\n this.fixtures,\n this.recordConfig,\n this.logger,\n );\n if (proxied) {\n this.journalRequest(req, pathname, 200);\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":";;;;;;;;;AAuBA,IAAa,WAAb,MAA2C;CACzC,AAAQ,WAA0B,EAAE;CACpC,AAAQ,SAA6B;CACrC,AAAQ,UAA0B;CAClC,AAAQ,WAAmC;CAC3C,AAAQ;CACR,AAAQ,UAAU;CAClB,AAAQ;CACR,AAAQ;CAER,YAAY,SAA2B;AACrC,OAAK,UAAU,WAAW,EAAE;AAC5B,OAAK,SAAS,IAAIA,sBAAO,SAAS;;CAKpC,WAAW,SAA4B;AACrC,OAAK,SAAS,KAAK,QAAQ;AAC3B,SAAO;;CAGT,UAAU,SAA0B,MAAc,MAAmC;EACnF,MAAM,SAASC,uCAAkB,KAAK;AACtC,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA,SAAS,MAAM;GAChB,CAAC;AACF,SAAO;;CAGT,MAAM,SAA0B,QAAqB,SAAwB;AAC3E,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA;GACD,CAAC;AACF,SAAO;;CAGT,WACE,SACA,UACA,MACA,MACM;EACN,MAAM,SAASC,2CAAsB,UAAU,MAAM,EACnD,QAAQ,MAAM,QACf,CAAC;AACF,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA,SAAS,MAAM;GAChB,CAAC;AACF,SAAO;;CAGT,WAAW,KAAa,UAAmC,SAAwB;EACjF,MAAM,SAASC,sCAAiB,SAAS;AACzC,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,UAAU,KAAK;GACxB;GACA;GACD,CAAC;AACF,SAAO;;CAGT,YAAY,SAA0B,MAAc,MAAmC;EACrF,MAAM,SAASC,4CAAuB,KAAK;AAC3C,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA,SAAS,MAAM;GAChB,CAAC;AACF,SAAO;;CAGT,YACE,WACA,QACA,SACM;AACN,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,WAAW;GACpB;GACA;GACD,CAAC;AACF,SAAO;;CAGT,gBAAgB,QAAgC;AAC9C,OAAK,eAAe;AACpB,SAAO;;CAGT,QAAc;AACZ,OAAK,WAAW,EAAE;AAClB,OAAK,eAAe;AACpB,SAAO;;CAKT,MAAM,cACJ,KACA,KACA,UACkB;AAClB,MAAI,IAAI,WAAW,UAAW,aAAa,OAAO,aAAa,GAC7D,QAAO;AAGT,MAAI,KAAK,SACP,MAAK,SAAS,iBAAiB,8BAA8B,EAAE,QAAQ,QAAQ,CAAC;EAGlF,MAAM,OAAO,MAAMC,yBAAS,IAAI;EAEhC,IAAI;AACJ,MAAI;AACF,WAAQ,KAAK,MAAM,KAAK;UAClB;AACN,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,qBAAqB,CAAC,CAAC;AACvD,QAAK,eAAe,KAAK,UAAU,IAAI;AACvC,UAAO;;EAGT,MAAM,UAAUC,iCAAY,OAAO,KAAK,SAAS;AAEjD,MAAI,SAAS;AACX,SAAMC,0CAAqB,KAAK,QAAQ,QAAQ,EAAE,SAAS,QAAQ,SAAS,CAAC;AAC7E,QAAK,eAAe,KAAK,UAAU,IAAI;AACvC,UAAO;;AAIT,MAAI,KAAK,cASP;OARgB,MAAMC,yCACpB,KACA,KACA,OACA,KAAK,UACL,KAAK,cACL,KAAK,OACN,EACY;AACX,SAAK,eAAe,KAAK,UAAU,IAAI;AACvC,WAAO;;;AAKX,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IAAI,KAAK,UAAU,EAAE,OAAO,6BAA6B,CAAC,CAAC;AAC/D,OAAK,eAAe,KAAK,UAAU,IAAI;AACvC,SAAO;;CAGT,SAA+C;AAC7C,SAAO;GACL,QAAQ;GACR,UAAU,KAAK,SAAS;GACzB;;CAGH,WAAW,SAAwB;AACjC,OAAK,UAAU;;CAGjB,WAAW,KAAmB;AAC5B,OAAK,UAAU;;CAGjB,YAAY,UAAiC;AAC3C,OAAK,WAAW;;CAKlB,MAAM,QAAyB;AAC7B,MAAI,KAAK,OACP,OAAM,IAAI,MAAM,kCAAkC;EAGpD,MAAM,OAAO,KAAK,QAAQ,QAAQ;EAClC,MAAM,OAAO,KAAK,QAAQ,QAAQ;AAElC,SAAO,IAAI,SAAiB,SAAS,WAAW;GAC9C,MAAM,MAAMC,UAAK,aAAa,OAAO,KAAK,QAAQ;IAChD,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,QAAQ,cAAc;AAWhF,QAAI,CAVY,MAAM,KAAK,cAAc,KAAK,KAAK,IAAI,SAAS,CAAC,OAAO,QAAQ;AAC9E,UAAK,OAAO,MAAM,2BAA2B,eAAe,QAAQ,IAAI,UAAU,MAAM;AACxF,SAAI,CAAC,IAAI,aAAa;AACpB,UAAI,UAAU,IAAI;AAClB,UAAI,IAAI,wBAAwB;gBACvB,CAAC,IAAI,cACd,KAAI,KAAK;AAEX,YAAO;MACP,IACc,CAAC,IAAI,aAAa;AAChC,SAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,SAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,CAAC,CAAC;;KAEjD;AAEF,OAAI,GAAG,SAAS,OAAO;AAEvB,OAAI,OAAO,MAAM,YAAY;IAC3B,MAAM,OAAO,IAAI,SAAS;AAC1B,QAAI,OAAO,SAAS,YAAY,SAAS,KACvC,MAAK,UAAU,UAAU,KAAK,GAAG,KAAK;AAExC,SAAK,SAAS;AACd,YAAQ,KAAK,QAAQ;KACrB;IACF;;CAGJ,MAAM,OAAsB;AAC1B,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,8BAA8B;EAEhD,MAAM,MAAM,KAAK;AACjB,QAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,OAAI,OAAO,QAA4B,MAAM,OAAO,IAAI,GAAG,SAAS,CAAE;IACtE;AACF,OAAK,SAAS;;CAGhB,IAAI,MAAc;AAChB,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,8BAA8B;AAEhD,SAAO,KAAK;;CAKd,AAAQ,eAAe,KAA2B,UAAkB,QAAsB;AACxF,MAAI,KAAK,QACP,MAAK,QAAQ,IAAI;GACf,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAASC,+BAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE;IAAQ,SAAS;IAAM;GACpC,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { Journal } from "./journal.cjs";
2
+ import { MetricsRegistry } from "./metrics.cjs";
3
+ import { Mountable } from "./types.cjs";
4
+ import { AGUIEvent, AGUIFixture, AGUIMockOptions, AGUIRecordConfig, AGUIRunAgentInput } from "./agui-types.cjs";
5
+ import * as http from "node:http";
6
+
7
+ //#region src/agui-mock.d.ts
8
+ declare class AGUIMock implements Mountable {
9
+ private fixtures;
10
+ private server;
11
+ private journal;
12
+ private registry;
13
+ private options;
14
+ private baseUrl;
15
+ private recordConfig;
16
+ private logger;
17
+ constructor(options?: AGUIMockOptions);
18
+ addFixture(fixture: AGUIFixture): this;
19
+ onMessage(pattern: string | RegExp, text: string, opts?: {
20
+ delayMs?: number;
21
+ }): this;
22
+ onRun(pattern: string | RegExp, events: AGUIEvent[], delayMs?: number): this;
23
+ onToolCall(pattern: string | RegExp, toolName: string, args: string, opts?: {
24
+ result?: string;
25
+ delayMs?: number;
26
+ }): this;
27
+ onStateKey(key: string, snapshot: Record<string, unknown>, delayMs?: number): this;
28
+ onReasoning(pattern: string | RegExp, text: string, opts?: {
29
+ delayMs?: number;
30
+ }): this;
31
+ onPredicate(predicate: (input: AGUIRunAgentInput) => boolean, events: AGUIEvent[], delayMs?: number): this;
32
+ enableRecording(config: AGUIRecordConfig): this;
33
+ reset(): this;
34
+ handleRequest(req: http.IncomingMessage, res: http.ServerResponse, pathname: string): Promise<boolean>;
35
+ health(): {
36
+ status: string;
37
+ fixtures: number;
38
+ };
39
+ setJournal(journal: Journal): void;
40
+ setBaseUrl(url: string): void;
41
+ setRegistry(registry: MetricsRegistry): void;
42
+ start(): Promise<string>;
43
+ stop(): Promise<void>;
44
+ get url(): string;
45
+ private journalRequest;
46
+ }
47
+ //# sourceMappingURL=agui-mock.d.ts.map
48
+ //#endregion
49
+ export { AGUIMock };
50
+ //# sourceMappingURL=agui-mock.d.cts.map
@@ -0,0 +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;OAWc,CAAA,OAAA,EAAA,MAAA,GA3DA,MA2DA,EAAA,MAAA,EA3DgB,SA2DhB,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;YAcZ,CAAA,OAAA,EAAA,MAAA,GA/DQ,MA+DR,EAAA,QAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IACL,MAAK,CAAA,EAAA,MAAA;IAET,OAAA,CAAA,EAAA,MAAA;MA2DiB,IAAA;YAQE,CAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EArHY,MAqHZ,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;aAMP,CAAA,OAAA,EAAA,MAAA,GAjHe,MAiHf,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAwCD,OAAA,CAAA,EAAA,MAAA;MA7NiB,IAAA;EAAS,WAAA,CAAA,SAAA,EAAA,CAAA,KAAA,EA+EnB,iBA/EmB,EAAA,GAAA,OAAA,EAAA,MAAA,EAgF9B,SAhF8B,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;0BA2FhB;;qBAcjB,IAAA,CAAK,sBACL,IAAA,CAAK,mCAET;;;;;sBA2DiB;;wBAQE;WAMP;UAwCD"}
@@ -0,0 +1,50 @@
1
+ import { Journal } from "./journal.js";
2
+ import { MetricsRegistry } from "./metrics.js";
3
+ import { Mountable } from "./types.js";
4
+ import { AGUIEvent, AGUIFixture, AGUIMockOptions, AGUIRecordConfig, AGUIRunAgentInput } from "./agui-types.js";
5
+ import * as http from "node:http";
6
+
7
+ //#region src/agui-mock.d.ts
8
+ declare class AGUIMock implements Mountable {
9
+ private fixtures;
10
+ private server;
11
+ private journal;
12
+ private registry;
13
+ private options;
14
+ private baseUrl;
15
+ private recordConfig;
16
+ private logger;
17
+ constructor(options?: AGUIMockOptions);
18
+ addFixture(fixture: AGUIFixture): this;
19
+ onMessage(pattern: string | RegExp, text: string, opts?: {
20
+ delayMs?: number;
21
+ }): this;
22
+ onRun(pattern: string | RegExp, events: AGUIEvent[], delayMs?: number): this;
23
+ onToolCall(pattern: string | RegExp, toolName: string, args: string, opts?: {
24
+ result?: string;
25
+ delayMs?: number;
26
+ }): this;
27
+ onStateKey(key: string, snapshot: Record<string, unknown>, delayMs?: number): this;
28
+ onReasoning(pattern: string | RegExp, text: string, opts?: {
29
+ delayMs?: number;
30
+ }): this;
31
+ onPredicate(predicate: (input: AGUIRunAgentInput) => boolean, events: AGUIEvent[], delayMs?: number): this;
32
+ enableRecording(config: AGUIRecordConfig): this;
33
+ reset(): this;
34
+ handleRequest(req: http.IncomingMessage, res: http.ServerResponse, pathname: string): Promise<boolean>;
35
+ health(): {
36
+ status: string;
37
+ fixtures: number;
38
+ };
39
+ setJournal(journal: Journal): void;
40
+ setBaseUrl(url: string): void;
41
+ setRegistry(registry: MetricsRegistry): void;
42
+ start(): Promise<string>;
43
+ stop(): Promise<void>;
44
+ get url(): string;
45
+ private journalRequest;
46
+ }
47
+ //# sourceMappingURL=agui-mock.d.ts.map
48
+ //#endregion
49
+ export { AGUIMock };
50
+ //# sourceMappingURL=agui-mock.d.ts.map
@@ -0,0 +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;OAWc,CAAA,OAAA,EAAA,MAAA,GA3DA,MA2DA,EAAA,MAAA,EA3DgB,SA2DhB,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;YAcZ,CAAA,OAAA,EAAA,MAAA,GA/DQ,MA+DR,EAAA,QAAA,EAAA,MAAA,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IACL,MAAK,CAAA,EAAA,MAAA;IAET,OAAA,CAAA,EAAA,MAAA;MA2DiB,IAAA;YAQE,CAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EArHY,MAqHZ,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;aAMP,CAAA,OAAA,EAAA,MAAA,GAjHe,MAiHf,EAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAAA;IAwCD,OAAA,CAAA,EAAA,MAAA;MA7NiB,IAAA;EAAS,WAAA,CAAA,SAAA,EAAA,CAAA,KAAA,EA+EnB,iBA/EmB,EAAA,GAAA,OAAA,EAAA,MAAA,EAgF9B,SAhF8B,EAAA,EAAA,OAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;0BA2FhB;;qBAcjB,IAAA,CAAK,sBACL,IAAA,CAAK,mCAET;;;;;sBA2DiB;;wBAQE;WAMP;UAwCD"}