@decartai/sdk 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +109 -10
  2. package/dist/create-client.d.ts +46 -0
  3. package/dist/create-client.js +208 -0
  4. package/dist/files/types.js +4 -1
  5. package/dist/index.d.ts +8 -150
  6. package/dist/index.js +6 -116
  7. package/dist/index.react-native.d.ts +36 -0
  8. package/dist/index.react-native.js +9 -0
  9. package/dist/package.js +4 -0
  10. package/dist/process/types.d.ts +2 -2
  11. package/dist/realtime/browser/frame-metadata-diagnostics.js +90 -0
  12. package/dist/realtime/browser/frame-metadata-worker.js +2 -0
  13. package/dist/realtime/browser/index.js +32 -0
  14. package/dist/realtime/{mirror-stream.js → browser/mirror-stream.js} +2 -2
  15. package/dist/realtime/{preflight.js → browser/preflight.js} +20 -115
  16. package/dist/realtime/browser/prepare-connection.js +57 -0
  17. package/dist/realtime/client.d.ts +11 -4
  18. package/dist/realtime/client.js +42 -37
  19. package/dist/realtime/config-realtime.js +51 -0
  20. package/dist/realtime/livekit.js +26 -0
  21. package/dist/realtime/media-channel.js +40 -11
  22. package/dist/realtime/methods.d.ts +1 -1
  23. package/dist/realtime/methods.js +7 -2
  24. package/dist/realtime/observability/connection-quality.d.ts +3 -3
  25. package/dist/realtime/observability/connection-quality.js +3 -1
  26. package/dist/realtime/observability/g2g.d.ts +14 -0
  27. package/dist/realtime/observability/realtime-observability.js +8 -32
  28. package/dist/realtime/observability/webrtc-stats.d.ts +4 -4
  29. package/dist/realtime/preflight-connectivity.js +102 -0
  30. package/dist/realtime/preflight-types.d.ts +30 -0
  31. package/dist/realtime/react-native/index.js +36 -0
  32. package/dist/realtime/react-native/preflight.js +10 -0
  33. package/dist/realtime/react-native/prepare-connection.js +26 -0
  34. package/dist/realtime/signaling-channel.js +3 -1
  35. package/dist/realtime/stream-session.js +5 -4
  36. package/dist/realtime/subscribe-client.js +11 -10
  37. package/dist/shared/model.d.ts +56 -37
  38. package/dist/shared/model.js +61 -18
  39. package/dist/shared/request.js +5 -4
  40. package/dist/shared/types.d.ts +2 -2
  41. package/dist/shared/types.js +9 -2
  42. package/dist/utils/errors.d.ts +3 -0
  43. package/dist/utils/errors.js +16 -1
  44. package/dist/utils/media.js +1 -1
  45. package/dist/utils/platform.js +17 -1
  46. package/dist/utils/runtime.js +13 -0
  47. package/dist/utils/user-agent.js +1 -1
  48. package/dist/version.js +9 -4
  49. package/package.json +10 -3
  50. package/dist/realtime/observability/glass-to-glass.d.ts +0 -41
  51. package/dist/realtime/observability/glass-to-glass.js +0 -229
  52. package/dist/realtime/observability/pixel-marker.js +0 -144
  53. package/dist/realtime/preflight.d.ts +0 -59
@@ -1,3 +1,4 @@
1
+ import { globalInstanceSchema } from "../utils/runtime.js";
1
2
  import { isFileRefId } from "../files/types.js";
2
3
  import { REALTIME_CONFIG } from "./config-realtime.js";
3
4
  import { z } from "zod";
@@ -5,9 +6,13 @@ import { z } from "zod";
5
6
  const setInputSchema = z.object({
6
7
  prompt: z.string().min(1).optional(),
7
8
  enhance: z.boolean().optional().default(true),
9
+ /**
10
+ * - `Blob`/`File`/data:/http(s):/base64 string: bytes traverse the wire as base64.
11
+ * - `"file_..."` id (from `client.files.upload(...).id`): sent as a server-side reference.
12
+ */
8
13
  image: z.union([
9
- z.instanceof(Blob),
10
- z.instanceof(File),
14
+ globalInstanceSchema("Blob"),
15
+ globalInstanceSchema("File"),
11
16
  z.string(),
12
17
  z.null()
13
18
  ]).optional()
@@ -17,7 +17,7 @@ type ConnectionQualityMetrics = {
17
17
  /**
18
18
  * Mid-stream (steady-state) glass-to-glass latency (ms) — the real per-frame
19
19
  * camera→display latency through the model, excluding startup. Only populated
20
- * when the opt-in pixel-marker measurement is on (`connect({ debugQuality: true })`)
20
+ * when the opt-in frame-metadata measurement is on (`connect({ debugQuality: true })`)
21
21
  * and past warm-up; null otherwise. When present it drives the latency verdict
22
22
  * instead of `rttMs`.
23
23
  */
@@ -36,8 +36,8 @@ type ConnectionQualityMetrics = {
36
36
  /** Server's view of upstream (client→server) jitter in ms, or null. Observational. */
37
37
  upstreamJitterMs: number | null;
38
38
  /**
39
- * End-to-end frame drop ratio (0–1) inferred from the pixel-marker seq stream.
40
- * Only populated under the opt-in g2g measurement; null otherwise.
39
+ * End-to-end frame drop ratio (0–1). Null until frame identity is propagated
40
+ * end-to-end; latency measurement itself only requires the capture timestamp.
41
41
  */
42
42
  g2gDropRatio: number | null;
43
43
  /** Estimated available upstream bandwidth in kbps. Chromium-only — null on Safari/Firefox. */
@@ -76,6 +76,7 @@ function minOrNull(values) {
76
76
  return values.length === 0 ? null : Math.min(...values);
77
77
  }
78
78
  var RingBuffer = class {
79
+ size;
79
80
  values = [];
80
81
  constructor(size) {
81
82
  this.size = size;
@@ -101,6 +102,7 @@ var RingBuffer = class {
101
102
  * warm-up state changes; `current()` returns the latest at any time.
102
103
  */
103
104
  var ConnectionQualityEvaluator = class {
105
+ thresholds;
104
106
  rtt;
105
107
  glassToGlass;
106
108
  loss;
@@ -216,4 +218,4 @@ var ConnectionQualityEvaluator = class {
216
218
  }
217
219
  };
218
220
  //#endregion
219
- export { ConnectionQualityEvaluator, extractSignals, scoreLowerBetter, worst };
221
+ export { ConnectionQualityEvaluator, RANK, extractSignals, scoreLowerBetter, scoreMetrics, worst };
@@ -0,0 +1,14 @@
1
+ //#region src/realtime/observability/g2g.d.ts
2
+ type G2GMetrics = {
3
+ /** Time from connection start to the first rendered output frame. */
4
+ ttffMs: number | null;
5
+ /** Median steady-state camera-to-display latency. */
6
+ medianMs: number | null;
7
+ /** 90th percentile steady-state camera-to-display latency. */
8
+ p90Ms: number | null;
9
+ sampleCount: number;
10
+ /** Recent stamped frames that never returned, from 0 to 1. */
11
+ dropRatio: number | null;
12
+ };
13
+ //#endregion
14
+ export { G2GMetrics };
@@ -1,11 +1,11 @@
1
1
  import { REALTIME_CONFIG } from "../config-realtime.js";
2
2
  import { ConnectionQualityEvaluator } from "./connection-quality.js";
3
- import { SeqTracker, createMarkerReader, createStampPump } from "./glass-to-glass.js";
4
3
  import { createLiveKitStatsProvider } from "./livekit-stats-provider.js";
5
4
  import { NullTelemetryReporter, TelemetryReporter } from "./telemetry-reporter.js";
6
5
  import { WebRTCStatsCollector } from "./webrtc-stats.js";
7
6
  //#region src/realtime/observability/realtime-observability.ts
8
7
  var RealtimeObservability = class {
8
+ options;
9
9
  telemetryReporter = new NullTelemetryReporter();
10
10
  telemetryReporterReady = false;
11
11
  pendingTelemetryDiagnostics = [];
@@ -16,37 +16,14 @@ var RealtimeObservability = class {
16
16
  stallStartMs = 0;
17
17
  connectionBreakdown = null;
18
18
  connectionQuality = new ConnectionQualityEvaluator();
19
- /** Glass-to-glass: shared tracker, outgoing stamp pump, and incoming reader. Null unless `debugQuality`. */
20
- seqTracker;
21
- markerReader;
22
- stampPump = null;
19
+ glassToGlass;
23
20
  constructor(options) {
24
21
  this.options = options;
25
- this.seqTracker = options.debugQuality ? new SeqTracker() : null;
26
- this.markerReader = this.seqTracker ? createMarkerReader(this.seqTracker) : null;
22
+ this.glassToGlass = options.glassToGlass;
27
23
  }
28
- /**
29
- * Wrap the outgoing stream so each frame carries a marker, feeding the shared
30
- * tracker that the reader matches against. Returns the stream to publish
31
- * (unchanged when g2g is off or stamping can't start). Owned here so the
32
- * writer and reader of the tracker share one lifecycle and survive reconnects.
33
- */
34
- attachOutgoingStream(stream, fps) {
35
- if (!this.seqTracker) return stream;
36
- try {
37
- this.stampPump = createStampPump(stream, {
38
- tracker: this.seqTracker,
39
- fps
40
- });
41
- return this.stampPump.stream;
42
- } catch (error) {
43
- this.options.logger.warn("Failed to start glass-to-glass stamp pump; continuing without it", { error: error instanceof Error ? error.message : String(error) });
44
- return stream;
45
- }
46
- }
47
- /** Feed the remote video track to the marker reader (called from the media channel). */
24
+ /** Feed the LiveKit remote video track to the frame-metadata reader. */
48
25
  attachRemoteVideoTrack(track) {
49
- this.markerReader?.attach(track);
26
+ this.glassToGlass?.attachRemoteVideoTrack(track);
50
27
  }
51
28
  /**
52
29
  * Mark the start of a connect attempt for glass-to-glass timing (resets the
@@ -55,7 +32,7 @@ var RealtimeObservability = class {
55
32
  * measurement is on.
56
33
  */
57
34
  markGlassToGlassStart() {
58
- this.seqTracker?.markStart(performance.now());
35
+ this.glassToGlass?.markStart();
59
36
  }
60
37
  diagnostic(name, data, timestamp = Date.now()) {
61
38
  this.options.logger.debug(name, data);
@@ -163,8 +140,7 @@ var RealtimeObservability = class {
163
140
  }
164
141
  stop() {
165
142
  this.stopStats();
166
- this.stampPump?.dispose();
167
- this.markerReader?.dispose();
143
+ this.glassToGlass?.dispose();
168
144
  this.telemetryReporter.stop();
169
145
  this.telemetryReporter = new NullTelemetryReporter();
170
146
  this.telemetryReporterReady = false;
@@ -175,7 +151,7 @@ var RealtimeObservability = class {
175
151
  return this.connectionQuality.current();
176
152
  }
177
153
  handleStats(stats) {
178
- if (this.seqTracker) stats.glassToGlass = this.seqTracker.snapshot();
154
+ if (this.glassToGlass) stats.glassToGlass = this.glassToGlass.snapshot();
179
155
  this.options.onStats?.(stats);
180
156
  this.telemetryReporter.addStats(stats);
181
157
  this.detectVideoStall(stats);
@@ -1,4 +1,4 @@
1
- import { G2GMetrics } from "./glass-to-glass.js";
1
+ import { G2GMetrics } from "./g2g.js";
2
2
 
3
3
  //#region src/realtime/observability/webrtc-stats.d.ts
4
4
  type WebRTCStats = {
@@ -133,8 +133,8 @@ type WebRTCStats = {
133
133
  };
134
134
  /**
135
135
  * True glass-to-glass latency + end-to-end drop signal, merged in by
136
- * `RealtimeObservability` when the opt-in pixel-marker measurement is active
137
- * (see glass-to-glass.ts). Null otherwise — the stats collector does not
136
+ * `RealtimeObservability` when the opt-in frame-metadata measurement is active.
137
+ * Null otherwise — the stats collector does not
138
138
  * populate it.
139
139
  */
140
140
  glassToGlass: G2GMetrics | null;
@@ -154,4 +154,4 @@ type IceCandidatePair = {
154
154
  remote: IceCandidateInfo;
155
155
  };
156
156
  //#endregion
157
- export { WebRTCStats };
157
+ export { IceCandidateInfo, IceCandidatePair, WebRTCStats };
@@ -0,0 +1,102 @@
1
+ //#region src/realtime/preflight-connectivity.ts
2
+ function candidateType(candidate) {
3
+ if (candidate.type) return candidate.type;
4
+ return /\btyp (\w+)/.exec(candidate.candidate)?.[1] ?? "";
5
+ }
6
+ function nowMs() {
7
+ return globalThis.performance?.now?.() ?? Date.now();
8
+ }
9
+ async function gatherIceCandidates(iceServers, timeoutMs, signal, logger) {
10
+ if (signal?.aborted) return {
11
+ transport: "failed",
12
+ rttMs: null
13
+ };
14
+ const PC = globalThis?.RTCPeerConnection;
15
+ if (typeof PC !== "function") {
16
+ logger.warn("preflight: RTCPeerConnection unavailable in this environment");
17
+ return {
18
+ transport: "failed",
19
+ rttMs: null
20
+ };
21
+ }
22
+ let pc = null;
23
+ try {
24
+ pc = new PC({ iceServers });
25
+ pc.createDataChannel("decart-preflight");
26
+ let sawSrflx = false;
27
+ let sawOtherCandidate = false;
28
+ let firstSrflxAt = null;
29
+ const start = nowMs();
30
+ await new Promise((resolve) => {
31
+ let settled = false;
32
+ let timer;
33
+ const finish = () => {
34
+ if (settled) return;
35
+ settled = true;
36
+ clearTimeout(timer);
37
+ signal?.removeEventListener("abort", finish);
38
+ resolve();
39
+ };
40
+ timer = setTimeout(finish, timeoutMs);
41
+ signal?.addEventListener("abort", finish, { once: true });
42
+ if (signal?.aborted) finish();
43
+ const peer = pc;
44
+ peer.onicecandidate = (event) => {
45
+ if (!event.candidate || event.candidate.candidate === "") return finish();
46
+ if (candidateType(event.candidate) === "srflx") {
47
+ sawSrflx = true;
48
+ if (firstSrflxAt === null) firstSrflxAt = nowMs();
49
+ } else sawOtherCandidate = true;
50
+ };
51
+ peer.onicegatheringstatechange = () => {
52
+ if (peer.iceGatheringState === "complete") finish();
53
+ };
54
+ peer.createOffer().then((offer) => peer.setLocalDescription(offer)).catch((error) => {
55
+ logger.warn("preflight: failed to create offer", { error: error instanceof Error ? error.message : String(error) });
56
+ finish();
57
+ });
58
+ });
59
+ const rttMs = firstSrflxAt !== null ? Math.round(firstSrflxAt - start) : null;
60
+ return {
61
+ transport: sawSrflx ? "udp" : sawOtherCandidate ? "relay" : "failed",
62
+ rttMs
63
+ };
64
+ } catch (error) {
65
+ logger.warn("preflight: connectivity probe threw", { error: error instanceof Error ? error.message : String(error) });
66
+ return {
67
+ transport: "failed",
68
+ rttMs: null
69
+ };
70
+ } finally {
71
+ try {
72
+ pc?.close();
73
+ } catch {}
74
+ }
75
+ }
76
+ function classifyConnectivity(metrics, thresholds) {
77
+ const reasons = [];
78
+ let quality;
79
+ if (metrics.transport === "failed") {
80
+ quality = "critical";
81
+ reasons.push("Could not establish any WebRTC connectivity (no ICE candidates gathered). Real-time streaming is unlikely to work on this network.");
82
+ } else if (metrics.transport === "relay") {
83
+ quality = "poor";
84
+ reasons.push("Direct UDP connectivity could not be confirmed; the session will need a TURN relay, which adds latency and can't be verified without starting a session.");
85
+ } else if (metrics.rttMs != null && metrics.rttMs > thresholds.marginalMs) {
86
+ quality = "poor";
87
+ reasons.push(`Network round-trip time is high (~${metrics.rttMs}ms > ${thresholds.marginalMs}ms); the real-time experience may feel laggy.`);
88
+ } else if (metrics.rttMs != null && metrics.rttMs > thresholds.goodMs) {
89
+ quality = "fair";
90
+ reasons.push(`Network round-trip time is elevated (~${metrics.rttMs}ms > ${thresholds.goodMs}ms).`);
91
+ } else quality = "good";
92
+ return {
93
+ quality,
94
+ metrics: {
95
+ transport: metrics.transport,
96
+ rttMs: metrics.rttMs
97
+ },
98
+ reasons
99
+ };
100
+ }
101
+ //#endregion
102
+ export { classifyConnectivity, gatherIceCandidates };
@@ -0,0 +1,30 @@
1
+ import { CustomModelDefinition, ModelDefinition } from "../shared/model.js";
2
+ import { ConnectionQuality } from "./observability/connection-quality.js";
3
+
4
+ //#region src/realtime/preflight-types.d.ts
5
+ type ConnectivityTransport = "udp" | "relay" | "failed";
6
+ type ConnectivityMetrics = {
7
+ transport: ConnectivityTransport;
8
+ rttMs: number | null;
9
+ g2gMs?: number | null;
10
+ ttffMs?: number | null;
11
+ g2gDropRatio?: number | null;
12
+ upstreamJitterMs?: number | null;
13
+ packetLoss?: number | null;
14
+ sampleCount?: number;
15
+ };
16
+ type ConnectivityReport = {
17
+ quality: ConnectionQuality;
18
+ metrics: ConnectivityMetrics;
19
+ reasons: string[];
20
+ };
21
+ type CheckConnectivityOptions = {
22
+ iceServers?: RTCIceServer[];
23
+ iceGatherTimeoutMs?: number;
24
+ signal?: AbortSignal;
25
+ deep?: boolean;
26
+ model?: ModelDefinition | CustomModelDefinition;
27
+ durationMs?: number;
28
+ };
29
+ //#endregion
30
+ export { CheckConnectivityOptions, ConnectivityMetrics, ConnectivityReport, ConnectivityTransport };
@@ -0,0 +1,36 @@
1
+ import { createRealTimeClient } from "../client.js";
2
+ import { createRealTimeSubscribeClient } from "../subscribe-client.js";
3
+ import { createReactNativePreflight } from "./preflight.js";
4
+ import { assertReactNativeReady, prepareReactNativeConnection, unsupportedReactNativeFeature } from "./prepare-connection.js";
5
+ //#region src/realtime/react-native/index.ts
6
+ const createReactNativeRealtime = (options) => {
7
+ const publish = createRealTimeClient({
8
+ baseUrl: options.publishBaseUrl,
9
+ apiKey: options.apiKey,
10
+ integration: options.integration,
11
+ logger: options.logger,
12
+ telemetryEnabled: options.telemetryEnabled,
13
+ prepareConnection: prepareReactNativeConnection
14
+ });
15
+ const subscribe = createRealTimeSubscribeClient({
16
+ baseUrl: options.subscribeBaseUrl,
17
+ apiKey: options.apiKey,
18
+ integration: options.integration,
19
+ logger: options.logger
20
+ });
21
+ const preflight = createReactNativePreflight({ logger: options.logger });
22
+ return {
23
+ connect: (stream, connectOptions) => publish.connect(stream, connectOptions),
24
+ subscribe: (subscribeOptions) => {
25
+ assertReactNativeReady();
26
+ return subscribe.subscribe(subscribeOptions);
27
+ },
28
+ checkConnectivity: async (checkOptions = {}) => {
29
+ assertReactNativeReady();
30
+ if (checkOptions.deep) unsupportedReactNativeFeature("Deep connectivity preflight");
31
+ return preflight.checkConnectivity(checkOptions);
32
+ }
33
+ };
34
+ };
35
+ //#endregion
36
+ export { createReactNativeRealtime };
@@ -0,0 +1,10 @@
1
+ import { REALTIME_CONFIG } from "../config-realtime.js";
2
+ import { classifyConnectivity, gatherIceCandidates } from "../preflight-connectivity.js";
3
+ //#region src/realtime/react-native/preflight.ts
4
+ function createReactNativePreflight({ logger }) {
5
+ return { checkConnectivity: async (options = {}) => {
6
+ return classifyConnectivity(await gatherIceCandidates(options.iceServers ?? REALTIME_CONFIG.preflight.defaultStunUrls.map((urls) => ({ urls })), options.iceGatherTimeoutMs ?? REALTIME_CONFIG.preflight.iceGatherTimeoutMs, options.signal, logger), REALTIME_CONFIG.preflight.rtt);
7
+ } };
8
+ }
9
+ //#endregion
10
+ export { createReactNativePreflight };
@@ -0,0 +1,26 @@
1
+ import { createReactNativeSetupRequiredError, createUnsupportedPlatformFeatureError } from "../../utils/errors.js";
2
+ import { RealtimeObservability } from "../observability/realtime-observability.js";
3
+ import { missingReactNativeRealtimeGlobals } from "../../utils/platform.js";
4
+ import { createLiveKitMediaChannel } from "../media-channel.js";
5
+ //#region src/realtime/react-native/prepare-connection.ts
6
+ function assertReactNativeReady() {
7
+ const missing = missingReactNativeRealtimeGlobals();
8
+ if (missing.length > 0) throw createReactNativeSetupRequiredError(missing);
9
+ }
10
+ function unsupportedReactNativeFeature(feature) {
11
+ throw createUnsupportedPlatformFeatureError(feature, "React Native");
12
+ }
13
+ const prepareReactNativeConnection = ({ stream, mirror, debugQuality, preferredVideoCodec, observability: observabilityOptions }) => {
14
+ assertReactNativeReady();
15
+ if (mirror !== false) unsupportedReactNativeFeature("Outgoing video mirroring");
16
+ if (debugQuality) unsupportedReactNativeFeature("debugQuality");
17
+ return {
18
+ stream: stream ?? new MediaStream(),
19
+ observability: new RealtimeObservability(observabilityOptions),
20
+ videoCodec: preferredVideoCodec,
21
+ createMediaChannel: createLiveKitMediaChannel,
22
+ dispose: () => {}
23
+ };
24
+ };
25
+ //#endregion
26
+ export { assertReactNativeReady, prepareReactNativeConnection, unsupportedReactNativeFeature };
@@ -36,6 +36,7 @@ function buildInitialStateRequest(initialState) {
36
36
  return null;
37
37
  }
38
38
  var SignalingChannel = class {
39
+ config;
39
40
  ws = null;
40
41
  events = mitt();
41
42
  pendingAcks = [];
@@ -66,7 +67,8 @@ var SignalingChannel = class {
66
67
  const userSetInitialState = opts.initialState != null && (opts.initialState.image != null || opts.initialState.imageRef != null || opts.initialState.prompt != null);
67
68
  const joinMessage = {
68
69
  type: "livekit_join",
69
- passthrough: opts.passthrough ?? !userSetInitialState
70
+ passthrough: opts.passthrough ?? !userSetInitialState,
71
+ ...opts.frameTiming ? { client_capabilities: { frame_timing: true } } : {}
70
72
  };
71
73
  if (!this.writeMessage(joinMessage)) {
72
74
  roomInfoWait.cancel();
@@ -1,6 +1,5 @@
1
1
  import { createConsoleLogger } from "../utils/logger.js";
2
2
  import { REALTIME_CONFIG } from "./config-realtime.js";
3
- import { MediaChannel } from "./media-channel.js";
4
3
  import { SignalingChannel } from "./signaling-channel.js";
5
4
  import mitt from "mitt";
6
5
  import pRetry, { AbortError } from "p-retry";
@@ -17,6 +16,7 @@ function getInitialImageSizeKb(image) {
17
16
  return Math.max(0, Math.round(bytes / 1024));
18
17
  }
19
18
  var StreamSession = class {
19
+ config;
20
20
  signaling;
21
21
  media;
22
22
  events = mitt();
@@ -110,7 +110,8 @@ var StreamSession = class {
110
110
  const { roomInfo, initialStateAck } = await this.signaling.openAndJoin({
111
111
  connectTimeout: REALTIME_CONFIG.session.connectionTimeoutMs,
112
112
  initialState,
113
- passthrough: this.config.initialPassthrough
113
+ passthrough: this.config.initialPassthrough,
114
+ frameTiming: this.config.frameTiming
114
115
  });
115
116
  this.watchInitialStateAck(initialStateAck, attempt);
116
117
  if (this.disposed || this.currentAttempt !== attempt) {
@@ -238,7 +239,7 @@ var StreamSession = class {
238
239
  logger: this.logger,
239
240
  observability: this.config.observability
240
241
  });
241
- this.media = new MediaChannel({
242
+ this.media = this.config.createMediaChannel({
242
243
  observability: this.config.observability,
243
244
  localStream: this.config.localStream,
244
245
  logger: this.logger,
@@ -267,4 +268,4 @@ var StreamSession = class {
267
268
  }
268
269
  };
269
270
  //#endregion
270
- export { StreamSession };
271
+ export { StreamSession, encodeSubscribeToken };
@@ -2,8 +2,8 @@ import { classifyWebrtcError } from "../utils/errors.js";
2
2
  import { createConsoleLogger } from "../utils/logger.js";
3
3
  import { createEventBuffer } from "./event-buffer.js";
4
4
  import { REALTIME_CONFIG } from "./config-realtime.js";
5
+ import { loadLiveKitClient } from "./livekit.js";
5
6
  import { RealtimeObservability } from "./observability/realtime-observability.js";
6
- import { ConnectionState, Room, RoomEvent, Track } from "livekit-client";
7
7
  //#region src/realtime/subscribe-client.ts
8
8
  function decodeSubscribeToken(token) {
9
9
  try {
@@ -16,11 +16,11 @@ function decodeSubscribeToken(token) {
16
16
  }
17
17
  function mapLiveKitState(state) {
18
18
  switch (state) {
19
- case ConnectionState.Connecting: return "connecting";
20
- case ConnectionState.Connected: return "connected";
21
- case ConnectionState.Reconnecting:
22
- case ConnectionState.SignalReconnecting: return "reconnecting";
23
- case ConnectionState.Disconnected: return "disconnected";
19
+ case "connecting": return "connecting";
20
+ case "connected": return "connected";
21
+ case "reconnecting":
22
+ case "signalReconnecting": return "reconnecting";
23
+ case "disconnected": return "disconnected";
24
24
  default: return "disconnected";
25
25
  }
26
26
  }
@@ -63,6 +63,7 @@ const createRealTimeSubscribeClient = (opts) => {
63
63
  emitOrBuffer("connectionChange", state);
64
64
  };
65
65
  try {
66
+ const { Room: LiveKitRoom, RoomEvent } = await loadLiveKitClient();
66
67
  observability = new RealtimeObservability({
67
68
  telemetryEnabled: false,
68
69
  apiKey,
@@ -76,11 +77,11 @@ const createRealTimeSubscribeClient = (opts) => {
76
77
  apiKey,
77
78
  roomName
78
79
  });
79
- room = new Room(REALTIME_CONFIG.livekit.roomOptions);
80
+ room = new LiveKitRoom(REALTIME_CONFIG.livekit.roomOptions);
80
81
  const activeRoom = room;
81
82
  activeRoom.on(RoomEvent.TrackSubscribed, (track, _pub, participant) => {
82
83
  if (!participant.identity.startsWith(REALTIME_CONFIG.livekit.inferenceServerIdentityPrefix)) return;
83
- if (track.kind !== Track.Kind.Video && track.kind !== Track.Kind.Audio) return;
84
+ if (track.kind !== "video" && track.kind !== "audio") return;
84
85
  const mediaStreamTrack = track.mediaStreamTrack;
85
86
  if (!mediaStreamTrack) return;
86
87
  const tracks = remoteStream?.getTracks() ?? [];
@@ -98,7 +99,7 @@ const createRealTimeSubscribeClient = (opts) => {
98
99
  observability.setLiveKitRoom(activeRoom);
99
100
  setState("connected");
100
101
  const client = {
101
- isConnected: () => activeRoom.state === ConnectionState.Connected,
102
+ isConnected: () => activeRoom.state === "connected",
102
103
  getConnectionState: () => mapLiveKitState(activeRoom.state),
103
104
  disconnect: () => {
104
105
  observability?.stop();
@@ -121,4 +122,4 @@ const createRealTimeSubscribeClient = (opts) => {
121
122
  return { subscribe };
122
123
  };
123
124
  //#endregion
124
- export { createRealTimeSubscribeClient };
125
+ export { createRealTimeSubscribeClient, decodeSubscribeToken };