@kuralle-syrinx/cf-agents 4.4.0 → 4.5.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/dist/build-session.d.ts +105 -0
- package/dist/build-session.d.ts.map +1 -0
- package/dist/build-session.js +74 -0
- package/dist/build-session.js.map +1 -0
- package/dist/connection-socket.d.ts +41 -0
- package/dist/connection-socket.d.ts.map +1 -0
- package/dist/connection-socket.js +90 -0
- package/dist/connection-socket.js.map +1 -0
- package/dist/durable-history.d.ts +17 -0
- package/dist/durable-history.d.ts.map +1 -0
- package/dist/durable-history.js +58 -0
- package/dist/durable-history.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/r2-recorder.d.ts +28 -0
- package/dist/r2-recorder.d.ts.map +1 -0
- package/dist/r2-recorder.js +303 -0
- package/dist/r2-recorder.js.map +1 -0
- package/dist/real-agent.compile-check.d.ts +12 -0
- package/dist/real-agent.compile-check.d.ts.map +1 -0
- package/dist/real-agent.compile-check.js +35 -0
- package/dist/real-agent.compile-check.js.map +1 -0
- package/dist/with-voice.d.ts +150 -0
- package/dist/with-voice.d.ts.map +1 -0
- package/dist/with-voice.js +359 -0
- package/dist/with-voice.js.map +1 -0
- package/package.json +26 -13
- package/src/build-session.test.ts +125 -0
- package/src/connection-socket.test.ts +114 -0
- package/src/r2-recorder.test.ts +185 -0
- package/src/with-voice.test.ts +798 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// R2-backed implementation of the transport EdgeRecorder. Taps inbound caller
|
|
4
|
+
// audio and outbound TTS audio and writes to an R2 bucket:
|
|
5
|
+
// - user.wav / assistant.wav : the per-speaker stems, time-aligned by wall-clock
|
|
6
|
+
// (user chunks at their wall offset; assistant re-anchored to
|
|
7
|
+
// playout) with silence filling the gaps — the durable artifacts.
|
|
8
|
+
// - conversation.wav : best-effort stereo mix (user = left, assistant = right),
|
|
9
|
+
// written only for short calls that stayed wholly in DO RAM;
|
|
10
|
+
// omitted (flagged in the manifest) once a stem streams out.
|
|
11
|
+
// - manifest.json : durations / byte lengths / truncation flags.
|
|
12
|
+
// Mirrors the Node `voice-recorder` conversation-track approach (wall-clock byte
|
|
13
|
+
// offsets + stereo interleave) but stays edge-safe (no node:fs).
|
|
14
|
+
//
|
|
15
|
+
// Memory: the DO has ~128 MB. Rather than buffer the whole call and gap-fill the full
|
|
16
|
+
// wall-clock length at finalize (which OOMs long/mostly-silent calls), each stem is
|
|
17
|
+
// gap-filled INCREMENTALLY and streamed to R2 via multipart upload: bytes accumulate
|
|
18
|
+
// into a bounded buffer and flush as 5 MiB parts, so retained memory stays ~O(part size)
|
|
19
|
+
// per stem regardless of call length. Short calls (< one part) never open a multipart and
|
|
20
|
+
// are written with a single put, exactly as before.
|
|
21
|
+
import { interleaveStereoPcm16, pcm16ToWav } from "@kuralle-syrinx/recorder/wav";
|
|
22
|
+
// R2 requires every multipart part except the last to be at least 5 MiB. We flush at
|
|
23
|
+
// exactly that: bigger parts waste RAM, smaller ones are rejected.
|
|
24
|
+
const PART_SIZE_BYTES = 5 * 1024 * 1024;
|
|
25
|
+
// Emit long silence gaps in small slices so a multi-minute gap never allocates its full
|
|
26
|
+
// wall-clock length at once (that was the OOM).
|
|
27
|
+
const SILENCE_SLICE_BYTES = 64 * 1024;
|
|
28
|
+
/** A FIFO byte queue that hands back exact-length runs without holding the whole call. */
|
|
29
|
+
class PartBuffer {
|
|
30
|
+
#chunks = [];
|
|
31
|
+
#size = 0;
|
|
32
|
+
push(bytes) {
|
|
33
|
+
if (bytes.byteLength === 0)
|
|
34
|
+
return;
|
|
35
|
+
this.#chunks.push(bytes);
|
|
36
|
+
this.#size += bytes.byteLength;
|
|
37
|
+
}
|
|
38
|
+
get size() {
|
|
39
|
+
return this.#size;
|
|
40
|
+
}
|
|
41
|
+
/** Remove and return the first `n` bytes (caller guarantees n <= size). */
|
|
42
|
+
take(n) {
|
|
43
|
+
const out = new Uint8Array(n);
|
|
44
|
+
let filled = 0;
|
|
45
|
+
while (filled < n) {
|
|
46
|
+
const head = this.#chunks[0];
|
|
47
|
+
const need = n - filled;
|
|
48
|
+
if (head.byteLength <= need) {
|
|
49
|
+
out.set(head, filled);
|
|
50
|
+
filled += head.byteLength;
|
|
51
|
+
this.#chunks.shift();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
out.set(head.subarray(0, need), filled);
|
|
55
|
+
this.#chunks[0] = head.subarray(need);
|
|
56
|
+
filled += need;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.#size -= n;
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
drain() {
|
|
63
|
+
return this.take(this.#size);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export class R2EdgeRecorder {
|
|
67
|
+
opts;
|
|
68
|
+
#prefix;
|
|
69
|
+
#user;
|
|
70
|
+
#assistant;
|
|
71
|
+
#maxBytes;
|
|
72
|
+
#now;
|
|
73
|
+
#finalized = false;
|
|
74
|
+
constructor(opts) {
|
|
75
|
+
this.opts = opts;
|
|
76
|
+
this.#prefix = `${opts.keyPrefix ?? "recordings"}/${opts.sessionId}/${opts.startedAtMs}`;
|
|
77
|
+
this.#user = this.#emptyStem(`${this.#prefix}/user.wav`);
|
|
78
|
+
this.#assistant = this.#emptyStem(`${this.#prefix}/assistant.wav`);
|
|
79
|
+
this.#maxBytes = opts.maxBytesPerStream;
|
|
80
|
+
this.#now = opts.now ?? Date.now;
|
|
81
|
+
}
|
|
82
|
+
onUserAudio(_contextId, audio, sampleRateHz) {
|
|
83
|
+
this.#append(this.#user, audio, sampleRateHz);
|
|
84
|
+
}
|
|
85
|
+
onAssistantAudio(_contextId, audio, sampleRateHz) {
|
|
86
|
+
this.#append(this.#assistant, audio, sampleRateHz);
|
|
87
|
+
}
|
|
88
|
+
async finalize(meta) {
|
|
89
|
+
if (this.#finalized)
|
|
90
|
+
return;
|
|
91
|
+
this.#finalized = true;
|
|
92
|
+
if (this.#user.dataBytes === 0 && this.#assistant.dataBytes === 0)
|
|
93
|
+
return;
|
|
94
|
+
const rate = this.#user.sampleRateHz; // conversation timeline runs at the user (input) rate
|
|
95
|
+
// Close each stem sequentially (never hold both full WAV copies at once). A stem that
|
|
96
|
+
// never streamed hands back its full mono PCM so a best-effort stereo mix can be built.
|
|
97
|
+
const userMono = await this.#closeStem(this.#user);
|
|
98
|
+
const assistantMono = await this.#closeStem(this.#assistant);
|
|
99
|
+
const conversation = userMono && assistantMono
|
|
100
|
+
? await this.#writeStereo(userMono, assistantMono, rate)
|
|
101
|
+
: {
|
|
102
|
+
path: `${this.#prefix}/conversation.wav`,
|
|
103
|
+
sampleRateHz: rate,
|
|
104
|
+
channels: 2,
|
|
105
|
+
encoding: "pcm_s16le",
|
|
106
|
+
byteLength: 0,
|
|
107
|
+
durationMs: 0,
|
|
108
|
+
omitted: true,
|
|
109
|
+
};
|
|
110
|
+
const manifest = {
|
|
111
|
+
schemaVersion: 1,
|
|
112
|
+
sessionId: meta.sessionId,
|
|
113
|
+
startedAtMs: this.opts.startedAtMs,
|
|
114
|
+
closedAtMs: meta.closedAtMs,
|
|
115
|
+
conversation,
|
|
116
|
+
user: this.#describe(this.#user),
|
|
117
|
+
assistant: this.#describe(this.#assistant),
|
|
118
|
+
};
|
|
119
|
+
await this.opts.bucket.put(`${this.#prefix}/manifest.json`, JSON.stringify(manifest, null, 2), {
|
|
120
|
+
httpMetadata: { contentType: "application/json" },
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
#emptyStem(key) {
|
|
124
|
+
return {
|
|
125
|
+
key,
|
|
126
|
+
buf: new PartBuffer(),
|
|
127
|
+
head: null,
|
|
128
|
+
multipart: false,
|
|
129
|
+
uploadId: null,
|
|
130
|
+
parts: [],
|
|
131
|
+
partSeq: 2,
|
|
132
|
+
tail: Promise.resolve(),
|
|
133
|
+
cursorBytes: 0,
|
|
134
|
+
dataBytes: 0,
|
|
135
|
+
sampleRateHz: 16000,
|
|
136
|
+
truncated: false,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
#append(stem, audio, sampleRateHz) {
|
|
140
|
+
stem.sampleRateHz = sampleRateHz;
|
|
141
|
+
if (this.#maxBytes !== undefined && stem.dataBytes + audio.byteLength > this.#maxBytes) {
|
|
142
|
+
stem.truncated = true;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Anchor each chunk at its wall-clock position so the two speakers line up on a shared
|
|
146
|
+
// timeline; never overlap the previous chunk. Emit the intervening silence + the chunk
|
|
147
|
+
// incrementally so the timeline is never materialised whole.
|
|
148
|
+
const wallOffset = this.#wallOffsetBytes(sampleRateHz);
|
|
149
|
+
const offsetBytes = Math.max(stem.cursorBytes, wallOffset);
|
|
150
|
+
const gap = offsetBytes - stem.cursorBytes;
|
|
151
|
+
if (gap > 0)
|
|
152
|
+
this.#emitSilence(stem, gap);
|
|
153
|
+
this.#emit(stem, audio.slice());
|
|
154
|
+
stem.cursorBytes = offsetBytes + audio.byteLength;
|
|
155
|
+
stem.dataBytes += audio.byteLength;
|
|
156
|
+
}
|
|
157
|
+
#emitSilence(stem, bytes) {
|
|
158
|
+
let remaining = bytes;
|
|
159
|
+
while (remaining > 0) {
|
|
160
|
+
const slice = Math.min(remaining, SILENCE_SLICE_BYTES);
|
|
161
|
+
this.#emit(stem, new Uint8Array(slice)); // zero = PCM16 silence
|
|
162
|
+
remaining -= slice;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
#emit(stem, bytes) {
|
|
166
|
+
if (bytes.byteLength === 0)
|
|
167
|
+
return;
|
|
168
|
+
stem.buf.push(bytes);
|
|
169
|
+
if (!stem.multipart && stem.buf.size >= PART_SIZE_BYTES) {
|
|
170
|
+
// Commit to multipart: retain the first part's worth as the deferred part 1 (its WAV
|
|
171
|
+
// header needs the final total length, known only at finalize), then stream the rest.
|
|
172
|
+
stem.head = stem.buf.take(PART_SIZE_BYTES);
|
|
173
|
+
stem.multipart = true;
|
|
174
|
+
this.#enqueueCreate(stem);
|
|
175
|
+
}
|
|
176
|
+
if (stem.multipart) {
|
|
177
|
+
while (stem.buf.size >= PART_SIZE_BYTES) {
|
|
178
|
+
this.#enqueueUpload(stem, stem.partSeq++, stem.buf.take(PART_SIZE_BYTES));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
#enqueueCreate(stem) {
|
|
183
|
+
stem.tail = stem.tail.then(async () => {
|
|
184
|
+
const mpu = await this.opts.bucket.createMultipartUpload(stem.key, {
|
|
185
|
+
httpMetadata: { contentType: "audio/wav" },
|
|
186
|
+
});
|
|
187
|
+
stem.uploadId = mpu.uploadId;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
#enqueueUpload(stem, partNumber, body) {
|
|
191
|
+
stem.tail = stem.tail.then(async () => {
|
|
192
|
+
const mpu = this.opts.bucket.resumeMultipartUpload(stem.key, stem.uploadId);
|
|
193
|
+
stem.parts.push(await mpu.uploadPart(partNumber, body));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** Flush a stem to R2. Returns its full mono PCM if it stayed in RAM, else null. */
|
|
197
|
+
async #closeStem(stem) {
|
|
198
|
+
if (!stem.multipart) {
|
|
199
|
+
const mono = stem.buf.drain();
|
|
200
|
+
await this.opts.bucket.put(stem.key, pcm16ToWav(mono, stem.sampleRateHz, 1), {
|
|
201
|
+
httpMetadata: { contentType: "audio/wav" },
|
|
202
|
+
});
|
|
203
|
+
return mono;
|
|
204
|
+
}
|
|
205
|
+
await stem.tail; // drain the queued create + middle-part uploads
|
|
206
|
+
const mpu = this.opts.bucket.resumeMultipartUpload(stem.key, stem.uploadId);
|
|
207
|
+
// Part 1 = the WAV header (now that the total data length is known) + the retained head.
|
|
208
|
+
const head = stem.head ?? new Uint8Array(0);
|
|
209
|
+
const part1 = concat(wavHeader(stem.cursorBytes, stem.sampleRateHz, 1), head);
|
|
210
|
+
stem.parts.push(await mpu.uploadPart(1, part1));
|
|
211
|
+
const remainder = stem.buf.drain();
|
|
212
|
+
if (remainder.byteLength > 0)
|
|
213
|
+
stem.parts.push(await mpu.uploadPart(stem.partSeq++, remainder));
|
|
214
|
+
stem.parts.sort((a, b) => a.partNumber - b.partNumber);
|
|
215
|
+
await mpu.complete(stem.parts);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
async #writeStereo(userPcm, assistantMono, rate) {
|
|
219
|
+
const assistantPcm = this.#assistant.sampleRateHz === rate
|
|
220
|
+
? assistantMono
|
|
221
|
+
: resamplePcm16(assistantMono, this.#assistant.sampleRateHz, rate);
|
|
222
|
+
const stereo = interleaveStereoPcm16(userPcm, assistantPcm);
|
|
223
|
+
await this.opts.bucket.put(`${this.#prefix}/conversation.wav`, pcm16ToWav(stereo, rate, 2), {
|
|
224
|
+
httpMetadata: { contentType: "audio/wav" },
|
|
225
|
+
});
|
|
226
|
+
return {
|
|
227
|
+
path: `${this.#prefix}/conversation.wav`,
|
|
228
|
+
sampleRateHz: rate,
|
|
229
|
+
channels: 2,
|
|
230
|
+
encoding: "pcm_s16le",
|
|
231
|
+
byteLength: stereo.byteLength,
|
|
232
|
+
durationMs: rate > 0 ? Math.round((stereo.byteLength / 4 / rate) * 1000) : 0,
|
|
233
|
+
omitted: false,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
#wallOffsetBytes(sampleRateHz) {
|
|
237
|
+
const elapsedMs = Math.max(0, this.#now() - this.opts.startedAtMs);
|
|
238
|
+
const bytes = Math.floor((elapsedMs * sampleRateHz * 2) / 1000);
|
|
239
|
+
return bytes - (bytes % 2);
|
|
240
|
+
}
|
|
241
|
+
#describe(stem) {
|
|
242
|
+
return {
|
|
243
|
+
path: stem.key,
|
|
244
|
+
sampleRateHz: stem.sampleRateHz,
|
|
245
|
+
encoding: "pcm_s16le",
|
|
246
|
+
channels: 1,
|
|
247
|
+
byteLength: stem.cursorBytes,
|
|
248
|
+
durationMs: stem.sampleRateHz > 0 ? Math.round((stem.cursorBytes / 2 / stem.sampleRateHz) * 1000) : 0,
|
|
249
|
+
truncated: stem.truncated,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function concat(a, b) {
|
|
254
|
+
const out = new Uint8Array(a.byteLength + b.byteLength);
|
|
255
|
+
out.set(a, 0);
|
|
256
|
+
out.set(b, a.byteLength);
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
/** Build the 44-byte canonical WAV (RIFF/PCM) header for a stream of `dataBytes` PCM16LE. */
|
|
260
|
+
function wavHeader(dataBytes, sampleRateHz, channels) {
|
|
261
|
+
const blockAlign = channels * 2; // 16-bit samples
|
|
262
|
+
const byteRate = sampleRateHz * blockAlign;
|
|
263
|
+
const out = new Uint8Array(44);
|
|
264
|
+
const view = new DataView(out.buffer);
|
|
265
|
+
writeAscii(view, 0, "RIFF");
|
|
266
|
+
view.setUint32(4, 36 + dataBytes, true);
|
|
267
|
+
writeAscii(view, 8, "WAVE");
|
|
268
|
+
writeAscii(view, 12, "fmt ");
|
|
269
|
+
view.setUint32(16, 16, true);
|
|
270
|
+
view.setUint16(20, 1, true); // PCM
|
|
271
|
+
view.setUint16(22, channels, true);
|
|
272
|
+
view.setUint32(24, sampleRateHz, true);
|
|
273
|
+
view.setUint32(28, byteRate, true);
|
|
274
|
+
view.setUint16(32, blockAlign, true);
|
|
275
|
+
view.setUint16(34, 16, true);
|
|
276
|
+
writeAscii(view, 36, "data");
|
|
277
|
+
view.setUint32(40, dataBytes, true);
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
function writeAscii(view, offset, text) {
|
|
281
|
+
for (let i = 0; i < text.length; i += 1)
|
|
282
|
+
view.setUint8(offset + i, text.charCodeAt(i));
|
|
283
|
+
}
|
|
284
|
+
function resamplePcm16(pcm, fromHz, toHz) {
|
|
285
|
+
if (fromHz === toHz || pcm.byteLength === 0)
|
|
286
|
+
return pcm;
|
|
287
|
+
const src = new DataView(pcm.buffer, pcm.byteOffset, pcm.byteLength);
|
|
288
|
+
const inSamples = pcm.byteLength >> 1;
|
|
289
|
+
const outSamples = Math.max(1, Math.round((inSamples * toHz) / fromHz));
|
|
290
|
+
const out = new Uint8Array(outSamples * 2);
|
|
291
|
+
const ov = new DataView(out.buffer);
|
|
292
|
+
const ratio = (inSamples - 1) / Math.max(1, outSamples - 1);
|
|
293
|
+
for (let i = 0; i < outSamples; i += 1) {
|
|
294
|
+
const x = i * ratio;
|
|
295
|
+
const i0 = Math.floor(x);
|
|
296
|
+
const i1 = Math.min(inSamples - 1, i0 + 1);
|
|
297
|
+
const frac = x - i0;
|
|
298
|
+
const s = src.getInt16(i0 * 2, true) * (1 - frac) + src.getInt16(i1 * 2, true) * frac;
|
|
299
|
+
ov.setInt16(i * 2, Math.round(s), true);
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=r2-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-recorder.js","sourceRoot":"","sources":["../src/r2-recorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Agent } from "agents";
|
|
2
|
+
interface Env extends Record<string, unknown> {
|
|
3
|
+
GEMINI_API_KEY: string;
|
|
4
|
+
}
|
|
5
|
+
declare const RealtimeVoiceAgent_base: typeof Agent<Env> & (new (...args: any[]) => import("./with-voice.js").WithVoiceMembers);
|
|
6
|
+
export declare class RealtimeVoiceAgent extends RealtimeVoiceAgent_base {
|
|
7
|
+
}
|
|
8
|
+
declare const CascadedVoiceAgent_base: typeof Agent<Env> & (new (...args: any[]) => import("./with-voice.js").WithVoiceMembers);
|
|
9
|
+
export declare class CascadedVoiceAgent extends CascadedVoiceAgent_base {
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=real-agent.compile-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-agent.compile-check.d.ts","sourceRoot":"","sources":["../src/real-agent.compile-check.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAK/B,UAAU,GAAI,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3C,cAAc,EAAE,MAAM,CAAC;CACxB;uCAGsD,OAAO,KAAK,CAAC,GAAG,CAAC;AAAxE,qBAAa,kBAAmB,SAAQ,uBAMtC;CAAG;uCAKkD,OAAO,KAAK,CAAC,GAAG,CAAC;AAAxE,qBAAa,kBAAmB,SAAQ,uBAYtC;CAAG"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Compile-only proof that `withVoice` composes with the REAL `agents` SDK Agent
|
|
4
|
+
// (not just the test's fake base). Type-checked by `tsc --noEmit`; never run.
|
|
5
|
+
// If the agents SDK changes the Agent / Connection / ConnectionContext surface
|
|
6
|
+
// the mixin relies on, this file fails to compile.
|
|
7
|
+
import { Agent } from "agents";
|
|
8
|
+
import { fromGeminiLive } from "@kuralle-syrinx/realtime";
|
|
9
|
+
import { withVoice } from "./with-voice.js";
|
|
10
|
+
// Realtime front: the agent's own kuralle runtime is the brain (default reasoner).
|
|
11
|
+
export class RealtimeVoiceAgent extends withVoice((Agent), {
|
|
12
|
+
pipeline: {
|
|
13
|
+
kind: "realtime",
|
|
14
|
+
front: (env) => fromGeminiLive({ apiKey: env.GEMINI_API_KEY }),
|
|
15
|
+
delegateToolName: "consult_knowledge",
|
|
16
|
+
},
|
|
17
|
+
}) {
|
|
18
|
+
}
|
|
19
|
+
// Cascaded: explicit reasoner + discrete stt/tts stages.
|
|
20
|
+
const noopPlugin = { initialize: async () => { }, close: async () => { } };
|
|
21
|
+
export class CascadedVoiceAgent extends withVoice((Agent), {
|
|
22
|
+
pipeline: {
|
|
23
|
+
kind: "cascaded",
|
|
24
|
+
stt: () => ({ plugin: noopPlugin, config: { model: "nova-3" } }),
|
|
25
|
+
tts: () => ({ plugin: noopPlugin, config: { voice_id: "v" } }),
|
|
26
|
+
},
|
|
27
|
+
reasoner: () => ({
|
|
28
|
+
// eslint-disable-next-line require-yield
|
|
29
|
+
stream: async function* () {
|
|
30
|
+
return;
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
}) {
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=real-agent.compile-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-agent.compile-check.js","sourceRoot":"","sources":["../src/real-agent.compile-check.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,mDAAmD;AAEnD,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAM5C,mFAAmF;AACnF,MAAM,OAAO,kBAAmB,SAAQ,SAAS,CAAyB,CAAA,KAAU,CAAA,EAAE;IACpF,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC;QAC9D,gBAAgB,EAAE,mBAAmB;KACtC;CACF,CAAC;CAAG;AAEL,yDAAyD;AACzD,MAAM,UAAU,GAAgB,EAAE,UAAU,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,CAAC;AAEtF,MAAM,OAAO,kBAAmB,SAAQ,SAAS,CAAyB,CAAA,KAAU,CAAA,EAAE;IACpF,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC;QAChE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,CAAC;KAC/D;IACD,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QACf,yCAAyC;QACzC,MAAM,EAAE,KAAK,SAAS,CAAC;YACrB,OAAO;QACT,CAAC;KACF,CAAC;CACH,CAAC;CAAG"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { Agent } from "agents";
|
|
2
|
+
import { type BackgroundAudioConfig, type EdgeRecorder } from "@kuralle-syrinx/server-websocket/edge";
|
|
3
|
+
import type { IdleTimeoutConfig, Reasoner } from "@kuralle-syrinx/core";
|
|
4
|
+
import { type VoiceConnection } from "./connection-socket.js";
|
|
5
|
+
import { type VoicePipeline, type VoicePipelineContext } from "./build-session.js";
|
|
6
|
+
type Constructor<T = object> = new (...args: any[]) => T;
|
|
7
|
+
/** The Agent surface the voice mixin relies on. (`env`/`name`/`runtime` are read via VoiceHostSurface.) */
|
|
8
|
+
type AgentLike = Constructor<Pick<Agent<Record<string, unknown>>, "sql" | "getConnections" | "keepAlive">>;
|
|
9
|
+
/**
|
|
10
|
+
* Fired the instant the front model invokes the delegate tool — BEFORE the reasoner runs.
|
|
11
|
+
* Lets an app emit a deterministic, in-language preamble or a "thinking" earcon that masks the
|
|
12
|
+
* reasoner's wait, instead of relying on the realtime front LLM to remember to speak one (cf.
|
|
13
|
+
* Vapi/Pipecat `on_function_calls_started`). Use `connection.send(...)` to signal the client
|
|
14
|
+
* (the idiomatic agents-SDK pattern) — e.g. trigger a cached client-side earcon/preamble.
|
|
15
|
+
*/
|
|
16
|
+
export interface ToolCallStartContext {
|
|
17
|
+
readonly toolName: string;
|
|
18
|
+
readonly args: Record<string, unknown>;
|
|
19
|
+
readonly sessionId: string;
|
|
20
|
+
/** The live agents-SDK connection — `connection.send(json)` to message the client. */
|
|
21
|
+
readonly connection: VoiceConnection;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Delegate (Responder-Thinker) observability — fired when the bridge hands a query to
|
|
25
|
+
* the Reasoner (G2, RFC bimodel-delegate-seam). Replaces the consumer-side pattern of
|
|
26
|
+
* wrapping the Reasoner just to log the query. `toolId`/`toolName` are present on
|
|
27
|
+
* realtime delegate turns, absent on cascade turns.
|
|
28
|
+
*/
|
|
29
|
+
export interface DelegateQueryContext<Env = unknown> {
|
|
30
|
+
readonly query: string;
|
|
31
|
+
readonly toolId?: string;
|
|
32
|
+
readonly toolName?: string;
|
|
33
|
+
readonly turnId: string;
|
|
34
|
+
readonly sessionId: string;
|
|
35
|
+
readonly connection: VoiceConnection;
|
|
36
|
+
/** The Worker env — bindings for logging/persistence (e.g. an R2 bucket). */
|
|
37
|
+
readonly env: Env;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Fired when the Reasoner produced the turn's final answer. Self-contained (carries the
|
|
41
|
+
* query again) so a consumer can log/persist the grounded Q&A pair or message its client.
|
|
42
|
+
*/
|
|
43
|
+
export interface DelegateResultContext<Env = unknown> {
|
|
44
|
+
readonly query: string;
|
|
45
|
+
readonly answer: string;
|
|
46
|
+
readonly durationMs: number;
|
|
47
|
+
readonly grounded: boolean;
|
|
48
|
+
readonly toolId?: string;
|
|
49
|
+
readonly toolName?: string;
|
|
50
|
+
readonly control?: {
|
|
51
|
+
name: string;
|
|
52
|
+
payload: unknown;
|
|
53
|
+
};
|
|
54
|
+
readonly blocked?: {
|
|
55
|
+
userFacingMessage: string;
|
|
56
|
+
payload?: unknown;
|
|
57
|
+
};
|
|
58
|
+
readonly turnId: string;
|
|
59
|
+
readonly sessionId: string;
|
|
60
|
+
/** The connection that initiated this delegate; use `send(...)` for an app message to that client. */
|
|
61
|
+
readonly connection: VoiceConnection;
|
|
62
|
+
/** The Worker env — bindings for logging/persistence (e.g. an R2 bucket). */
|
|
63
|
+
readonly env: Env;
|
|
64
|
+
}
|
|
65
|
+
export interface WithVoiceOptions<Env> {
|
|
66
|
+
/**
|
|
67
|
+
* The connection wire protocol this host speaks. `"edge"` (default) is the Syrinx
|
|
68
|
+
* browser/edge JSON+envelope protocol (`runVoiceEdgeWebSocketConnection`). `"twilio"`
|
|
69
|
+
* speaks the Twilio Media Streams protocol (μ-law 8 kHz both ways,
|
|
70
|
+
* `runTwilioEdgeWebSocketConnection`) for a PSTN leg. `"telnyx"` speaks the Telnyx
|
|
71
|
+
* Media Streaming protocol (PCMU/PCMA/G722/L16, `runTelnyxEdgeWebSocketConnection`).
|
|
72
|
+
* One transport per Agent class — route `/ws` to an `"edge"` agent, `/twilio` to a
|
|
73
|
+
* `"twilio"` agent, and `/telnyx` to a `"telnyx"` agent.
|
|
74
|
+
*/
|
|
75
|
+
readonly transport?: "edge" | "twilio" | "telnyx";
|
|
76
|
+
/** The voice pipeline: `{ kind: "realtime", ... }` or `{ kind: "cascaded", ... }`. */
|
|
77
|
+
readonly pipeline: VoicePipeline<Env>;
|
|
78
|
+
/**
|
|
79
|
+
* The brain. Defaults to `fromKuralleRuntime(this.runtime, { sessionId })` when
|
|
80
|
+
* the Agent exposes a kuralle `runtime`. Required for cascaded pipelines on
|
|
81
|
+
* agents without a `runtime`.
|
|
82
|
+
*/
|
|
83
|
+
readonly reasoner?: (env: Env, ctx: VoicePipelineContext) => Reasoner | Promise<Reasoner>;
|
|
84
|
+
/** Optional per-call recorder (e.g. an R2-backed `EdgeRecorder`). Applies to the `"edge"` transport. */
|
|
85
|
+
readonly recorder?: (env: Env, ctx: VoicePipelineContext) => EdgeRecorder | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Ambient/thinking bed mixed (ducked) under assistant speech on both transports.
|
|
88
|
+
* On the `"twilio"` transport the bed also fills the gaps between turns as
|
|
89
|
+
* comfort noise — pure digital silence on a phone line reads as "the call died".
|
|
90
|
+
* The thinking loop follows the G3 tool-call cues. Sources are raw mono PCM16
|
|
91
|
+
* (e.g. `ffmpeg -i office.mp3 -ac 1 -ar 16000 -f s16le office.pcm`).
|
|
92
|
+
*/
|
|
93
|
+
readonly backgroundAudio?: BackgroundAudioConfig;
|
|
94
|
+
/**
|
|
95
|
+
* Fired when the front model starts a delegate tool call, before the reasoner runs — the seam
|
|
96
|
+
* for a deterministic latency-masking preamble / "thinking" earcon. Throwing here never affects
|
|
97
|
+
* the call. See {@link ToolCallStartContext}.
|
|
98
|
+
*/
|
|
99
|
+
readonly onToolCallStart?: (ctx: ToolCallStartContext) => void | Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Delegate observability (G2): fired when the bridge hands a query to the Reasoner.
|
|
102
|
+
* Subscribe instead of wrapping the Reasoner to log. Throwing here never affects the call.
|
|
103
|
+
*/
|
|
104
|
+
readonly onDelegateQuery?: (ctx: DelegateQueryContext<Env>) => void | Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Delegate observability (G2): fired when the Reasoner produced the turn's final answer —
|
|
107
|
+
* the hook for logging/persisting the grounded Q&A pair or sending a post-result app message
|
|
108
|
+
* through `ctx.connection` (query + answer + durationMs + grounded). Throwing here never affects
|
|
109
|
+
* the call.
|
|
110
|
+
*/
|
|
111
|
+
readonly onDelegateResult?: (ctx: DelegateResultContext<Env>) => void | Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* G4 durable session state (default on). Persists the reasoner conversation to the
|
|
114
|
+
* Agent's DO-SQLite so a session resumes with the same context after eviction/
|
|
115
|
+
* hibernation: cascaded pipelines re-seed the ReasoningBridge; realtime pipelines
|
|
116
|
+
* record the transcript, feed it to delegate turns as prior context, and expose it
|
|
117
|
+
* (plus any provider-native resume handle) to the `front()` factory via
|
|
118
|
+
* `ctx.resume`. Set false for the pre-G4 ephemeral behavior.
|
|
119
|
+
*/
|
|
120
|
+
readonly durableHistory?: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* G3: ms a pending tool call may run before the time-triggered `tool_call_delayed`
|
|
123
|
+
* ("still working") wire cue fires. 0 disables the delayed phase. Default: 2000.
|
|
124
|
+
*/
|
|
125
|
+
readonly delayCueAfterMs?: number;
|
|
126
|
+
/**
|
|
127
|
+
* Idle-timeout override for this host. Omitted → the 15s telephony re-engagement
|
|
128
|
+
* ("Are you still there?" then disconnect). Browser/edge hosts should pass
|
|
129
|
+
* `{ durationMs: 0 }` to disable it: a demo/playground user granting mic or reading
|
|
130
|
+
* the UI must not be nagged or hung up on. Leave the default on `"twilio"` hosts.
|
|
131
|
+
*/
|
|
132
|
+
readonly idleTimeout?: Partial<IdleTimeoutConfig>;
|
|
133
|
+
readonly inputSampleRateHz?: number;
|
|
134
|
+
readonly outputSampleRateHz?: number;
|
|
135
|
+
readonly resumeWindowMs?: number;
|
|
136
|
+
/**
|
|
137
|
+
* Derive the Syrinx session id. When supplied, this resolver is authoritative;
|
|
138
|
+
* otherwise the `?sessionId=` query param is used, then a per-connection random id.
|
|
139
|
+
* The Agent instance `name` is not used because concurrent connections to one DO
|
|
140
|
+
* must not silently share a session.
|
|
141
|
+
*/
|
|
142
|
+
readonly sessionId?: (request: Request, agentName: string) => string;
|
|
143
|
+
}
|
|
144
|
+
export interface WithVoiceMembers {
|
|
145
|
+
/** Force-end the voice session on a connection (e.g. moderation, takeover). */
|
|
146
|
+
forceEndVoice(connection: VoiceConnection): void;
|
|
147
|
+
}
|
|
148
|
+
export declare function withVoice<Env, TBase extends AgentLike>(Base: TBase, options: WithVoiceOptions<Env>): TBase & Constructor<WithVoiceMembers>;
|
|
149
|
+
export {};
|
|
150
|
+
//# sourceMappingURL=with-voice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"with-voice.d.ts","sourceRoot":"","sources":["../src/with-voice.ts"],"names":[],"mappings":"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"}
|