@voxevolv/client 0.3.15 → 0.3.17
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/CHANGELOG.md +25 -0
- package/README.md +5 -1
- package/dist/browser/index.js +7 -7
- package/dist/browser/index.js.map +4 -4
- package/dist/browser/index.js.sha384 +1 -1
- package/dist/browser/manifest.json +1 -1
- package/dist/connection-diagnostics.d.ts +24 -0
- package/dist/connection-diagnostics.d.ts.map +1 -0
- package/dist/connection-diagnostics.js +155 -0
- package/dist/connection-diagnostics.js.map +1 -0
- package/dist/diagnostics.d.ts +19 -2
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +49 -8
- package/dist/diagnostics.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +149 -25
- package/dist/index.js.map +2 -2
- package/dist/presentations.d.ts +20 -0
- package/dist/presentations.d.ts.map +1 -0
- package/dist/presentations.js +91 -0
- package/dist/presentations.js.map +1 -0
- package/dist/rpc-transport-diagnostics.d.ts +6 -0
- package/dist/rpc-transport-diagnostics.d.ts.map +1 -0
- package/dist/rpc-transport-diagnostics.js +84 -0
- package/dist/rpc-transport-diagnostics.js.map +1 -0
- package/dist/vendor/livekit-client.esm.mjs +32 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
Room,
|
|
8
8
|
RoomEvent,
|
|
9
9
|
RpcError,
|
|
10
|
-
Track
|
|
10
|
+
Track,
|
|
11
|
+
version as LIVEKIT_VERSION
|
|
11
12
|
} from "./vendor/livekit-client.esm.mjs";
|
|
12
13
|
import {
|
|
13
14
|
createClientMessage,
|
|
@@ -23,8 +24,12 @@ import {
|
|
|
23
24
|
import { ContextDeliveryError, contextRetryDelay, ReplacementContextQueue } from "./context-queue.js";
|
|
24
25
|
import {
|
|
25
26
|
ClientDiagnosticBuffer,
|
|
27
|
+
diagnosticId,
|
|
26
28
|
DiagnosticDeliveryError
|
|
27
29
|
} from "./diagnostics.js";
|
|
30
|
+
import { observeConnectionTransport } from "./connection-diagnostics.js";
|
|
31
|
+
import { observeRpcTransport } from "./rpc-transport-diagnostics.js";
|
|
32
|
+
import { Presentations } from "./presentations.js";
|
|
28
33
|
import { TranscriptStore } from "./transcripts.js";
|
|
29
34
|
import { TranscriptStore as TranscriptStore2 } from "./transcripts.js";
|
|
30
35
|
import {
|
|
@@ -49,7 +54,7 @@ import {
|
|
|
49
54
|
decodeContextUpdatedPayload,
|
|
50
55
|
decodeRealtimeEvent
|
|
51
56
|
} from "./generated/protocol-codecs.js";
|
|
52
|
-
var SDK_VERSION = "0.3.
|
|
57
|
+
var SDK_VERSION = "0.3.17";
|
|
53
58
|
var CLIENT_HELLO_TIMEOUT_MS = 3e4;
|
|
54
59
|
var RECOVERY_BUFFER_STALL_MS = 5e3;
|
|
55
60
|
var RECOVERY_BUFFER_POLL_MS = 500;
|
|
@@ -172,9 +177,33 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
172
177
|
diagnosticLoopTimer;
|
|
173
178
|
diagnosticLoopExpectedAt = 0;
|
|
174
179
|
diagnosticLoopDelayMs;
|
|
180
|
+
diagnosticLoopHistory = [];
|
|
181
|
+
diagnosticLastSampleAt = 0;
|
|
182
|
+
stopRpcDiagnostics;
|
|
183
|
+
diagnosticEngine;
|
|
184
|
+
connectionDiagnostics;
|
|
185
|
+
diagnosticRecoveryId;
|
|
186
|
+
presentations = new Presentations(
|
|
187
|
+
(request) => this.room.localParticipant.performRpc(request),
|
|
188
|
+
() => this.sessionEnded || ["disconnected", "failed"].includes(this.stateValue),
|
|
189
|
+
() => {
|
|
190
|
+
this.recordDiagnostic({ operation: "tool_rpc", status: "failed", reason: "presentation_receipt_failed" });
|
|
191
|
+
this.flushDiagnostics("presentation_receipt_failed");
|
|
192
|
+
}
|
|
193
|
+
);
|
|
175
194
|
diagnosticReportTimeoutMs = DIAGNOSTIC_REPORT_TIMEOUT_MS;
|
|
176
195
|
onDiagnosticVisibilityChange = () => {
|
|
177
|
-
|
|
196
|
+
const reason = globalThis.document?.visibilityState === "hidden" ? "page_hidden" : "page_visible";
|
|
197
|
+
this.recordDiagnostic({ operation: "browser_runtime", status: "succeeded", reason });
|
|
198
|
+
this.flushDiagnostics(reason);
|
|
199
|
+
};
|
|
200
|
+
onDiagnosticNetworkChange = (event) => {
|
|
201
|
+
this.recordDiagnostic({ operation: "browser_runtime", status: "succeeded", reason: `network_${event.type}` });
|
|
202
|
+
this.flushDiagnostics(`network_${event.type}`);
|
|
203
|
+
};
|
|
204
|
+
onDiagnosticLifecycle = (event) => {
|
|
205
|
+
this.recordDiagnostic({ operation: "browser_runtime", status: "succeeded", reason: event.type });
|
|
206
|
+
this.flushDiagnostics(`page_${event.type}`);
|
|
178
207
|
};
|
|
179
208
|
constructor(credentials, options = {}) {
|
|
180
209
|
validateSessionCredentials(credentials);
|
|
@@ -194,6 +223,9 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
194
223
|
this.startupRetryOptions = resolveStartupRetryOptions(options.startupRetry);
|
|
195
224
|
this.lifecycleCredentials = options.lifecycleCredentials ?? "same-origin";
|
|
196
225
|
this.room = new Room({ adaptiveStream: true, dynacast: true });
|
|
226
|
+
this.room.voxevolvObserveEngine = () => {
|
|
227
|
+
this.startRpcDiagnostics();
|
|
228
|
+
};
|
|
197
229
|
for (const [name, handler] of Object.entries(options.tools ?? {})) {
|
|
198
230
|
this.registerTool(name, handler);
|
|
199
231
|
}
|
|
@@ -213,6 +245,7 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
213
245
|
this.flushDiagnostics("tool_rpc_worker_request");
|
|
214
246
|
return "{}";
|
|
215
247
|
});
|
|
248
|
+
this.startRpcDiagnostics();
|
|
216
249
|
this.bindRoomEvents();
|
|
217
250
|
}
|
|
218
251
|
async invokeBrowserTool(invocation) {
|
|
@@ -248,17 +281,29 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
248
281
|
throw new RpcError(1500, "Tool invocation arguments must be an object");
|
|
249
282
|
}
|
|
250
283
|
const arguments_ = payload.arguments;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
record({ stage: "handler_started"
|
|
256
|
-
this.flushDiagnostics("
|
|
257
|
-
|
|
284
|
+
const context = { callId };
|
|
285
|
+
const toolCall = { name: toolName, callId, arguments: arguments_ };
|
|
286
|
+
const execute = async () => {
|
|
287
|
+
this.notifyListeners(this.toolCallListeners, toolCall);
|
|
288
|
+
record({ stage: "handler_started" });
|
|
289
|
+
this.flushDiagnostics("tool_rpc_received");
|
|
290
|
+
slowTimer = globalThis.setTimeout(() => {
|
|
291
|
+
record({ stage: "handler_started", reason: "handler_slow" });
|
|
292
|
+
this.flushDiagnostics("tool_rpc_slow");
|
|
293
|
+
}, 2e3);
|
|
294
|
+
const result = await handler(arguments_, context);
|
|
295
|
+
return JSON.stringify(result ?? null);
|
|
296
|
+
};
|
|
258
297
|
let response;
|
|
259
298
|
try {
|
|
260
|
-
|
|
261
|
-
|
|
299
|
+
if (payload.presentation_id !== void 0) {
|
|
300
|
+
if (toolName !== "present_attachment" || typeof payload.presentation_id !== "string" || !payload.presentation_id || payload.presentation_id.length > 200) {
|
|
301
|
+
throw new RpcError(1500, "Invalid presentation identity");
|
|
302
|
+
}
|
|
303
|
+
response = await this.presentations.invoke(payload.presentation_id, invocation.callerIdentity, execute);
|
|
304
|
+
} else {
|
|
305
|
+
response = await execute();
|
|
306
|
+
}
|
|
262
307
|
} catch (error) {
|
|
263
308
|
const message = error instanceof Error ? error.message : "Browser tool handler failed";
|
|
264
309
|
throw new RpcError(1500, message, void 0, { cause: error });
|
|
@@ -671,6 +716,15 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
671
716
|
} catch (error) {
|
|
672
717
|
if (signal?.aborted) this.rejectPendingCommands(toError(abortError(signal)));
|
|
673
718
|
this.disconnectMode = recovering ? "rejoin" : "connect_failure";
|
|
719
|
+
if (!recovering) {
|
|
720
|
+
this.livekitResumeController?.abort(toError(error));
|
|
721
|
+
this.livekitResumeController = null;
|
|
722
|
+
this.failRecovery(toError(error));
|
|
723
|
+
this.recoveryStartedAt = null;
|
|
724
|
+
this.diagnosticRecoveryId = void 0;
|
|
725
|
+
this.reconnectStartedAt = null;
|
|
726
|
+
this.pendingReconnect = null;
|
|
727
|
+
}
|
|
674
728
|
try {
|
|
675
729
|
await this.room.disconnect();
|
|
676
730
|
} catch {
|
|
@@ -1072,15 +1126,32 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
1072
1126
|
setState(state) {
|
|
1073
1127
|
if (this.stateValue === state) return;
|
|
1074
1128
|
this.stateValue = state;
|
|
1129
|
+
this.recordDiagnostic({ operation: "browser_runtime", status: "succeeded", reason: `state_${state}` });
|
|
1075
1130
|
if (state === "connecting" || state === "resuming") {
|
|
1131
|
+
this.startRpcDiagnostics();
|
|
1076
1132
|
this.startDiagnosticLoopMonitor();
|
|
1133
|
+
globalThis.addEventListener?.("online", this.onDiagnosticNetworkChange);
|
|
1134
|
+
globalThis.addEventListener?.("offline", this.onDiagnosticNetworkChange);
|
|
1077
1135
|
globalThis.document?.addEventListener?.("visibilitychange", this.onDiagnosticVisibilityChange);
|
|
1136
|
+
globalThis.document?.addEventListener?.("freeze", this.onDiagnosticLifecycle);
|
|
1137
|
+
globalThis.document?.addEventListener?.("resume", this.onDiagnosticLifecycle);
|
|
1078
1138
|
}
|
|
1079
1139
|
if (state === "disconnected" || state === "failed") {
|
|
1080
1140
|
globalThis.clearInterval(this.diagnosticLoopTimer);
|
|
1081
1141
|
this.diagnosticLoopTimer = void 0;
|
|
1082
1142
|
this.diagnosticLoopDelayMs = void 0;
|
|
1143
|
+
this.diagnosticLoopHistory = [];
|
|
1144
|
+
this.connectionDiagnostics?.stop();
|
|
1145
|
+
this.connectionDiagnostics = void 0;
|
|
1146
|
+
this.diagnosticRecoveryId = void 0;
|
|
1147
|
+
globalThis.removeEventListener?.("online", this.onDiagnosticNetworkChange);
|
|
1148
|
+
globalThis.removeEventListener?.("offline", this.onDiagnosticNetworkChange);
|
|
1149
|
+
this.stopRpcDiagnostics?.();
|
|
1150
|
+
this.stopRpcDiagnostics = void 0;
|
|
1151
|
+
this.diagnosticEngine = void 0;
|
|
1083
1152
|
globalThis.document?.removeEventListener?.("visibilitychange", this.onDiagnosticVisibilityChange);
|
|
1153
|
+
globalThis.document?.removeEventListener?.("freeze", this.onDiagnosticLifecycle);
|
|
1154
|
+
globalThis.document?.removeEventListener?.("resume", this.onDiagnosticLifecycle);
|
|
1084
1155
|
this.flushDiagnostics("session_finished");
|
|
1085
1156
|
const error = new Error(`Session ${state}`);
|
|
1086
1157
|
this.contextGeneration += 1;
|
|
@@ -1114,12 +1185,23 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
1114
1185
|
});
|
|
1115
1186
|
}
|
|
1116
1187
|
});
|
|
1117
|
-
this.room.on(RoomEvent.
|
|
1118
|
-
this.room.on(RoomEvent.
|
|
1188
|
+
this.room.on(RoomEvent.SignalConnected, () => this.startRpcDiagnostics());
|
|
1189
|
+
this.room.on(RoomEvent.SignalReconnecting, () => this.handleLiveKitReconnecting("signal_reconnecting"));
|
|
1190
|
+
this.room.on(RoomEvent.Reconnecting, () => this.handleLiveKitReconnecting("media_reconnecting"));
|
|
1119
1191
|
this.room.on(RoomEvent.Reconnected, () => {
|
|
1192
|
+
this.startRpcDiagnostics();
|
|
1120
1193
|
void this.handleLiveKitReconnected();
|
|
1121
1194
|
});
|
|
1122
1195
|
this.room.on(RoomEvent.Disconnected, (reason) => {
|
|
1196
|
+
if (!this.disconnectMode && !this.sessionEnded) {
|
|
1197
|
+
this.recordDiagnostic({
|
|
1198
|
+
operation: "connection_transport",
|
|
1199
|
+
status: "failed",
|
|
1200
|
+
reason: "room_disconnected",
|
|
1201
|
+
disconnect_reason: reason
|
|
1202
|
+
});
|
|
1203
|
+
this.flushDiagnostics("room_disconnected");
|
|
1204
|
+
}
|
|
1123
1205
|
this.handleLiveKitDisconnected(reason);
|
|
1124
1206
|
});
|
|
1125
1207
|
this.room.on(RoomEvent.ConnectionStateChanged, (state) => {
|
|
@@ -1199,8 +1281,9 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
1199
1281
|
this.notifyListeners(this.listeners, value);
|
|
1200
1282
|
});
|
|
1201
1283
|
}
|
|
1202
|
-
handleLiveKitReconnecting() {
|
|
1284
|
+
handleLiveKitReconnecting(trigger = "livekit_reconnecting") {
|
|
1203
1285
|
if (this.sessionEnded || this.disconnectMode === "intentional" || this.terminalReplayController || ["disconnected", "failed"].includes(this.stateValue)) return;
|
|
1286
|
+
this.recordDiagnostic({ operation: "connection_transport", status: "started", reason: trigger });
|
|
1204
1287
|
this.agentReady = false;
|
|
1205
1288
|
if (this.autoRejoinPromise !== null || this.activeRecovery?.strategy === "rejoin" || this.disconnectMode === "rejoin") {
|
|
1206
1289
|
this.setState("resuming");
|
|
@@ -1380,6 +1463,7 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
1380
1463
|
this.notifyListeners(this.endedListeners, event);
|
|
1381
1464
|
}
|
|
1382
1465
|
beginRecovery(strategy, attempt, details) {
|
|
1466
|
+
this.diagnosticRecoveryId ??= diagnosticId();
|
|
1383
1467
|
this.recoveryStartedAt ??= Date.now();
|
|
1384
1468
|
this.activeRecovery = { strategy, attempt, details };
|
|
1385
1469
|
this.recordDiagnostic({
|
|
@@ -1412,6 +1496,7 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
1412
1496
|
});
|
|
1413
1497
|
this.flushDiagnostics("recovery_completed");
|
|
1414
1498
|
this.recoveryStartedAt = null;
|
|
1499
|
+
this.diagnosticRecoveryId = void 0;
|
|
1415
1500
|
this.activeRecovery = null;
|
|
1416
1501
|
this.notifyListeners(this.recoveryListeners, {
|
|
1417
1502
|
status: "succeeded",
|
|
@@ -1519,6 +1604,7 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
1519
1604
|
this.livekitResumeController?.abort();
|
|
1520
1605
|
this.livekitResumeController = null;
|
|
1521
1606
|
this.recoveryStartedAt = null;
|
|
1607
|
+
this.diagnosticRecoveryId = void 0;
|
|
1522
1608
|
this.autoRejoinController?.abort();
|
|
1523
1609
|
this.autoRejoinController = null;
|
|
1524
1610
|
this.manualRejoinController?.abort();
|
|
@@ -1967,30 +2053,68 @@ var RealtimeSession = class _RealtimeSession {
|
|
|
1967
2053
|
options
|
|
1968
2054
|
);
|
|
1969
2055
|
}
|
|
2056
|
+
startRpcDiagnostics() {
|
|
2057
|
+
const engine = this.room.engine;
|
|
2058
|
+
if (!engine || this.stopRpcDiagnostics && this.diagnosticEngine === engine) return;
|
|
2059
|
+
this.stopRpcDiagnostics?.();
|
|
2060
|
+
this.diagnosticEngine = engine;
|
|
2061
|
+
this.connectionDiagnostics?.stop();
|
|
2062
|
+
this.connectionDiagnostics = observeConnectionTransport(engine, (entry) => {
|
|
2063
|
+
if (this.disconnectMode || this.sessionEnded || ["disconnected", "failed"].includes(this.stateValue)) return;
|
|
2064
|
+
this.recordDiagnostic(entry);
|
|
2065
|
+
this.flushDiagnostics("connection_transport");
|
|
2066
|
+
});
|
|
2067
|
+
this.stopRpcDiagnostics = observeRpcTransport(engine, (entry) => {
|
|
2068
|
+
this.recordDiagnostic(entry);
|
|
2069
|
+
this.flushDiagnostics("tool_rpc_transport");
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
1970
2072
|
startDiagnosticLoopMonitor() {
|
|
1971
2073
|
if (this.diagnosticLoopTimer !== void 0) return;
|
|
1972
2074
|
this.diagnosticLoopExpectedAt = performance.now() + 1e3;
|
|
1973
|
-
this.
|
|
1974
|
-
|
|
1975
|
-
this.diagnosticLoopDelayMs = Math.max(0, now - this.diagnosticLoopExpectedAt);
|
|
1976
|
-
this.diagnosticLoopExpectedAt = now + 1e3;
|
|
1977
|
-
}, 1e3);
|
|
2075
|
+
this.diagnosticLastSampleAt = performance.now();
|
|
2076
|
+
this.diagnosticLoopTimer = globalThis.setInterval(() => this.sampleDiagnosticLoop(performance.now()), 1e3);
|
|
1978
2077
|
this.diagnosticLoopTimer.unref?.();
|
|
1979
2078
|
}
|
|
2079
|
+
sampleDiagnosticLoop(now) {
|
|
2080
|
+
const delay = Math.max(0, now - this.diagnosticLoopExpectedAt);
|
|
2081
|
+
this.diagnosticLoopDelayMs = delay;
|
|
2082
|
+
this.diagnosticLoopExpectedAt = now + 1e3;
|
|
2083
|
+
this.diagnosticLoopHistory.push({ at: now, delay });
|
|
2084
|
+
this.diagnosticLoopHistory = this.diagnosticLoopHistory.filter((sample) => now - sample.at < 3e4).slice(-30);
|
|
2085
|
+
if (delay >= 100 || now - this.diagnosticLastSampleAt >= 1e4) {
|
|
2086
|
+
this.diagnosticLastSampleAt = now;
|
|
2087
|
+
this.recordDiagnostic({ operation: "browser_runtime", status: "succeeded", reason: "timer_sample" });
|
|
2088
|
+
if (delay >= 1e3) this.flushDiagnostics("browser_stall");
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
1980
2091
|
recordDiagnostic(entry) {
|
|
2092
|
+
if (entry.operation === "connection_transport") {
|
|
2093
|
+
this.diagnosticRecoveryId ??= diagnosticId();
|
|
2094
|
+
}
|
|
1981
2095
|
const snapshot = {
|
|
1982
2096
|
...entry,
|
|
2097
|
+
recovery_id: this.diagnosticRecoveryId,
|
|
2098
|
+
connection_id: entry.connection_id ?? this.connectionId,
|
|
2099
|
+
participant_sid: this.room.localParticipant.sid || void 0,
|
|
2100
|
+
livekit_version: LIVEKIT_VERSION,
|
|
2101
|
+
browser_online: globalThis.navigator?.onLine,
|
|
1983
2102
|
connection_state: this.stateValue,
|
|
1984
|
-
buffered_bytes: this.reliableDataChannelBufferedBytes()
|
|
2103
|
+
buffered_bytes: entry.buffered_bytes ?? this.reliableDataChannelBufferedBytes()
|
|
1985
2104
|
};
|
|
1986
|
-
if (
|
|
2105
|
+
if (["connection_transport", "connection_recovery"].includes(entry.operation)) {
|
|
2106
|
+
snapshot.transport_history ??= this.connectionDiagnostics?.snapshotHistory();
|
|
2107
|
+
}
|
|
2108
|
+
if (["tool_rpc", "rpc_transport", "browser_runtime", "connection_recovery", "connection_transport"].includes(entry.operation)) {
|
|
1987
2109
|
snapshot.page_visibility = globalThis.document?.visibilityState;
|
|
1988
2110
|
snapshot.active_context_transfers = this.activeContextTransfers;
|
|
1989
2111
|
snapshot.active_context_bytes = this.activeContextBytes;
|
|
1990
2112
|
if (this.diagnosticLoopTimer !== void 0) {
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2113
|
+
const now = performance.now();
|
|
2114
|
+
snapshot.event_loop_delay_ms = Math.max(this.diagnosticLoopDelayMs ?? 0, now - this.diagnosticLoopExpectedAt);
|
|
2115
|
+
snapshot.event_loop_delay_max_ms = Math.max(
|
|
2116
|
+
snapshot.event_loop_delay_ms,
|
|
2117
|
+
...this.diagnosticLoopHistory.filter((sample) => now - sample.at < 3e4).map((sample) => sample.delay)
|
|
1994
2118
|
);
|
|
1995
2119
|
}
|
|
1996
2120
|
}
|