@alexkroman1/aai 0.12.3 → 1.0.2
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/.turbo/turbo-build.log +20 -0
- package/CHANGELOG.md +174 -0
- package/dist/constants-VTFoymJ-.js +47 -0
- package/dist/host/_run-code.d.ts +1 -1
- package/dist/host/_runtime-conformance.d.ts +4 -5
- package/dist/host/builtin-tools.d.ts +11 -9
- package/dist/host/runtime-barrel.d.ts +15 -0
- package/dist/{direct-executor-DRRrZUp0.js → host/runtime-barrel.js} +453 -348
- package/dist/host/runtime-config.d.ts +42 -0
- package/dist/host/runtime.d.ts +119 -35
- package/dist/host/s2s.d.ts +14 -38
- package/dist/host/server.d.ts +16 -8
- package/dist/host/session-ctx.d.ts +55 -0
- package/dist/host/session.d.ts +20 -70
- package/dist/host/tool-executor.d.ts +20 -0
- package/dist/host/unstorage-kv.d.ts +1 -1
- package/dist/host/ws-handler.d.ts +4 -2
- package/dist/index.d.ts +9 -20
- package/dist/index.js +63 -2
- package/dist/{isolate → sdk}/_internal-types.d.ts +5 -9
- package/dist/{isolate → sdk}/constants.d.ts +6 -4
- package/dist/sdk/define.d.ts +66 -0
- package/dist/{isolate → sdk}/kv.d.ts +1 -49
- package/dist/sdk/manifest-barrel.d.ts +8 -0
- package/dist/sdk/manifest-barrel.js +52 -0
- package/dist/sdk/manifest.d.ts +50 -0
- package/dist/{isolate → sdk}/protocol.d.ts +59 -36
- package/dist/sdk/protocol.js +163 -0
- package/dist/{isolate → sdk}/system-prompt.d.ts +2 -2
- package/dist/sdk/types.d.ts +201 -0
- package/dist/sdk/ws-upgrade.d.ts +5 -0
- package/dist/{system-prompt-DYAYFW99.js → system-prompt-nik_iavo.js} +10 -10
- package/dist/types-Cfx_4QDK.js +39 -0
- package/dist/ws-upgrade-BeOQ7fXL.js +30 -0
- package/exports-no-dev-deps.test.ts +62 -0
- package/host/_mock-ws.ts +185 -0
- package/host/_run-code.ts +217 -0
- package/host/_runtime-conformance.ts +143 -0
- package/host/_test-utils.ts +276 -0
- package/host/builtin-tools.test.ts +774 -0
- package/host/builtin-tools.ts +255 -0
- package/host/cleanup.test.ts +422 -0
- package/host/fixture-replay.test.ts +463 -0
- package/host/fixtures/README.md +40 -0
- package/host/fixtures/greeting-session-sequence.json +40 -0
- package/host/fixtures/reply-audio-samples.json +42 -0
- package/host/fixtures/reply-lifecycle.json +21 -0
- package/host/fixtures/session-ready.json +48 -0
- package/host/fixtures/session-updated.json +45 -0
- package/host/fixtures/simple-question-sequence.json +73 -0
- package/host/fixtures/tool-call-sequence.json +114 -0
- package/host/fixtures/tool-calls.json +11 -0
- package/host/fixtures/tool-config-session-sequence.json +51 -0
- package/host/fixtures/user-speech-recognition.json +30 -0
- package/host/fixtures/web-search-sequence.json +122 -0
- package/host/integration.test.ts +222 -0
- package/host/runtime-barrel.ts +25 -0
- package/host/runtime-config.test.ts +71 -0
- package/host/runtime-config.ts +99 -0
- package/host/runtime.test.ts +641 -0
- package/host/runtime.ts +308 -0
- package/host/s2s-fixtures.test.ts +237 -0
- package/host/s2s.test.ts +562 -0
- package/host/s2s.ts +310 -0
- package/host/server-shutdown.test.ts +76 -0
- package/host/server.test.ts +116 -0
- package/host/server.ts +223 -0
- package/host/session-ctx.ts +107 -0
- package/host/session-fixture-replay.test.ts +136 -0
- package/host/session-prompt.test.ts +77 -0
- package/host/session.test.ts +590 -0
- package/host/session.ts +370 -0
- package/host/tool-executor.test.ts +124 -0
- package/host/tool-executor.ts +80 -0
- package/host/unstorage-kv.test.ts +99 -0
- package/host/unstorage-kv.ts +69 -0
- package/host/ws-handler.test.ts +739 -0
- package/host/ws-handler.ts +255 -0
- package/index.ts +16 -0
- package/package.json +24 -72
- package/sdk/_internal-types.test.ts +34 -0
- package/sdk/_internal-types.ts +115 -0
- package/sdk/compat-fixtures/README.md +26 -0
- package/sdk/compat-fixtures/v1.json +68 -0
- package/sdk/constants.ts +77 -0
- package/sdk/define.test.ts +57 -0
- package/sdk/define.ts +88 -0
- package/sdk/kv.ts +60 -0
- package/sdk/manifest-barrel.ts +12 -0
- package/sdk/manifest.test.ts +56 -0
- package/sdk/manifest.ts +89 -0
- package/sdk/protocol-compat.test.ts +187 -0
- package/sdk/protocol-snapshot.test.ts +199 -0
- package/sdk/protocol.test.ts +170 -0
- package/sdk/protocol.ts +223 -0
- package/sdk/schema-alignment.test.ts +191 -0
- package/sdk/system-prompt.test.ts +111 -0
- package/sdk/system-prompt.ts +74 -0
- package/sdk/tsconfig.json +12 -0
- package/sdk/types-inference.test.ts +122 -0
- package/sdk/types.test.ts +14 -0
- package/sdk/types.ts +226 -0
- package/sdk/utils.test.ts +52 -0
- package/sdk/utils.ts +20 -0
- package/sdk/ws-upgrade.test.ts +48 -0
- package/sdk/ws-upgrade.ts +13 -0
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +10 -0
- package/tsdown.config.ts +26 -0
- package/vitest.config.ts +17 -0
- package/dist/host/_test-utils.d.ts +0 -73
- package/dist/host/direct-executor.d.ts +0 -130
- package/dist/host/index.d.ts +0 -19
- package/dist/host/index.js +0 -165
- package/dist/host/matchers.d.ts +0 -20
- package/dist/host/matchers.js +0 -41
- package/dist/host/server.js +0 -164
- package/dist/host/testing.d.ts +0 -294
- package/dist/host/testing.js +0 -2
- package/dist/host/vite-plugin.d.ts +0 -15
- package/dist/host/vite-plugin.js +0 -83
- package/dist/isolate/_kv-utils.d.ts +0 -10
- package/dist/isolate/_utils.js +0 -17
- package/dist/isolate/hooks.d.ts +0 -44
- package/dist/isolate/hooks.js +0 -58
- package/dist/isolate/index.d.ts +0 -18
- package/dist/isolate/index.js +0 -6
- package/dist/isolate/kv.js +0 -1
- package/dist/isolate/protocol.js +0 -2
- package/dist/isolate/types.d.ts +0 -418
- package/dist/isolate/types.js +0 -175
- package/dist/protocol-rcOrz7T3.js +0 -183
- package/dist/testing-BreLdpq-.js +0 -513
- package/dist/types.test-d.d.ts +0 -7
- /package/dist/{isolate/_utils.d.ts → sdk/utils.d.ts} +0 -0
package/dist/testing-BreLdpq-.js
DELETED
|
@@ -1,513 +0,0 @@
|
|
|
1
|
-
import "./isolate/types.js";
|
|
2
|
-
import { i as createUnstorageKv, t as createRuntime } from "./direct-executor-DRRrZUp0.js";
|
|
3
|
-
import { vi } from "vitest";
|
|
4
|
-
import { createStorage } from "unstorage";
|
|
5
|
-
import "nanoevents";
|
|
6
|
-
import { resolve } from "node:path";
|
|
7
|
-
//#region host/_mock-ws.ts
|
|
8
|
-
/**
|
|
9
|
-
* A mock WebSocket implementation for testing.
|
|
10
|
-
*
|
|
11
|
-
* Extends `EventTarget` to simulate WebSocket behavior without a real
|
|
12
|
-
* network connection. Records all sent messages in the {@link sent}
|
|
13
|
-
* array and provides helper methods to simulate incoming messages,
|
|
14
|
-
* connection events, and errors.
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* const ws = new MockWebSocket("wss://example.com");
|
|
19
|
-
* ws.send(JSON.stringify({ type: "ping" }));
|
|
20
|
-
* ws.simulateMessage(JSON.stringify({ type: "pong" }));
|
|
21
|
-
* assertEquals(ws.sentJson(), [{ type: "ping" }]);
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
var MockWebSocket = class MockWebSocket extends EventTarget {
|
|
25
|
-
static CONNECTING = 0;
|
|
26
|
-
static OPEN = 1;
|
|
27
|
-
static CLOSING = 2;
|
|
28
|
-
static CLOSED = 3;
|
|
29
|
-
readyState = MockWebSocket.CONNECTING;
|
|
30
|
-
binaryType = "arraybuffer";
|
|
31
|
-
/** All messages passed to {@link send}, in order. */
|
|
32
|
-
sent = [];
|
|
33
|
-
url;
|
|
34
|
-
/**
|
|
35
|
-
* Create a new MockWebSocket.
|
|
36
|
-
*
|
|
37
|
-
* Automatically transitions to `OPEN` state on the next microtask,
|
|
38
|
-
* dispatching an `"open"` event.
|
|
39
|
-
*
|
|
40
|
-
* @param url - The WebSocket URL.
|
|
41
|
-
* @param _protocols - Ignored; accepted for API compatibility.
|
|
42
|
-
*/
|
|
43
|
-
constructor(url, _protocols) {
|
|
44
|
-
super();
|
|
45
|
-
this.url = typeof url === "string" ? url : url.toString();
|
|
46
|
-
queueMicrotask(() => {
|
|
47
|
-
if (this.readyState === MockWebSocket.CONNECTING) {
|
|
48
|
-
this.readyState = MockWebSocket.OPEN;
|
|
49
|
-
this.dispatchEvent(new Event("open"));
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
addEventListener(type, listener) {
|
|
54
|
-
super.addEventListener(type, listener);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Record a sent message without transmitting it.
|
|
58
|
-
*
|
|
59
|
-
* @param data - The message data to record.
|
|
60
|
-
*/
|
|
61
|
-
send(data) {
|
|
62
|
-
this.sent.push(data);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Transition to `CLOSED` state and dispatch a `"close"` event.
|
|
66
|
-
*
|
|
67
|
-
* @param code - The close code (defaults to 1000).
|
|
68
|
-
* @param _reason - Ignored; accepted for API compatibility.
|
|
69
|
-
*/
|
|
70
|
-
close(code, _reason) {
|
|
71
|
-
this.readyState = MockWebSocket.CLOSED;
|
|
72
|
-
this.dispatchEvent(Object.assign(new Event("close"), { code: code ?? 1e3 }));
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Simulate receiving a message from the server.
|
|
76
|
-
*
|
|
77
|
-
* @param data - The message data (string or binary).
|
|
78
|
-
*/
|
|
79
|
-
simulateMessage(data) {
|
|
80
|
-
this.dispatchEvent(new MessageEvent("message", { data }));
|
|
81
|
-
}
|
|
82
|
-
/** Transition to `OPEN` state and dispatch an `"open"` event. */
|
|
83
|
-
open() {
|
|
84
|
-
this.readyState = MockWebSocket.OPEN;
|
|
85
|
-
this.dispatchEvent(new Event("open"));
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Shorthand for {@link simulateMessage}.
|
|
89
|
-
*
|
|
90
|
-
* @param data - The message data to dispatch.
|
|
91
|
-
*/
|
|
92
|
-
msg(data) {
|
|
93
|
-
this.simulateMessage(data);
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Simulate a connection close from the server.
|
|
97
|
-
*
|
|
98
|
-
* @param code - The close code (defaults to 1000).
|
|
99
|
-
*/
|
|
100
|
-
disconnect(code = 1e3) {
|
|
101
|
-
this.dispatchEvent(Object.assign(new Event("close"), { code }));
|
|
102
|
-
}
|
|
103
|
-
/** Dispatch an `"error"` event on this socket. */
|
|
104
|
-
error() {
|
|
105
|
-
this.dispatchEvent(new Event("error"));
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Return all sent string messages parsed as JSON objects.
|
|
109
|
-
*
|
|
110
|
-
* Binary messages are filtered out.
|
|
111
|
-
*
|
|
112
|
-
* @returns An array of parsed JSON objects from sent string messages.
|
|
113
|
-
*/
|
|
114
|
-
sentJson() {
|
|
115
|
-
return this.sent.filter((d) => typeof d === "string").map((s) => JSON.parse(s));
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
const g = globalThis;
|
|
119
|
-
/**
|
|
120
|
-
* Replace `globalThis.WebSocket` with {@link MockWebSocket} for testing.
|
|
121
|
-
*
|
|
122
|
-
* Returns a handle that tracks all created mock sockets and can restore the
|
|
123
|
-
* original `WebSocket` constructor. Supports the `using` declaration via
|
|
124
|
-
* `Symbol.dispose` for automatic cleanup.
|
|
125
|
-
*
|
|
126
|
-
* @returns An object with `created` array, `lastWs` getter, `restore()`, and `[Symbol.dispose]()`.
|
|
127
|
-
*
|
|
128
|
-
* @example
|
|
129
|
-
* ```ts
|
|
130
|
-
* using mock = installMockWebSocket();
|
|
131
|
-
* const session = new Session("wss://example.com");
|
|
132
|
-
* const ws = mock.lastWs!;
|
|
133
|
-
* ws.simulateMessage(JSON.stringify({ type: "ready" }));
|
|
134
|
-
* // mock automatically restores WebSocket when disposed
|
|
135
|
-
* ```
|
|
136
|
-
*/
|
|
137
|
-
function installMockWebSocket() {
|
|
138
|
-
const saved = globalThis.WebSocket;
|
|
139
|
-
const created = [];
|
|
140
|
-
g.WebSocket = class extends MockWebSocket {
|
|
141
|
-
constructor(url, protocols) {
|
|
142
|
-
super(url, protocols);
|
|
143
|
-
created.push(this);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
return {
|
|
147
|
-
created,
|
|
148
|
-
get lastWs() {
|
|
149
|
-
return created.at(-1) ?? null;
|
|
150
|
-
},
|
|
151
|
-
restore() {
|
|
152
|
-
globalThis.WebSocket = saved;
|
|
153
|
-
},
|
|
154
|
-
[Symbol.dispose]() {
|
|
155
|
-
this.restore();
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
//#endregion
|
|
160
|
-
//#region host/_test-utils.ts
|
|
161
|
-
/** Yield to the microtask queue so pending promises settle. */
|
|
162
|
-
function flush() {
|
|
163
|
-
return new Promise((r) => queueMicrotask(r));
|
|
164
|
-
}
|
|
165
|
-
/** Create a stub Session with all methods as vi.fn() spies. */
|
|
166
|
-
function makeStubSession(overrides) {
|
|
167
|
-
return {
|
|
168
|
-
start: vi.fn(() => Promise.resolve()),
|
|
169
|
-
stop: vi.fn(() => Promise.resolve()),
|
|
170
|
-
onAudio: vi.fn(),
|
|
171
|
-
onAudioReady: vi.fn(),
|
|
172
|
-
onCancel: vi.fn(),
|
|
173
|
-
onReset: vi.fn(),
|
|
174
|
-
onHistory: vi.fn(),
|
|
175
|
-
waitForTurn: vi.fn(() => Promise.resolve()),
|
|
176
|
-
...overrides
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
vi.fn(), vi.fn(), vi.fn(), vi.fn();
|
|
180
|
-
resolve(import.meta.dirname, "fixtures");
|
|
181
|
-
//#endregion
|
|
182
|
-
//#region host/testing.ts
|
|
183
|
-
/**
|
|
184
|
-
* Testing utilities for AAI agents.
|
|
185
|
-
*
|
|
186
|
-
* Provides a test harness for unit-testing agents without audio, network,
|
|
187
|
-
* or an LLM. Use {@link createTestHarness} to create a harness from a
|
|
188
|
-
* `defineAgent()` result, then drive tool calls and multi-turn conversations.
|
|
189
|
-
*
|
|
190
|
-
* @example
|
|
191
|
-
* ```ts
|
|
192
|
-
* import { describe, expect, test } from "vitest";
|
|
193
|
-
* import { createTestHarness } from "@alexkroman1/aai/testing";
|
|
194
|
-
* import agent from "./agent.ts";
|
|
195
|
-
*
|
|
196
|
-
* describe("my agent", () => {
|
|
197
|
-
* test("greet tool returns greeting", async () => {
|
|
198
|
-
* const t = createTestHarness(agent);
|
|
199
|
-
* const result = await t.executeTool("greet", { name: "Alice" });
|
|
200
|
-
* expect(result).toBe("Hello, Alice!");
|
|
201
|
-
* });
|
|
202
|
-
*
|
|
203
|
-
* test("multi-turn conversation", async () => {
|
|
204
|
-
* const t = createTestHarness(agent);
|
|
205
|
-
* const turn1 = await t.turn("Add a pizza", [
|
|
206
|
-
* { tool: "add_pizza", args: { size: "large", crust: "regular", toppings: ["pepperoni"], quantity: 1 } },
|
|
207
|
-
* ]);
|
|
208
|
-
* expect(turn1).toHaveCalledTool("add_pizza");
|
|
209
|
-
*
|
|
210
|
-
* const turn2 = await t.turn("View my order", [
|
|
211
|
-
* { tool: "view_order", args: {} },
|
|
212
|
-
* ]);
|
|
213
|
-
* expect(turn2).toHaveCalledTool("view_order");
|
|
214
|
-
* });
|
|
215
|
-
* });
|
|
216
|
-
* ```
|
|
217
|
-
*
|
|
218
|
-
* @packageDocumentation
|
|
219
|
-
*/
|
|
220
|
-
/**
|
|
221
|
-
* Result of a simulated turn via {@link TestHarness.turn}.
|
|
222
|
-
*
|
|
223
|
-
* Contains all tool calls that were executed and provides assertion helpers
|
|
224
|
-
* for verifying agent behavior in tests.
|
|
225
|
-
*
|
|
226
|
-
* @example
|
|
227
|
-
* ```ts
|
|
228
|
-
* const result = await t.turn("search for flights", [
|
|
229
|
-
* { tool: "search_flights", args: { destination: "NYC" } },
|
|
230
|
-
* ]);
|
|
231
|
-
*
|
|
232
|
-
* // Check if a tool was called
|
|
233
|
-
* expect(result).toHaveCalledTool("search_flights");
|
|
234
|
-
*
|
|
235
|
-
* // Check tool was called with specific args
|
|
236
|
-
* expect(result).toHaveCalledTool("search_flights", { destination: "NYC" });
|
|
237
|
-
*
|
|
238
|
-
* // Access raw tool call data
|
|
239
|
-
* expect(result.toolCalls[0].result).toContain("JFK");
|
|
240
|
-
* ```
|
|
241
|
-
*
|
|
242
|
-
* @public
|
|
243
|
-
*/
|
|
244
|
-
var TurnResult = class {
|
|
245
|
-
/** The user text that initiated this turn. */
|
|
246
|
-
text;
|
|
247
|
-
/** All tool calls executed during this turn, in order. */
|
|
248
|
-
toolCalls;
|
|
249
|
-
/** Convenience accessor: just the result strings from each tool call. */
|
|
250
|
-
toolResults;
|
|
251
|
-
/** @internal */
|
|
252
|
-
constructor(text, toolCalls) {
|
|
253
|
-
this.text = text;
|
|
254
|
-
this.toolCalls = toolCalls;
|
|
255
|
-
this.toolResults = toolCalls.map((tc) => tc.result);
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Check whether a tool was called during this turn.
|
|
259
|
-
*
|
|
260
|
-
* When `args` is provided, checks that at least one call to the named tool
|
|
261
|
-
* contains all specified key-value pairs (partial match).
|
|
262
|
-
*
|
|
263
|
-
* @param toolName - The tool name to look for.
|
|
264
|
-
* @param args - Optional partial args to match against.
|
|
265
|
-
* @returns `true` if a matching tool call was found.
|
|
266
|
-
*
|
|
267
|
-
* @example
|
|
268
|
-
* ```ts
|
|
269
|
-
* result.toHaveCalledTool("add_pizza"); // any call
|
|
270
|
-
* result.toHaveCalledTool("add_pizza", { size: "large" }); // partial match
|
|
271
|
-
* ```
|
|
272
|
-
*/
|
|
273
|
-
toHaveCalledTool(toolName, args) {
|
|
274
|
-
return this.toolCalls.some((tc) => {
|
|
275
|
-
if (tc.toolName !== toolName) return false;
|
|
276
|
-
if (!args) return true;
|
|
277
|
-
return Object.entries(args).every(([key, value]) => JSON.stringify(tc.args[key]) === JSON.stringify(value));
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Get all calls to a specific tool during this turn.
|
|
282
|
-
*
|
|
283
|
-
* @param toolName - The tool name to filter by.
|
|
284
|
-
* @returns Array of matching tool calls (may be empty).
|
|
285
|
-
*/
|
|
286
|
-
getToolCalls(toolName) {
|
|
287
|
-
return this.toolCalls.filter((tc) => tc.toolName === toolName);
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Get the parsed JSON result of the first call to a specific tool.
|
|
291
|
-
*
|
|
292
|
-
* Throws if the tool was not called during this turn.
|
|
293
|
-
*
|
|
294
|
-
* @typeParam T - The expected shape of the parsed result.
|
|
295
|
-
* @param toolName - The tool name to look up.
|
|
296
|
-
* @returns The parsed result, cast to `T`.
|
|
297
|
-
*
|
|
298
|
-
* @example
|
|
299
|
-
* ```ts
|
|
300
|
-
* const order = turn.toolResult<{ pizzas: Pizza[]; total: string }>("view_order");
|
|
301
|
-
* expect(order.pizzas).toHaveLength(2);
|
|
302
|
-
* ```
|
|
303
|
-
*/
|
|
304
|
-
toolResult(toolName) {
|
|
305
|
-
const call = this.toolCalls.find((tc) => tc.toolName === toolName);
|
|
306
|
-
if (!call) throw new Error(`Tool "${toolName}" was not called during this turn`);
|
|
307
|
-
return JSON.parse(call.result);
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
/**
|
|
311
|
-
* Test harness for unit-testing AAI agents without audio, network, or LLM.
|
|
312
|
-
*
|
|
313
|
-
* Created via {@link createTestHarness}. Maintains conversation state across
|
|
314
|
-
* turns, executes tools against the real agent code, and records all tool
|
|
315
|
-
* calls for assertions.
|
|
316
|
-
*
|
|
317
|
-
* @example
|
|
318
|
-
* ```ts
|
|
319
|
-
* import { createTestHarness } from "@alexkroman1/aai/testing";
|
|
320
|
-
* import agent from "./agent.ts";
|
|
321
|
-
*
|
|
322
|
-
* const t = createTestHarness(agent);
|
|
323
|
-
*
|
|
324
|
-
* // Execute a single tool
|
|
325
|
-
* const result = await t.executeTool("greet", { name: "Alice" });
|
|
326
|
-
*
|
|
327
|
-
* // Simulate a full turn with tool calls
|
|
328
|
-
* const turn = await t.turn("hello", [
|
|
329
|
-
* { tool: "greet", args: { name: "Alice" } },
|
|
330
|
-
* ]);
|
|
331
|
-
* expect(turn).toHaveCalledTool("greet");
|
|
332
|
-
* ```
|
|
333
|
-
*
|
|
334
|
-
* @public
|
|
335
|
-
*/
|
|
336
|
-
var TestHarness = class {
|
|
337
|
-
/** @internal */
|
|
338
|
-
_executor;
|
|
339
|
-
/** @internal */
|
|
340
|
-
_sessionId;
|
|
341
|
-
_messages = [];
|
|
342
|
-
_onTurnCalls = [];
|
|
343
|
-
_connected = false;
|
|
344
|
-
/** @internal */
|
|
345
|
-
constructor(executor, sessionId) {
|
|
346
|
-
this._executor = executor;
|
|
347
|
-
this._sessionId = sessionId;
|
|
348
|
-
}
|
|
349
|
-
/** Conversation messages accumulated across turns. */
|
|
350
|
-
get messages() {
|
|
351
|
-
return this._messages;
|
|
352
|
-
}
|
|
353
|
-
/** All `onTurn` hook invocations (the text argument) recorded so far. */
|
|
354
|
-
get turns() {
|
|
355
|
-
return this._onTurnCalls;
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Fire the `onConnect` lifecycle hook.
|
|
359
|
-
*
|
|
360
|
-
* Called automatically on the first {@link turn} call if not called manually.
|
|
361
|
-
*/
|
|
362
|
-
async connect() {
|
|
363
|
-
if (this._connected) return;
|
|
364
|
-
this._connected = true;
|
|
365
|
-
await this._executor.hooks.callHook("connect", this._sessionId);
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Fire the `onDisconnect` lifecycle hook and clean up session state.
|
|
369
|
-
*/
|
|
370
|
-
async disconnect() {
|
|
371
|
-
if (!this._connected) return;
|
|
372
|
-
this._connected = false;
|
|
373
|
-
await this._executor.hooks.callHook("disconnect", this._sessionId);
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Execute a single tool by name with the given arguments.
|
|
377
|
-
*
|
|
378
|
-
* The tool runs with full agent context (env, state, kv, messages).
|
|
379
|
-
* The call is **not** recorded in conversation history — use {@link turn}
|
|
380
|
-
* for that.
|
|
381
|
-
*
|
|
382
|
-
* @param toolName - The tool to execute.
|
|
383
|
-
* @param args - Arguments to pass to the tool.
|
|
384
|
-
* @returns The tool's string result.
|
|
385
|
-
*
|
|
386
|
-
* @example
|
|
387
|
-
* ```ts
|
|
388
|
-
* const result = await t.executeTool("get_weather", { city: "London" });
|
|
389
|
-
* const data = JSON.parse(result);
|
|
390
|
-
* expect(data.temp).toBeDefined();
|
|
391
|
-
* ```
|
|
392
|
-
*/
|
|
393
|
-
async executeTool(toolName, args = {}) {
|
|
394
|
-
return this._executor.executeTool(toolName, args, this._sessionId, this._messages);
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* Simulate a user turn: add the user message, execute the given tool calls
|
|
398
|
-
* in sequence, and record everything.
|
|
399
|
-
*
|
|
400
|
-
* This is the primary method for testing agent behavior. It:
|
|
401
|
-
* 1. Fires `onConnect` if this is the first turn
|
|
402
|
-
* 2. Adds the user message to conversation history
|
|
403
|
-
* 3. Fires the `onTurn` hook
|
|
404
|
-
* 4. Executes each tool call in order
|
|
405
|
-
* 5. Returns a {@link TurnResult} with assertion helpers
|
|
406
|
-
*
|
|
407
|
-
* @param text - The user's spoken/typed input.
|
|
408
|
-
* @param toolCalls - Tool calls to execute (simulating what the LLM would invoke).
|
|
409
|
-
* @returns A {@link TurnResult} with recorded tool calls and assertion methods.
|
|
410
|
-
*
|
|
411
|
-
* @example
|
|
412
|
-
* ```ts
|
|
413
|
-
* const turn = await t.turn("Add pepperoni pizza", [
|
|
414
|
-
* { tool: "add_pizza", args: { size: "large", crust: "regular", toppings: ["pepperoni"], quantity: 1 } },
|
|
415
|
-
* ]);
|
|
416
|
-
* expect(turn).toHaveCalledTool("add_pizza", { size: "large" });
|
|
417
|
-
* expect(turn.toolCalls[0].result).toContain("$14.99");
|
|
418
|
-
* ```
|
|
419
|
-
*/
|
|
420
|
-
async turn(text, toolCalls = []) {
|
|
421
|
-
await this.connect();
|
|
422
|
-
this._messages.push({
|
|
423
|
-
role: "user",
|
|
424
|
-
content: text
|
|
425
|
-
});
|
|
426
|
-
this._onTurnCalls.push(text);
|
|
427
|
-
await this._executor.hooks.callHook("turn", this._sessionId, text);
|
|
428
|
-
const recorded = [];
|
|
429
|
-
for (const tc of toolCalls) {
|
|
430
|
-
const result = await this._executor.executeTool(tc.tool, tc.args, this._sessionId, this._messages);
|
|
431
|
-
recorded.push({
|
|
432
|
-
toolName: tc.tool,
|
|
433
|
-
args: tc.args,
|
|
434
|
-
result
|
|
435
|
-
});
|
|
436
|
-
this._messages.push({
|
|
437
|
-
role: "tool",
|
|
438
|
-
content: result
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
return new TurnResult(text, recorded);
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
|
-
* Add a user message to conversation history without executing tools.
|
|
445
|
-
*
|
|
446
|
-
* Useful for setting up conversation context before a turn.
|
|
447
|
-
*/
|
|
448
|
-
addUserMessage(text) {
|
|
449
|
-
this._messages.push({
|
|
450
|
-
role: "user",
|
|
451
|
-
content: text
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Add an assistant message to conversation history.
|
|
456
|
-
*
|
|
457
|
-
* Useful for simulating prior assistant responses in multi-turn tests.
|
|
458
|
-
*/
|
|
459
|
-
addAssistantMessage(text) {
|
|
460
|
-
this._messages.push({
|
|
461
|
-
role: "assistant",
|
|
462
|
-
content: text
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Reset conversation state: clears messages, step/turn history.
|
|
467
|
-
*
|
|
468
|
-
* Does **not** reset KV store — create a new harness for that.
|
|
469
|
-
*/
|
|
470
|
-
reset() {
|
|
471
|
-
this._messages = [];
|
|
472
|
-
this._onTurnCalls = [];
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
|
-
/**
|
|
476
|
-
* Create a test harness for unit-testing an agent.
|
|
477
|
-
*
|
|
478
|
-
* The harness wraps the agent's tool definitions and lifecycle hooks,
|
|
479
|
-
* providing a simple API for executing tools and simulating multi-turn
|
|
480
|
-
* conversations — all without audio, network, or an LLM.
|
|
481
|
-
*
|
|
482
|
-
* @param agent - The agent definition returned by `defineAgent()`.
|
|
483
|
-
* @param options - Optional environment and KV store overrides.
|
|
484
|
-
* @returns A {@link TestHarness} instance.
|
|
485
|
-
*
|
|
486
|
-
* @example
|
|
487
|
-
* ```ts
|
|
488
|
-
* import { createTestHarness } from "@alexkroman1/aai/testing";
|
|
489
|
-
* import agent from "./agent.ts";
|
|
490
|
-
*
|
|
491
|
-
* const t = createTestHarness(agent);
|
|
492
|
-
* const result = await t.executeTool("my_tool", { key: "value" });
|
|
493
|
-
* ```
|
|
494
|
-
*
|
|
495
|
-
* @example With environment variables
|
|
496
|
-
* ```ts
|
|
497
|
-
* const t = createTestHarness(agent, {
|
|
498
|
-
* env: { API_KEY: "test-key" },
|
|
499
|
-
* });
|
|
500
|
-
* ```
|
|
501
|
-
*
|
|
502
|
-
* @public
|
|
503
|
-
*/
|
|
504
|
-
function createTestHarness(agent, options = {}) {
|
|
505
|
-
const { env = {}, kv = createUnstorageKv({ storage: createStorage() }) } = options;
|
|
506
|
-
return new TestHarness(createRuntime({
|
|
507
|
-
agent,
|
|
508
|
-
env,
|
|
509
|
-
kv
|
|
510
|
-
}), `test-${Date.now()}`);
|
|
511
|
-
}
|
|
512
|
-
//#endregion
|
|
513
|
-
export { makeStubSession as a, flush as i, TurnResult as n, MockWebSocket as o, createTestHarness as r, installMockWebSocket as s, TestHarness as t };
|
package/dist/types.test-d.d.ts
DELETED
|
File without changes
|