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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +34 -0
- package/dist/agent/base.d.ts +5 -1
- package/dist/agent/base.d.ts.map +1 -1
- package/dist/agent/base.js +8 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +15 -2
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/kernl/kernl.d.ts +1 -2
- package/dist/kernl/kernl.d.ts.map +1 -1
- package/dist/kernl/kernl.js +61 -1
- package/dist/lib/env.d.ts.map +1 -1
- package/dist/lib/env.js +1 -1
- package/dist/lifecycle/__tests__/hooks.test.d.ts +2 -0
- package/dist/lifecycle/__tests__/hooks.test.d.ts.map +1 -0
- package/dist/lifecycle/__tests__/hooks.test.js +553 -0
- package/dist/lifecycle.d.ts +222 -120
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +5 -23
- package/dist/memory/memory.js +1 -1
- package/dist/realtime/index.d.ts +1 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js +1 -1
- package/dist/realtime/session.d.ts +31 -22
- package/dist/realtime/session.d.ts.map +1 -1
- package/dist/realtime/session.js +64 -55
- package/dist/realtime/transport.d.ts +45 -0
- package/dist/realtime/transport.d.ts.map +1 -0
- package/dist/realtime/transport.js +32 -0
- package/dist/realtime/types.d.ts +8 -2
- package/dist/realtime/types.d.ts.map +1 -1
- package/dist/thread/__tests__/thread.test.js +2 -2
- package/dist/thread/thread.d.ts +2 -2
- package/dist/thread/thread.d.ts.map +1 -1
- package/dist/thread/thread.js +75 -8
- package/dist/thread/types.d.ts +1 -1
- package/dist/thread/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/agent/base.ts +13 -1
- package/src/agent.ts +17 -3
- package/src/context.ts +2 -0
- package/src/index.ts +10 -1
- package/src/kernl/kernl.ts +67 -3
- package/src/lib/env.ts +3 -1
- package/src/lifecycle/__tests__/hooks.test.ts +668 -0
- package/src/lifecycle.ts +289 -163
- package/src/memory/memory.ts +1 -1
- package/src/realtime/index.ts +1 -1
- package/src/realtime/session.ts +88 -64
- package/src/realtime/transport.ts +64 -0
- package/src/realtime/types.ts +10 -2
- package/src/thread/__tests__/thread.test.ts +2 -2
- package/src/thread/thread.ts +88 -10
- package/src/thread/types.ts +1 -1
- package/dist/realtime/channel.d.ts +0 -30
- package/dist/realtime/channel.d.ts.map +0 -1
- package/dist/realtime/channel.js +0 -1
- package/src/realtime/channel.ts +0 -32
package/src/realtime/session.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
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<
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
234
|
+
// text output → 'text'
|
|
157
235
|
case "text.output":
|
|
158
236
|
this.emit("text", event);
|
|
159
237
|
break;
|
|
160
238
|
|
|
161
|
-
//
|
|
239
|
+
// ERrors → 'error'
|
|
162
240
|
case "session.error":
|
|
163
241
|
this.emit("error", event.error);
|
|
164
242
|
break;
|
|
165
243
|
|
|
166
|
-
//
|
|
244
|
+
// tool calls - handled internally
|
|
167
245
|
case "tool.call":
|
|
168
246
|
this.performActions(event);
|
|
169
247
|
break;
|
|
170
248
|
|
|
171
|
-
//
|
|
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
|
+
}
|
package/src/realtime/types.ts
CHANGED
|
@@ -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",
|
package/src/thread/thread.ts
CHANGED
|
@@ -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
|
-
|
|
94
|
-
|
|
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 =
|
|
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 =
|
|
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 →
|
|
225
|
+
// if an error event occurred → throw it
|
|
224
226
|
if (err) {
|
|
225
|
-
|
|
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
|
|
282
|
-
|
|
283
|
-
|
|
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,
|
package/src/thread/types.ts
CHANGED
|
@@ -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?:
|
|
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"}
|
package/dist/realtime/channel.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/src/realtime/channel.ts
DELETED
|
@@ -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
|
-
}
|