@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.
Files changed (86) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +19 -0
  3. package/dist/{_internal-types-3p3OJZPb.js → _internal-types-DFL07G3f.js} +2 -0
  4. package/dist/assemblyai-C969QGi4.js +35 -0
  5. package/dist/cartesia-BfQPOQ7Y.js +37 -0
  6. package/dist/host/_pipeline-test-fakes.d.ts +3 -1
  7. package/dist/host/providers/stt/deepgram.d.ts +28 -0
  8. package/dist/host/providers/tts/cartesia.d.ts +1 -1
  9. package/dist/host/providers/tts/rime.d.ts +44 -0
  10. package/dist/host/runtime-barrel.d.ts +4 -2
  11. package/dist/host/runtime-barrel.js +1434 -1209
  12. package/dist/host/runtime.d.ts +2 -2
  13. package/dist/host/s2s.d.ts +16 -16
  14. package/dist/host/session-core.d.ts +37 -0
  15. package/dist/host/transports/pipeline-transport.d.ts +48 -0
  16. package/dist/host/transports/s2s-transport.d.ts +19 -0
  17. package/dist/host/transports/types.d.ts +45 -0
  18. package/dist/host/ws-handler.d.ts +14 -10
  19. package/dist/sdk/_internal-types.d.ts +2 -0
  20. package/dist/sdk/manifest-barrel.js +1 -1
  21. package/dist/sdk/protocol.d.ts +6 -5
  22. package/dist/sdk/providers/llm-barrel.js +1 -1
  23. package/dist/sdk/providers/stt/deepgram.d.ts +35 -0
  24. package/dist/sdk/providers/stt-barrel.d.ts +1 -0
  25. package/dist/sdk/providers/stt-barrel.js +2 -2
  26. package/dist/sdk/providers/tts/cartesia.d.ts +12 -4
  27. package/dist/sdk/providers/tts/rime.d.ts +42 -0
  28. package/dist/sdk/providers/tts-barrel.d.ts +1 -0
  29. package/dist/sdk/providers/tts-barrel.js +2 -2
  30. package/host/_pipeline-test-fakes.ts +6 -3
  31. package/host/_test-utils.ts +209 -128
  32. package/host/builtin-tools.ts +1 -0
  33. package/host/cleanup.test.ts +25 -298
  34. package/host/integration/pipeline-reference.integration.test.ts +30 -35
  35. package/host/providers/resolve.ts +10 -2
  36. package/host/providers/stt/deepgram.test.ts +229 -0
  37. package/host/providers/stt/deepgram.ts +172 -0
  38. package/host/providers/tts/cartesia.ts +7 -3
  39. package/host/providers/tts/rime.test.ts +251 -0
  40. package/host/providers/tts/rime.ts +322 -0
  41. package/host/runtime-barrel.ts +4 -2
  42. package/host/runtime.test.ts +16 -47
  43. package/host/runtime.ts +131 -23
  44. package/host/s2s.test.ts +122 -131
  45. package/host/s2s.ts +44 -52
  46. package/host/session-core.test.ts +257 -0
  47. package/host/session-core.ts +262 -0
  48. package/host/to-vercel-tools.test.ts +9 -1
  49. package/host/transports/pipeline-transport.test.ts +653 -0
  50. package/host/transports/pipeline-transport.ts +532 -0
  51. package/host/{fixture-replay.test.ts → transports/s2s-transport-fixtures.test.ts} +76 -106
  52. package/host/transports/s2s-transport.test.ts +56 -0
  53. package/host/transports/s2s-transport.ts +116 -0
  54. package/host/transports/types.test.ts +22 -0
  55. package/host/transports/types.ts +51 -0
  56. package/host/ws-handler.test.ts +324 -242
  57. package/host/ws-handler.ts +56 -59
  58. package/package.json +2 -1
  59. package/sdk/__snapshots__/exports.test.ts.snap +3 -3
  60. package/sdk/__snapshots__/schema-shapes.test.ts.snap +1 -0
  61. package/sdk/_internal-types.ts +3 -0
  62. package/sdk/protocol-compat.test.ts +8 -0
  63. package/sdk/protocol.ts +6 -5
  64. package/sdk/providers/stt/deepgram.ts +43 -0
  65. package/sdk/providers/stt-barrel.ts +2 -0
  66. package/sdk/providers/tts/cartesia.ts +15 -5
  67. package/sdk/providers/tts/rime.ts +52 -0
  68. package/sdk/providers/tts-barrel.ts +2 -0
  69. package/sdk/schema-alignment.test.ts +18 -6
  70. package/dist/assemblyai-Cxg9eobY.js +0 -18
  71. package/dist/cartesia-DwDk2tEu.js +0 -10
  72. package/dist/host/pipeline-session-ctx.d.ts +0 -24
  73. package/dist/host/pipeline-session.d.ts +0 -52
  74. package/dist/host/session-ctx.d.ts +0 -73
  75. package/dist/host/session.d.ts +0 -62
  76. package/host/pipeline-session-ctx.test.ts +0 -31
  77. package/host/pipeline-session-ctx.ts +0 -36
  78. package/host/pipeline-session.test.ts +0 -672
  79. package/host/pipeline-session.ts +0 -533
  80. package/host/s2s-fixtures.test.ts +0 -237
  81. package/host/session-ctx.test.ts +0 -387
  82. package/host/session-ctx.ts +0 -134
  83. package/host/session-fixture-replay.test.ts +0 -128
  84. package/host/session.test.ts +0 -634
  85. package/host/session.ts +0 -412
  86. /package/dist/{anthropic-BrUCPKUc.js → anthropic-CcLZygAr.js} +0 -0
@@ -7,18 +7,10 @@
7
7
  * error, and reset to prevent memory leaks in long-running processes.
8
8
  */
9
9
 
10
- import { afterEach, describe, expect, test, vi } from "vitest";
10
+ import { describe, expect, test, vi } from "vitest";
11
11
  import { MockWebSocket } from "./_mock-ws.ts";
12
- import {
13
- makeClient,
14
- makeMockHandle,
15
- makeSessionOpts,
16
- makeStubSession,
17
- silentLogger,
18
- } from "./_test-utils.ts";
19
- import type { S2sHandle } from "./s2s.ts";
20
- import type { Session } from "./session.ts";
21
- import { _internals, createS2sSession, type S2sSessionOptions } from "./session.ts";
12
+ import { makeMockCore, silentLogger } from "./_test-utils.ts";
13
+ import type { SessionCore } from "./session-core.ts";
22
14
  import { wireSessionSocket } from "./ws-handler.ts";
23
15
 
24
16
  const defaultConfig = { audioFormat: "pcm16" as const, sampleRate: 16_000, ttsSampleRate: 24_000 };
@@ -27,13 +19,13 @@ const defaultConfig = { audioFormat: "pcm16" as const, sampleRate: 16_000, ttsSa
27
19
 
28
20
  describe("wireSessionSocket resource cleanup", () => {
29
21
  test("session.stop() is called exactly once on normal close", async () => {
30
- const session = makeStubSession();
22
+ const core = makeMockCore();
31
23
  const ws = new MockWebSocket("ws://test");
32
24
  ws.readyState = MockWebSocket.OPEN;
33
25
 
34
26
  wireSessionSocket(ws, {
35
27
  sessions: new Map(),
36
- createSession: () => session,
28
+ createSession: () => core,
37
29
  readyConfig: defaultConfig,
38
30
  logger: silentLogger,
39
31
  });
@@ -41,21 +33,20 @@ describe("wireSessionSocket resource cleanup", () => {
41
33
  ws.close();
42
34
 
43
35
  await vi.waitFor(() => {
44
- expect(session.stop).toHaveBeenCalledOnce();
36
+ expect(core.stop).toHaveBeenCalledOnce();
45
37
  });
46
38
  });
47
39
 
48
40
  test("session is removed from sessions map even when stop() rejects", async () => {
49
- const sessions = new Map<string, Session>();
50
- const session = makeStubSession();
51
- session.stop = vi.fn(() => Promise.reject(new Error("stop failed")));
41
+ const sessions = new Map<string, SessionCore>();
42
+ const core = makeMockCore({ stop: vi.fn(() => Promise.reject(new Error("stop failed"))) });
52
43
 
53
44
  const ws = new MockWebSocket("ws://test");
54
45
  ws.readyState = MockWebSocket.OPEN;
55
46
 
56
47
  wireSessionSocket(ws, {
57
48
  sessions,
58
- createSession: () => session,
49
+ createSession: () => core,
59
50
  readyConfig: defaultConfig,
60
51
  logger: silentLogger,
61
52
  });
@@ -69,43 +60,42 @@ describe("wireSessionSocket resource cleanup", () => {
69
60
  });
70
61
 
71
62
  test("message buffer is cleared when start() fails", async () => {
72
- const session = makeStubSession();
73
- session.start = vi.fn(() => Promise.reject(new Error("start failed")));
74
- const sessions = new Map<string, Session>();
63
+ const core = makeMockCore({ start: vi.fn(() => Promise.reject(new Error("start failed"))) });
64
+ const sessions = new Map<string, SessionCore>();
75
65
 
76
66
  const ws = new MockWebSocket("ws://test");
77
67
  ws.readyState = MockWebSocket.OPEN;
78
68
 
79
69
  wireSessionSocket(ws, {
80
70
  sessions,
81
- createSession: () => session,
71
+ createSession: () => core,
82
72
  readyConfig: defaultConfig,
83
73
  logger: silentLogger,
84
74
  });
85
75
 
86
- // Send messages while start is failing
87
- ws.simulateMessage(JSON.stringify({ type: "audio_ready" }));
76
+ // Send a binary frame while start is failing (string frames are now dropped as non-binary)
77
+ ws.simulateMessage(new ArrayBuffer(4));
88
78
 
89
79
  await vi.waitFor(() => {
90
80
  expect(sessions.size).toBe(0);
91
81
  });
92
82
 
93
83
  // Session is null, further messages should be silently ignored (no throw)
94
- ws.simulateMessage(JSON.stringify({ type: "audio_ready" }));
95
84
  ws.simulateMessage(new ArrayBuffer(4));
96
85
  });
97
86
 
98
87
  test("multiple rapid closes don't double-invoke stop()", async () => {
99
- const session = makeStubSession();
100
- session.stop = vi.fn(() => new Promise<void>((r) => setTimeout(r, 50)));
101
- const sessions = new Map<string, Session>();
88
+ const core = makeMockCore({
89
+ stop: vi.fn(() => new Promise<void>((r) => setTimeout(r, 50))),
90
+ });
91
+ const sessions = new Map<string, SessionCore>();
102
92
 
103
93
  const ws = new MockWebSocket("ws://test");
104
94
  ws.readyState = MockWebSocket.OPEN;
105
95
 
106
96
  wireSessionSocket(ws, {
107
97
  sessions,
108
- createSession: () => session,
98
+ createSession: () => core,
109
99
  readyConfig: defaultConfig,
110
100
  logger: silentLogger,
111
101
  });
@@ -115,18 +105,18 @@ describe("wireSessionSocket resource cleanup", () => {
115
105
  // Even if close event fires again, stop should only be called once
116
106
  // because the session reference is captured on first close
117
107
  await vi.waitFor(() => {
118
- expect(session.stop).toHaveBeenCalledOnce();
108
+ expect(core.stop).toHaveBeenCalledOnce();
119
109
  });
120
110
  });
121
111
 
122
112
  test("close before open does not throw or leak", () => {
123
113
  const ws = new MockWebSocket("ws://test");
124
114
  ws.readyState = MockWebSocket.CONNECTING;
125
- const sessions = new Map<string, Session>();
115
+ const sessions = new Map<string, SessionCore>();
126
116
 
127
117
  wireSessionSocket(ws, {
128
118
  sessions,
129
- createSession: () => makeStubSession(),
119
+ createSession: () => makeMockCore(),
130
120
  readyConfig: defaultConfig,
131
121
  logger: silentLogger,
132
122
  });
@@ -137,286 +127,23 @@ describe("wireSessionSocket resource cleanup", () => {
137
127
  });
138
128
 
139
129
  test("error event after close does not throw", async () => {
140
- const session = makeStubSession();
130
+ const core = makeMockCore();
141
131
  const ws = new MockWebSocket("ws://test");
142
132
  ws.readyState = MockWebSocket.OPEN;
143
133
 
144
134
  wireSessionSocket(ws, {
145
135
  sessions: new Map(),
146
- createSession: () => session,
136
+ createSession: () => core,
147
137
  readyConfig: defaultConfig,
148
138
  logger: silentLogger,
149
139
  });
150
140
 
151
141
  ws.close();
152
142
  await vi.waitFor(() => {
153
- expect(session.stop).toHaveBeenCalled();
143
+ expect(core.stop).toHaveBeenCalled();
154
144
  });
155
145
 
156
146
  // Error after close should not throw
157
147
  ws.dispatchEvent(new Event("error"));
158
148
  });
159
149
  });
160
-
161
- // ─── createS2sSession cleanup tests ──────────────────────────────────────────
162
-
163
- describe("createS2sSession resource cleanup", () => {
164
- let connectSpy: ReturnType<typeof vi.spyOn>;
165
- let mockHandle: ReturnType<typeof makeMockHandle>;
166
-
167
- function setup(overrides?: Partial<S2sSessionOptions>) {
168
- mockHandle = makeMockHandle();
169
- connectSpy = vi.spyOn(_internals, "connectS2s").mockResolvedValue(mockHandle);
170
- const client = makeClient();
171
- const opts = makeSessionOpts({ client, ...overrides });
172
- const session = createS2sSession(opts);
173
- return { session, client, opts, mockHandle };
174
- }
175
-
176
- afterEach(() => {
177
- connectSpy?.mockRestore();
178
- });
179
-
180
- test("stop() closes S2S handle and waits for in-flight turn", async () => {
181
- let resolveToolCall!: (value: string) => void;
182
- const executeTool = vi.fn(
183
- () =>
184
- new Promise<string>((r) => {
185
- resolveToolCall = r;
186
- }),
187
- );
188
- const { session, mockHandle } = setup({ executeTool });
189
- await session.start();
190
-
191
- // Start a tool call
192
- mockHandle._fire("replyStarted", { replyId: "r1" });
193
- mockHandle._fire("event", { type: "tool_call", toolCallId: "c1", toolName: "t1", args: {} });
194
- await vi.waitFor(() => expect(executeTool).toHaveBeenCalled());
195
-
196
- // Stop while tool is in-flight
197
- const stopPromise = session.stop();
198
- resolveToolCall("done");
199
- await stopPromise;
200
-
201
- expect(mockHandle.close).toHaveBeenCalled();
202
- });
203
-
204
- test("onReset clears pendingTools and conversation messages", async () => {
205
- const executeTool = vi.fn(async () => "result");
206
- const { session, mockHandle } = setup({ executeTool });
207
- await session.start();
208
-
209
- // Accumulate some tool calls
210
- mockHandle._fire("replyStarted", { replyId: "r1" });
211
- mockHandle._fire("event", { type: "tool_call", toolCallId: "c1", toolName: "t1", args: {} });
212
- await session.waitForTurn();
213
-
214
- // Send a user transcript to add conversation messages
215
- mockHandle._fire("event", { type: "user_transcript", text: "Hello" });
216
-
217
- // Reset — should clear pending tools and conversation
218
- session.onReset();
219
-
220
- // Verify old handle was closed
221
- expect(mockHandle.close).toHaveBeenCalled();
222
- });
223
-
224
- test("onReset invalidates currentReplyId to discard stale tool results", async () => {
225
- let resolveToolCall!: (value: string) => void;
226
- const executeTool = vi.fn(
227
- () =>
228
- new Promise<string>((r) => {
229
- resolveToolCall = r;
230
- }),
231
- );
232
- const handles: ReturnType<typeof makeMockHandle>[] = [];
233
- const spy = vi.spyOn(_internals, "connectS2s").mockImplementation(async () => {
234
- const h = makeMockHandle();
235
- handles.push(h);
236
- return h;
237
- });
238
-
239
- const client = makeClient();
240
- const session = createS2sSession(makeSessionOpts({ client, executeTool }));
241
- await session.start();
242
-
243
- // biome-ignore lint/style/noNonNullAssertion: test assertions after length check
244
- const firstHandle = handles[0]!;
245
-
246
- // Start a tool call on the first handle
247
- firstHandle._fire("replyStarted", { replyId: "r1" });
248
- firstHandle._fire("event", { type: "tool_call", toolCallId: "c1", toolName: "t1", args: {} });
249
- await vi.waitFor(() => expect(executeTool).toHaveBeenCalled());
250
-
251
- // Reset while tool is in-flight
252
- session.onReset();
253
-
254
- // Tool finishes late — result should be discarded due to generation mismatch
255
- resolveToolCall("stale-result");
256
- await session.waitForTurn();
257
-
258
- // New handle should not receive the stale result
259
- const newHandle = handles[1];
260
- expect(newHandle?.sendToolResult).not.toHaveBeenCalled();
261
-
262
- spy.mockRestore();
263
- });
264
-
265
- test("stop() is safe to call without start()", async () => {
266
- const client = makeClient();
267
- const session = createS2sSession(makeSessionOpts({ client }));
268
- // stop() without start() — should not throw
269
- await session.stop();
270
- });
271
-
272
- test("stop() prevents orphaned S2S connection when called during start()", async () => {
273
- let resolveConnect!: (value: S2sHandle) => void;
274
- const handle = makeMockHandle();
275
- const spy = vi.spyOn(_internals, "connectS2s").mockImplementation(
276
- () =>
277
- new Promise((r) => {
278
- resolveConnect = r as (value: S2sHandle) => void;
279
- }),
280
- );
281
-
282
- const client = makeClient();
283
- const session = createS2sSession(makeSessionOpts({ client }));
284
-
285
- const startPromise = session.start();
286
- const stopPromise = session.stop();
287
-
288
- // Connection resolves after stop — handle must be closed immediately
289
- resolveConnect(handle);
290
- await startPromise;
291
- await stopPromise;
292
-
293
- expect(handle.close).toHaveBeenCalled();
294
- spy.mockRestore();
295
- });
296
-
297
- test("S2S error event closes handle and emits error to client", async () => {
298
- const { session, client, mockHandle } = setup();
299
- await session.start();
300
-
301
- mockHandle._fire("error", new Error("S2S crashed"));
302
-
303
- expect(mockHandle.close).toHaveBeenCalled();
304
- expect(client.events).toContainEqual({
305
- type: "error",
306
- code: "internal",
307
- message: "S2S crashed",
308
- });
309
- });
310
-
311
- test("S2S close event nullifies the handle reference", async () => {
312
- const { session, mockHandle } = setup();
313
- await session.start();
314
-
315
- // Simulate S2S WebSocket close
316
- mockHandle._fire("close", 1000, "normal");
317
-
318
- // Sending audio after close should not throw (no-ops via ?. on null s2s)
319
- session.onAudio(new Uint8Array([1, 2, 3]));
320
- });
321
-
322
- test("sessionExpired event closes the S2S handle", async () => {
323
- const { session, mockHandle } = setup();
324
- await session.start();
325
-
326
- mockHandle._fire("sessionExpired");
327
- // The handler calls handle.close() directly
328
- expect(mockHandle.close).toHaveBeenCalled();
329
- });
330
-
331
- test("rapid resets close all stale connections", async () => {
332
- const handles: ReturnType<typeof makeMockHandle>[] = [];
333
- const resolvers: ((h: S2sHandle) => void)[] = [];
334
-
335
- const spy = vi.spyOn(_internals, "connectS2s").mockImplementation(
336
- () =>
337
- new Promise<S2sHandle>((resolve) => {
338
- const h = makeMockHandle();
339
- handles.push(h);
340
- resolvers.push(resolve as (value: S2sHandle) => void);
341
- }),
342
- );
343
-
344
- const client = makeClient();
345
- const session = createS2sSession(makeSessionOpts({ client }));
346
-
347
- const startPromise = session.start();
348
- session.onReset();
349
- session.onReset();
350
-
351
- expect(resolvers.length).toBe(3);
352
-
353
- // Resolve in order — first two are stale
354
- // biome-ignore lint/style/noNonNullAssertion: test assertions after length check
355
- resolvers[0]?.(handles[0]!);
356
- // biome-ignore lint/style/noNonNullAssertion: test assertions after length check
357
- resolvers[1]?.(handles[1]!);
358
- // biome-ignore lint/style/noNonNullAssertion: test assertions after length check
359
- resolvers[2]?.(handles[2]!);
360
-
361
- await startPromise;
362
-
363
- await vi.waitFor(() => {
364
- expect(handles[0]?.close).toHaveBeenCalled();
365
- expect(handles[1]?.close).toHaveBeenCalled();
366
- });
367
- expect(handles[2]?.close).not.toHaveBeenCalled();
368
-
369
- spy.mockRestore();
370
- });
371
-
372
- test("concurrent tool calls all complete before stop() resolves", async () => {
373
- const resolvers: ((value: string) => void)[] = [];
374
- const executeTool = vi.fn(
375
- () =>
376
- new Promise<string>((r) => {
377
- resolvers.push(r);
378
- }),
379
- );
380
- const { session, mockHandle } = setup({ executeTool });
381
- await session.start();
382
-
383
- mockHandle._fire("replyStarted", { replyId: "r1" });
384
- mockHandle._fire("event", { type: "tool_call", toolCallId: "c1", toolName: "t1", args: {} });
385
- mockHandle._fire("event", { type: "tool_call", toolCallId: "c2", toolName: "t2", args: {} });
386
-
387
- await vi.waitFor(() => expect(executeTool).toHaveBeenCalledTimes(2));
388
-
389
- // Stop while both tools are in-flight
390
- const stopPromise = session.stop();
391
-
392
- // Resolve both tools
393
- resolvers[0]?.("result-1");
394
- resolvers[1]?.("result-2");
395
-
396
- await stopPromise;
397
- // If we get here, turnPromise was properly awaited
398
- expect(mockHandle.close).toHaveBeenCalled();
399
- });
400
-
401
- test("connectS2s failure does not leak resources", async () => {
402
- const spy = vi.spyOn(_internals, "connectS2s").mockRejectedValue(new Error("network error"));
403
- const client = makeClient();
404
- const session = createS2sSession(makeSessionOpts({ client }));
405
-
406
- await session.start();
407
-
408
- // Client should get error event
409
- expect(client.events).toContainEqual(
410
- expect.objectContaining({
411
- type: "error",
412
- code: "internal",
413
- message: "network error",
414
- }),
415
- );
416
-
417
- // stop() should not throw even after failed start
418
- await session.stop();
419
-
420
- spy.mockRestore();
421
- });
422
- });
@@ -24,11 +24,10 @@ import { dirname, join } from "node:path";
24
24
  import { fileURLToPath } from "node:url";
25
25
  import { openai } from "@ai-sdk/openai";
26
26
  import { describe, expect, test } from "vitest";
27
- import type { AgentConfig, ExecuteTool } from "../../sdk/_internal-types.ts";
28
- import type { ClientEvent, ClientSink } from "../../sdk/protocol.ts";
29
- import { createPipelineSession } from "../pipeline-session.ts";
27
+ import type { ClientSink } from "../../sdk/protocol.ts";
30
28
  import { openAssemblyAI } from "../providers/stt/assemblyai.ts";
31
29
  import { openCartesia } from "../providers/tts/cartesia.ts";
30
+ import { createRuntime } from "../runtime.ts";
32
31
  import { consoleLogger } from "../runtime-config.ts";
33
32
 
34
33
  const here = dirname(fileURLToPath(import.meta.url));
@@ -59,47 +58,48 @@ describe.skipIf(!envReady)("pipeline integration — reference stack", () => {
59
58
  );
60
59
  }
61
60
  const pcm = await readFile(fixturePath);
62
- const events: ClientEvent[] = [];
61
+ const userTranscripts: string[] = [];
63
62
  const audioOut: Uint8Array[] = [];
63
+ let replyDone = false;
64
+
64
65
  const client: ClientSink = {
65
66
  open: true,
66
67
  event: (e) => {
67
- events.push(e);
68
+ if (e.type === "user_transcript") userTranscripts.push(e.text);
69
+ if (e.type === "reply_done") replyDone = true;
68
70
  },
69
71
  playAudioChunk: (chunk) => {
70
72
  audioOut.push(chunk);
71
73
  },
72
- playAudioDone: () => {
73
- /* no-op: test asserts on audioOut chunks directly */
74
- },
75
- };
76
-
77
- const agentConfig: AgentConfig = {
78
- name: "int",
79
- systemPrompt: "You reply in one short sentence.",
80
- greeting: "",
81
- maxSteps: 1,
74
+ playAudioDone: () => undefined,
82
75
  };
83
76
 
84
- const executeTool: ExecuteTool = async () => "";
85
-
86
- const session = createPipelineSession({
87
- id: "int-1",
88
- agent: "pipeline-reference",
89
- client,
90
- agentConfig,
91
- toolSchemas: [],
92
- executeTool,
77
+ const runtime = createRuntime({
78
+ agent: {
79
+ name: "int",
80
+ systemPrompt: "You reply in one short sentence.",
81
+ greeting: "",
82
+ maxSteps: 1,
83
+ tools: {},
84
+ },
85
+ env: {
86
+ // biome-ignore lint/style/noNonNullAssertion: envReady guard ensures presence
87
+ ASSEMBLYAI_API_KEY: process.env.ASSEMBLYAI_API_KEY!,
88
+ // biome-ignore lint/style/noNonNullAssertion: envReady guard ensures presence
89
+ CARTESIA_API_KEY: process.env.CARTESIA_API_KEY!,
90
+ },
93
91
  stt: openAssemblyAI({ model: "u3pro-rt" }),
94
92
  llm: openai("gpt-4o-mini"),
95
93
  tts: openCartesia({ voice: "694f9389-aac1-45b6-b726-9d9369183238" }),
96
- // biome-ignore lint/style/noNonNullAssertion: envReady guard ensures presence
97
- sttApiKey: process.env.ASSEMBLYAI_API_KEY!,
98
- // biome-ignore lint/style/noNonNullAssertion: envReady guard ensures presence
99
- ttsApiKey: process.env.CARTESIA_API_KEY!,
100
94
  logger: consoleLogger,
101
95
  });
102
96
 
97
+ const session = runtime.createSession({
98
+ id: "int-1",
99
+ agent: "pipeline-reference",
100
+ client,
101
+ });
102
+
103
103
  await session.start();
104
104
  session.onAudioReady();
105
105
 
@@ -110,15 +110,10 @@ describe.skipIf(!envReady)("pipeline integration — reference stack", () => {
110
110
  session.onAudio(new Uint8Array(chunk));
111
111
  await new Promise((r) => setTimeout(r, 100));
112
112
  }
113
- await session.waitForTurn();
114
113
  await session.stop();
115
114
 
116
- const userTranscript = events.find((e) => e.type === "user_transcript");
117
- expect(userTranscript).toBeDefined();
118
- expect(String((userTranscript as { text: string }).text).toLowerCase()).toContain(
119
- "how are you",
120
- );
121
- expect(events.some((e) => e.type === "reply_done")).toBe(true);
115
+ expect(userTranscripts.some((t) => t.toLowerCase().includes("how are you"))).toBe(true);
116
+ expect(replyDone).toBe(true);
122
117
  expect(audioOut.length).toBeGreaterThan(0);
123
118
  }, 60_000);
124
119
  });
@@ -17,7 +17,9 @@ import { createAnthropic } from "@ai-sdk/anthropic";
17
17
  import type { LanguageModel } from "ai";
18
18
  import { ANTHROPIC_KIND, type AnthropicOptions } from "../../sdk/providers/llm/anthropic.ts";
19
19
  import { ASSEMBLYAI_KIND, type AssemblyAIOptions } from "../../sdk/providers/stt/assemblyai.ts";
20
+ import { DEEPGRAM_KIND, type DeepgramOptions } from "../../sdk/providers/stt/deepgram.ts";
20
21
  import { CARTESIA_KIND, type CartesiaOptions } from "../../sdk/providers/tts/cartesia.ts";
22
+ import { RIME_KIND, type RimeOptions } from "../../sdk/providers/tts/rime.ts";
21
23
  import type {
22
24
  LlmProvider,
23
25
  SttOpener,
@@ -26,7 +28,9 @@ import type {
26
28
  TtsProvider,
27
29
  } from "../../sdk/providers.ts";
28
30
  import { openAssemblyAI } from "./stt/assemblyai.ts";
31
+ import { openDeepgram } from "./stt/deepgram.ts";
29
32
  import { openCartesia } from "./tts/cartesia.ts";
33
+ import { openRime } from "./tts/rime.ts";
30
34
 
31
35
  /**
32
36
  * Look up a provider API key: agent env first (set via `aai secret put` or
@@ -42,9 +46,11 @@ export function resolveStt(descriptor: SttProvider): SttOpener {
42
46
  switch (descriptor.kind) {
43
47
  case ASSEMBLYAI_KIND:
44
48
  return openAssemblyAI(descriptor.options as unknown as AssemblyAIOptions);
49
+ case DEEPGRAM_KIND:
50
+ return openDeepgram(descriptor.options as unknown as DeepgramOptions);
45
51
  default:
46
52
  throw new Error(
47
- `Unknown STT provider kind: "${descriptor.kind}". Supported: ${ASSEMBLYAI_KIND}.`,
53
+ `Unknown STT provider kind: "${descriptor.kind}". Supported: ${ASSEMBLYAI_KIND}, ${DEEPGRAM_KIND}.`,
48
54
  );
49
55
  }
50
56
  }
@@ -54,9 +60,11 @@ export function resolveTts(descriptor: TtsProvider): TtsOpener {
54
60
  switch (descriptor.kind) {
55
61
  case CARTESIA_KIND:
56
62
  return openCartesia(descriptor.options as unknown as CartesiaOptions);
63
+ case RIME_KIND:
64
+ return openRime(descriptor.options as unknown as RimeOptions);
57
65
  default:
58
66
  throw new Error(
59
- `Unknown TTS provider kind: "${descriptor.kind}". Supported: ${CARTESIA_KIND}.`,
67
+ `Unknown TTS provider kind: "${descriptor.kind}". Supported: ${CARTESIA_KIND}, ${RIME_KIND}.`,
60
68
  );
61
69
  }
62
70
  }