@alexkroman1/aai 1.4.5 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +19 -0
- package/dist/{_internal-types-3p3OJZPb.js → _internal-types-DFL07G3f.js} +2 -0
- package/dist/assemblyai-C969QGi4.js +35 -0
- package/dist/cartesia-BfQPOQ7Y.js +37 -0
- package/dist/host/_pipeline-test-fakes.d.ts +3 -1
- package/dist/host/providers/stt/deepgram.d.ts +28 -0
- package/dist/host/providers/tts/cartesia.d.ts +1 -1
- package/dist/host/providers/tts/rime.d.ts +44 -0
- package/dist/host/runtime-barrel.d.ts +4 -2
- package/dist/host/runtime-barrel.js +1434 -1209
- package/dist/host/runtime.d.ts +2 -2
- package/dist/host/s2s.d.ts +16 -16
- package/dist/host/session-core.d.ts +37 -0
- package/dist/host/transports/pipeline-transport.d.ts +48 -0
- package/dist/host/transports/s2s-transport.d.ts +19 -0
- package/dist/host/transports/types.d.ts +45 -0
- package/dist/host/ws-handler.d.ts +14 -10
- package/dist/sdk/_internal-types.d.ts +2 -0
- package/dist/sdk/manifest-barrel.js +1 -1
- package/dist/sdk/protocol.d.ts +6 -5
- package/dist/sdk/providers/llm-barrel.js +1 -1
- package/dist/sdk/providers/stt/deepgram.d.ts +35 -0
- package/dist/sdk/providers/stt-barrel.d.ts +1 -0
- package/dist/sdk/providers/stt-barrel.js +2 -2
- package/dist/sdk/providers/tts/cartesia.d.ts +12 -4
- package/dist/sdk/providers/tts/rime.d.ts +42 -0
- package/dist/sdk/providers/tts-barrel.d.ts +1 -0
- package/dist/sdk/providers/tts-barrel.js +2 -2
- package/host/_pipeline-test-fakes.ts +6 -3
- package/host/_test-utils.ts +209 -128
- package/host/builtin-tools.ts +1 -0
- package/host/cleanup.test.ts +25 -298
- package/host/integration/pipeline-reference.integration.test.ts +30 -35
- package/host/providers/resolve.ts +10 -2
- package/host/providers/stt/deepgram.test.ts +229 -0
- package/host/providers/stt/deepgram.ts +172 -0
- package/host/providers/tts/cartesia.ts +7 -3
- package/host/providers/tts/rime.test.ts +251 -0
- package/host/providers/tts/rime.ts +322 -0
- package/host/runtime-barrel.ts +4 -2
- package/host/runtime.test.ts +16 -47
- package/host/runtime.ts +131 -23
- package/host/s2s.test.ts +122 -131
- package/host/s2s.ts +44 -52
- package/host/session-core.test.ts +257 -0
- package/host/session-core.ts +262 -0
- package/host/to-vercel-tools.test.ts +9 -1
- package/host/transports/pipeline-transport.test.ts +653 -0
- package/host/transports/pipeline-transport.ts +532 -0
- package/host/{fixture-replay.test.ts → transports/s2s-transport-fixtures.test.ts} +76 -106
- package/host/transports/s2s-transport.test.ts +56 -0
- package/host/transports/s2s-transport.ts +116 -0
- package/host/transports/types.test.ts +22 -0
- package/host/transports/types.ts +51 -0
- package/host/ws-handler.test.ts +324 -242
- package/host/ws-handler.ts +56 -59
- package/package.json +2 -1
- package/sdk/__snapshots__/exports.test.ts.snap +3 -3
- package/sdk/__snapshots__/schema-shapes.test.ts.snap +1 -0
- package/sdk/_internal-types.ts +3 -0
- package/sdk/protocol-compat.test.ts +8 -0
- package/sdk/protocol.ts +6 -5
- package/sdk/providers/stt/deepgram.ts +43 -0
- package/sdk/providers/stt-barrel.ts +2 -0
- package/sdk/providers/tts/cartesia.ts +15 -5
- package/sdk/providers/tts/rime.ts +52 -0
- package/sdk/providers/tts-barrel.ts +2 -0
- package/sdk/schema-alignment.test.ts +18 -6
- package/dist/assemblyai-Cxg9eobY.js +0 -18
- package/dist/cartesia-DwDk2tEu.js +0 -10
- package/dist/host/pipeline-session-ctx.d.ts +0 -24
- package/dist/host/pipeline-session.d.ts +0 -52
- package/dist/host/session-ctx.d.ts +0 -73
- package/dist/host/session.d.ts +0 -62
- package/host/pipeline-session-ctx.test.ts +0 -31
- package/host/pipeline-session-ctx.ts +0 -36
- package/host/pipeline-session.test.ts +0 -672
- package/host/pipeline-session.ts +0 -533
- package/host/s2s-fixtures.test.ts +0 -237
- package/host/session-ctx.test.ts +0 -387
- package/host/session-ctx.ts +0 -134
- package/host/session-fixture-replay.test.ts +0 -128
- package/host/session.test.ts +0 -634
- package/host/session.ts +0 -412
- /package/dist/{anthropic-BrUCPKUc.js → anthropic-CcLZygAr.js} +0 -0
package/host/_test-utils.ts
CHANGED
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
|
-
import { createNanoEvents } from "nanoevents";
|
|
6
5
|
import { vi } from "vitest";
|
|
7
6
|
import type { AgentConfig } from "../sdk/_internal-types.ts";
|
|
8
7
|
import type { ClientSink } from "../sdk/protocol.ts";
|
|
9
8
|
import type { AgentDef, ToolContext, ToolDef } from "../sdk/types.ts";
|
|
10
9
|
import { DEFAULT_SYSTEM_PROMPT } from "../sdk/types.ts";
|
|
11
10
|
import { createRuntime } from "./runtime.ts";
|
|
12
|
-
import type {
|
|
13
|
-
import type {
|
|
14
|
-
import { _internals
|
|
11
|
+
import type { ConnectS2sOptions, S2sCallbacks, S2sHandle } from "./s2s.ts";
|
|
12
|
+
import type { SessionCore } from "./session-core.ts";
|
|
13
|
+
import { _internals as s2sTransportInternals } from "./transports/s2s-transport.ts";
|
|
15
14
|
|
|
16
15
|
/** Yield to the microtask queue so pending promises settle. */
|
|
17
16
|
export function flush(): Promise<void> {
|
|
@@ -54,9 +53,12 @@ export function makeConfig(overrides: Partial<AgentConfig> = {}): AgentConfig {
|
|
|
54
53
|
};
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
// ─── SessionCore mock ───────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/** Create a SessionCore-shaped mock with all methods as vi.fn() spies. */
|
|
59
|
+
export function makeMockCore(overrides?: Partial<SessionCore>): SessionCore {
|
|
59
60
|
return {
|
|
61
|
+
id: "test",
|
|
60
62
|
start: vi.fn(() => Promise.resolve()),
|
|
61
63
|
stop: vi.fn(() => Promise.resolve()),
|
|
62
64
|
onAudio: vi.fn(),
|
|
@@ -64,59 +66,47 @@ export function makeStubSession(overrides?: Partial<Session>): Session {
|
|
|
64
66
|
onCancel: vi.fn(),
|
|
65
67
|
onReset: vi.fn(),
|
|
66
68
|
onHistory: vi.fn(),
|
|
67
|
-
|
|
69
|
+
onReplyStarted: vi.fn(),
|
|
70
|
+
onReplyDone: vi.fn(),
|
|
71
|
+
onCancelled: vi.fn(),
|
|
72
|
+
onAudioChunk: vi.fn(),
|
|
73
|
+
onAudioDone: vi.fn(),
|
|
74
|
+
onUserTranscript: vi.fn(),
|
|
75
|
+
onAgentTranscript: vi.fn(),
|
|
76
|
+
onToolCall: vi.fn(),
|
|
77
|
+
onError: vi.fn(),
|
|
78
|
+
onSpeechStarted: vi.fn(),
|
|
79
|
+
onSpeechStopped: vi.fn(),
|
|
68
80
|
...overrides,
|
|
69
81
|
};
|
|
70
82
|
}
|
|
71
83
|
|
|
72
|
-
// ───
|
|
73
|
-
|
|
74
|
-
export type MockS2sHandle = S2sHandle & {
|
|
75
|
-
_fire: <K extends keyof S2sEvents>(type: K, ...args: Parameters<S2sEvents[K]>) => void;
|
|
76
|
-
};
|
|
84
|
+
// ─── S2sHandle mock ─────────────────────────────────────────────────────────
|
|
77
85
|
|
|
78
|
-
/** Create a mock S2sHandle backed by
|
|
79
|
-
export function makeMockHandle():
|
|
80
|
-
const emitter = createNanoEvents<S2sEvents>();
|
|
86
|
+
/** Create a mock S2sHandle backed by vi.fn() spies. */
|
|
87
|
+
export function makeMockHandle(): S2sHandle {
|
|
81
88
|
return {
|
|
82
|
-
on: emitter.on.bind(emitter),
|
|
83
89
|
sendAudio: vi.fn(),
|
|
84
90
|
sendAudioRaw: vi.fn(),
|
|
85
91
|
sendToolResult: vi.fn(),
|
|
86
92
|
updateSession: vi.fn(),
|
|
87
93
|
resumeSession: vi.fn(),
|
|
88
94
|
close: vi.fn(),
|
|
89
|
-
_fire<K extends keyof S2sEvents>(type: K, ...args: Parameters<S2sEvents[K]>) {
|
|
90
|
-
emitter.emit(type, ...args);
|
|
91
|
-
},
|
|
92
95
|
};
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const events: unknown[] = [];
|
|
102
|
-
const audioChunks: Uint8Array[] = [];
|
|
103
|
-
let audioDoneCount = 0;
|
|
98
|
+
/**
|
|
99
|
+
* Minimal ClientSink stub that satisfies the 3-method interface.
|
|
100
|
+
* All methods are vi.fn() spies. Use in tests that need a valid ClientSink
|
|
101
|
+
* but don't need to inspect event payloads (e.g. routing / creation tests).
|
|
102
|
+
*/
|
|
103
|
+
export function makeClientSink(overrides?: Partial<ClientSink>): ClientSink {
|
|
104
104
|
return {
|
|
105
105
|
open: true,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
event: vi.fn((e: unknown) => {
|
|
112
|
-
events.push(e);
|
|
113
|
-
}),
|
|
114
|
-
playAudioChunk: vi.fn((chunk: Uint8Array) => {
|
|
115
|
-
audioChunks.push(chunk);
|
|
116
|
-
}),
|
|
117
|
-
playAudioDone: vi.fn(() => {
|
|
118
|
-
audioDoneCount++;
|
|
119
|
-
}),
|
|
106
|
+
event: vi.fn(),
|
|
107
|
+
playAudioChunk: vi.fn(),
|
|
108
|
+
playAudioDone: vi.fn(),
|
|
109
|
+
...overrides,
|
|
120
110
|
};
|
|
121
111
|
}
|
|
122
112
|
|
|
@@ -132,28 +122,13 @@ export const silentLogger: {
|
|
|
132
122
|
debug: vi.fn(),
|
|
133
123
|
};
|
|
134
124
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
systemPrompt: DEFAULT_SYSTEM_PROMPT,
|
|
143
|
-
greeting: "Hello!",
|
|
144
|
-
},
|
|
145
|
-
toolSchemas: [],
|
|
146
|
-
apiKey: "test-key",
|
|
147
|
-
s2sConfig: {
|
|
148
|
-
wssUrl: "wss://fake",
|
|
149
|
-
inputSampleRate: 16_000,
|
|
150
|
-
outputSampleRate: 24_000,
|
|
151
|
-
},
|
|
152
|
-
executeTool: vi.fn(async () => "tool-result"),
|
|
153
|
-
createWebSocket: vi.fn(),
|
|
154
|
-
logger: silentLogger,
|
|
155
|
-
...overrides,
|
|
156
|
-
};
|
|
125
|
+
/**
|
|
126
|
+
* Fresh logger with per-call `vi.fn()` spies. Use for tests that assert on
|
|
127
|
+
* log output — {@link silentLogger} is a shared singleton and accumulates
|
|
128
|
+
* call history across tests.
|
|
129
|
+
*/
|
|
130
|
+
export function makeLogger() {
|
|
131
|
+
return { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() };
|
|
157
132
|
}
|
|
158
133
|
|
|
159
134
|
// ─── Fixture replay helpers ──────────────────────────────────────────────────
|
|
@@ -165,90 +140,179 @@ export function loadFixture<T = Record<string, unknown>[]>(name: string): T {
|
|
|
165
140
|
return JSON.parse(readFileSync(resolve(FIXTURE_DIR, name), "utf-8"));
|
|
166
141
|
}
|
|
167
142
|
|
|
143
|
+
// ─── Fixture session helpers (transport-layer spy) ──────────────────────────
|
|
144
|
+
|
|
168
145
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
146
|
+
* A tracking ClientSink that records all calls into typed arrays for easy
|
|
147
|
+
* test assertions. Compatible with makeClientSink() but with inspection APIs.
|
|
148
|
+
* Uses the 3-method sink interface — event() dispatches are tracked by type.
|
|
172
149
|
*/
|
|
173
|
-
type
|
|
150
|
+
export type TrackingClientSink = ClientSink & {
|
|
151
|
+
agentTranscripts: string[];
|
|
152
|
+
userTranscripts: string[];
|
|
153
|
+
toolCallEvents: { callId: string; name: string; args: unknown }[];
|
|
154
|
+
audioChunks: Uint8Array[];
|
|
155
|
+
replyDoneCount: number;
|
|
156
|
+
cancelledCount: number;
|
|
157
|
+
speechStartedCount: number;
|
|
158
|
+
speechStoppedCount: number;
|
|
159
|
+
events: import("../sdk/protocol.ts").ClientEvent[];
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export function makeTrackingClient(): TrackingClientSink {
|
|
163
|
+
const agentTranscripts: string[] = [];
|
|
164
|
+
const userTranscripts: string[] = [];
|
|
165
|
+
const toolCallEvents: { callId: string; name: string; args: unknown }[] = [];
|
|
166
|
+
const audioChunks: Uint8Array[] = [];
|
|
167
|
+
const events: import("../sdk/protocol.ts").ClientEvent[] = [];
|
|
168
|
+
let replyDoneCount = 0;
|
|
169
|
+
let cancelledCount = 0;
|
|
170
|
+
let speechStartedCount = 0;
|
|
171
|
+
let speechStoppedCount = 0;
|
|
174
172
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
173
|
+
return {
|
|
174
|
+
open: true,
|
|
175
|
+
agentTranscripts,
|
|
176
|
+
userTranscripts,
|
|
177
|
+
toolCallEvents,
|
|
178
|
+
audioChunks,
|
|
179
|
+
events,
|
|
180
|
+
get replyDoneCount() {
|
|
181
|
+
return replyDoneCount;
|
|
182
|
+
},
|
|
183
|
+
get cancelledCount() {
|
|
184
|
+
return cancelledCount;
|
|
185
|
+
},
|
|
186
|
+
get speechStartedCount() {
|
|
187
|
+
return speechStartedCount;
|
|
188
|
+
},
|
|
189
|
+
get speechStoppedCount() {
|
|
190
|
+
return speechStoppedCount;
|
|
191
|
+
},
|
|
192
|
+
event: vi.fn((e: import("../sdk/protocol.ts").ClientEvent) => {
|
|
193
|
+
events.push(e);
|
|
194
|
+
switch (e.type) {
|
|
195
|
+
case "agent_transcript":
|
|
196
|
+
agentTranscripts.push(e.text);
|
|
197
|
+
break;
|
|
198
|
+
case "user_transcript":
|
|
199
|
+
userTranscripts.push(e.text);
|
|
200
|
+
break;
|
|
201
|
+
case "tool_call":
|
|
202
|
+
toolCallEvents.push({ callId: e.toolCallId, name: e.toolName, args: e.args });
|
|
203
|
+
break;
|
|
204
|
+
case "reply_done":
|
|
205
|
+
replyDoneCount++;
|
|
206
|
+
break;
|
|
207
|
+
case "cancelled":
|
|
208
|
+
cancelledCount++;
|
|
209
|
+
break;
|
|
210
|
+
case "speech_started":
|
|
211
|
+
speechStartedCount++;
|
|
212
|
+
break;
|
|
213
|
+
case "speech_stopped":
|
|
214
|
+
speechStoppedCount++;
|
|
215
|
+
break;
|
|
216
|
+
default:
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
196
219
|
}),
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
type: "tool_call",
|
|
200
|
-
toolCallId: m.call_id as string,
|
|
201
|
-
toolName: m.name as string,
|
|
202
|
-
args: (m.args as Record<string, unknown>) ?? {},
|
|
220
|
+
playAudioChunk: vi.fn((chunk: Uint8Array) => {
|
|
221
|
+
audioChunks.push(chunk);
|
|
203
222
|
}),
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
},
|
|
208
|
-
};
|
|
223
|
+
playAudioDone: vi.fn(),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
209
226
|
|
|
210
227
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* Converts raw wire-format JSON (from fixtures/) into typed `_fire()` calls.
|
|
214
|
-
* This is the inverse of `dispatchS2sMessage` in s2s.ts — it translates
|
|
215
|
-
* snake_case API fields to camelCase event payloads.
|
|
216
|
-
*
|
|
217
|
-
* Messages that don't map to an event (audio, `reply.content_part.*`) are skipped.
|
|
228
|
+
* Translate a single fixture wire-format message directly into S2sCallbacks calls.
|
|
229
|
+
* This is the callback-based equivalent of the old FIXTURE_DISPATCH / replayFixtureMessages.
|
|
218
230
|
*/
|
|
219
|
-
export function
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
export function fireFixtureMessage(callbacks: S2sCallbacks, msg: Record<string, unknown>): void {
|
|
232
|
+
switch (msg.type) {
|
|
233
|
+
case "session.ready":
|
|
234
|
+
callbacks.onSessionReady(msg.session_id as string);
|
|
235
|
+
break;
|
|
236
|
+
case "session.updated":
|
|
237
|
+
break; // no callback
|
|
238
|
+
case "reply.started":
|
|
239
|
+
callbacks.onReplyStarted(msg.reply_id as string);
|
|
240
|
+
break;
|
|
241
|
+
case "reply.done":
|
|
242
|
+
if (msg.status === "interrupted") callbacks.onCancelled();
|
|
243
|
+
else callbacks.onReplyDone();
|
|
244
|
+
break;
|
|
245
|
+
case "transcript.user":
|
|
246
|
+
callbacks.onUserTranscript(msg.text as string);
|
|
247
|
+
break;
|
|
248
|
+
case "transcript.agent":
|
|
249
|
+
callbacks.onAgentTranscript(msg.text as string, Boolean(msg.interrupted));
|
|
250
|
+
break;
|
|
251
|
+
case "tool.call":
|
|
252
|
+
callbacks.onToolCall(
|
|
253
|
+
msg.call_id as string,
|
|
254
|
+
msg.name as string,
|
|
255
|
+
(msg.args ?? {}) as Record<string, unknown>,
|
|
256
|
+
);
|
|
257
|
+
break;
|
|
258
|
+
case "input.speech.started":
|
|
259
|
+
callbacks.onSpeechStarted();
|
|
260
|
+
break;
|
|
261
|
+
case "input.speech.stopped":
|
|
262
|
+
callbacks.onSpeechStopped();
|
|
263
|
+
break;
|
|
264
|
+
case "session.error": {
|
|
265
|
+
const code = msg.code as string;
|
|
266
|
+
if (code === "session_not_found" || code === "session_forbidden")
|
|
267
|
+
callbacks.onSessionExpired();
|
|
268
|
+
else callbacks.onError(new Error((msg.message ?? "session error") as string));
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case "error":
|
|
272
|
+
callbacks.onError(new Error((msg.message ?? "error") as string));
|
|
273
|
+
break;
|
|
274
|
+
case "reply.audio":
|
|
275
|
+
break; // skip — audio tested separately
|
|
276
|
+
default:
|
|
277
|
+
break;
|
|
225
278
|
}
|
|
226
279
|
}
|
|
227
280
|
|
|
228
|
-
// ─── Real-executor fixture replay ────────────────────────────────────────────
|
|
229
|
-
|
|
230
281
|
/**
|
|
231
282
|
* Create a real Runtime-backed session for fixture replay testing.
|
|
232
283
|
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
284
|
+
* Spies on s2s-transport.ts `_internals.connectS2s` (the transport-layer seam
|
|
285
|
+
* added in Task 15) so that captured S2sCallbacks can be fired directly —
|
|
286
|
+
* no nanoevents, no old S2sEvents system.
|
|
236
287
|
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
* turnPromise chaining).
|
|
288
|
+
* Call `await ctx.start()` first to trigger the spy, then `ctx.replay(name)`
|
|
289
|
+
* or fire `ctx.mockCallbacks.on*` directly.
|
|
240
290
|
*
|
|
241
|
-
* Call `cleanup()`
|
|
291
|
+
* Call `cleanup()` in afterEach to restore the spy.
|
|
242
292
|
*/
|
|
243
293
|
export function createFixtureSession(
|
|
244
294
|
// biome-ignore lint/suspicious/noExplicitAny: test helper accepts any agent state type
|
|
245
295
|
agent: AgentDef<any>,
|
|
246
296
|
opts?: { env?: Record<string, string> },
|
|
247
297
|
) {
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
298
|
+
let capturedCallbacks: S2sCallbacks | null = null;
|
|
299
|
+
const fakeHandle: S2sHandle = {
|
|
300
|
+
sendAudio: vi.fn(),
|
|
301
|
+
sendAudioRaw: vi.fn(),
|
|
302
|
+
sendToolResult: vi.fn(),
|
|
303
|
+
updateSession: vi.fn(),
|
|
304
|
+
resumeSession: vi.fn(),
|
|
305
|
+
close: vi.fn(),
|
|
306
|
+
};
|
|
251
307
|
|
|
308
|
+
const connectSpy = vi
|
|
309
|
+
.spyOn(s2sTransportInternals, "connectS2s")
|
|
310
|
+
.mockImplementation(async (connectOpts: ConnectS2sOptions) => {
|
|
311
|
+
capturedCallbacks = connectOpts.callbacks;
|
|
312
|
+
return fakeHandle;
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const client = makeTrackingClient();
|
|
252
316
|
const executor = createRuntime({
|
|
253
317
|
agent,
|
|
254
318
|
env: opts?.env ?? {},
|
|
@@ -261,14 +325,31 @@ export function createFixtureSession(
|
|
|
261
325
|
client,
|
|
262
326
|
});
|
|
263
327
|
|
|
328
|
+
function getCallbacks(): S2sCallbacks {
|
|
329
|
+
if (!capturedCallbacks) throw new Error("must call start() before accessing callbacks");
|
|
330
|
+
return capturedCallbacks;
|
|
331
|
+
}
|
|
332
|
+
|
|
264
333
|
return {
|
|
265
334
|
session,
|
|
266
335
|
client,
|
|
267
|
-
|
|
336
|
+
fakeHandle,
|
|
268
337
|
executor,
|
|
269
|
-
/**
|
|
338
|
+
/** Trigger transport.start() — fires the connectS2s spy and captures callbacks. */
|
|
339
|
+
async start() {
|
|
340
|
+
await session.start();
|
|
341
|
+
if (!capturedCallbacks) throw new Error("connectS2s was never called during start()");
|
|
342
|
+
},
|
|
343
|
+
/** Direct access to the captured S2sCallbacks for manual event firing. */
|
|
344
|
+
get mockCallbacks(): S2sCallbacks {
|
|
345
|
+
return getCallbacks();
|
|
346
|
+
},
|
|
347
|
+
/** Replay a fixture file by translating each message to S2sCallbacks calls. */
|
|
270
348
|
replay(fixtureName: string) {
|
|
271
|
-
|
|
349
|
+
const cbs = getCallbacks();
|
|
350
|
+
for (const msg of loadFixture(fixtureName)) {
|
|
351
|
+
fireFixtureMessage(cbs, msg as Record<string, unknown>);
|
|
352
|
+
}
|
|
272
353
|
},
|
|
273
354
|
/** Restore the connectS2s spy. Call in afterEach. */
|
|
274
355
|
cleanup() {
|
package/host/builtin-tools.ts
CHANGED
|
@@ -242,6 +242,7 @@ export function resolveAllBuiltins(
|
|
|
242
242
|
for (const [toolName, def] of resolveBuiltin(name, opts)) {
|
|
243
243
|
defs[toolName] = def;
|
|
244
244
|
schemas.push({
|
|
245
|
+
type: "function",
|
|
245
246
|
name: toolName,
|
|
246
247
|
description: def.description,
|
|
247
248
|
parameters: z.toJSONSchema(def.parameters ?? EMPTY_PARAMS) as ToolSchema["parameters"],
|