kernl 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +34 -0
  3. package/dist/agent/base.d.ts +5 -1
  4. package/dist/agent/base.d.ts.map +1 -1
  5. package/dist/agent/base.js +8 -0
  6. package/dist/agent.d.ts +1 -1
  7. package/dist/agent.d.ts.map +1 -1
  8. package/dist/agent.js +15 -2
  9. package/dist/context.d.ts +2 -0
  10. package/dist/context.d.ts.map +1 -1
  11. package/dist/context.js +2 -0
  12. package/dist/index.d.ts +2 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -1
  15. package/dist/kernl/kernl.d.ts +1 -2
  16. package/dist/kernl/kernl.d.ts.map +1 -1
  17. package/dist/kernl/kernl.js +61 -1
  18. package/dist/lib/env.d.ts.map +1 -1
  19. package/dist/lib/env.js +1 -1
  20. package/dist/lifecycle/__tests__/hooks.test.d.ts +2 -0
  21. package/dist/lifecycle/__tests__/hooks.test.d.ts.map +1 -0
  22. package/dist/lifecycle/__tests__/hooks.test.js +553 -0
  23. package/dist/lifecycle.d.ts +222 -120
  24. package/dist/lifecycle.d.ts.map +1 -1
  25. package/dist/lifecycle.js +5 -23
  26. package/dist/memory/memory.js +1 -1
  27. package/dist/realtime/index.d.ts +1 -1
  28. package/dist/realtime/index.d.ts.map +1 -1
  29. package/dist/realtime/index.js +1 -1
  30. package/dist/realtime/session.d.ts +31 -22
  31. package/dist/realtime/session.d.ts.map +1 -1
  32. package/dist/realtime/session.js +64 -55
  33. package/dist/realtime/transport.d.ts +45 -0
  34. package/dist/realtime/transport.d.ts.map +1 -0
  35. package/dist/realtime/transport.js +32 -0
  36. package/dist/realtime/types.d.ts +8 -2
  37. package/dist/realtime/types.d.ts.map +1 -1
  38. package/dist/thread/__tests__/thread.test.js +2 -2
  39. package/dist/thread/thread.d.ts +2 -2
  40. package/dist/thread/thread.d.ts.map +1 -1
  41. package/dist/thread/thread.js +75 -8
  42. package/dist/thread/types.d.ts +1 -1
  43. package/dist/thread/types.d.ts.map +1 -1
  44. package/package.json +4 -4
  45. package/src/agent/base.ts +13 -1
  46. package/src/agent.ts +17 -3
  47. package/src/context.ts +2 -0
  48. package/src/index.ts +10 -1
  49. package/src/kernl/kernl.ts +67 -3
  50. package/src/lib/env.ts +3 -1
  51. package/src/lifecycle/__tests__/hooks.test.ts +668 -0
  52. package/src/lifecycle.ts +289 -163
  53. package/src/memory/memory.ts +1 -1
  54. package/src/realtime/index.ts +1 -1
  55. package/src/realtime/session.ts +88 -64
  56. package/src/realtime/transport.ts +64 -0
  57. package/src/realtime/types.ts +10 -2
  58. package/src/thread/__tests__/thread.test.ts +2 -2
  59. package/src/thread/thread.ts +88 -10
  60. package/src/thread/types.ts +1 -1
  61. package/dist/realtime/channel.d.ts +0 -30
  62. package/dist/realtime/channel.d.ts.map +0 -1
  63. package/dist/realtime/channel.js +0 -1
  64. package/src/realtime/channel.ts +0 -32
@@ -1,11 +1,12 @@
1
- import { EventEmitter } from "node:events";
2
-
1
+ import { Emitter } from "@kernl-sdk/shared";
3
2
  import {
4
3
  RealtimeModel,
5
4
  RealtimeConnection,
6
5
  RealtimeServerEvent,
7
6
  RealtimeSessionConfig,
7
+ RealtimeChannel,
8
8
  ToolCallEvent,
9
+ TransportStatus,
9
10
  message,
10
11
  } from "@kernl-sdk/protocol";
11
12
 
@@ -13,16 +14,28 @@ import { Context, UnknownContext } from "@/context";
13
14
  import { MisconfiguredError } from "@/lib/error";
14
15
 
15
16
  import { RealtimeAgent } from "./agent";
16
- import type { RealtimeChannel } from "./channel";
17
17
  import type { RealtimeSessionOptions } from "./types";
18
18
 
19
+ /**
20
+ * Events emitted by a realtime session.
21
+ */
22
+ export type RealtimeSessionEvents = {
23
+ audio: [event: RealtimeServerEvent];
24
+ transcript: [event: RealtimeServerEvent];
25
+ text: [event: RealtimeServerEvent];
26
+ error: [error: Error];
27
+ status: [status: TransportStatus];
28
+ };
29
+
19
30
  /**
20
31
  * A realtime session manages the connection to a realtime model.
21
32
  *
22
33
  * Handles the bidirectional communication between an agent and a model,
23
34
  * including audio I/O (via channels), tool execution, and event routing.
24
35
  */
25
- export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
36
+ export class RealtimeSession<
37
+ TContext = UnknownContext,
38
+ > extends Emitter<RealtimeSessionEvents> {
26
39
  /**
27
40
  * Session ID. Null until connected.
28
41
  */
@@ -85,6 +98,8 @@ export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
85
98
  const options = {
86
99
  ...this.options.connectOptions,
87
100
  sessionConfig,
101
+ credential:
102
+ this.options.credential ?? this.options.connectOptions?.credential,
88
103
  };
89
104
 
90
105
  this.connection = this.options.transport
@@ -94,6 +109,10 @@ export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
94
109
  this.connection.on("event", this.onEvent.bind(this));
95
110
  this.connection.on("error", (e) => this.emit("error", e));
96
111
  this.connection.on("status", (s) => this.emit("status", s));
112
+ this.connection.on("interrupted", () => this.channel?.interrupt());
113
+
114
+ // Emit current status (may have been set before listeners attached)
115
+ this.emit("status", this.connection.status);
97
116
 
98
117
  this.init();
99
118
  }
@@ -114,6 +133,60 @@ export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
114
133
  });
115
134
  }
116
135
 
136
+ /**
137
+ * Send audio to the model.
138
+ */
139
+ sendAudio(audio: string): void {
140
+ this.connection?.send({ kind: "audio.input.append", audio });
141
+ }
142
+
143
+ /**
144
+ * Commit the audio buffer (signal end of speech).
145
+ */
146
+ commit(): void {
147
+ this.connection?.send({ kind: "audio.input.commit" });
148
+ }
149
+
150
+ /**
151
+ * Send a text message to the model.
152
+ */
153
+ sendMessage(text: string): void {
154
+ this.connection?.send({
155
+ kind: "item.create",
156
+ item: message({ role: "user", text }),
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Interrupt the current response.
162
+ */
163
+ interrupt(): void {
164
+ this.connection?.send({ kind: "response.cancel" });
165
+ this.channel?.interrupt();
166
+ }
167
+
168
+ /**
169
+ * Mute audio input.
170
+ */
171
+ mute(): void {
172
+ this.connection?.mute();
173
+ }
174
+
175
+ /**
176
+ * Unmute audio input.
177
+ */
178
+ unmute(): void {
179
+ this.connection?.unmute();
180
+ }
181
+
182
+ /**
183
+ * Close the session and release resources.
184
+ */
185
+ close(): void {
186
+ this.channel?.close();
187
+ this.connection?.close();
188
+ }
189
+
117
190
  /**
118
191
  * Build session configuration from agent.
119
192
  */
@@ -124,6 +197,11 @@ export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
124
197
  instructions: await this.agent.instructions(this.context),
125
198
  tools: tools.map((t) => t.serialize()),
126
199
  voice: this.agent.voice,
200
+ turnDetection: { mode: "server_vad" },
201
+ audio: {
202
+ inputFormat: { mimeType: "audio/pcm", sampleRate: 24000 },
203
+ outputFormat: { mimeType: "audio/pcm", sampleRate: 24000 },
204
+ },
127
205
  };
128
206
  }
129
207
 
@@ -138,7 +216,7 @@ export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
138
216
  */
139
217
  private onEvent(event: RealtimeServerEvent): void {
140
218
  switch (event.kind) {
141
- // Audio output → 'audio'
219
+ // audio output → 'audio'
142
220
  case "audio.output.delta":
143
221
  this.channel?.sendAudio(event.audio);
144
222
  this.emit("audio", event);
@@ -147,28 +225,28 @@ export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
147
225
  this.emit("audio", event);
148
226
  break;
149
227
 
150
- // Speech transcriptions → 'transcript'
228
+ // speech transcriptions → 'transcript'
151
229
  case "transcript.input":
152
230
  case "transcript.output":
153
231
  this.emit("transcript", event);
154
232
  break;
155
233
 
156
- // Text output → 'text'
234
+ // text output → 'text'
157
235
  case "text.output":
158
236
  this.emit("text", event);
159
237
  break;
160
238
 
161
- // Errors → 'error'
239
+ // ERrors → 'error'
162
240
  case "session.error":
163
241
  this.emit("error", event.error);
164
242
  break;
165
243
 
166
- // Tool calls - handled internally
244
+ // tool calls - handled internally
167
245
  case "tool.call":
168
246
  this.performActions(event);
169
247
  break;
170
248
 
171
- // Session lifecycle - internal state
249
+ // session lifecycle - internal state
172
250
  case "session.created":
173
251
  this.id = event.session.id;
174
252
  break;
@@ -202,58 +280,4 @@ export class RealtimeSession<TContext = UnknownContext> extends EventEmitter {
202
280
  error: result.error ?? undefined,
203
281
  });
204
282
  }
205
-
206
- /**
207
- * Send audio to the model.
208
- */
209
- sendAudio(audio: string): void {
210
- this.connection?.send({ kind: "audio.input.append", audio });
211
- }
212
-
213
- /**
214
- * Commit the audio buffer (signal end of speech).
215
- */
216
- commit(): void {
217
- this.connection?.send({ kind: "audio.input.commit" });
218
- }
219
-
220
- /**
221
- * Send a text message to the model.
222
- */
223
- sendMessage(text: string): void {
224
- this.connection?.send({
225
- kind: "item.create",
226
- item: message({ role: "user", text }),
227
- });
228
- }
229
-
230
- /**
231
- * Interrupt the current response.
232
- */
233
- interrupt(): void {
234
- this.connection?.send({ kind: "response.cancel" });
235
- this.channel?.interrupt();
236
- }
237
-
238
- /**
239
- * Mute audio input.
240
- */
241
- mute(): void {
242
- this.connection?.mute();
243
- }
244
-
245
- /**
246
- * Unmute audio input.
247
- */
248
- unmute(): void {
249
- this.connection?.unmute();
250
- }
251
-
252
- /**
253
- * Close the session and release resources.
254
- */
255
- close(): void {
256
- this.channel?.close();
257
- this.connection?.close();
258
- }
259
283
  }
@@ -0,0 +1,64 @@
1
+ import type {
2
+ RealtimeModel,
3
+ RealtimeConnection,
4
+ RealtimeTransport,
5
+ RealtimeConnectOptions,
6
+ WebSocketConstructor,
7
+ } from "@kernl-sdk/protocol";
8
+
9
+ /**
10
+ * Options for creating a WebSocket transport.
11
+ */
12
+ export interface WebSocketTransportOptions {
13
+ /**
14
+ * WebSocket constructor to use.
15
+ *
16
+ * Required in Node.js <22 (provide the 'ws' package).
17
+ * Optional in browsers and Node.js 22+ (uses globalThis.WebSocket).
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import WebSocket from 'ws';
22
+ * new WebSocketTransport({ websocket: WebSocket });
23
+ * ```
24
+ */
25
+ websocket?: WebSocketConstructor;
26
+ }
27
+
28
+ /**
29
+ * WebSocket transport for realtime connections.
30
+ *
31
+ * Use this transport when you need to provide a custom WebSocket implementation,
32
+ * such as the 'ws' package in Node.js <22.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * // Node.js <22
37
+ * import WebSocket from 'ws';
38
+ * const session = new RealtimeSession(agent, {
39
+ * transport: new WebSocketTransport({ websocket: WebSocket }),
40
+ * ...
41
+ * });
42
+ *
43
+ * // Browser or Node.js 22+ - no transport needed
44
+ * const session = new RealtimeSession(agent, { ... });
45
+ * ```
46
+ */
47
+ export class WebSocketTransport implements RealtimeTransport {
48
+ readonly handlesAudio = false;
49
+ private WS: WebSocketConstructor | undefined;
50
+
51
+ constructor(options?: WebSocketTransportOptions) {
52
+ this.WS = options?.websocket;
53
+ }
54
+
55
+ async connect(
56
+ model: RealtimeModel,
57
+ options?: RealtimeConnectOptions,
58
+ ): Promise<RealtimeConnection> {
59
+ return model.connect({
60
+ ...options,
61
+ websocket: this.WS ?? options?.websocket,
62
+ });
63
+ }
64
+ }
@@ -2,13 +2,13 @@ import {
2
2
  RealtimeModel,
3
3
  RealtimeTransport,
4
4
  RealtimeConnectOptions,
5
+ RealtimeChannel,
6
+ ClientCredential,
5
7
  } from "@kernl-sdk/protocol";
6
8
 
7
9
  import { Context, UnknownContext } from "@/context";
8
10
  import type { BaseAgentConfig } from "@/agent/base";
9
11
 
10
- import type { RealtimeChannel } from "./channel";
11
-
12
12
  /**
13
13
  * Configuration for a realtime agent.
14
14
  */
@@ -66,6 +66,14 @@ export interface RealtimeSessionOptions<TContext = UnknownContext> {
66
66
  */
67
67
  context?: Context<TContext>;
68
68
 
69
+ /**
70
+ * Ephemeral credential for client-side connections.
71
+ *
72
+ * Obtained from model.authenticate() on the server.
73
+ * Shorthand for connectOptions.credential.
74
+ */
75
+ credential?: ClientCredential;
76
+
69
77
  /**
70
78
  * Options passed to model.connect() or transport.connect().
71
79
  */
@@ -316,7 +316,7 @@ describe("Thread", () => {
316
316
  toolId: "simple",
317
317
  state: IN_PROGRESS,
318
318
  callId: "call_1",
319
- arguments: "first",
319
+ arguments: JSON.stringify({ value: "first" }),
320
320
  },
321
321
  ],
322
322
  finishReason: "stop",
@@ -343,7 +343,7 @@ describe("Thread", () => {
343
343
  toolId: "simple",
344
344
  state: IN_PROGRESS,
345
345
  callId: "call_2",
346
- arguments: "second",
346
+ arguments: JSON.stringify({ value: "second" }),
347
347
  },
348
348
  ],
349
349
  finishReason: "stop",
@@ -19,6 +19,8 @@ import {
19
19
  LanguageModel,
20
20
  LanguageModelItem,
21
21
  LanguageModelRequest,
22
+ type LanguageModelUsage,
23
+ type LanguageModelFinishReason,
22
24
  } from "@kernl-sdk/protocol";
23
25
  import { randomID, filter } from "@kernl-sdk/shared/lib";
24
26
 
@@ -90,8 +92,8 @@ export class Thread<
90
92
  readonly tid: string;
91
93
  readonly namespace: string;
92
94
  readonly agent: Agent<TContext, TOutput>;
93
- readonly context: Context<TContext>;
94
- readonly model: LanguageModel; /* inherited from the agent unless specified */
95
+ context: Context<TContext>;
96
+ model: LanguageModel; /* inherited from the agent unless specified */
95
97
  readonly parent: Task<TContext> | null; /* parent task which spawned this thread */
96
98
  readonly createdAt: Date;
97
99
  readonly updatedAt: Date;
@@ -200,7 +202,7 @@ export class Thread<
200
202
  */
201
203
  private async *_execute(): AsyncGenerator<ThreadStreamEvent, void> {
202
204
  for (;;) {
203
- let err = false;
205
+ let err: Error | undefined = undefined;
204
206
 
205
207
  if (this.abort?.signal.aborted) {
206
208
  return;
@@ -209,7 +211,7 @@ export class Thread<
209
211
  const events = [];
210
212
  for await (const e of this.tick()) {
211
213
  if (e.kind === "error") {
212
- err = true;
214
+ err = e.error;
213
215
  logger.error(e.error); // (TODO): onError callback in options
214
216
  }
215
217
  // we don't want deltas in the history
@@ -220,9 +222,9 @@ export class Thread<
220
222
  yield e;
221
223
  }
222
224
 
223
- // if an error event occurred → terminate
225
+ // if an error event occurred → throw it
224
226
  if (err) {
225
- return;
227
+ throw err;
226
228
  }
227
229
 
228
230
  // if model returns a message with no action intentions → terminal state
@@ -276,21 +278,59 @@ export class Thread<
276
278
 
277
279
  const req = await this.prepareModelRequest(this.history);
278
280
 
281
+ this.agent.emit("model.call.start", {
282
+ kind: "model.call.start",
283
+ provider: this.model.provider,
284
+ modelId: this.model.modelId,
285
+ settings: req.settings ?? {},
286
+ threadId: this.tid,
287
+ agentId: this.agent.id,
288
+ context: this.context,
289
+ });
290
+
291
+ let usage: LanguageModelUsage | undefined;
292
+ let finishReason: LanguageModelFinishReason = "unknown";
293
+
279
294
  try {
280
295
  if (this.model.stream) {
281
- const stream = this.model.stream(req);
282
- for await (const event of stream) {
283
- yield event; // [text-delta, tool-call, message, reasoning, ...]
296
+ for await (const event of this.model.stream(req)) {
297
+ if (event.kind === "finish") {
298
+ usage = event.usage;
299
+ finishReason = event.finishReason;
300
+ }
301
+ yield event;
284
302
  }
285
303
  } else {
286
304
  // fallback: blocking generate, yield events as batch
287
305
  const res = await this.model.generate(req);
306
+ usage = res.usage;
307
+ finishReason = res.finishReason;
288
308
  for (const event of res.content) {
289
309
  yield event;
290
310
  }
291
- // (TODO): this.stats.usage.add(res.usage)
292
311
  }
312
+
313
+ this.agent.emit("model.call.end", {
314
+ kind: "model.call.end",
315
+ provider: this.model.provider,
316
+ modelId: this.model.modelId,
317
+ finishReason,
318
+ usage,
319
+ threadId: this.tid,
320
+ agentId: this.agent.id,
321
+ context: this.context,
322
+ });
293
323
  } catch (error) {
324
+ this.agent.emit("model.call.end", {
325
+ kind: "model.call.end",
326
+ provider: this.model.provider,
327
+ modelId: this.model.modelId,
328
+ finishReason: "error",
329
+ threadId: this.tid,
330
+ agentId: this.agent.id,
331
+ context: this.context,
332
+ });
333
+
294
334
  yield {
295
335
  kind: "error",
296
336
  error: error instanceof Error ? error : new Error(String(error)),
@@ -430,6 +470,18 @@ export class Thread<
430
470
  private async executeTools(calls: ToolCall[]): Promise<ThreadEventInner[]> {
431
471
  return await Promise.all(
432
472
  calls.map(async (call: ToolCall) => {
473
+ const parsedArgs = JSON.parse(call.arguments || "{}");
474
+
475
+ this.agent.emit("tool.call.start", {
476
+ kind: "tool.call.start",
477
+ threadId: this.tid,
478
+ agentId: this.agent.id,
479
+ context: this.context,
480
+ toolId: call.toolId,
481
+ callId: call.callId,
482
+ args: parsedArgs,
483
+ });
484
+
433
485
  try {
434
486
  const tool = this.agent.tool(call.toolId);
435
487
  if (!tool) {
@@ -449,6 +501,21 @@ export class Thread<
449
501
  ctx.approve(call.callId); // mark this call as approved
450
502
  const res = await tool.invoke(ctx, call.arguments, call.callId);
451
503
 
504
+ this.agent.emit("tool.call.end", {
505
+ kind: "tool.call.end",
506
+ threadId: this.tid,
507
+ agentId: this.agent.id,
508
+ context: this.context,
509
+ toolId: call.toolId,
510
+ callId: call.callId,
511
+ state: res.state,
512
+ result:
513
+ typeof res.result === "string"
514
+ ? res.result
515
+ : JSON.stringify(res.result),
516
+ error: res.error,
517
+ });
518
+
452
519
  return {
453
520
  kind: "tool-result" as const,
454
521
  callId: call.callId,
@@ -458,6 +525,17 @@ export class Thread<
458
525
  error: res.error,
459
526
  };
460
527
  } catch (error) {
528
+ this.agent.emit("tool.call.end", {
529
+ kind: "tool.call.end",
530
+ threadId: this.tid,
531
+ agentId: this.agent.id,
532
+ context: this.context,
533
+ toolId: call.toolId,
534
+ callId: call.callId,
535
+ state: FAILED,
536
+ error: error instanceof Error ? error.message : String(error),
537
+ });
538
+
461
539
  return {
462
540
  kind: "tool-result" as const,
463
541
  callId: call.callId,
@@ -179,7 +179,7 @@ export interface ThreadOptions<
179
179
  * Options passed to agent.execute() and agent.stream().
180
180
  */
181
181
  export interface ThreadExecuteOptions<TContext> {
182
- context?: Context<TContext>;
182
+ context?: TContext;
183
183
  model?: LanguageModel;
184
184
  task?: Task<TContext>;
185
185
  threadId?: string;
@@ -1,30 +0,0 @@
1
- import { EventEmitter } from "node:events";
2
- /**
3
- * Base interface for audio I/O channels.
4
- *
5
- * Channels bridge between audio sources (browser mic, Twilio, Discord)
6
- * and the realtime session. They handle audio capture/playback and emit
7
- * events that the session listens to.
8
- *
9
- * Events emitted:
10
- * - 'audio' (audio: string) - Raw audio chunk (base64)
11
- * - 'commit' () - User finished speaking (VAD or manual)
12
- * - 'interrupt' () - User started speaking mid-response
13
- */
14
- export interface RealtimeChannel extends EventEmitter {
15
- /**
16
- * Send audio to be played/transmitted by the channel.
17
- * Called by session when audio is received from the model.
18
- */
19
- sendAudio(audio: string): void;
20
- /**
21
- * Interrupt current audio playback.
22
- * Called by session when response is cancelled.
23
- */
24
- interrupt(): void;
25
- /**
26
- * Clean up resources and close the channel.
27
- */
28
- close(): void;
29
- }
30
- //# sourceMappingURL=channel.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/realtime/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;;OAGG;IACH,SAAS,IAAI,IAAI,CAAC;IAElB;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;CACf"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,32 +0,0 @@
1
- import { EventEmitter } from "node:events";
2
-
3
- /**
4
- * Base interface for audio I/O channels.
5
- *
6
- * Channels bridge between audio sources (browser mic, Twilio, Discord)
7
- * and the realtime session. They handle audio capture/playback and emit
8
- * events that the session listens to.
9
- *
10
- * Events emitted:
11
- * - 'audio' (audio: string) - Raw audio chunk (base64)
12
- * - 'commit' () - User finished speaking (VAD or manual)
13
- * - 'interrupt' () - User started speaking mid-response
14
- */
15
- export interface RealtimeChannel extends EventEmitter {
16
- /**
17
- * Send audio to be played/transmitted by the channel.
18
- * Called by session when audio is received from the model.
19
- */
20
- sendAudio(audio: string): void;
21
-
22
- /**
23
- * Interrupt current audio playback.
24
- * Called by session when response is cancelled.
25
- */
26
- interrupt(): void;
27
-
28
- /**
29
- * Clean up resources and close the channel.
30
- */
31
- close(): void;
32
- }