@iloveagents/foundry-agent 0.3.0 → 0.4.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.
- package/README.md +16 -0
- package/dist/client/agui-runner.d.ts +53 -0
- package/dist/client/agui-runner.js +320 -0
- package/dist/client/runner-events.d.ts +54 -0
- package/dist/client/runner-events.js +1 -0
- package/dist/client/service-fetch.d.ts +112 -0
- package/dist/client/service-fetch.js +244 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +10 -0
- package/dist/msal/auth-config.d.ts +91 -0
- package/dist/msal/auth-config.js +70 -0
- package/dist/msal/auth-store.d.ts +95 -0
- package/dist/msal/auth-store.js +372 -0
- package/dist/msal/index.d.ts +3 -0
- package/dist/msal/index.js +3 -0
- package/dist/msal/token-fetch.d.ts +16 -0
- package/dist/msal/token-fetch.js +57 -0
- package/dist/store/citation-store.d.ts +42 -0
- package/dist/store/citation-store.js +14 -0
- package/dist/store/link-store.d.ts +29 -0
- package/dist/store/link-store.js +28 -0
- package/dist/store/streaming-status-store.d.ts +15 -0
- package/dist/store/streaming-status-store.js +9 -0
- package/dist/tools/registry.d.ts +48 -0
- package/dist/tools/registry.js +50 -0
- package/package.json +23 -9
- package/AGENTS.md +0 -91
- package/CHANGELOG.md +0 -180
- package/CLAUDE.md +0 -1
- package/src/__tests__/agui-runner.test.ts +0 -404
- package/src/__tests__/auth-store.test.ts +0 -596
- package/src/__tests__/citation-store.test.ts +0 -52
- package/src/__tests__/client-tool-registry.test.ts +0 -84
- package/src/__tests__/link-store.test.ts +0 -48
- package/src/__tests__/service-fetch.test.ts +0 -525
- package/src/__tests__/streaming-status-store.test.ts +0 -22
- package/src/__tests__/token-fetch.test.ts +0 -134
- package/src/client/agui-runner.ts +0 -382
- package/src/client/runner-events.ts +0 -27
- package/src/client/service-fetch.ts +0 -318
- package/src/index.ts +0 -27
- package/src/msal/auth-config.ts +0 -150
- package/src/msal/auth-store.ts +0 -517
- package/src/msal/index.ts +0 -14
- package/src/msal/token-fetch.ts +0 -68
- package/src/store/citation-store.ts +0 -52
- package/src/store/link-store.ts +0 -53
- package/src/store/streaming-status-store.ts +0 -21
- package/src/tools/registry.ts +0 -112
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -8
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { AgentSubscriber } from "@ag-ui/client";
|
|
3
|
-
|
|
4
|
-
// Scripted event playback for the mocked HttpAgent. Each test sets `script`
|
|
5
|
-
// to an array of (subscriber) => Promise<void> functions. The mock's
|
|
6
|
-
// runAgent fires them in order, synchronously yielding the loop between
|
|
7
|
-
// each one so the runner's queue can drain.
|
|
8
|
-
|
|
9
|
-
type ScriptedRun = (subscriber: AgentSubscriber, agent: { messages: unknown[] }) => Promise<void>;
|
|
10
|
-
|
|
11
|
-
let nextScript: ScriptedRun[] = [];
|
|
12
|
-
let runCount = 0;
|
|
13
|
-
let lastMessages: unknown[] = [];
|
|
14
|
-
|
|
15
|
-
vi.mock("@ag-ui/client", () => {
|
|
16
|
-
class MockHttpAgent {
|
|
17
|
-
threadId: string;
|
|
18
|
-
state: Record<string, unknown> = {};
|
|
19
|
-
messages: unknown[] = [];
|
|
20
|
-
|
|
21
|
-
constructor(opts: { url?: string; threadId?: string }) {
|
|
22
|
-
this.threadId = opts.threadId ?? "thread-1";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
setMessages(messages: unknown[]) {
|
|
26
|
-
this.messages = [...messages];
|
|
27
|
-
lastMessages = this.messages;
|
|
28
|
-
}
|
|
29
|
-
setState(state: Record<string, unknown>) {
|
|
30
|
-
this.state = state;
|
|
31
|
-
}
|
|
32
|
-
addMessage(message: unknown) {
|
|
33
|
-
this.messages.push(message);
|
|
34
|
-
lastMessages = this.messages;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async runAgent(_params: unknown, subscriber: AgentSubscriber): Promise<void> {
|
|
38
|
-
const turnIdx = runCount;
|
|
39
|
-
runCount++;
|
|
40
|
-
const script = nextScript[turnIdx];
|
|
41
|
-
if (script) await script(subscriber, this);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
abortRun() {
|
|
45
|
-
// no-op for tests
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return { HttpAgent: MockHttpAgent };
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
import { AGUIRunner } from "../client/agui-runner.ts";
|
|
52
|
-
import { clientToolRegistry } from "../tools/registry.ts";
|
|
53
|
-
import type { RunnerEvent } from "../client/runner-events.ts";
|
|
54
|
-
|
|
55
|
-
function makeRegistry(client: {
|
|
56
|
-
isRegistered: (name: string) => boolean;
|
|
57
|
-
executeTool?: (name: string, args: string) => Promise<string>;
|
|
58
|
-
}) {
|
|
59
|
-
return {
|
|
60
|
-
isRegistered: client.isRegistered,
|
|
61
|
-
executeTool: client.executeTool ?? (async () => JSON.stringify({})),
|
|
62
|
-
getActiveSchemas: () => [],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function collect(
|
|
67
|
-
runner: AGUIRunner,
|
|
68
|
-
registry: ReturnType<typeof makeRegistry>,
|
|
69
|
-
): Promise<RunnerEvent[]> {
|
|
70
|
-
const out: RunnerEvent[] = [];
|
|
71
|
-
for await (const evt of runner.run({ messages: [], registry })) {
|
|
72
|
-
out.push(evt);
|
|
73
|
-
}
|
|
74
|
-
return out;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function collectWithMessages(
|
|
78
|
-
runner: AGUIRunner,
|
|
79
|
-
messages: Parameters<AGUIRunner["run"]>[0]["messages"],
|
|
80
|
-
registry: ReturnType<typeof makeRegistry>,
|
|
81
|
-
): Promise<RunnerEvent[]> {
|
|
82
|
-
const out: RunnerEvent[] = [];
|
|
83
|
-
for await (const evt of runner.run({ messages, registry })) {
|
|
84
|
-
out.push(evt);
|
|
85
|
-
}
|
|
86
|
-
return out;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
describe("AGUIRunner", () => {
|
|
90
|
-
beforeEach(() => {
|
|
91
|
-
nextScript = [];
|
|
92
|
-
runCount = 0;
|
|
93
|
-
lastMessages = [];
|
|
94
|
-
clientToolRegistry.setState({ globalTools: new Map(), pageTools: new Map() });
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("emits the canonical event sequence for a text-only run", async () => {
|
|
98
|
-
nextScript = [
|
|
99
|
-
async (s) => {
|
|
100
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
101
|
-
await s.onTextMessageStartEvent?.({ event: { type: "TEXT_MESSAGE_START" } } as never);
|
|
102
|
-
await s.onTextMessageContentEvent?.({
|
|
103
|
-
event: { type: "TEXT_MESSAGE_CONTENT", delta: "Hello" } as never,
|
|
104
|
-
} as never);
|
|
105
|
-
await s.onTextMessageContentEvent?.({
|
|
106
|
-
event: { type: "TEXT_MESSAGE_CONTENT", delta: " world" } as never,
|
|
107
|
-
} as never);
|
|
108
|
-
await s.onTextMessageEndEvent?.({ event: { type: "TEXT_MESSAGE_END" } } as never);
|
|
109
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
110
|
-
},
|
|
111
|
-
];
|
|
112
|
-
|
|
113
|
-
const runner = new AGUIRunner();
|
|
114
|
-
const events = await collect(runner, makeRegistry({ isRegistered: () => false }));
|
|
115
|
-
|
|
116
|
-
const types = events.map((e) => e.type);
|
|
117
|
-
expect(types).toContain("turn-started");
|
|
118
|
-
expect(types).toContain("request-sent");
|
|
119
|
-
expect(types).toContain("run-started");
|
|
120
|
-
expect(types).toContain("text-delta");
|
|
121
|
-
expect(types).toContain("text-message-end");
|
|
122
|
-
expect(types).toContain("run-finished");
|
|
123
|
-
|
|
124
|
-
const deltas = events.filter(
|
|
125
|
-
(e): e is Extract<RunnerEvent, { type: "text-delta" }> => e.type === "text-delta",
|
|
126
|
-
);
|
|
127
|
-
expect(deltas.map((d) => d.delta).join("")).toBe("Hello world");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("emits tool-call events with isClientSide flag", async () => {
|
|
131
|
-
nextScript = [
|
|
132
|
-
async (s) => {
|
|
133
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
134
|
-
await s.onToolCallStartEvent?.({
|
|
135
|
-
event: {
|
|
136
|
-
type: "TOOL_CALL_START",
|
|
137
|
-
toolCallId: "tc-1",
|
|
138
|
-
toolCallName: "server_tool",
|
|
139
|
-
} as never,
|
|
140
|
-
} as never);
|
|
141
|
-
await s.onToolCallArgsEvent?.({
|
|
142
|
-
event: { type: "TOOL_CALL_ARGS", toolCallId: "tc-1", delta: '{"q":"x"}' } as never,
|
|
143
|
-
} as never);
|
|
144
|
-
await s.onToolCallEndEvent?.({
|
|
145
|
-
event: { type: "TOOL_CALL_END", toolCallId: "tc-1" } as never,
|
|
146
|
-
} as never);
|
|
147
|
-
await s.onToolCallResultEvent?.({
|
|
148
|
-
event: {
|
|
149
|
-
type: "TOOL_CALL_RESULT",
|
|
150
|
-
toolCallId: "tc-1",
|
|
151
|
-
content: '{"ok":true}',
|
|
152
|
-
} as never,
|
|
153
|
-
} as never);
|
|
154
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
155
|
-
},
|
|
156
|
-
];
|
|
157
|
-
|
|
158
|
-
const runner = new AGUIRunner();
|
|
159
|
-
const events = await collect(runner, makeRegistry({ isRegistered: () => false }));
|
|
160
|
-
|
|
161
|
-
const start = events.find(
|
|
162
|
-
(e): e is Extract<RunnerEvent, { type: "tool-call-start" }> => e.type === "tool-call-start",
|
|
163
|
-
);
|
|
164
|
-
expect(start).toBeDefined();
|
|
165
|
-
expect(start!.isClientSide).toBe(false);
|
|
166
|
-
expect(start!.name).toBe("server_tool");
|
|
167
|
-
|
|
168
|
-
const result = events.find(
|
|
169
|
-
(e): e is Extract<RunnerEvent, { type: "tool-call-result" }> => e.type === "tool-call-result",
|
|
170
|
-
);
|
|
171
|
-
expect(result).toBeDefined();
|
|
172
|
-
expect(result!.result).toEqual({ ok: true });
|
|
173
|
-
expect(result!.isError).toBe(false);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("intercepts client-side tools and re-issues the run with the result", async () => {
|
|
177
|
-
const executeTool = vi.fn().mockResolvedValue('{"navigated":true}');
|
|
178
|
-
|
|
179
|
-
nextScript = [
|
|
180
|
-
async (s) => {
|
|
181
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
182
|
-
await s.onToolCallStartEvent?.({
|
|
183
|
-
event: {
|
|
184
|
-
type: "TOOL_CALL_START",
|
|
185
|
-
toolCallId: "tc-1",
|
|
186
|
-
toolCallName: "ui_navigate",
|
|
187
|
-
} as never,
|
|
188
|
-
} as never);
|
|
189
|
-
await s.onToolCallArgsEvent?.({
|
|
190
|
-
event: {
|
|
191
|
-
type: "TOOL_CALL_ARGS",
|
|
192
|
-
toolCallId: "tc-1",
|
|
193
|
-
delta: '{"path":"/foo"}',
|
|
194
|
-
} as never,
|
|
195
|
-
} as never);
|
|
196
|
-
await s.onToolCallEndEvent?.({
|
|
197
|
-
event: { type: "TOOL_CALL_END", toolCallId: "tc-1" } as never,
|
|
198
|
-
} as never);
|
|
199
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
200
|
-
},
|
|
201
|
-
async (s) => {
|
|
202
|
-
// Second turn after follow-up: agent acknowledges with text + finishes.
|
|
203
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
204
|
-
await s.onTextMessageStartEvent?.({ event: { type: "TEXT_MESSAGE_START" } } as never);
|
|
205
|
-
await s.onTextMessageContentEvent?.({
|
|
206
|
-
event: { type: "TEXT_MESSAGE_CONTENT", delta: "Done" } as never,
|
|
207
|
-
} as never);
|
|
208
|
-
await s.onTextMessageEndEvent?.({ event: { type: "TEXT_MESSAGE_END" } } as never);
|
|
209
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
210
|
-
},
|
|
211
|
-
];
|
|
212
|
-
|
|
213
|
-
const runner = new AGUIRunner();
|
|
214
|
-
const events = await collect(
|
|
215
|
-
runner,
|
|
216
|
-
makeRegistry({
|
|
217
|
-
isRegistered: (name) => name === "ui_navigate",
|
|
218
|
-
executeTool,
|
|
219
|
-
}),
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
expect(executeTool).toHaveBeenCalledWith("ui_navigate", '{"path":"/foo"}');
|
|
223
|
-
expect(runCount).toBe(2); // Initial + follow-up
|
|
224
|
-
|
|
225
|
-
// Second turn was issued with the same toolCallId in the assistant message
|
|
226
|
-
const lastAssistantMsg = lastMessages.find(
|
|
227
|
-
(m): m is { role: string; toolCalls?: Array<{ id: string }> } =>
|
|
228
|
-
typeof m === "object" && m !== null && (m as { role?: string }).role === "assistant",
|
|
229
|
-
);
|
|
230
|
-
expect(lastAssistantMsg?.toolCalls?.[0]?.id).toBe("tc-1");
|
|
231
|
-
|
|
232
|
-
// tool-call-start should have isClientSide=true
|
|
233
|
-
const start = events.find(
|
|
234
|
-
(e): e is Extract<RunnerEvent, { type: "tool-call-start" }> => e.type === "tool-call-start",
|
|
235
|
-
);
|
|
236
|
-
expect(start!.isClientSide).toBe(true);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it("preserves pre-tool assistant text in the follow-up replay", async () => {
|
|
240
|
-
// Regression: if the model emits text before the tool call in the
|
|
241
|
-
// same turn ("Looking up..." then ui_navigate), the follow-up
|
|
242
|
-
// assistant message must include that text. Otherwise the next-turn
|
|
243
|
-
// context drifts (no server-side thread state — full history rides
|
|
244
|
-
// each request).
|
|
245
|
-
const executeTool = vi.fn().mockResolvedValue('{"ok":true}');
|
|
246
|
-
|
|
247
|
-
nextScript = [
|
|
248
|
-
async (s) => {
|
|
249
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
250
|
-
await s.onTextMessageStartEvent?.({ event: { type: "TEXT_MESSAGE_START" } } as never);
|
|
251
|
-
await s.onTextMessageContentEvent?.({
|
|
252
|
-
event: { type: "TEXT_MESSAGE_CONTENT", delta: "Navigating..." } as never,
|
|
253
|
-
} as never);
|
|
254
|
-
await s.onTextMessageEndEvent?.({ event: { type: "TEXT_MESSAGE_END" } } as never);
|
|
255
|
-
await s.onToolCallStartEvent?.({
|
|
256
|
-
event: {
|
|
257
|
-
type: "TOOL_CALL_START",
|
|
258
|
-
toolCallId: "tc-pre",
|
|
259
|
-
toolCallName: "ui_navigate",
|
|
260
|
-
} as never,
|
|
261
|
-
} as never);
|
|
262
|
-
await s.onToolCallArgsEvent?.({
|
|
263
|
-
event: {
|
|
264
|
-
type: "TOOL_CALL_ARGS",
|
|
265
|
-
toolCallId: "tc-pre",
|
|
266
|
-
delta: '{"path":"/x"}',
|
|
267
|
-
} as never,
|
|
268
|
-
} as never);
|
|
269
|
-
await s.onToolCallEndEvent?.({
|
|
270
|
-
event: { type: "TOOL_CALL_END", toolCallId: "tc-pre" } as never,
|
|
271
|
-
} as never);
|
|
272
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
273
|
-
},
|
|
274
|
-
async (s) => {
|
|
275
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
276
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
277
|
-
},
|
|
278
|
-
];
|
|
279
|
-
|
|
280
|
-
const runner = new AGUIRunner();
|
|
281
|
-
await collect(
|
|
282
|
-
runner,
|
|
283
|
-
makeRegistry({
|
|
284
|
-
isRegistered: (name) => name === "ui_navigate",
|
|
285
|
-
executeTool,
|
|
286
|
-
}),
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
expect(runCount).toBe(2);
|
|
290
|
-
const lastAssistant = lastMessages.find(
|
|
291
|
-
(m): m is { role: string; content: string; toolCalls?: Array<{ id: string }> } =>
|
|
292
|
-
typeof m === "object" && m !== null && (m as { role?: string }).role === "assistant",
|
|
293
|
-
);
|
|
294
|
-
expect(lastAssistant?.content).toBe("Navigating...");
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it("preserves hidden protocol messages from AG-UI snapshots across visible UI turns", async () => {
|
|
298
|
-
const registry = makeRegistry({ isRegistered: () => false });
|
|
299
|
-
const runner = new AGUIRunner();
|
|
300
|
-
|
|
301
|
-
nextScript = [
|
|
302
|
-
async (s, agent) => {
|
|
303
|
-
agent.messages = [
|
|
304
|
-
{
|
|
305
|
-
id: "spaces-global-guidance:abc",
|
|
306
|
-
role: "system",
|
|
307
|
-
content: "Tenant guidance",
|
|
308
|
-
},
|
|
309
|
-
{ id: "u-1", role: "user", content: "What is your name?" },
|
|
310
|
-
{ id: "a-1", role: "assistant", content: "Magic Luna." },
|
|
311
|
-
];
|
|
312
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
313
|
-
},
|
|
314
|
-
];
|
|
315
|
-
|
|
316
|
-
await collectWithMessages(
|
|
317
|
-
runner,
|
|
318
|
-
[{ id: "u-1", role: "user", content: "What is your name?" }],
|
|
319
|
-
registry,
|
|
320
|
-
);
|
|
321
|
-
|
|
322
|
-
nextScript = [
|
|
323
|
-
async (s) => {
|
|
324
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
325
|
-
},
|
|
326
|
-
];
|
|
327
|
-
|
|
328
|
-
const events = await collectWithMessages(
|
|
329
|
-
runner,
|
|
330
|
-
[
|
|
331
|
-
{ id: "u-1", role: "user", content: "What is your name?" },
|
|
332
|
-
{ id: "a-1", role: "assistant", content: "Magic Luna." },
|
|
333
|
-
{ id: "u-2", role: "user", content: "Say your name again." },
|
|
334
|
-
],
|
|
335
|
-
registry,
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
const request = events.find(
|
|
339
|
-
(event): event is Extract<RunnerEvent, { type: "request-sent" }> =>
|
|
340
|
-
event.type === "request-sent",
|
|
341
|
-
);
|
|
342
|
-
expect(request?.input.messages).toEqual([
|
|
343
|
-
{
|
|
344
|
-
id: "spaces-global-guidance:abc",
|
|
345
|
-
role: "system",
|
|
346
|
-
content: "Tenant guidance",
|
|
347
|
-
},
|
|
348
|
-
{ id: "u-1", role: "user", content: "What is your name?" },
|
|
349
|
-
{ id: "a-1", role: "assistant", content: "Magic Luna." },
|
|
350
|
-
{ id: "u-2", role: "user", content: "Say your name again." },
|
|
351
|
-
]);
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it("yields turn-started exactly once per run iteration", async () => {
|
|
355
|
-
nextScript = [
|
|
356
|
-
async (s) => {
|
|
357
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
358
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
359
|
-
},
|
|
360
|
-
];
|
|
361
|
-
|
|
362
|
-
const runner = new AGUIRunner();
|
|
363
|
-
const events = await collect(runner, makeRegistry({ isRegistered: () => false }));
|
|
364
|
-
|
|
365
|
-
const turnStarts = events.filter((e) => e.type === "turn-started");
|
|
366
|
-
expect(turnStarts.length).toBe(1);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it("emits request-sent before any subscriber events", async () => {
|
|
370
|
-
nextScript = [
|
|
371
|
-
async (s) => {
|
|
372
|
-
await s.onRunFinishedEvent?.({ event: { type: "RUN_FINISHED" } } as never);
|
|
373
|
-
},
|
|
374
|
-
];
|
|
375
|
-
|
|
376
|
-
const runner = new AGUIRunner();
|
|
377
|
-
const events = await collect(runner, makeRegistry({ isRegistered: () => false }));
|
|
378
|
-
|
|
379
|
-
const requestIdx = events.findIndex((e) => e.type === "request-sent");
|
|
380
|
-
const finishedIdx = events.findIndex((e) => e.type === "run-finished");
|
|
381
|
-
expect(requestIdx).toBeGreaterThanOrEqual(0);
|
|
382
|
-
expect(requestIdx).toBeLessThan(finishedIdx);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it("translates RUN_ERROR into run-error with the message", async () => {
|
|
386
|
-
nextScript = [
|
|
387
|
-
async (s) => {
|
|
388
|
-
await s.onRunStartedEvent?.({ event: { type: "RUN_STARTED" } } as never);
|
|
389
|
-
await s.onRunErrorEvent?.({
|
|
390
|
-
event: { type: "RUN_ERROR", message: "boom" } as never,
|
|
391
|
-
} as never);
|
|
392
|
-
},
|
|
393
|
-
];
|
|
394
|
-
|
|
395
|
-
const runner = new AGUIRunner();
|
|
396
|
-
const events = await collect(runner, makeRegistry({ isRegistered: () => false }));
|
|
397
|
-
|
|
398
|
-
const err = events.find(
|
|
399
|
-
(e): e is Extract<RunnerEvent, { type: "run-error" }> => e.type === "run-error",
|
|
400
|
-
);
|
|
401
|
-
expect(err).toBeDefined();
|
|
402
|
-
expect(err!.message).toBe("boom");
|
|
403
|
-
});
|
|
404
|
-
});
|