@kuralle-syrinx/server-websocket 4.4.1 → 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/backchannel-cue-fixtures.d.ts +4 -0
- package/dist/backchannel-cue-fixtures.d.ts.map +1 -0
- package/dist/backchannel-cue-fixtures.js +25 -0
- package/dist/backchannel-cue-fixtures.js.map +1 -0
- package/dist/background-audio.d.ts +59 -0
- package/dist/background-audio.d.ts.map +1 -0
- package/dist/background-audio.js +248 -0
- package/dist/background-audio.js.map +1 -0
- package/dist/browser-opus.d.ts +12 -0
- package/dist/browser-opus.d.ts.map +1 -0
- package/dist/browser-opus.js +48 -0
- package/dist/browser-opus.js.map +1 -0
- package/dist/carrier-commands.d.ts +124 -0
- package/dist/carrier-commands.d.ts.map +1 -0
- package/dist/carrier-commands.js +193 -0
- package/dist/carrier-commands.js.map +1 -0
- package/dist/edge-telnyx.d.ts +37 -0
- package/dist/edge-telnyx.d.ts.map +1 -0
- package/dist/edge-telnyx.js +320 -0
- package/dist/edge-telnyx.js.map +1 -0
- package/dist/edge-twilio.d.ts +31 -0
- package/dist/edge-twilio.d.ts.map +1 -0
- package/dist/edge-twilio.js +286 -0
- package/dist/edge-twilio.js.map +1 -0
- package/dist/edge.d.ts +71 -0
- package/dist/edge.d.ts.map +1 -0
- package/dist/edge.js +560 -0
- package/dist/edge.js.map +1 -0
- package/dist/inbound-audio.d.ts +13 -0
- package/dist/inbound-audio.d.ts.map +1 -0
- package/dist/inbound-audio.js +66 -0
- package/dist/inbound-audio.js.map +1 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +718 -0
- package/dist/index.js.map +1 -0
- package/dist/json-message.d.ts +7 -0
- package/dist/json-message.d.ts.map +1 -0
- package/dist/json-message.js +41 -0
- package/dist/json-message.js.map +1 -0
- package/dist/outbound-playout-pipeline.d.ts +53 -0
- package/dist/outbound-playout-pipeline.d.ts.map +1 -0
- package/dist/outbound-playout-pipeline.js +148 -0
- package/dist/outbound-playout-pipeline.js.map +1 -0
- package/dist/paced-playout.d.ts +42 -0
- package/dist/paced-playout.d.ts.map +1 -0
- package/dist/paced-playout.js +151 -0
- package/dist/paced-playout.js.map +1 -0
- package/dist/playout-progress.d.ts +26 -0
- package/dist/playout-progress.d.ts.map +1 -0
- package/dist/playout-progress.js +64 -0
- package/dist/playout-progress.js.map +1 -0
- package/dist/session-store.d.ts +39 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +80 -0
- package/dist/session-store.js.map +1 -0
- package/dist/smartpbx.d.ts +47 -0
- package/dist/smartpbx.d.ts.map +1 -0
- package/dist/smartpbx.js +437 -0
- package/dist/smartpbx.js.map +1 -0
- package/dist/telnyx-codec.d.ts +41 -0
- package/dist/telnyx-codec.d.ts.map +1 -0
- package/dist/telnyx-codec.js +114 -0
- package/dist/telnyx-codec.js.map +1 -0
- package/dist/telnyx.d.ts +52 -0
- package/dist/telnyx.d.ts.map +1 -0
- package/dist/telnyx.js +550 -0
- package/dist/telnyx.js.map +1 -0
- package/dist/test-helpers.d.ts +20 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +242 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/transport-helpers.d.ts +12 -0
- package/dist/transport-helpers.d.ts.map +1 -0
- package/dist/transport-helpers.js +90 -0
- package/dist/transport-helpers.js.map +1 -0
- package/dist/transport-host.d.ts +56 -0
- package/dist/transport-host.d.ts.map +1 -0
- package/dist/transport-host.js +110 -0
- package/dist/transport-host.js.map +1 -0
- package/dist/turn-metrics.d.ts +79 -0
- package/dist/turn-metrics.d.ts.map +1 -0
- package/dist/turn-metrics.js +208 -0
- package/dist/turn-metrics.js.map +1 -0
- package/dist/twilio-auth.d.ts +12 -0
- package/dist/twilio-auth.d.ts.map +1 -0
- package/dist/twilio-auth.js +44 -0
- package/dist/twilio-auth.js.map +1 -0
- package/dist/twilio.d.ts +56 -0
- package/dist/twilio.d.ts.map +1 -0
- package/dist/twilio.js +511 -0
- package/dist/twilio.js.map +1 -0
- package/dist/websocket-close.d.ts +12 -0
- package/dist/websocket-close.d.ts.map +1 -0
- package/dist/websocket-close.js +58 -0
- package/dist/websocket-close.js.map +1 -0
- package/dist/websocket-lifecycle.d.ts +29 -0
- package/dist/websocket-lifecycle.d.ts.map +1 -0
- package/dist/websocket-lifecycle.js +87 -0
- package/dist/websocket-lifecycle.js.map +1 -0
- package/dist/websocket-upgrade.d.ts +9 -0
- package/dist/websocket-upgrade.d.ts.map +1 -0
- package/dist/websocket-upgrade.js +91 -0
- package/dist/websocket-upgrade.js.map +1 -0
- package/dist/wire-carrier-control.d.ts +27 -0
- package/dist/wire-carrier-control.d.ts.map +1 -0
- package/dist/wire-carrier-control.js +116 -0
- package/dist/wire-carrier-control.js.map +1 -0
- package/package.json +32 -11
- package/src/admission-control.test.ts +270 -0
- package/src/background-audio.test.ts +224 -0
- package/src/browser-opus.test.ts +41 -0
- package/src/browser-pacing.test.ts +440 -0
- package/src/carrier-commands.test.ts +198 -0
- package/src/edge-telnyx.test.ts +333 -0
- package/src/edge-twilio.test.ts +293 -0
- package/src/edge.test.ts +727 -0
- package/src/graceful-drain.test.ts +306 -0
- package/src/inbound-audio.test.ts +58 -0
- package/src/index.test.ts +2078 -0
- package/src/outbound-playout-pipeline.test.ts +314 -0
- package/src/paced-playout.test.ts +247 -0
- package/src/playout-progress.test.ts +56 -0
- package/src/session-store.test.ts +274 -0
- package/src/smartpbx.test.ts +986 -0
- package/src/telnyx.test.ts +1633 -0
- package/src/transport-host.test.ts +51 -0
- package/src/turn-metrics.test.ts +503 -0
- package/src/twilio-auth.test.ts +36 -0
- package/src/twilio.test.ts +1274 -0
- package/src/websocket-close.test.ts +63 -0
- package/src/websocket-lifecycle.test.ts +49 -0
package/dist/edge.js
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import { Route, SYRINX_AUDIO_ENVELOPE_NAME, encodeSyrinxAudioEnvelope, } from "@kuralle-syrinx/core";
|
|
3
|
+
import { TimerScheduler } from "@kuralle-syrinx/core";
|
|
4
|
+
import { BackgroundAudioMixer, wireBackgroundAudio, } from "./background-audio.js";
|
|
5
|
+
import { createWorkersInboundSocket, } from "@kuralle-syrinx/ws/workers";
|
|
6
|
+
import { decodeInboundBinaryAudio, rememberContextSampleRate, resampleAudioBytes, socketDataToBytes, } from "./inbound-audio.js";
|
|
7
|
+
import { TurnMetricsTracker } from "./turn-metrics.js";
|
|
8
|
+
const DEFAULT_STARTUP_TIMEOUT_MS = 15_000;
|
|
9
|
+
const DEFAULT_MAX_SESSION_DURATION_MS = 30 * 60_000;
|
|
10
|
+
const DEFAULT_MAX_INBOUND_MESSAGE_BYTES = 2 * 1024 * 1024;
|
|
11
|
+
const DEFAULT_RESUME_WINDOW_MS = 15_000;
|
|
12
|
+
const DEFAULT_KEEP_ALIVE_INTERVAL_MS = 15_000;
|
|
13
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 60_000;
|
|
14
|
+
const KEEP_ALIVE_KEY = "voice.edge.keep_alive";
|
|
15
|
+
export function createVoiceEdgeWebSocketResponse(request, options, ctx) {
|
|
16
|
+
return createVoiceEdgeWebSocketUpgrade(request, options, ctx).response;
|
|
17
|
+
}
|
|
18
|
+
export function createVoiceEdgeWebSocketUpgrade(request, options, ctx) {
|
|
19
|
+
const inbound = createWorkersInboundSocket(ctx);
|
|
20
|
+
void runVoiceEdgeWebSocketConnection(inbound.socket, request, options);
|
|
21
|
+
return { response: inbound.response, controller: inbound.controller };
|
|
22
|
+
}
|
|
23
|
+
export async function runVoiceEdgeWebSocketConnection(socket, request, options) {
|
|
24
|
+
const scheduler = options.scheduler ?? new TimerScheduler();
|
|
25
|
+
const contextIdFn = options.contextId ?? defaultContextId;
|
|
26
|
+
const inputSampleRateHz = positiveInteger(options.inputSampleRateHz) ?? 16000;
|
|
27
|
+
const outputSampleRateHz = positiveInteger(options.outputSampleRateHz) ?? 16000;
|
|
28
|
+
const startupTimeoutMs = nonNegativeInteger(options.startupTimeoutMs) ?? DEFAULT_STARTUP_TIMEOUT_MS;
|
|
29
|
+
const maxSessionDurationMs = nonNegativeInteger(options.maxSessionDurationMs) ?? DEFAULT_MAX_SESSION_DURATION_MS;
|
|
30
|
+
const maxInboundMessageBytes = positiveInteger(options.maxInboundMessageBytes) ?? DEFAULT_MAX_INBOUND_MESSAGE_BYTES;
|
|
31
|
+
const resumeWindowMs = nonNegativeInteger(options.resumeWindowMs) ?? DEFAULT_RESUME_WINDOW_MS;
|
|
32
|
+
const keepAliveIntervalMs = nonNegativeInteger(options.keepAliveIntervalMs) ?? DEFAULT_KEEP_ALIVE_INTERVAL_MS;
|
|
33
|
+
const idleTimeoutMs = nonNegativeInteger(options.idleTimeoutMs) ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
34
|
+
const rawBinaryInput = options.rawBinaryInput ?? false;
|
|
35
|
+
const state = {
|
|
36
|
+
managed: null,
|
|
37
|
+
initialContextId: contextIdFn(),
|
|
38
|
+
streamingResamplers: new Map(),
|
|
39
|
+
};
|
|
40
|
+
const disposers = [];
|
|
41
|
+
const pendingMessages = [];
|
|
42
|
+
let pendingMessageBytes = 0;
|
|
43
|
+
let ready = false;
|
|
44
|
+
let closed = false;
|
|
45
|
+
let maxSessionTimedOut = false;
|
|
46
|
+
let lastClientMessageMs = Date.now();
|
|
47
|
+
let session = null;
|
|
48
|
+
const sendError = (message) => {
|
|
49
|
+
sendJson(socket, {
|
|
50
|
+
type: "error",
|
|
51
|
+
component: "transport",
|
|
52
|
+
category: "invalid_input",
|
|
53
|
+
message,
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const processMessage = (data, isBinary) => {
|
|
57
|
+
if (!session || !state.managed)
|
|
58
|
+
return;
|
|
59
|
+
const managed = state.managed;
|
|
60
|
+
options.sessionStore.update(managed.id, (stored) => {
|
|
61
|
+
stored.currentContextId = handleClientMessage(session, data, isBinary, stored.currentContextId, contextIdFn, inputSampleRateHz, rawBinaryInput, stored.contextSampleRates, stored.inputSequence, state.streamingResamplers);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
socket.onMessage((data, isBinary) => {
|
|
65
|
+
try {
|
|
66
|
+
lastClientMessageMs = Date.now();
|
|
67
|
+
const byteLength = socketDataByteLength(data);
|
|
68
|
+
if (byteLength > maxInboundMessageBytes) {
|
|
69
|
+
sendError(`Websocket message exceeds maxInboundMessageBytes (${String(maxInboundMessageBytes)})`);
|
|
70
|
+
socket.dispose();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!ready) {
|
|
74
|
+
pendingMessageBytes += byteLength;
|
|
75
|
+
if (pendingMessageBytes > maxInboundMessageBytes) {
|
|
76
|
+
sendError(`Pending websocket input exceeds maxInboundMessageBytes (${String(maxInboundMessageBytes)}) before session ready`);
|
|
77
|
+
socket.dispose();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
pendingMessages.push({ data: cloneSocketData(data), isBinary, byteLength });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
processMessage(data, isBinary);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
sendError(err instanceof Error ? err.message : String(err));
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// Tear down on EITHER a clean close or an error-path disconnect, exactly once (guarded by
|
|
90
|
+
// `closed`). On Cloudflare Workers a DO webSocketError surfaces here as onError with no
|
|
91
|
+
// matching onClose, so finalize/release must also run on the error path — otherwise the
|
|
92
|
+
// in-memory recording is lost and the session lease never releases.
|
|
93
|
+
const teardownConnection = () => {
|
|
94
|
+
if (closed)
|
|
95
|
+
return;
|
|
96
|
+
closed = true;
|
|
97
|
+
for (const dispose of disposers.splice(0))
|
|
98
|
+
dispose();
|
|
99
|
+
if (state.managed) {
|
|
100
|
+
state.managed.connectionCount = Math.max(0, state.managed.connectionCount - 1);
|
|
101
|
+
void options.sessionStore.release(state.managed.id, maxSessionTimedOut ? 0 : resumeWindowMs);
|
|
102
|
+
}
|
|
103
|
+
if (options.recorder) {
|
|
104
|
+
void Promise.resolve(options.recorder.finalize({ sessionId: state.managed?.id ?? "unknown", closedAtMs: Date.now() })).catch(() => undefined);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
socket.onClose(teardownConnection);
|
|
108
|
+
socket.onError(teardownConnection);
|
|
109
|
+
try {
|
|
110
|
+
// An explicit resolver is authoritative. Hosts such as cf-agents resolve the
|
|
111
|
+
// id before creating the session so the reasoner, session store, and transport
|
|
112
|
+
// all use the same identity even when the request carries a different wire id.
|
|
113
|
+
const requestedSessionId = sanitizeSessionId(options.sessionId?.(request) ?? sessionIdFromRequest(request) ?? defaultSessionId());
|
|
114
|
+
const leased = await withScheduledTimeout(options.sessionStore.lease(requestedSessionId, async () => {
|
|
115
|
+
const sess = await options.createSession(request);
|
|
116
|
+
if (closed) {
|
|
117
|
+
await sess.close().catch(() => undefined);
|
|
118
|
+
throw new Error("websocket session startup aborted");
|
|
119
|
+
}
|
|
120
|
+
await sess.start();
|
|
121
|
+
if (closed) {
|
|
122
|
+
await sess.close().catch(() => undefined);
|
|
123
|
+
throw new Error("websocket session startup aborted");
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
id: requestedSessionId,
|
|
127
|
+
session: sess,
|
|
128
|
+
currentContextId: state.initialContextId,
|
|
129
|
+
contextSampleRates: new Map(),
|
|
130
|
+
inputSequence: { lastSequence: null },
|
|
131
|
+
turnMetricsTurns: new Map(),
|
|
132
|
+
closeTimer: null,
|
|
133
|
+
connectionCount: 1,
|
|
134
|
+
};
|
|
135
|
+
}), startupTimeoutMs, scheduler, "voice.edge.startup_timeout");
|
|
136
|
+
state.managed = leased.managed;
|
|
137
|
+
session = leased.managed.session;
|
|
138
|
+
if (closed) {
|
|
139
|
+
await options.sessionStore.release(leased.managed.id, 0);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
wireEdgeSessionEvents(session, socket, disposers, outputSampleRateHz, leased.managed.turnMetricsTurns, options.recorder, options.backgroundAudio ? new BackgroundAudioMixer(options.backgroundAudio) : undefined);
|
|
143
|
+
if (options.recorder) {
|
|
144
|
+
const recorder = options.recorder;
|
|
145
|
+
disposers.push(session.bus.on("user.audio_received", (pkt) => {
|
|
146
|
+
const audio = pkt;
|
|
147
|
+
recorder.onUserAudio(audio.contextId, audio.audio, inputSampleRateHz);
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
if (maxSessionDurationMs > 0) {
|
|
151
|
+
scheduler.schedule("voice.edge.max_session_duration", maxSessionDurationMs, () => {
|
|
152
|
+
maxSessionTimedOut = true;
|
|
153
|
+
sendJson(socket, {
|
|
154
|
+
type: "error",
|
|
155
|
+
component: "transport",
|
|
156
|
+
category: "session_timeout",
|
|
157
|
+
message: "Websocket max session duration exceeded",
|
|
158
|
+
});
|
|
159
|
+
socket.dispose();
|
|
160
|
+
});
|
|
161
|
+
disposers.push(() => scheduler.cancel("voice.edge.max_session_duration"));
|
|
162
|
+
}
|
|
163
|
+
if (keepAliveIntervalMs > 0) {
|
|
164
|
+
const heartbeat = () => {
|
|
165
|
+
if (closed)
|
|
166
|
+
return;
|
|
167
|
+
if (idleTimeoutMs > 0 && Date.now() - lastClientMessageMs > idleTimeoutMs) {
|
|
168
|
+
sendJson(socket, {
|
|
169
|
+
type: "error",
|
|
170
|
+
component: "transport",
|
|
171
|
+
category: "idle_timeout",
|
|
172
|
+
message: `Websocket idle for more than idleTimeoutMs (${String(idleTimeoutMs)})`,
|
|
173
|
+
});
|
|
174
|
+
socket.dispose();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// Re-arm: on the Workers DO scheduler this keeps the alarm (and thus the
|
|
178
|
+
// Durable Object) alive while the client is active; on Node it is a plain
|
|
179
|
+
// interval. Cancelled on close so an idle DO can be evicted.
|
|
180
|
+
scheduler.schedule(KEEP_ALIVE_KEY, keepAliveIntervalMs, heartbeat);
|
|
181
|
+
};
|
|
182
|
+
scheduler.schedule(KEEP_ALIVE_KEY, keepAliveIntervalMs, heartbeat);
|
|
183
|
+
disposers.push(() => scheduler.cancel(KEEP_ALIVE_KEY));
|
|
184
|
+
}
|
|
185
|
+
sendJson(socket, {
|
|
186
|
+
type: "ready",
|
|
187
|
+
sessionId: leased.managed.id,
|
|
188
|
+
turnId: leased.managed.currentContextId,
|
|
189
|
+
resumed: leased.resumed,
|
|
190
|
+
resumeWindowMs,
|
|
191
|
+
maxSessionDurationMs,
|
|
192
|
+
audio: {
|
|
193
|
+
inputSampleRateHz,
|
|
194
|
+
outputSampleRateHz,
|
|
195
|
+
encoding: "pcm_s16le",
|
|
196
|
+
supportedInputCodecs: ["pcm_s16le"],
|
|
197
|
+
channels: 1,
|
|
198
|
+
binaryEnvelope: SYRINX_AUDIO_ENVELOPE_NAME,
|
|
199
|
+
rawBinaryInput,
|
|
200
|
+
maxInboundMessageBytes,
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
ready = true;
|
|
204
|
+
for (const pending of pendingMessages.splice(0)) {
|
|
205
|
+
pendingMessageBytes -= pending.byteLength;
|
|
206
|
+
processMessage(pending.data, pending.isBinary);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
sendJson(socket, {
|
|
211
|
+
type: "error",
|
|
212
|
+
component: "session",
|
|
213
|
+
category: "initialization",
|
|
214
|
+
message: err instanceof Error ? err.message : String(err),
|
|
215
|
+
});
|
|
216
|
+
socket.dispose();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function wireEdgeSessionEvents(session, socket, disposers, outputSampleRateHz, turnMetricsTurns, recorder, backgroundAudio) {
|
|
220
|
+
if (backgroundAudio)
|
|
221
|
+
wireBackgroundAudio(session, backgroundAudio);
|
|
222
|
+
// Same per-turn latency decomposition the Node websocket path emits (turn-metrics.ts) —
|
|
223
|
+
// the browser/edge protocol is shared, so the `metrics` message must be too. Unlike
|
|
224
|
+
// Node, this transport is client-paced (no server-side pacer guarantees a
|
|
225
|
+
// `tts.playout_progress` completion), so also finalize on `tts.end` — see
|
|
226
|
+
// TurnMetricsTrackerOptions.finalizeOnTtsEnd.
|
|
227
|
+
const turnMetrics = new TurnMetricsTracker(session.bus, (message) => sendJson(socket, message), turnMetricsTurns, { finalizeOnTtsEnd: true });
|
|
228
|
+
turnMetrics.wire(disposers);
|
|
229
|
+
const onSession = (event, handler) => {
|
|
230
|
+
session.on(event, handler);
|
|
231
|
+
disposers.push(() => session.off(event, handler));
|
|
232
|
+
};
|
|
233
|
+
onSession("user_input_final", (event) => {
|
|
234
|
+
// Skip empty transcripts — a trailing-silence turn (e.g. realtime VAD re-triggering on silence)
|
|
235
|
+
// produces an empty user input that would render as a blank transcript bubble.
|
|
236
|
+
if (!event.text.trim())
|
|
237
|
+
return;
|
|
238
|
+
sendJson(socket, { type: "stt_output", turnId: event.turnId, transcript: event.text, confidence: event.confidence });
|
|
239
|
+
});
|
|
240
|
+
onSession("agent_text_delta", (event) => {
|
|
241
|
+
sendJson(socket, { type: "agent_chunk", turnId: event.turnId, text: event.delta });
|
|
242
|
+
});
|
|
243
|
+
// G3 (RFC bimodel-delegate-seam): typed preamble/filler lifecycle. The standard
|
|
244
|
+
// "thinking" wire cue — clients key earcons/indicators on these instead of an
|
|
245
|
+
// app-invented message. started fires before the reasoner runs; delayed is the
|
|
246
|
+
// time-triggered "still working"; complete/failed end the wait (incl. barge-in).
|
|
247
|
+
onSession("tool_call_cue", (event) => {
|
|
248
|
+
sendJson(socket, {
|
|
249
|
+
type: `tool_call_${event.phase}`,
|
|
250
|
+
turnId: event.turnId,
|
|
251
|
+
toolId: event.toolId,
|
|
252
|
+
toolName: event.toolName,
|
|
253
|
+
...(event.afterMs !== undefined ? { afterMs: event.afterMs } : {}),
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
onSession("agent_finished", (event) => {
|
|
257
|
+
sendJson(socket, { type: "agent_end", turnId: event.turnId });
|
|
258
|
+
});
|
|
259
|
+
onSession("error", (event) => {
|
|
260
|
+
sendJson(socket, { type: "error", component: event.stage, category: event.category, message: event.message });
|
|
261
|
+
});
|
|
262
|
+
disposers.push(session.bus.on("tts.audio", (pkt) => {
|
|
263
|
+
const audio = pkt;
|
|
264
|
+
const ttsSampleRate = requireTtsAudioSampleRate(audio.sampleRateHz) ?? outputSampleRateHz;
|
|
265
|
+
// Recorder gets the CLEAN assistant track; the bed is a wire-level effect.
|
|
266
|
+
recorder?.onAssistantAudio(audio.contextId, audio.audio, ttsSampleRate);
|
|
267
|
+
const wireAudio = backgroundAudio ? backgroundAudio.mix(audio.audio, ttsSampleRate) : audio.audio;
|
|
268
|
+
sendJson(socket, {
|
|
269
|
+
type: "tts_chunk",
|
|
270
|
+
turnId: audio.contextId,
|
|
271
|
+
sequence: 1,
|
|
272
|
+
sampleRateHz: ttsSampleRate,
|
|
273
|
+
encoding: "pcm_s16le",
|
|
274
|
+
channels: 1,
|
|
275
|
+
byteLength: wireAudio.byteLength,
|
|
276
|
+
durationMs: pcm16DurationMs(wireAudio, outputSampleRateHz),
|
|
277
|
+
});
|
|
278
|
+
socket.send(encodeSyrinxAudioEnvelope({
|
|
279
|
+
type: "audio",
|
|
280
|
+
contextId: audio.contextId,
|
|
281
|
+
sequence: 1,
|
|
282
|
+
sampleRateHz: ttsSampleRate,
|
|
283
|
+
encoding: "pcm_s16le",
|
|
284
|
+
channels: 1,
|
|
285
|
+
byteLength: wireAudio.byteLength,
|
|
286
|
+
durationMs: pcm16DurationMs(wireAudio, outputSampleRateHz),
|
|
287
|
+
}, wireAudio));
|
|
288
|
+
}), session.bus.on("tts.end", (pkt) => {
|
|
289
|
+
const end = pkt;
|
|
290
|
+
sendJson(socket, { type: "tts_end", ...(end.contextId ? { turnId: end.contextId } : {}) });
|
|
291
|
+
}), session.bus.on("eos.turn_complete", (pkt) => {
|
|
292
|
+
const turn = pkt;
|
|
293
|
+
sendJson(socket, { type: "turn_complete", turnId: turn.contextId, transcript: turn.text ?? "" });
|
|
294
|
+
}),
|
|
295
|
+
// Barge-in: tell the client to flush queued playout immediately (same wire
|
|
296
|
+
// messages as the Node server path — the browser client flushes its jitter
|
|
297
|
+
// buffer on either).
|
|
298
|
+
session.bus.on("interrupt.detected", (pkt) => {
|
|
299
|
+
const interrupt = pkt;
|
|
300
|
+
sendJson(socket, { type: "audio_clear", turnId: interrupt.contextId, reason: "barge_in" });
|
|
301
|
+
sendJson(socket, { type: "agent_interrupted", turnId: interrupt.contextId, reason: "barge_in" });
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
function handleClientMessage(session, data, isBinary, currentContextId, contextId, inputSampleRateHz, rawBinaryInput, contextSampleRates, inputSequence, streamingResamplers) {
|
|
305
|
+
if (isBinary) {
|
|
306
|
+
const binaryAudio = decodeInboundBinaryAudio(socketDataToBytes(data), inputSampleRateHz, rawBinaryInput, inputSampleRateHz, streamingResamplers, null);
|
|
307
|
+
const nextContextId = binaryAudio.contextId ?? currentContextId;
|
|
308
|
+
if (nextContextId !== currentContextId) {
|
|
309
|
+
session.bus.push(Route.Main, {
|
|
310
|
+
kind: "turn.change",
|
|
311
|
+
contextId: nextContextId,
|
|
312
|
+
previousContextId: currentContextId,
|
|
313
|
+
reason: "websocket_binary_audio_turn",
|
|
314
|
+
timestampMs: Date.now(),
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
rememberContextSampleRate(contextSampleRates, nextContextId, binaryAudio.sampleRateHz);
|
|
318
|
+
rememberInputSequence(session, inputSequence, nextContextId, binaryAudio.sequence);
|
|
319
|
+
const audio = resampleAudioBytes(binaryAudio.audio, binaryAudio.sampleRateHz, inputSampleRateHz, streamingResamplers);
|
|
320
|
+
session.bus.push(Route.Main, {
|
|
321
|
+
kind: "user.audio_received",
|
|
322
|
+
contextId: nextContextId,
|
|
323
|
+
timestampMs: Date.now(),
|
|
324
|
+
audio,
|
|
325
|
+
sampleRateHz: inputSampleRateHz,
|
|
326
|
+
});
|
|
327
|
+
return nextContextId;
|
|
328
|
+
}
|
|
329
|
+
const message = parseClientMessage(JSON.parse(typeof data === "string" ? data : textDecoder.decode(data)));
|
|
330
|
+
if (message.type === "ping")
|
|
331
|
+
return currentContextId;
|
|
332
|
+
if (message.type === "codec_capability")
|
|
333
|
+
return currentContextId; // edge sends pcm_s16le downlink; no-op
|
|
334
|
+
if (message.type === "client_interrupt") {
|
|
335
|
+
const interruptedContextId = message.assistantContextId ?? message.contextId ?? currentContextId;
|
|
336
|
+
if (interruptedContextId)
|
|
337
|
+
session.requestClientInterrupt(interruptedContextId);
|
|
338
|
+
return currentContextId;
|
|
339
|
+
}
|
|
340
|
+
if (message.type === "playout_progress") {
|
|
341
|
+
// On client-rendered-audio transports the browser is the playout clock: the server streams
|
|
342
|
+
// audio envelopes and the client schedules them. Map its reported position onto the same
|
|
343
|
+
// `tts.playout_progress` packet the server-paced path emits, so turn-truncation (realtime
|
|
344
|
+
// barge-in) and turn-metrics consume an accurate played-out offset instead of a stale 0.
|
|
345
|
+
const progressContextId = message.contextId ?? currentContextId;
|
|
346
|
+
if (progressContextId) {
|
|
347
|
+
session.bus.push(Route.Main, {
|
|
348
|
+
kind: "tts.playout_progress",
|
|
349
|
+
contextId: progressContextId,
|
|
350
|
+
timestampMs: Date.now(),
|
|
351
|
+
playedOutMs: message.playedOutMs,
|
|
352
|
+
complete: message.complete ?? false,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return currentContextId;
|
|
356
|
+
}
|
|
357
|
+
if (message.type === "text") {
|
|
358
|
+
const nextContextId = message.contextId ?? contextId();
|
|
359
|
+
session.bus.push(Route.Main, {
|
|
360
|
+
kind: "turn.change",
|
|
361
|
+
contextId: nextContextId,
|
|
362
|
+
previousContextId: currentContextId,
|
|
363
|
+
reason: "websocket_text_turn",
|
|
364
|
+
timestampMs: Date.now(),
|
|
365
|
+
});
|
|
366
|
+
session.bus.push(Route.Main, {
|
|
367
|
+
kind: "user.text_received",
|
|
368
|
+
contextId: nextContextId,
|
|
369
|
+
timestampMs: Date.now(),
|
|
370
|
+
text: message.text,
|
|
371
|
+
});
|
|
372
|
+
return nextContextId;
|
|
373
|
+
}
|
|
374
|
+
const nextContextId = message.contextId ?? currentContextId;
|
|
375
|
+
if (nextContextId !== currentContextId) {
|
|
376
|
+
session.bus.push(Route.Main, {
|
|
377
|
+
kind: "turn.change",
|
|
378
|
+
contextId: nextContextId,
|
|
379
|
+
previousContextId: currentContextId,
|
|
380
|
+
reason: "websocket_audio_turn",
|
|
381
|
+
timestampMs: Date.now(),
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
rememberContextSampleRate(contextSampleRates, nextContextId, message.sampleRateHz);
|
|
385
|
+
rememberInputSequence(session, inputSequence, nextContextId, message.sequence);
|
|
386
|
+
const audio = resampleAudioBytes(decodeBase64(message.audio), message.sampleRateHz, inputSampleRateHz, streamingResamplers);
|
|
387
|
+
session.bus.push(Route.Main, {
|
|
388
|
+
kind: "user.audio_received",
|
|
389
|
+
contextId: nextContextId,
|
|
390
|
+
timestampMs: Date.now(),
|
|
391
|
+
audio,
|
|
392
|
+
sampleRateHz: inputSampleRateHz,
|
|
393
|
+
});
|
|
394
|
+
return nextContextId;
|
|
395
|
+
}
|
|
396
|
+
function parseClientMessage(value) {
|
|
397
|
+
if (!isRecord(value))
|
|
398
|
+
throw new Error("Websocket JSON message must be an object");
|
|
399
|
+
if (value.type === "ping")
|
|
400
|
+
return { type: "ping" };
|
|
401
|
+
if (value.type === "codec_capability") {
|
|
402
|
+
// The edge transmits pcm_s16le downlink only (no opus encoder on workerd); accept the client's
|
|
403
|
+
// capability advert and no-op it. The client decodes per-frame `encoding`, so pcm is always safe.
|
|
404
|
+
const downlinkEncoding = value.downlinkEncoding === "opus" ? "opus" : "pcm_s16le";
|
|
405
|
+
return { type: "codec_capability", downlinkEncoding };
|
|
406
|
+
}
|
|
407
|
+
if (value.type === "client_interrupt") {
|
|
408
|
+
return {
|
|
409
|
+
type: "client_interrupt",
|
|
410
|
+
assistantContextId: optionalString(value.assistantContextId),
|
|
411
|
+
contextId: optionalString(value.contextId),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
if (value.type === "playout_progress") {
|
|
415
|
+
const playedOutMs = nonNegativeInteger(value.playedOutMs);
|
|
416
|
+
if (playedOutMs === null) {
|
|
417
|
+
throw new Error("Websocket playout_progress playedOutMs must be a non-negative integer");
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
type: "playout_progress",
|
|
421
|
+
contextId: optionalString(value.contextId),
|
|
422
|
+
playedOutMs,
|
|
423
|
+
complete: typeof value.complete === "boolean" ? value.complete : undefined,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (value.type === "text") {
|
|
427
|
+
const text = optionalString(value.text);
|
|
428
|
+
if (!text)
|
|
429
|
+
throw new Error("Websocket JSON text must be a non-empty string");
|
|
430
|
+
return { type: "text", text, contextId: optionalString(value.contextId) };
|
|
431
|
+
}
|
|
432
|
+
if (value.type === "audio") {
|
|
433
|
+
const audio = optionalString(value.audio);
|
|
434
|
+
const sampleRateHz = positiveInteger(value.sampleRateHz);
|
|
435
|
+
if (!audio)
|
|
436
|
+
throw new Error("Websocket JSON audio must be a non-empty base64 string");
|
|
437
|
+
if (!sampleRateHz)
|
|
438
|
+
throw new Error("JSON websocket audio sampleRateHz must be a positive integer");
|
|
439
|
+
return {
|
|
440
|
+
type: "audio",
|
|
441
|
+
audio,
|
|
442
|
+
contextId: optionalString(value.contextId),
|
|
443
|
+
sampleRateHz,
|
|
444
|
+
sequence: nonNegativeInteger(value.sequence) ?? undefined,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
throw new Error("Unsupported client message type");
|
|
448
|
+
}
|
|
449
|
+
function rememberInputSequence(session, state, contextId, sequence) {
|
|
450
|
+
if (sequence === undefined)
|
|
451
|
+
return;
|
|
452
|
+
const previous = state.lastSequence;
|
|
453
|
+
if (previous !== null && sequence <= previous) {
|
|
454
|
+
throw new Error(`Websocket audio sequence must increase monotonically: ${String(previous)} -> ${String(sequence)}`);
|
|
455
|
+
}
|
|
456
|
+
if (previous !== null && sequence > previous + 1) {
|
|
457
|
+
session.bus.push(Route.Background, {
|
|
458
|
+
kind: "metric.conversation",
|
|
459
|
+
contextId,
|
|
460
|
+
timestampMs: Date.now(),
|
|
461
|
+
name: "websocket.audio_sequence_gap",
|
|
462
|
+
value: JSON.stringify({ expected: previous + 1, actual: sequence, missed: sequence - previous - 1 }),
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
state.lastSequence = sequence;
|
|
466
|
+
}
|
|
467
|
+
function sendJson(socket, value) {
|
|
468
|
+
if (!socket.isOpen)
|
|
469
|
+
return;
|
|
470
|
+
socket.send(JSON.stringify(value));
|
|
471
|
+
}
|
|
472
|
+
const textEncoder = new TextEncoder();
|
|
473
|
+
const textDecoder = new TextDecoder();
|
|
474
|
+
function decodeBase64(value) {
|
|
475
|
+
const raw = globalThis.atob(value);
|
|
476
|
+
const bytes = new Uint8Array(raw.length);
|
|
477
|
+
for (let i = 0; i < raw.length; i += 1)
|
|
478
|
+
bytes[i] = raw.charCodeAt(i);
|
|
479
|
+
return bytes;
|
|
480
|
+
}
|
|
481
|
+
function socketDataByteLength(data) {
|
|
482
|
+
return typeof data === "string" ? textEncoder.encode(data).byteLength : data.byteLength;
|
|
483
|
+
}
|
|
484
|
+
function cloneSocketData(data) {
|
|
485
|
+
return typeof data === "string" ? data : data.slice();
|
|
486
|
+
}
|
|
487
|
+
function sessionIdFromRequest(request) {
|
|
488
|
+
try {
|
|
489
|
+
const parsed = new URL(request.url);
|
|
490
|
+
return parsed.searchParams.get("sessionId") ?? parsed.searchParams.get("session_id");
|
|
491
|
+
}
|
|
492
|
+
catch {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function sanitizeSessionId(value) {
|
|
497
|
+
const trimmed = value.trim();
|
|
498
|
+
if (/^[A-Za-z0-9_.:-]{1,128}$/.test(trimmed))
|
|
499
|
+
return trimmed;
|
|
500
|
+
return defaultSessionId();
|
|
501
|
+
}
|
|
502
|
+
function isRecord(value) {
|
|
503
|
+
return typeof value === "object" && value !== null;
|
|
504
|
+
}
|
|
505
|
+
function optionalString(value) {
|
|
506
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
507
|
+
}
|
|
508
|
+
function positiveInteger(value) {
|
|
509
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0)
|
|
510
|
+
return null;
|
|
511
|
+
return value;
|
|
512
|
+
}
|
|
513
|
+
function nonNegativeInteger(value) {
|
|
514
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0)
|
|
515
|
+
return null;
|
|
516
|
+
return value;
|
|
517
|
+
}
|
|
518
|
+
function requireTtsAudioSampleRate(value) {
|
|
519
|
+
return positiveInteger(value);
|
|
520
|
+
}
|
|
521
|
+
function pcm16DurationMs(audio, sampleRateHz) {
|
|
522
|
+
if (sampleRateHz <= 0)
|
|
523
|
+
return 0;
|
|
524
|
+
return Math.round((audio.byteLength / 2 / sampleRateHz) * 1000);
|
|
525
|
+
}
|
|
526
|
+
function defaultContextId() {
|
|
527
|
+
return `turn-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
528
|
+
}
|
|
529
|
+
function defaultSessionId() {
|
|
530
|
+
return `session-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
531
|
+
}
|
|
532
|
+
async function withScheduledTimeout(promise, timeoutMs, scheduler, key) {
|
|
533
|
+
if (timeoutMs <= 0)
|
|
534
|
+
return await promise;
|
|
535
|
+
let settled = false;
|
|
536
|
+
return await new Promise((resolve, reject) => {
|
|
537
|
+
scheduler.schedule(key, timeoutMs, () => {
|
|
538
|
+
if (settled)
|
|
539
|
+
return;
|
|
540
|
+
settled = true;
|
|
541
|
+
reject(new Error(`websocket session startup exceeded ${String(timeoutMs)}ms`));
|
|
542
|
+
});
|
|
543
|
+
promise
|
|
544
|
+
.then((value) => {
|
|
545
|
+
if (settled)
|
|
546
|
+
return;
|
|
547
|
+
settled = true;
|
|
548
|
+
scheduler.cancel(key);
|
|
549
|
+
resolve(value);
|
|
550
|
+
})
|
|
551
|
+
.catch((err) => {
|
|
552
|
+
if (settled)
|
|
553
|
+
return;
|
|
554
|
+
settled = true;
|
|
555
|
+
scheduler.cancel(key);
|
|
556
|
+
reject(err);
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
//# sourceMappingURL=edge.js.map
|
package/dist/edge.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type StreamingPcm16Resampler } from "@kuralle-syrinx/core/audio";
|
|
2
|
+
export type OpusIngressDecoder = (wire: Uint8Array, sampleRateHz: number) => Uint8Array;
|
|
3
|
+
export interface DecodedInboundBinaryAudio {
|
|
4
|
+
readonly contextId?: string;
|
|
5
|
+
readonly sampleRateHz: number;
|
|
6
|
+
readonly sequence?: number;
|
|
7
|
+
readonly audio: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
export declare function socketDataToBytes(data: string | Uint8Array): Uint8Array;
|
|
10
|
+
export declare function decodeInboundBinaryAudio(data: Uint8Array, defaultSampleRateHz: number, rawBinaryInput: boolean, engineInputSampleRateHz: number, streamingResamplers: Map<string, StreamingPcm16Resampler>, decodeOpusIngress: OpusIngressDecoder | null): DecodedInboundBinaryAudio;
|
|
11
|
+
export declare function rememberContextSampleRate(contextSampleRates: Map<string, number>, contextId: string, sampleRateHz: number): void;
|
|
12
|
+
export declare function resampleAudioBytes(audio: Uint8Array, sourceSampleRateHz: number, targetSampleRateHz: number, streamingResamplers: Map<string, StreamingPcm16Resampler>): Uint8Array;
|
|
13
|
+
//# sourceMappingURL=inbound-audio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-audio.d.ts","sourceRoot":"","sources":["../src/inbound-audio.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,KAAK,UAAU,CAAC;AAExF,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,CAGvE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,UAAU,EAChB,mBAAmB,EAAE,MAAM,EAC3B,cAAc,EAAE,OAAO,EACvB,uBAAuB,EAAE,MAAM,EAC/B,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,EACzD,iBAAiB,EAAE,kBAAkB,GAAG,IAAI,GAC3C,yBAAyB,CAyB3B;AAkBD,wBAAgB,yBAAyB,CACvC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACvC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,IAAI,CAMN;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EACjB,kBAAkB,EAAE,MAAM,EAC1B,kBAAkB,EAAE,MAAM,EAC1B,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,GACxD,UAAU,CAQZ"}
|