realtime-avatar 0.27.2 → 0.27.3

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/README.md CHANGED
@@ -86,8 +86,12 @@ if (recording.status === "ready") {
86
86
 
87
87
  Omitted or `"off"` disables recording. `"audio"` records published user and avatar audio;
88
88
  `"video"` records published video without audio; `"audio_video"` keeps both on one media timeline.
89
+ `"participants"` keeps each participant's voice and camera in its own continuous file.
90
+ `"tracks"` saves separate published audio/video tracks and replacement-camera segments;
91
+ use `listRecordings({ sessionId: call.sessionId })` because its initial grant omits recording
92
+ metadata so older installed clients remain compatible.
89
93
  Recording does not enable the microphone, camera, or screen sharing. Only tracks that participants
90
- authorize and publish can be recorded. Camera controls remain a future SDK feature.
94
+ authorize and publish can be recorded.
91
95
 
92
96
  Recordings may finish processing after a call ends. Use `listRecordings({ sessionId })` to find
93
97
  them, or refresh `getRecording(recordingId)` while processing. These methods and
@@ -98,6 +102,15 @@ default); each URL expires at `expiresAt` (up to one hour, capped by retention).
98
102
  access before replaying or seeking after expiry. An expired URL does not delete the file.
99
103
  Treat the URL as private: anyone who has it can play that file until it expires.
100
104
 
105
+ For participant replay, pass every ready `{ recording, url }` asset to `RecordingPlayer`
106
+ from `realtime-avatar/react`. It accepts combined participant MP4s and separate audio/video
107
+ segments, including camera republication. Follow `nextCursor` when listing a session's recordings.
108
+ The optional `mediaStartOffsetMs` preserves a file's first native media timestamp; it delays
109
+ that track's visibility without shifting its playback clock. The player preserves native
110
+ timestamps across gaps and uses the artifact's duration instead of an initially truncated
111
+ WebM container duration. `mediaMutedIntervalsMs` hides a muted camera while keeping media
112
+ clocks and audio running, including audio carried by a legacy combined file.
113
+
101
114
  ## Optional connection history
102
115
 
103
116
  Set `connectionHistory: true` in the server's `startCall` policy when the caller needs a
@@ -1,4 +1,4 @@
1
- import { sessionModeSchema, liveKitSessionReleaseReasonSchema, RealtimeAvatar, ROUTE_TIMEOUT_MS, isQueued, RealtimeAvatarHttpError, RealtimeAvatarError } from './chunk-YR5NJTEO.js';
1
+ import { sessionModeSchema, liveKitSessionReleaseReasonSchema, RealtimeAvatar, ROUTE_TIMEOUT_MS, isQueued, RealtimeAvatarHttpError, RealtimeAvatarError } from './chunk-W4DGBG4Z.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  // ../http-client/src/generated/error-semantics.ts
@@ -239,13 +239,17 @@ var schema26 = z.strictObject({
239
239
  var clipLibraryResponseSchema = schema24;
240
240
  var clipLibraryUpdateSchema = schema26;
241
241
  var RECORDED_MEDIA_MODES = ["audio", "video", "audio_video"];
242
- var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.");
242
+ var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants", "tracks"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.");
243
243
  var RECORDING_STATUSES = ["pending", "recording", "processing", "ready", "failed", "expired"];
244
244
  z.enum(RECORDING_STATUSES);
245
245
  var recordingParticipantSchema = z.object({
246
246
  participantIdentity: z.string().min(1).max(160),
247
247
  role: z.enum(["user", "avatar"])
248
248
  }).strict();
249
+ var recordingMutedIntervalSchema = z.object({
250
+ startMs: z.number().int().min(0),
251
+ endMs: z.number().int().positive()
252
+ }).strict().refine((interval) => interval.endMs > interval.startMs, "Muted interval must end after it starts");
249
253
  var recordingMetadataSchema = z.object({
250
254
  sessionId: z.string().min(1),
251
255
  recordingId: z.string().min(1),
@@ -254,13 +258,15 @@ var recordingMetadataSchema = z.object({
254
258
  retainedUntil: z.string().datetime({ offset: true }),
255
259
  participant: recordingParticipantSchema.optional(),
256
260
  mediaStartedAt: z.string().datetime({ offset: true }).optional(),
257
- mediaEndedAt: z.string().datetime({ offset: true }).optional()
261
+ mediaEndedAt: z.string().datetime({ offset: true }).optional(),
262
+ mediaStartOffsetMs: z.number().int().min(0).optional().describe("First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero."),
263
+ mediaMutedIntervalsMs: z.array(recordingMutedIntervalSchema).optional().describe("Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals.")
258
264
  });
259
265
  var recordingArtifactSchema = z.discriminatedUnion("status", [
260
266
  recordingMetadataSchema.extend({ status: z.enum(["pending", "recording", "processing", "expired"]) }).strict(),
261
267
  recordingMetadataSchema.extend({
262
268
  status: z.literal("ready"),
263
- mediaType: z.enum(["audio/mp4", "video/mp4"]),
269
+ mediaType: z.enum(["audio/mp4", "video/mp4", "audio/ogg", "video/webm"]),
264
270
  sizeBytes: z.number().int().positive(),
265
271
  durationMs: z.number().int().nonnegative().nullable()
266
272
  }).strict(),
@@ -669,7 +675,7 @@ var liveKitSessionReleaseReasonSchema = z.enum([
669
675
 
670
676
  // ../http-client/src/client.ts
671
677
  var DEFAULT_BASE_URL = "https://realtimeavatar.ai/api/v1";
672
- var SDK_VERSION = "0.27.2";
678
+ var SDK_VERSION = "0.27.3";
673
679
  var RealtimeAvatar = class {
674
680
  #apiKey;
675
681
  #baseUrl;
package/dist/express.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig } from './types-BH9O5leB.js';
2
- import './types-D_s7ff7w.js';
1
+ import { P as ProxyConfig } from './types-lgS4Z9rd.js';
2
+ import './types-kgmS7ECd.js';
3
3
  import 'zod';
4
4
 
5
5
  type Expressish = {
package/dist/express.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-SP5SYYSS.js';
2
- import './chunk-YR5NJTEO.js';
1
+ import { createProxyHandler } from './chunk-QBOIOZ3O.js';
2
+ import './chunk-W4DGBG4Z.js';
3
3
  import './chunk-MCIXU6XP.js';
4
4
 
5
5
  // ../proxy/src/express.ts
package/dist/hono.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig } from './types-BH9O5leB.js';
2
- import './types-D_s7ff7w.js';
1
+ import { P as ProxyConfig } from './types-lgS4Z9rd.js';
2
+ import './types-kgmS7ECd.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/hono.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-SP5SYYSS.js';
2
- import './chunk-YR5NJTEO.js';
1
+ import { createProxyHandler } from './chunk-QBOIOZ3O.js';
2
+ import './chunk-W4DGBG4Z.js';
3
3
  import './chunk-MCIXU6XP.js';
4
4
 
5
5
  // ../proxy/src/hono.ts
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as CallPolicy, C as CallMode, S as StartCallResult, E as EndCallOptions, A as Avatar, b as AvatarUpdate, c as AvatarSourceSwap, d as ClipLibraryDeclaration, e as ClipLibraryUpdate, L as LoopRedirect, f as ClipLibrary, g as AssetKind, h as Asset, i as ListSessionsOptions, U as UsageSessionPage, j as UsageSession, k as ListRecordingsQuery, l as ListRecordingsResponse, R as RecordingArtifact, m as RecordingAccessResponse, n as CreditBalance, T as TranscriptPayload } from './types-D_s7ff7w.js';
2
- export { o as AvatarClip, p as CallConnection, q as CallQueued, r as ClipDeclaration, s as ClipLibraryPlan, t as ClipSource, u as ContextMessage, v as EndCallReason, w as ExternalLlm, x as RecordingArtifactStatus, y as RecordingMode, V as VideoPolicy, z as isQueued, B as listRecordingsResponseSchema, D as recordingAccessResponseSchema, F as recordingArtifactSchema, G as recordingModeSchema } from './types-D_s7ff7w.js';
1
+ import { a as CallPolicy, C as CallMode, S as StartCallResult, E as EndCallOptions, A as Avatar, b as AvatarUpdate, c as AvatarSourceSwap, d as ClipLibraryDeclaration, e as ClipLibraryUpdate, L as LoopRedirect, f as ClipLibrary, g as AssetKind, h as Asset, i as ListSessionsOptions, U as UsageSessionPage, j as UsageSession, k as ListRecordingsQuery, l as ListRecordingsResponse, R as RecordingArtifact, m as RecordingAccessResponse, n as CreditBalance, T as TranscriptPayload } from './types-kgmS7ECd.js';
2
+ export { o as AvatarClip, p as CallConnection, q as CallQueued, r as ClipDeclaration, s as ClipLibraryPlan, t as ClipSource, u as ContextMessage, v as EndCallReason, w as ExternalLlm, x as RecordingArtifactStatus, y as RecordingMode, V as VideoPolicy, z as isQueued, B as listRecordingsResponseSchema, D as recordingAccessResponseSchema, F as recordingArtifactSchema, G as recordingModeSchema } from './types-kgmS7ECd.js';
3
3
  import { ConnectionHistoryResponse } from './connection-history.js';
4
4
  import { z } from 'zod';
5
5
 
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-YR5NJTEO.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-W4DGBG4Z.js';
2
2
  import './chunk-MCIXU6XP.js';
package/dist/nextjs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig } from './types-BH9O5leB.js';
2
- import './types-D_s7ff7w.js';
1
+ import { P as ProxyConfig } from './types-lgS4Z9rd.js';
2
+ import './types-kgmS7ECd.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/nextjs.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-SP5SYYSS.js';
2
- import './chunk-YR5NJTEO.js';
1
+ import { createProxyHandler } from './chunk-QBOIOZ3O.js';
2
+ import './chunk-W4DGBG4Z.js';
3
3
  import './chunk-MCIXU6XP.js';
4
4
 
5
5
  // ../proxy/src/nextjs.ts
@@ -266,6 +266,11 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
266
266
  }, z.core.$strict>>;
267
267
  mediaStartedAt: z.ZodOptional<z.ZodString>;
268
268
  mediaEndedAt: z.ZodOptional<z.ZodString>;
269
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
270
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
271
+ startMs: z.ZodNumber;
272
+ endMs: z.ZodNumber;
273
+ }, z.core.$strict>>>;
269
274
  status: z.ZodEnum<{
270
275
  pending: "pending";
271
276
  recording: "recording";
@@ -291,10 +296,17 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
291
296
  }, z.core.$strict>>;
292
297
  mediaStartedAt: z.ZodOptional<z.ZodString>;
293
298
  mediaEndedAt: z.ZodOptional<z.ZodString>;
299
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
300
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
301
+ startMs: z.ZodNumber;
302
+ endMs: z.ZodNumber;
303
+ }, z.core.$strict>>>;
294
304
  status: z.ZodLiteral<"ready">;
295
305
  mediaType: z.ZodEnum<{
296
306
  "audio/mp4": "audio/mp4";
297
307
  "video/mp4": "video/mp4";
308
+ "audio/ogg": "audio/ogg";
309
+ "video/webm": "video/webm";
298
310
  }>;
299
311
  sizeBytes: z.ZodNumber;
300
312
  durationMs: z.ZodNullable<z.ZodNumber>;
@@ -317,6 +329,11 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
317
329
  }, z.core.$strict>>;
318
330
  mediaStartedAt: z.ZodOptional<z.ZodString>;
319
331
  mediaEndedAt: z.ZodOptional<z.ZodString>;
332
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
333
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
334
+ startMs: z.ZodNumber;
335
+ endMs: z.ZodNumber;
336
+ }, z.core.$strict>>>;
320
337
  status: z.ZodLiteral<"failed">;
321
338
  errorCode: z.ZodEnum<{
322
339
  recording_failed: "recording_failed";
@@ -329,6 +346,7 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
329
346
  audio_video: "audio_video";
330
347
  off: "off";
331
348
  participants: "participants";
349
+ tracks: "tracks";
332
350
  }>>;
333
351
  recordings: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
334
352
  sessionId: z.ZodString;
@@ -349,6 +367,11 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
349
367
  }, z.core.$strict>>;
350
368
  mediaStartedAt: z.ZodOptional<z.ZodString>;
351
369
  mediaEndedAt: z.ZodOptional<z.ZodString>;
370
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
371
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
372
+ startMs: z.ZodNumber;
373
+ endMs: z.ZodNumber;
374
+ }, z.core.$strict>>>;
352
375
  status: z.ZodEnum<{
353
376
  pending: "pending";
354
377
  recording: "recording";
@@ -374,10 +397,17 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
374
397
  }, z.core.$strict>>;
375
398
  mediaStartedAt: z.ZodOptional<z.ZodString>;
376
399
  mediaEndedAt: z.ZodOptional<z.ZodString>;
400
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
401
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
402
+ startMs: z.ZodNumber;
403
+ endMs: z.ZodNumber;
404
+ }, z.core.$strict>>>;
377
405
  status: z.ZodLiteral<"ready">;
378
406
  mediaType: z.ZodEnum<{
379
407
  "audio/mp4": "audio/mp4";
380
408
  "video/mp4": "video/mp4";
409
+ "audio/ogg": "audio/ogg";
410
+ "video/webm": "video/webm";
381
411
  }>;
382
412
  sizeBytes: z.ZodNumber;
383
413
  durationMs: z.ZodNullable<z.ZodNumber>;
@@ -400,6 +430,11 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
400
430
  }, z.core.$strict>>;
401
431
  mediaStartedAt: z.ZodOptional<z.ZodString>;
402
432
  mediaEndedAt: z.ZodOptional<z.ZodString>;
433
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
434
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
435
+ startMs: z.ZodNumber;
436
+ endMs: z.ZodNumber;
437
+ }, z.core.$strict>>>;
403
438
  status: z.ZodLiteral<"failed">;
404
439
  errorCode: z.ZodEnum<{
405
440
  recording_failed: "recording_failed";
@@ -1,7 +1,7 @@
1
1
  import { LiveKitRoomProps } from '@livekit/react-native';
2
2
  export { AudioSession, VideoTrack, VideoTrackProps, registerGlobals } from '@livekit/react-native';
3
- import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-DW_EOvmE.js';
4
- export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, h as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, i as CallTranscriptSegment, j as CartesiaTtsModel, k as ClipResult, l as ClosingTurnResult, m as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, n as DEFAULT_CREDITS_LOW_LEAD_SECONDS, o as DEFAULT_GOVERNOR_CONFIG, p as DEFAULT_GRACE_CEILING_SECONDS, q as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, r as DEFAULT_IDLE_SECONDS, s as DEFAULT_IDLE_WARN_LEAD_SECONDS, t as DEFAULT_READY_TIMEOUT_SECONDS, u as DEFAULT_TURN_TIMEOUT_SECONDS, v as DeclaredInputSource, E as EndReason, w as EndedEvent, x as ExtendResult, F as FishTtsModel, y as FreezeReadingFn, G as Governor, z as GovernorAction, H as GovernorConfig, I as GovernorSignal, J as GovernorState, K as GraceWindowClosedEvent, M as GraceWindowOpenEvent, N as GraceWindowState, O as IdleWarningEvent, P as InboundRtpCursor, Q as InboundRtpReading, R as KnownBehaviorState, S as LLMProvider, T as LLMSelection, U as LiveKitAvatarGrantState, V as LiveKitAvatarGrantStatus, W as LiveKitCapacityState, X as LiveKitConnectionStatus, Y as LiveKitSessionRequest, Z as LiveKitSessionStartResult, _ as MAX_RECONNECT_ATTEMPTS, $ as MAX_SESSION_INSTRUCTIONS_CHARS, a0 as ProxyClientOptions, a1 as QualityCap, a2 as RECONNECT_BACKOFF_MS, a3 as RealtimeAvatarRequestOptions, a4 as RealtimeSessionApi, a5 as RealtimeSessionMedia, a6 as RealtimeSessionRoomSinks, a7 as ReconnectPolicy, a8 as ReconnectingEvent, a9 as RecoveryState, aa as RetryStep, ab as SendTurnOptions, ac as SessionBehavior, ad as SessionClip, ae as SessionClocks, af as SessionEndReason, ag as SessionLifecycleApi, ah as SessionLifecyclePhase, ai as SessionLifecyclePhaseKind, aj as SessionLifecycleRoomBridge, ak as SessionLifecycleRoomBridgeProps, al as SessionReadinessFailure, am as SurfaceLayers, an as TranscriptSender, ao as TranscriptSenderOptions, ap as TurnState, aq as TurnTimeoutEvent, ar as UseAvatarQualityGovernorInput, as as UseLiveKitAvatarGrantInput, at as UseRealtimeSessionInput, au as UseSessionLifecycleInput, av as VoiceSpec, aw as VoiceSpecInput, ax as capacityErrorFromBusy, ay as capacityStateFromGrant, az as createProxyClient, aA as isNativeLiveTrackSubscribed, aB as knownBehaviorStates, aC as mapTurnState, aD as readInboundRtp, aE as sessionBehaviorSchema, aF as sessionClipSchema, aG as splitCallTranscript, aH as useAvatarAdaptivePlayoutDelay, aI as useAvatarCamera, aJ as useAvatarPlayoutDelay, aK as useAvatarQualityGovernor, aL as useCallTranscript, aM as useLiveKitAvatarGrant, aN as useLiveTrackProducing, aO as useMicLease, aP as useRealtimeSession, aQ as useReleaseMicLeaseOnTrackEnded, aR as useSessionLifecycle } from './proxy-client-DW_EOvmE.js';
3
+ import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-BAY77sJZ.js';
4
+ export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, h as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, i as CallTranscriptSegment, j as CartesiaTtsModel, k as ClipResult, l as ClosingTurnResult, m as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, n as DEFAULT_CREDITS_LOW_LEAD_SECONDS, o as DEFAULT_GOVERNOR_CONFIG, p as DEFAULT_GRACE_CEILING_SECONDS, q as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, r as DEFAULT_IDLE_SECONDS, s as DEFAULT_IDLE_WARN_LEAD_SECONDS, t as DEFAULT_READY_TIMEOUT_SECONDS, u as DEFAULT_TURN_TIMEOUT_SECONDS, v as DeclaredInputSource, E as EndReason, w as EndedEvent, x as ExtendResult, F as FishTtsModel, y as FreezeReadingFn, G as Governor, z as GovernorAction, H as GovernorConfig, I as GovernorSignal, J as GovernorState, K as GraceWindowClosedEvent, M as GraceWindowOpenEvent, N as GraceWindowState, O as IdleWarningEvent, P as InboundRtpCursor, Q as InboundRtpReading, R as KnownBehaviorState, S as LLMProvider, T as LLMSelection, U as LiveKitAvatarGrantState, V as LiveKitAvatarGrantStatus, W as LiveKitCapacityState, X as LiveKitConnectionStatus, Y as LiveKitSessionRequest, Z as LiveKitSessionStartResult, _ as MAX_RECONNECT_ATTEMPTS, $ as MAX_SESSION_INSTRUCTIONS_CHARS, a0 as ProxyClientOptions, a1 as QualityCap, a2 as RECONNECT_BACKOFF_MS, a3 as RealtimeAvatarRequestOptions, a4 as RealtimeSessionApi, a5 as RealtimeSessionMedia, a6 as RealtimeSessionRoomSinks, a7 as ReconnectPolicy, a8 as ReconnectingEvent, a9 as RecoveryState, aa as RetryStep, ab as SendTurnOptions, ac as SessionBehavior, ad as SessionClip, ae as SessionClocks, af as SessionEndReason, ag as SessionLifecycleApi, ah as SessionLifecyclePhase, ai as SessionLifecyclePhaseKind, aj as SessionLifecycleRoomBridge, ak as SessionLifecycleRoomBridgeProps, al as SessionReadinessFailure, am as SurfaceLayers, an as TranscriptSender, ao as TranscriptSenderOptions, ap as TurnState, aq as TurnTimeoutEvent, ar as UseAvatarQualityGovernorInput, as as UseLiveKitAvatarGrantInput, at as UseRealtimeSessionInput, au as UseSessionLifecycleInput, av as VoiceSpec, aw as VoiceSpecInput, ax as capacityErrorFromBusy, ay as capacityStateFromGrant, az as createProxyClient, aA as isNativeLiveTrackSubscribed, aB as knownBehaviorStates, aC as mapTurnState, aD as readInboundRtp, aE as sessionBehaviorSchema, aF as sessionClipSchema, aG as splitCallTranscript, aH as useAvatarAdaptivePlayoutDelay, aI as useAvatarCamera, aJ as useAvatarPlayoutDelay, aK as useAvatarQualityGovernor, aL as useCallTranscript, aM as useLiveKitAvatarGrant, aN as useLiveTrackProducing, aO as useMicLease, aP as useRealtimeSession, aQ as useReleaseMicLeaseOnTrackEnded, aR as useSessionLifecycle } from './proxy-client-BAY77sJZ.js';
5
5
  import { ReactNode, ReactElement } from 'react';
6
6
  import { StyleProp, ViewStyle } from 'react-native';
7
7
  export { useChat, useConnectionState, useLocalParticipant, useRoomContext, useTrackToggle, useTranscriptions, useVoiceAssistant } from '@livekit/components-react';
@@ -1630,13 +1630,17 @@ var styles = StyleSheet.create({
1630
1630
  }
1631
1631
  });
1632
1632
  var RECORDED_MEDIA_MODES = ["audio", "video", "audio_video"];
1633
- var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.");
1633
+ var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants", "tracks"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.");
1634
1634
  var RECORDING_STATUSES = ["pending", "recording", "processing", "ready", "failed", "expired"];
1635
1635
  z.enum(RECORDING_STATUSES);
1636
1636
  var recordingParticipantSchema = z.object({
1637
1637
  participantIdentity: z.string().min(1).max(160),
1638
1638
  role: z.enum(["user", "avatar"])
1639
1639
  }).strict();
1640
+ var recordingMutedIntervalSchema = z.object({
1641
+ startMs: z.number().int().min(0),
1642
+ endMs: z.number().int().positive()
1643
+ }).strict().refine((interval) => interval.endMs > interval.startMs, "Muted interval must end after it starts");
1640
1644
  var recordingMetadataSchema = z.object({
1641
1645
  sessionId: z.string().min(1),
1642
1646
  recordingId: z.string().min(1),
@@ -1645,13 +1649,15 @@ var recordingMetadataSchema = z.object({
1645
1649
  retainedUntil: z.string().datetime({ offset: true }),
1646
1650
  participant: recordingParticipantSchema.optional(),
1647
1651
  mediaStartedAt: z.string().datetime({ offset: true }).optional(),
1648
- mediaEndedAt: z.string().datetime({ offset: true }).optional()
1652
+ mediaEndedAt: z.string().datetime({ offset: true }).optional(),
1653
+ mediaStartOffsetMs: z.number().int().min(0).optional().describe("First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero."),
1654
+ mediaMutedIntervalsMs: z.array(recordingMutedIntervalSchema).optional().describe("Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals.")
1649
1655
  });
1650
1656
  var recordingArtifactSchema = z.discriminatedUnion("status", [
1651
1657
  recordingMetadataSchema.extend({ status: z.enum(["pending", "recording", "processing", "expired"]) }).strict(),
1652
1658
  recordingMetadataSchema.extend({
1653
1659
  status: z.literal("ready"),
1654
- mediaType: z.enum(["audio/mp4", "video/mp4"]),
1660
+ mediaType: z.enum(["audio/mp4", "video/mp4", "audio/ogg", "video/webm"]),
1655
1661
  sizeBytes: z.number().int().positive(),
1656
1662
  durationMs: z.number().int().nonnegative().nullable()
1657
1663
  }).strict(),
package/dist/react.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ToolRegistration, RegisteredTool } from './tools.js';
2
- import { ak as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit, aS as CallMicrophone, aT as CallAudioPlayback, aU as CallMicrophoneProblem } from './proxy-client-DW_EOvmE.js';
3
- export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, aV as AvatarVideoSurface, aW as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, i as CallTranscriptSegment, j as CartesiaTtsModel, k as ClipResult, l as ClosingTurnResult, m as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, n as DEFAULT_CREDITS_LOW_LEAD_SECONDS, o as DEFAULT_GOVERNOR_CONFIG, p as DEFAULT_GRACE_CEILING_SECONDS, q as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, r as DEFAULT_IDLE_SECONDS, s as DEFAULT_IDLE_WARN_LEAD_SECONDS, t as DEFAULT_READY_TIMEOUT_SECONDS, u as DEFAULT_TURN_TIMEOUT_SECONDS, v as DeclaredInputSource, E as EndReason, w as EndedEvent, x as ExtendResult, F as FishTtsModel, y as FreezeReadingFn, G as Governor, z as GovernorAction, H as GovernorConfig, I as GovernorSignal, J as GovernorState, K as GraceWindowClosedEvent, M as GraceWindowOpenEvent, N as GraceWindowState, O as IdleWarningEvent, P as InboundRtpCursor, Q as InboundRtpReading, R as KnownBehaviorState, S as LLMProvider, T as LLMSelection, U as LiveKitAvatarGrantState, V as LiveKitAvatarGrantStatus, W as LiveKitCapacityState, X as LiveKitConnectionStatus, Y as LiveKitSessionRequest, Z as LiveKitSessionStartResult, aX as LivePlaybackKeeper, _ as MAX_RECONNECT_ATTEMPTS, $ as MAX_SESSION_INSTRUCTIONS_CHARS, aY as MIC_LEASE_ENDED_TIMEOUT_MS, aZ as MicLease, a_ as NETWORK_QUALITY_POLICY, a$ as NetworkQualityStatus, b0 as PlayableVideoElement, a0 as ProxyClientOptions, a1 as QualityCap, a2 as RECONNECT_BACKOFF_MS, b1 as RealtimeAvatarCapacityError, b2 as RealtimeAvatarLiveKitRoom, b3 as RealtimeAvatarLiveKitRoomProps, a3 as RealtimeAvatarRequestOptions, a4 as RealtimeSessionApi, a5 as RealtimeSessionMedia, a6 as RealtimeSessionRoomSinks, a7 as ReconnectPolicy, a8 as ReconnectingEvent, a9 as RecoveryState, aa as RetryStep, ab as SendTurnOptions, ac as SessionBehavior, ad as SessionClip, ae as SessionClocks, af as SessionEndReason, ag as SessionLifecycleApi, ah as SessionLifecyclePhase, ai as SessionLifecyclePhaseKind, aj as SessionLifecycleRoomBridge, al as SessionReadinessFailure, am as SurfaceLayers, an as TranscriptSender, ao as TranscriptSenderOptions, ap as TurnState, aq as TurnTimeoutEvent, ar as UseAvatarQualityGovernorInput, as as UseLiveKitAvatarGrantInput, at as UseRealtimeSessionInput, au as UseSessionLifecycleInput, av as VoiceSpec, aw as VoiceSpecInput, ax as capacityErrorFromBusy, ay as capacityStateFromGrant, az as createProxyClient, aB as knownBehaviorStates, aC as mapTurnState, aD as readInboundRtp, aE as sessionBehaviorSchema, aF as sessionClipSchema, b4 as shouldReplayPendingTurn, aG as splitCallTranscript, aH as useAvatarAdaptivePlayoutDelay, aI as useAvatarCamera, aJ as useAvatarPlayoutDelay, aK as useAvatarQualityGovernor, aL as useCallTranscript, aM as useLiveKitAvatarGrant, aO as useMicLease, aP as useRealtimeSession, aQ as useReleaseMicLeaseOnTrackEnded, aR as useSessionLifecycle } from './proxy-client-DW_EOvmE.js';
2
+ import { ak as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit, aS as CallMicrophone, aT as CallAudioPlayback, aU as CallMicrophoneProblem } from './proxy-client-BAY77sJZ.js';
3
+ export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, aV as AvatarVideoSurface, aW as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, i as CallTranscriptSegment, j as CartesiaTtsModel, k as ClipResult, l as ClosingTurnResult, m as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, n as DEFAULT_CREDITS_LOW_LEAD_SECONDS, o as DEFAULT_GOVERNOR_CONFIG, p as DEFAULT_GRACE_CEILING_SECONDS, q as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, r as DEFAULT_IDLE_SECONDS, s as DEFAULT_IDLE_WARN_LEAD_SECONDS, t as DEFAULT_READY_TIMEOUT_SECONDS, u as DEFAULT_TURN_TIMEOUT_SECONDS, v as DeclaredInputSource, E as EndReason, w as EndedEvent, x as ExtendResult, F as FishTtsModel, y as FreezeReadingFn, G as Governor, z as GovernorAction, H as GovernorConfig, I as GovernorSignal, J as GovernorState, K as GraceWindowClosedEvent, M as GraceWindowOpenEvent, N as GraceWindowState, O as IdleWarningEvent, P as InboundRtpCursor, Q as InboundRtpReading, R as KnownBehaviorState, S as LLMProvider, T as LLMSelection, U as LiveKitAvatarGrantState, V as LiveKitAvatarGrantStatus, W as LiveKitCapacityState, X as LiveKitConnectionStatus, Y as LiveKitSessionRequest, Z as LiveKitSessionStartResult, aX as LivePlaybackKeeper, _ as MAX_RECONNECT_ATTEMPTS, $ as MAX_SESSION_INSTRUCTIONS_CHARS, aY as MIC_LEASE_ENDED_TIMEOUT_MS, aZ as MicLease, a_ as NETWORK_QUALITY_POLICY, a$ as NetworkQualityStatus, b0 as PlayableVideoElement, a0 as ProxyClientOptions, a1 as QualityCap, a2 as RECONNECT_BACKOFF_MS, b1 as RealtimeAvatarCapacityError, b2 as RealtimeAvatarLiveKitRoom, b3 as RealtimeAvatarLiveKitRoomProps, a3 as RealtimeAvatarRequestOptions, a4 as RealtimeSessionApi, a5 as RealtimeSessionMedia, a6 as RealtimeSessionRoomSinks, a7 as ReconnectPolicy, a8 as ReconnectingEvent, a9 as RecoveryState, aa as RetryStep, ab as SendTurnOptions, ac as SessionBehavior, ad as SessionClip, ae as SessionClocks, af as SessionEndReason, ag as SessionLifecycleApi, ah as SessionLifecyclePhase, ai as SessionLifecyclePhaseKind, aj as SessionLifecycleRoomBridge, al as SessionReadinessFailure, am as SurfaceLayers, an as TranscriptSender, ao as TranscriptSenderOptions, ap as TurnState, aq as TurnTimeoutEvent, ar as UseAvatarQualityGovernorInput, as as UseLiveKitAvatarGrantInput, at as UseRealtimeSessionInput, au as UseSessionLifecycleInput, av as VoiceSpec, aw as VoiceSpecInput, ax as capacityErrorFromBusy, ay as capacityStateFromGrant, az as createProxyClient, aB as knownBehaviorStates, aC as mapTurnState, aD as readInboundRtp, aE as sessionBehaviorSchema, aF as sessionClipSchema, b4 as shouldReplayPendingTurn, aG as splitCallTranscript, aH as useAvatarAdaptivePlayoutDelay, aI as useAvatarCamera, aJ as useAvatarPlayoutDelay, aK as useAvatarQualityGovernor, aL as useCallTranscript, aM as useLiveKitAvatarGrant, aO as useMicLease, aP as useRealtimeSession, aQ as useReleaseMicLeaseOnTrackEnded, aR as useSessionLifecycle } from './proxy-client-BAY77sJZ.js';
4
4
  import { CSSProperties, ReactNode } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
- import { R as RecordingArtifact } from './recording-DjswTx5B.js';
6
+ import { R as RecordingArtifact } from './recording-ydFVySSd.js';
7
7
  export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-CtRZdtsj.js';
8
8
  import 'zod';
9
9
  import 'livekit-client';
@@ -201,7 +201,7 @@ type RecordingPlaybackAsset = {
201
201
  recording: RecordingArtifact;
202
202
  url: string;
203
203
  };
204
- /** Each participant file carries only its own voice; one clock drives both files. */
204
+ /** Participant files and separate track segments share one clock without mixing voices. */
205
205
  declare function RecordingPlayer({ assets, className }: {
206
206
  assets: readonly RecordingPlaybackAsset[];
207
207
  className?: string;
package/dist/react.js CHANGED
@@ -2249,13 +2249,17 @@ var AVATAR_VIDEO_BADGE = {
2249
2249
  var AVATAR_VIDEO_BADGE_DOT = { width: 6, height: 6, borderRadius: 9999, background: "#34d399" };
2250
2250
  var AVATAR_VIDEO_OVERLAY = { position: "absolute", inset: 0, zIndex: 40 };
2251
2251
  var RECORDED_MEDIA_MODES = ["audio", "video", "audio_video"];
2252
- var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.");
2252
+ var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants", "tracks"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.");
2253
2253
  var RECORDING_STATUSES = ["pending", "recording", "processing", "ready", "failed", "expired"];
2254
2254
  z.enum(RECORDING_STATUSES);
2255
2255
  var recordingParticipantSchema = z.object({
2256
2256
  participantIdentity: z.string().min(1).max(160),
2257
2257
  role: z.enum(["user", "avatar"])
2258
2258
  }).strict();
2259
+ var recordingMutedIntervalSchema = z.object({
2260
+ startMs: z.number().int().min(0),
2261
+ endMs: z.number().int().positive()
2262
+ }).strict().refine((interval) => interval.endMs > interval.startMs, "Muted interval must end after it starts");
2259
2263
  var recordingMetadataSchema = z.object({
2260
2264
  sessionId: z.string().min(1),
2261
2265
  recordingId: z.string().min(1),
@@ -2264,13 +2268,15 @@ var recordingMetadataSchema = z.object({
2264
2268
  retainedUntil: z.string().datetime({ offset: true }),
2265
2269
  participant: recordingParticipantSchema.optional(),
2266
2270
  mediaStartedAt: z.string().datetime({ offset: true }).optional(),
2267
- mediaEndedAt: z.string().datetime({ offset: true }).optional()
2271
+ mediaEndedAt: z.string().datetime({ offset: true }).optional(),
2272
+ mediaStartOffsetMs: z.number().int().min(0).optional().describe("First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero."),
2273
+ mediaMutedIntervalsMs: z.array(recordingMutedIntervalSchema).optional().describe("Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals.")
2268
2274
  });
2269
2275
  var recordingArtifactSchema = z.discriminatedUnion("status", [
2270
2276
  recordingMetadataSchema.extend({ status: z.enum(["pending", "recording", "processing", "expired"]) }).strict(),
2271
2277
  recordingMetadataSchema.extend({
2272
2278
  status: z.literal("ready"),
2273
- mediaType: z.enum(["audio/mp4", "video/mp4"]),
2279
+ mediaType: z.enum(["audio/mp4", "video/mp4", "audio/ogg", "video/webm"]),
2274
2280
  sizeBytes: z.number().int().positive(),
2275
2281
  durationMs: z.number().int().nonnegative().nullable()
2276
2282
  }).strict(),
@@ -4592,43 +4598,57 @@ function recordingTimeline(recordings) {
4592
4598
  if (new Set(recordings.map((recording) => recording.sessionId)).size > 1) throw new Error("Recordings belong to different sessions");
4593
4599
  if (new Set(recordings.map((recording) => recording.recordingId)).size !== recordings.length) throw new Error("Duplicate recording assets");
4594
4600
  const participants = recordings.filter((recording) => recording.participant && recording.status === "ready");
4595
- if (new Set(participants.map((recording) => recording.participant?.role)).size !== participants.length) throw new Error("Duplicate participant recordings");
4596
4601
  const entries = participants.map((recording) => {
4597
4602
  if (recording.status !== "ready" || !recording.mediaStartedAt || !recording.mediaEndedAt || recording.durationMs === null) {
4598
4603
  throw new Error("Recording media timing is unavailable");
4599
4604
  }
4600
4605
  const start = Date.parse(recording.mediaStartedAt);
4601
4606
  const end = Math.min(Date.parse(recording.mediaEndedAt), start + recording.durationMs);
4602
- if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) throw new Error("Recording media timing is invalid");
4603
- return { recording, start, end };
4607
+ const activeStart = start + (recording.mediaStartOffsetMs ?? 0);
4608
+ if (!Number.isFinite(start) || !Number.isFinite(end) || !Number.isFinite(activeStart) || end <= activeStart) throw new Error("Recording media timing is invalid");
4609
+ return { recording, start, activeStart, end };
4604
4610
  });
4605
4611
  if (!entries.length) return null;
4612
+ const laneEnds = /* @__PURE__ */ new Map();
4613
+ for (const { recording, activeStart, end } of [...entries].sort((a, b) => a.activeStart - b.activeStart)) {
4614
+ const kinds = recording.mode === "audio_video" ? ["audio", "video"] : [recording.mode];
4615
+ for (const kind of kinds) {
4616
+ const lane = `${recording.participant?.role}:${kind}`;
4617
+ if (activeStart < (laneEnds.get(lane) ?? -Infinity)) throw new Error(`Duplicate participant ${kind} recordings overlap`);
4618
+ laneEnds.set(lane, end);
4619
+ }
4620
+ }
4606
4621
  const originMs = Math.min(...entries.map((entry) => entry.start));
4607
4622
  return {
4608
4623
  originMs,
4609
4624
  durationMs: Math.max(...entries.map((entry) => entry.end)) - originMs,
4610
- entries: entries.map(({ recording, start, end }) => ({ recording, startMs: start - originMs, endMs: end - originMs }))
4625
+ entries: entries.map(({ recording, start, activeStart, end }) => ({ recording, startMs: start - originMs, activeStartMs: activeStart - originMs, endMs: end - originMs }))
4611
4626
  };
4612
4627
  }
4613
4628
  function recordingPositions(timeline, positionMs) {
4614
4629
  return timeline.entries.map((entry) => ({
4615
4630
  recordingId: entry.recording.recordingId,
4616
- active: positionMs >= entry.startMs && positionMs < entry.endMs,
4631
+ active: positionMs >= entry.activeStartMs && positionMs < entry.endMs,
4617
4632
  timeSeconds: Math.max(0, Math.min(positionMs, entry.endMs) - entry.startMs) / 1e3
4618
4633
  }));
4619
4634
  }
4620
- function exhausted(element, seconds) {
4621
- return Number.isFinite(element.duration) && seconds >= element.duration || element.ended && seconds >= element.currentTime;
4635
+ function recordingVideoVisible(recording, timeSeconds) {
4636
+ const mediaMs = timeSeconds * 1e3;
4637
+ return recording.mode !== "audio" && !(recording.mediaMutedIntervalsMs ?? []).some(
4638
+ (interval) => mediaMs >= interval.startMs && mediaMs < interval.endMs
4639
+ );
4622
4640
  }
4623
4641
  function RecordingPlayer({ assets, className }) {
4642
+ const timelineKey = JSON.stringify(assets.map((asset) => asset.recording));
4624
4643
  const timeline = useMemo(() => {
4625
4644
  try {
4626
4645
  return recordingTimeline(assets.map((asset) => asset.recording));
4627
4646
  } catch {
4628
4647
  return null;
4629
4648
  }
4630
- }, [assets]);
4649
+ }, [timelineKey]);
4631
4650
  const media = useRef(/* @__PURE__ */ new Map());
4651
+ const seeks = useRef(/* @__PURE__ */ new Map());
4632
4652
  const position = useRef(0);
4633
4653
  const anchor = useRef(null);
4634
4654
  const [displayPosition, setDisplayPosition] = useState(0);
@@ -4636,6 +4656,11 @@ function RecordingPlayer({ assets, className }) {
4636
4656
  const [buffering, setBuffering] = useState(false);
4637
4657
  const [error, setError] = useState("");
4638
4658
  const sourceKey = assets.map((asset) => `${asset.recording.recordingId}:${asset.url}`).join("|");
4659
+ const seekMedia = (recordingId, element, seconds) => {
4660
+ if (element.readyState < 1 || Math.abs(element.currentTime - seconds) <= 0.02) return;
4661
+ seeks.current.set(recordingId, { seconds, retries: 0 });
4662
+ element.currentTime = seconds;
4663
+ };
4639
4664
  useEffect(() => {
4640
4665
  if (!timeline) return;
4641
4666
  const sessionId = timeline.entries[0].recording.sessionId;
@@ -4652,6 +4677,7 @@ function RecordingPlayer({ assets, className }) {
4652
4677
  let last = performance.now();
4653
4678
  let painted = last;
4654
4679
  const starting = /* @__PURE__ */ new Set();
4680
+ const active = /* @__PURE__ */ new Set();
4655
4681
  const stopMedia = () => {
4656
4682
  for (const element of media.current.values()) element.pause();
4657
4683
  };
@@ -4668,19 +4694,23 @@ function RecordingPlayer({ assets, className }) {
4668
4694
  const states = recordingPositions(timeline, proposed);
4669
4695
  const waiting = playing && states.some((state) => {
4670
4696
  const element = media.current.get(state.recordingId);
4671
- return state.active && (!element || !exhausted(element, state.timeSeconds) && (element.readyState < 2 || element.seeking));
4697
+ return state.active && (!element || (!element.ended || seeks.current.has(state.recordingId)) && (element.readyState < 2 || element.seeking));
4672
4698
  });
4673
4699
  setBuffering(waiting);
4674
4700
  if (!waiting) position.current = proposed;
4675
4701
  for (const state of recordingPositions(timeline, position.current)) {
4676
4702
  const element = media.current.get(state.recordingId);
4677
4703
  if (!element) continue;
4678
- if (!state.active || exhausted(element, state.timeSeconds)) {
4704
+ if (!state.active) {
4679
4705
  element.pause();
4706
+ active.delete(state.recordingId);
4680
4707
  continue;
4681
4708
  }
4682
- if (element.readyState >= 1 && Math.abs(element.currentTime - state.timeSeconds) > 0.08) {
4683
- element.currentTime = state.timeSeconds;
4709
+ if (!active.has(state.recordingId)) seekMedia(state.recordingId, element, state.timeSeconds);
4710
+ if (element.readyState >= 1) active.add(state.recordingId);
4711
+ if (element.ended && !seeks.current.has(state.recordingId)) {
4712
+ element.pause();
4713
+ continue;
4684
4714
  }
4685
4715
  if (!playing || waiting) {
4686
4716
  element.pause();
@@ -4716,14 +4746,14 @@ function RecordingPlayer({ assets, className }) {
4716
4746
  stopMedia();
4717
4747
  };
4718
4748
  }, [timeline, playing, sourceKey]);
4719
- const activeIds = new Set(timeline ? recordingPositions(timeline, displayPosition).filter((state) => state.active).map((state) => state.recordingId) : []);
4749
+ const positions = new Map(timeline ? recordingPositions(timeline, displayPosition).map((state) => [state.recordingId, state]) : []);
4720
4750
  const seek = (value) => {
4721
4751
  position.current = value;
4722
4752
  setDisplayPosition(value);
4723
4753
  if (timeline) for (const state of recordingPositions(timeline, value)) {
4724
4754
  const element = media.current.get(state.recordingId);
4725
4755
  if (!element) continue;
4726
- if (element.readyState >= 1) element.currentTime = state.timeSeconds;
4756
+ seekMedia(state.recordingId, element, state.timeSeconds);
4727
4757
  if (!state.active) element.pause();
4728
4758
  }
4729
4759
  };
@@ -4737,8 +4767,8 @@ function RecordingPlayer({ assets, className }) {
4737
4767
  if (position.current >= timeline.durationMs) seek(0);
4738
4768
  for (const state of recordingPositions(timeline, position.current)) {
4739
4769
  const element = media.current.get(state.recordingId);
4740
- if (state.active && element && !exhausted(element, state.timeSeconds)) {
4741
- if (element.readyState >= 1) element.currentTime = state.timeSeconds;
4770
+ if (state.active && element) {
4771
+ seekMedia(state.recordingId, element, state.timeSeconds);
4742
4772
  void element.play().catch((cause) => {
4743
4773
  if (cause instanceof Error && cause.name === "AbortError") return;
4744
4774
  for (const current of media.current.values()) current.pause();
@@ -4752,6 +4782,8 @@ function RecordingPlayer({ assets, className }) {
4752
4782
  return /* @__PURE__ */ jsxs("div", { className, children: [
4753
4783
  /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 240px), 1fr))", gap: 12 }, children: assets.map(({ recording, url }) => {
4754
4784
  if (!recording.participant) return null;
4785
+ const state = positions.get(recording.recordingId);
4786
+ const visible = Boolean(state?.active && recordingVideoVisible(recording, state.timeSeconds));
4755
4787
  const label = recording.participant.role === "user" ? "User" : "Character";
4756
4788
  const ref = (element) => {
4757
4789
  if (element) media.current.set(recording.recordingId, element);
@@ -4762,22 +4794,36 @@ function RecordingPlayer({ assets, className }) {
4762
4794
  setPlaying(false);
4763
4795
  setError("A recording could not load. Refresh access and try again.");
4764
4796
  };
4765
- return recording.mode !== "audio" ? /* @__PURE__ */ jsxs("figure", { hidden: !activeIds.has(recording.recordingId), style: { margin: 0 }, children: [
4797
+ const onLoadedMetadata = (event) => {
4798
+ const state2 = timeline && recordingPositions(timeline, position.current).find((item) => item.recordingId === recording.recordingId);
4799
+ if (state2) seekMedia(recording.recordingId, event.currentTarget, state2.timeSeconds);
4800
+ };
4801
+ const onSeeked = (event) => {
4802
+ const pending = seeks.current.get(recording.recordingId);
4803
+ if (!pending) return;
4804
+ if (Math.abs(event.currentTarget.currentTime - pending.seconds) > 0.08 && pending.retries < 2) {
4805
+ pending.retries++;
4806
+ event.currentTarget.currentTime = pending.seconds;
4807
+ } else seeks.current.delete(recording.recordingId);
4808
+ };
4809
+ return recording.mode !== "audio" ? /* @__PURE__ */ jsxs("figure", { hidden: !visible, style: { margin: 0 }, children: [
4766
4810
  /* @__PURE__ */ jsx("figcaption", { children: label }),
4767
4811
  /* @__PURE__ */ jsx(
4768
4812
  "video",
4769
4813
  {
4770
4814
  ref,
4771
4815
  src: url,
4816
+ muted: recording.mode === "video",
4772
4817
  playsInline: true,
4773
4818
  preload: "auto",
4774
4819
  onError,
4775
- hidden: !activeIds.has(recording.recordingId),
4820
+ onLoadedMetadata,
4821
+ onSeeked,
4822
+ hidden: !visible,
4776
4823
  style: { width: "100%", maxHeight: 360, objectFit: "contain" }
4777
4824
  }
4778
- ),
4779
- !activeIds.has(recording.recordingId) ? /* @__PURE__ */ jsx("p", { children: "No recording at this time." }) : null
4780
- ] }, recording.recordingId) : /* @__PURE__ */ jsx("audio", { ref, src: url, preload: "auto", onError, "aria-label": `${label} audio` }, recording.recordingId);
4825
+ )
4826
+ ] }, recording.recordingId) : /* @__PURE__ */ jsx("audio", { ref, src: url, preload: "auto", onError, onLoadedMetadata, onSeeked, "aria-label": `${label} audio` }, recording.recordingId);
4781
4827
  }) }),
4782
4828
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
4783
4829
  /* @__PURE__ */ jsx("button", { type: "button", onClick: toggle, disabled: !timeline, style: { minHeight: 44, minWidth: 44 }, children: playing ? "Pause" : "Play" }),
@@ -6,6 +6,7 @@ declare const recordingModeSchema: z.ZodEnum<{
6
6
  audio_video: "audio_video";
7
7
  off: "off";
8
8
  participants: "participants";
9
+ tracks: "tracks";
9
10
  }>;
10
11
  type RecordingMode = z.infer<typeof recordingModeSchema>;
11
12
  declare const recordingArtifactStatusSchema: z.ZodEnum<{
@@ -44,6 +45,11 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
44
45
  }, z.core.$strict>>;
45
46
  mediaStartedAt: z.ZodOptional<z.ZodString>;
46
47
  mediaEndedAt: z.ZodOptional<z.ZodString>;
48
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
49
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
50
+ startMs: z.ZodNumber;
51
+ endMs: z.ZodNumber;
52
+ }, z.core.$strict>>>;
47
53
  status: z.ZodEnum<{
48
54
  pending: "pending";
49
55
  recording: "recording";
@@ -69,10 +75,17 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
69
75
  }, z.core.$strict>>;
70
76
  mediaStartedAt: z.ZodOptional<z.ZodString>;
71
77
  mediaEndedAt: z.ZodOptional<z.ZodString>;
78
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
79
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
80
+ startMs: z.ZodNumber;
81
+ endMs: z.ZodNumber;
82
+ }, z.core.$strict>>>;
72
83
  status: z.ZodLiteral<"ready">;
73
84
  mediaType: z.ZodEnum<{
74
85
  "audio/mp4": "audio/mp4";
75
86
  "video/mp4": "video/mp4";
87
+ "audio/ogg": "audio/ogg";
88
+ "video/webm": "video/webm";
76
89
  }>;
77
90
  sizeBytes: z.ZodNumber;
78
91
  durationMs: z.ZodNullable<z.ZodNumber>;
@@ -95,6 +108,11 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
95
108
  }, z.core.$strict>>;
96
109
  mediaStartedAt: z.ZodOptional<z.ZodString>;
97
110
  mediaEndedAt: z.ZodOptional<z.ZodString>;
111
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
112
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
113
+ startMs: z.ZodNumber;
114
+ endMs: z.ZodNumber;
115
+ }, z.core.$strict>>>;
98
116
  status: z.ZodLiteral<"failed">;
99
117
  errorCode: z.ZodEnum<{
100
118
  recording_failed: "recording_failed";
@@ -128,6 +146,11 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
128
146
  }, z.core.$strict>>;
129
147
  mediaStartedAt: z.ZodOptional<z.ZodString>;
130
148
  mediaEndedAt: z.ZodOptional<z.ZodString>;
149
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
150
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
151
+ startMs: z.ZodNumber;
152
+ endMs: z.ZodNumber;
153
+ }, z.core.$strict>>>;
131
154
  status: z.ZodEnum<{
132
155
  pending: "pending";
133
156
  recording: "recording";
@@ -153,10 +176,17 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
153
176
  }, z.core.$strict>>;
154
177
  mediaStartedAt: z.ZodOptional<z.ZodString>;
155
178
  mediaEndedAt: z.ZodOptional<z.ZodString>;
179
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
180
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
181
+ startMs: z.ZodNumber;
182
+ endMs: z.ZodNumber;
183
+ }, z.core.$strict>>>;
156
184
  status: z.ZodLiteral<"ready">;
157
185
  mediaType: z.ZodEnum<{
158
186
  "audio/mp4": "audio/mp4";
159
187
  "video/mp4": "video/mp4";
188
+ "audio/ogg": "audio/ogg";
189
+ "video/webm": "video/webm";
160
190
  }>;
161
191
  sizeBytes: z.ZodNumber;
162
192
  durationMs: z.ZodNullable<z.ZodNumber>;
@@ -179,6 +209,11 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
179
209
  }, z.core.$strict>>;
180
210
  mediaStartedAt: z.ZodOptional<z.ZodString>;
181
211
  mediaEndedAt: z.ZodOptional<z.ZodString>;
212
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
213
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
214
+ startMs: z.ZodNumber;
215
+ endMs: z.ZodNumber;
216
+ }, z.core.$strict>>>;
182
217
  status: z.ZodLiteral<"failed">;
183
218
  errorCode: z.ZodEnum<{
184
219
  recording_failed: "recording_failed";
@@ -1,5 +1,5 @@
1
- import { R as RecordingArtifact } from './recording-DjswTx5B.js';
2
- export { L as ListRecordingsQuery, a as ListRecordingsResponse, b as RecordingAccessResponse, c as RecordingArtifactStatus, d as RecordingMode, e as RecordingParticipant, l as listRecordingsQuerySchema, f as listRecordingsResponseSchema, r as recordingAccessResponseSchema, g as recordingArtifactSchema, h as recordingArtifactStatusSchema, i as recordingModeSchema, j as recordingParticipantSchema } from './recording-DjswTx5B.js';
1
+ import { R as RecordingArtifact } from './recording-ydFVySSd.js';
2
+ export { L as ListRecordingsQuery, a as ListRecordingsResponse, b as RecordingAccessResponse, c as RecordingArtifactStatus, d as RecordingMode, e as RecordingParticipant, l as listRecordingsQuerySchema, f as listRecordingsResponseSchema, r as recordingAccessResponseSchema, g as recordingArtifactSchema, h as recordingArtifactStatusSchema, i as recordingModeSchema, j as recordingParticipantSchema } from './recording-ydFVySSd.js';
3
3
  import 'zod';
4
4
 
5
5
  declare function recordingTimeline(recordings: readonly RecordingArtifact[]): {
@@ -13,7 +13,7 @@ declare function recordingTimeline(recordings: readonly RecordingArtifact[]): {
13
13
  createdAt: string;
14
14
  retainedUntil: string;
15
15
  status: "ready";
16
- mediaType: "audio/mp4" | "video/mp4";
16
+ mediaType: "audio/mp4" | "video/mp4" | "audio/ogg" | "video/webm";
17
17
  sizeBytes: number;
18
18
  durationMs: number | null;
19
19
  participant?: {
@@ -22,8 +22,14 @@ declare function recordingTimeline(recordings: readonly RecordingArtifact[]): {
22
22
  } | undefined;
23
23
  mediaStartedAt?: string | undefined;
24
24
  mediaEndedAt?: string | undefined;
25
+ mediaStartOffsetMs?: number | undefined;
26
+ mediaMutedIntervalsMs?: {
27
+ startMs: number;
28
+ endMs: number;
29
+ }[] | undefined;
25
30
  };
26
31
  startMs: number;
32
+ activeStartMs: number;
27
33
  endMs: number;
28
34
  }[];
29
35
  } | null;
package/dist/recording.js CHANGED
@@ -2,13 +2,17 @@ import { z } from 'zod';
2
2
 
3
3
  // ../http-client/src/generated/recording.ts
4
4
  var RECORDED_MEDIA_MODES = ["audio", "video", "audio_video"];
5
- var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.");
5
+ var recordingModeSchema = z.enum(["off", ...RECORDED_MEDIA_MODES, "participants", "tracks"]).describe("Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.");
6
6
  var RECORDING_STATUSES = ["pending", "recording", "processing", "ready", "failed", "expired"];
7
7
  var recordingArtifactStatusSchema = z.enum(RECORDING_STATUSES);
8
8
  var recordingParticipantSchema = z.object({
9
9
  participantIdentity: z.string().min(1).max(160),
10
10
  role: z.enum(["user", "avatar"])
11
11
  }).strict();
12
+ var recordingMutedIntervalSchema = z.object({
13
+ startMs: z.number().int().min(0),
14
+ endMs: z.number().int().positive()
15
+ }).strict().refine((interval) => interval.endMs > interval.startMs, "Muted interval must end after it starts");
12
16
  var recordingMetadataSchema = z.object({
13
17
  sessionId: z.string().min(1),
14
18
  recordingId: z.string().min(1),
@@ -17,13 +21,15 @@ var recordingMetadataSchema = z.object({
17
21
  retainedUntil: z.string().datetime({ offset: true }),
18
22
  participant: recordingParticipantSchema.optional(),
19
23
  mediaStartedAt: z.string().datetime({ offset: true }).optional(),
20
- mediaEndedAt: z.string().datetime({ offset: true }).optional()
24
+ mediaEndedAt: z.string().datetime({ offset: true }).optional(),
25
+ mediaStartOffsetMs: z.number().int().min(0).optional().describe("First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero."),
26
+ mediaMutedIntervalsMs: z.array(recordingMutedIntervalSchema).optional().describe("Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals.")
21
27
  });
22
28
  var recordingArtifactSchema = z.discriminatedUnion("status", [
23
29
  recordingMetadataSchema.extend({ status: z.enum(["pending", "recording", "processing", "expired"]) }).strict(),
24
30
  recordingMetadataSchema.extend({
25
31
  status: z.literal("ready"),
26
- mediaType: z.enum(["audio/mp4", "video/mp4"]),
32
+ mediaType: z.enum(["audio/mp4", "video/mp4", "audio/ogg", "video/webm"]),
27
33
  sizeBytes: z.number().int().positive(),
28
34
  durationMs: z.number().int().nonnegative().nullable()
29
35
  }).strict(),
@@ -52,28 +58,37 @@ function recordingTimeline(recordings) {
52
58
  if (new Set(recordings.map((recording) => recording.sessionId)).size > 1) throw new Error("Recordings belong to different sessions");
53
59
  if (new Set(recordings.map((recording) => recording.recordingId)).size !== recordings.length) throw new Error("Duplicate recording assets");
54
60
  const participants = recordings.filter((recording) => recording.participant && recording.status === "ready");
55
- if (new Set(participants.map((recording) => recording.participant?.role)).size !== participants.length) throw new Error("Duplicate participant recordings");
56
61
  const entries = participants.map((recording) => {
57
62
  if (recording.status !== "ready" || !recording.mediaStartedAt || !recording.mediaEndedAt || recording.durationMs === null) {
58
63
  throw new Error("Recording media timing is unavailable");
59
64
  }
60
65
  const start = Date.parse(recording.mediaStartedAt);
61
66
  const end = Math.min(Date.parse(recording.mediaEndedAt), start + recording.durationMs);
62
- if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) throw new Error("Recording media timing is invalid");
63
- return { recording, start, end };
67
+ const activeStart = start + (recording.mediaStartOffsetMs ?? 0);
68
+ if (!Number.isFinite(start) || !Number.isFinite(end) || !Number.isFinite(activeStart) || end <= activeStart) throw new Error("Recording media timing is invalid");
69
+ return { recording, start, activeStart, end };
64
70
  });
65
71
  if (!entries.length) return null;
72
+ const laneEnds = /* @__PURE__ */ new Map();
73
+ for (const { recording, activeStart, end } of [...entries].sort((a, b) => a.activeStart - b.activeStart)) {
74
+ const kinds = recording.mode === "audio_video" ? ["audio", "video"] : [recording.mode];
75
+ for (const kind of kinds) {
76
+ const lane = `${recording.participant?.role}:${kind}`;
77
+ if (activeStart < (laneEnds.get(lane) ?? -Infinity)) throw new Error(`Duplicate participant ${kind} recordings overlap`);
78
+ laneEnds.set(lane, end);
79
+ }
80
+ }
66
81
  const originMs = Math.min(...entries.map((entry) => entry.start));
67
82
  return {
68
83
  originMs,
69
84
  durationMs: Math.max(...entries.map((entry) => entry.end)) - originMs,
70
- entries: entries.map(({ recording, start, end }) => ({ recording, startMs: start - originMs, endMs: end - originMs }))
85
+ entries: entries.map(({ recording, start, activeStart, end }) => ({ recording, startMs: start - originMs, activeStartMs: activeStart - originMs, endMs: end - originMs }))
71
86
  };
72
87
  }
73
88
  function recordingPositions(timeline, positionMs) {
74
89
  return timeline.entries.map((entry) => ({
75
90
  recordingId: entry.recording.recordingId,
76
- active: positionMs >= entry.startMs && positionMs < entry.endMs,
91
+ active: positionMs >= entry.activeStartMs && positionMs < entry.endMs,
77
92
  timeSeconds: Math.max(0, Math.min(positionMs, entry.endMs) - entry.startMs) / 1e3
78
93
  }));
79
94
  }
package/dist/server.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, RealtimeAvatarOptions, StartCallOptions, clipLibraryDeclarationSchema, verifyTranscript } from './index.js';
2
- export { h as Asset, g as AssetKind, A as Avatar, o as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, p as CallConnection, C as CallMode, a as CallPolicy, q as CallQueued, r as ClipDeclaration, f as ClipLibrary, d as ClipLibraryDeclaration, s as ClipLibraryPlan, e as ClipLibraryUpdate, t as ClipSource, u as ContextMessage, n as CreditBalance, E as EndCallOptions, v as EndCallReason, w as ExternalLlm, k as ListRecordingsQuery, l as ListRecordingsResponse, i as ListSessionsOptions, L as LoopRedirect, m as RecordingAccessResponse, R as RecordingArtifact, x as RecordingArtifactStatus, y as RecordingMode, S as StartCallResult, T as TranscriptPayload, j as UsageSession, U as UsageSessionPage, V as VideoPolicy, z as isQueued, B as listRecordingsResponseSchema, D as recordingAccessResponseSchema, F as recordingArtifactSchema, G as recordingModeSchema } from './types-D_s7ff7w.js';
2
+ export { h as Asset, g as AssetKind, A as Avatar, o as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, p as CallConnection, C as CallMode, a as CallPolicy, q as CallQueued, r as ClipDeclaration, f as ClipLibrary, d as ClipLibraryDeclaration, s as ClipLibraryPlan, e as ClipLibraryUpdate, t as ClipSource, u as ContextMessage, n as CreditBalance, E as EndCallOptions, v as EndCallReason, w as ExternalLlm, k as ListRecordingsQuery, l as ListRecordingsResponse, i as ListSessionsOptions, L as LoopRedirect, m as RecordingAccessResponse, R as RecordingArtifact, x as RecordingArtifactStatus, y as RecordingMode, S as StartCallResult, T as TranscriptPayload, j as UsageSession, U as UsageSessionPage, V as VideoPolicy, z as isQueued, B as listRecordingsResponseSchema, D as recordingAccessResponseSchema, F as recordingArtifactSchema, G as recordingModeSchema } from './types-kgmS7ECd.js';
3
3
  import './connection-history.js';
4
4
  import 'zod';
package/dist/server.js CHANGED
@@ -1,2 +1,2 @@
1
- export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-YR5NJTEO.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-W4DGBG4Z.js';
2
2
  import './chunk-MCIXU6XP.js';
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig } from './types-BH9O5leB.js';
2
- import './types-D_s7ff7w.js';
1
+ import { P as ProxyConfig } from './types-lgS4Z9rd.js';
2
+ import './types-kgmS7ECd.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-SP5SYYSS.js';
2
- import './chunk-YR5NJTEO.js';
1
+ import { createProxyHandler } from './chunk-QBOIOZ3O.js';
2
+ import './chunk-W4DGBG4Z.js';
3
3
  import './chunk-MCIXU6XP.js';
4
4
 
5
5
  // src/tanstack-start.ts
@@ -176,10 +176,10 @@ interface components {
176
176
  */
177
177
  content_policy?: "minor-sexual-content-v1";
178
178
  /**
179
- * @description Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.
179
+ * @description Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.
180
180
  * @enum {string}
181
181
  */
182
- recording?: "off" | "audio" | "video" | "audio_video" | "participants";
182
+ recording?: "off" | "audio" | "video" | "audio_video" | "participants" | "tracks";
183
183
  connection_history?: boolean;
184
184
  client_metadata?: {
185
185
  [key: string]: string;
@@ -264,6 +264,13 @@ interface components {
264
264
  mediaStartedAt?: string;
265
265
  /** Format: date-time */
266
266
  mediaEndedAt?: string;
267
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
268
+ mediaStartOffsetMs?: number;
269
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
270
+ mediaMutedIntervalsMs?: {
271
+ startMs: number;
272
+ endMs: number;
273
+ }[];
267
274
  /** @enum {string} */
268
275
  status: "pending" | "recording" | "processing" | "expired";
269
276
  } | {
@@ -284,10 +291,17 @@ interface components {
284
291
  mediaStartedAt?: string;
285
292
  /** Format: date-time */
286
293
  mediaEndedAt?: string;
294
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
295
+ mediaStartOffsetMs?: number;
296
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
297
+ mediaMutedIntervalsMs?: {
298
+ startMs: number;
299
+ endMs: number;
300
+ }[];
287
301
  /** @constant */
288
302
  status: "ready";
289
303
  /** @enum {string} */
290
- mediaType: "audio/mp4" | "video/mp4";
304
+ mediaType: "audio/mp4" | "video/mp4" | "audio/ogg" | "video/webm";
291
305
  sizeBytes: number;
292
306
  durationMs: number | null;
293
307
  } | {
@@ -308,16 +322,23 @@ interface components {
308
322
  mediaStartedAt?: string;
309
323
  /** Format: date-time */
310
324
  mediaEndedAt?: string;
325
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
326
+ mediaStartOffsetMs?: number;
327
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
328
+ mediaMutedIntervalsMs?: {
329
+ startMs: number;
330
+ endMs: number;
331
+ }[];
311
332
  /** @constant */
312
333
  status: "failed";
313
334
  /** @enum {string} */
314
335
  errorCode: "recording_failed" | "recording_unavailable";
315
336
  };
316
337
  /**
317
- * @description Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices.
338
+ * @description Server-owned recording policy; omitted means off. Legacy audio includes user and avatar audio. participants saves one continuous audio/video file per participant, without mixing their voices. tracks saves each published participant audio/video track separately; list recordings by session after capture.
318
339
  * @enum {string}
319
340
  */
320
- readonly recording_mode?: "off" | "audio" | "video" | "audio_video" | "participants";
341
+ readonly recording_mode?: "off" | "audio" | "video" | "audio_video" | "participants" | "tracks";
321
342
  readonly recordings?: ({
322
343
  sessionId: string;
323
344
  recordingId: string;
@@ -336,6 +357,13 @@ interface components {
336
357
  mediaStartedAt?: string;
337
358
  /** Format: date-time */
338
359
  mediaEndedAt?: string;
360
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
361
+ mediaStartOffsetMs?: number;
362
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
363
+ mediaMutedIntervalsMs?: {
364
+ startMs: number;
365
+ endMs: number;
366
+ }[];
339
367
  /** @enum {string} */
340
368
  status: "pending" | "recording" | "processing" | "expired";
341
369
  } | {
@@ -356,10 +384,17 @@ interface components {
356
384
  mediaStartedAt?: string;
357
385
  /** Format: date-time */
358
386
  mediaEndedAt?: string;
387
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
388
+ mediaStartOffsetMs?: number;
389
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
390
+ mediaMutedIntervalsMs?: {
391
+ startMs: number;
392
+ endMs: number;
393
+ }[];
359
394
  /** @constant */
360
395
  status: "ready";
361
396
  /** @enum {string} */
362
- mediaType: "audio/mp4" | "video/mp4";
397
+ mediaType: "audio/mp4" | "video/mp4" | "audio/ogg" | "video/webm";
363
398
  sizeBytes: number;
364
399
  durationMs: number | null;
365
400
  } | {
@@ -380,6 +415,13 @@ interface components {
380
415
  mediaStartedAt?: string;
381
416
  /** Format: date-time */
382
417
  mediaEndedAt?: string;
418
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
419
+ mediaStartOffsetMs?: number;
420
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
421
+ mediaMutedIntervalsMs?: {
422
+ startMs: number;
423
+ endMs: number;
424
+ }[];
383
425
  /** @constant */
384
426
  status: "failed";
385
427
  /** @enum {string} */
@@ -919,6 +961,13 @@ interface components {
919
961
  mediaStartedAt?: string;
920
962
  /** Format: date-time */
921
963
  mediaEndedAt?: string;
964
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
965
+ mediaStartOffsetMs?: number;
966
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
967
+ mediaMutedIntervalsMs?: {
968
+ startMs: number;
969
+ endMs: number;
970
+ }[];
922
971
  /** @enum {string} */
923
972
  status: "pending" | "recording" | "processing" | "expired";
924
973
  } | {
@@ -939,10 +988,17 @@ interface components {
939
988
  mediaStartedAt?: string;
940
989
  /** Format: date-time */
941
990
  mediaEndedAt?: string;
991
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
992
+ mediaStartOffsetMs?: number;
993
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
994
+ mediaMutedIntervalsMs?: {
995
+ startMs: number;
996
+ endMs: number;
997
+ }[];
942
998
  /** @constant */
943
999
  status: "ready";
944
1000
  /** @enum {string} */
945
- mediaType: "audio/mp4" | "video/mp4";
1001
+ mediaType: "audio/mp4" | "video/mp4" | "audio/ogg" | "video/webm";
946
1002
  sizeBytes: number;
947
1003
  durationMs: number | null;
948
1004
  } | {
@@ -963,6 +1019,13 @@ interface components {
963
1019
  mediaStartedAt?: string;
964
1020
  /** Format: date-time */
965
1021
  mediaEndedAt?: string;
1022
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
1023
+ mediaStartOffsetMs?: number;
1024
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
1025
+ mediaMutedIntervalsMs?: {
1026
+ startMs: number;
1027
+ endMs: number;
1028
+ }[];
966
1029
  /** @constant */
967
1030
  status: "failed";
968
1031
  /** @enum {string} */
@@ -987,6 +1050,13 @@ interface components {
987
1050
  mediaStartedAt?: string;
988
1051
  /** Format: date-time */
989
1052
  mediaEndedAt?: string;
1053
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
1054
+ mediaStartOffsetMs?: number;
1055
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
1056
+ mediaMutedIntervalsMs?: {
1057
+ startMs: number;
1058
+ endMs: number;
1059
+ }[];
990
1060
  /** @enum {string} */
991
1061
  status: "pending" | "recording" | "processing" | "expired";
992
1062
  } | {
@@ -1007,10 +1077,17 @@ interface components {
1007
1077
  mediaStartedAt?: string;
1008
1078
  /** Format: date-time */
1009
1079
  mediaEndedAt?: string;
1080
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
1081
+ mediaStartOffsetMs?: number;
1082
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
1083
+ mediaMutedIntervalsMs?: {
1084
+ startMs: number;
1085
+ endMs: number;
1086
+ }[];
1010
1087
  /** @constant */
1011
1088
  status: "ready";
1012
1089
  /** @enum {string} */
1013
- mediaType: "audio/mp4" | "video/mp4";
1090
+ mediaType: "audio/mp4" | "video/mp4" | "audio/ogg" | "video/webm";
1014
1091
  sizeBytes: number;
1015
1092
  durationMs: number | null;
1016
1093
  } | {
@@ -1031,6 +1108,13 @@ interface components {
1031
1108
  mediaStartedAt?: string;
1032
1109
  /** Format: date-time */
1033
1110
  mediaEndedAt?: string;
1111
+ /** @description First playable native media timestamp relative to mediaStartedAt. Playback retains this offset; it does not reset the file clock to zero. */
1112
+ mediaStartOffsetMs?: number;
1113
+ /** @description Intervals of hidden camera video relative to mediaStartedAt, using native media timestamps. Audio playback continues through these intervals. */
1114
+ mediaMutedIntervalsMs?: {
1115
+ startMs: number;
1116
+ endMs: number;
1117
+ }[];
1034
1118
  /** @constant */
1035
1119
  status: "failed";
1036
1120
  /** @enum {string} */
@@ -1323,6 +1407,7 @@ declare const recordingModeSchema: z.ZodEnum<{
1323
1407
  video: "video";
1324
1408
  audio_video: "audio_video";
1325
1409
  participants: "participants";
1410
+ tracks: "tracks";
1326
1411
  }>;
1327
1412
  type RecordingMode = z.infer<typeof recordingModeSchema>;
1328
1413
  declare const recordingArtifactStatusSchema: z.ZodEnum<{
@@ -1353,6 +1438,11 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1353
1438
  }, z.core.$strict>>;
1354
1439
  mediaStartedAt: z.ZodOptional<z.ZodString>;
1355
1440
  mediaEndedAt: z.ZodOptional<z.ZodString>;
1441
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
1442
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
1443
+ startMs: z.ZodNumber;
1444
+ endMs: z.ZodNumber;
1445
+ }, z.core.$strict>>>;
1356
1446
  status: z.ZodEnum<{
1357
1447
  pending: "pending";
1358
1448
  recording: "recording";
@@ -1378,10 +1468,17 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1378
1468
  }, z.core.$strict>>;
1379
1469
  mediaStartedAt: z.ZodOptional<z.ZodString>;
1380
1470
  mediaEndedAt: z.ZodOptional<z.ZodString>;
1471
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
1472
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
1473
+ startMs: z.ZodNumber;
1474
+ endMs: z.ZodNumber;
1475
+ }, z.core.$strict>>>;
1381
1476
  status: z.ZodLiteral<"ready">;
1382
1477
  mediaType: z.ZodEnum<{
1383
1478
  "audio/mp4": "audio/mp4";
1384
1479
  "video/mp4": "video/mp4";
1480
+ "audio/ogg": "audio/ogg";
1481
+ "video/webm": "video/webm";
1385
1482
  }>;
1386
1483
  sizeBytes: z.ZodNumber;
1387
1484
  durationMs: z.ZodNullable<z.ZodNumber>;
@@ -1404,6 +1501,11 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1404
1501
  }, z.core.$strict>>;
1405
1502
  mediaStartedAt: z.ZodOptional<z.ZodString>;
1406
1503
  mediaEndedAt: z.ZodOptional<z.ZodString>;
1504
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
1505
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
1506
+ startMs: z.ZodNumber;
1507
+ endMs: z.ZodNumber;
1508
+ }, z.core.$strict>>>;
1407
1509
  status: z.ZodLiteral<"failed">;
1408
1510
  errorCode: z.ZodEnum<{
1409
1511
  recording_failed: "recording_failed";
@@ -1437,6 +1539,11 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
1437
1539
  }, z.core.$strict>>;
1438
1540
  mediaStartedAt: z.ZodOptional<z.ZodString>;
1439
1541
  mediaEndedAt: z.ZodOptional<z.ZodString>;
1542
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
1543
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
1544
+ startMs: z.ZodNumber;
1545
+ endMs: z.ZodNumber;
1546
+ }, z.core.$strict>>>;
1440
1547
  status: z.ZodEnum<{
1441
1548
  pending: "pending";
1442
1549
  recording: "recording";
@@ -1462,10 +1569,17 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
1462
1569
  }, z.core.$strict>>;
1463
1570
  mediaStartedAt: z.ZodOptional<z.ZodString>;
1464
1571
  mediaEndedAt: z.ZodOptional<z.ZodString>;
1572
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
1573
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
1574
+ startMs: z.ZodNumber;
1575
+ endMs: z.ZodNumber;
1576
+ }, z.core.$strict>>>;
1465
1577
  status: z.ZodLiteral<"ready">;
1466
1578
  mediaType: z.ZodEnum<{
1467
1579
  "audio/mp4": "audio/mp4";
1468
1580
  "video/mp4": "video/mp4";
1581
+ "audio/ogg": "audio/ogg";
1582
+ "video/webm": "video/webm";
1469
1583
  }>;
1470
1584
  sizeBytes: z.ZodNumber;
1471
1585
  durationMs: z.ZodNullable<z.ZodNumber>;
@@ -1488,6 +1602,11 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
1488
1602
  }, z.core.$strict>>;
1489
1603
  mediaStartedAt: z.ZodOptional<z.ZodString>;
1490
1604
  mediaEndedAt: z.ZodOptional<z.ZodString>;
1605
+ mediaStartOffsetMs: z.ZodOptional<z.ZodNumber>;
1606
+ mediaMutedIntervalsMs: z.ZodOptional<z.ZodArray<z.ZodObject<{
1607
+ startMs: z.ZodNumber;
1608
+ endMs: z.ZodNumber;
1609
+ }, z.core.$strict>>>;
1491
1610
  status: z.ZodLiteral<"failed">;
1492
1611
  errorCode: z.ZodEnum<{
1493
1612
  recording_failed: "recording_failed";
@@ -1,4 +1,4 @@
1
- import { C as CallMode, a as CallPolicy } from './types-D_s7ff7w.js';
1
+ import { C as CallMode, a as CallPolicy } from './types-kgmS7ECd.js';
2
2
 
3
3
  /** The three things a caller can ask the proxy to do. Gate on these, not on URLs. */
4
4
  type ProxyOperation = "connect" | "end" | "avatars" | "credits";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "realtime-avatar",
3
- "version": "0.27.2",
3
+ "version": "0.27.3",
4
4
  "description": "Realtime Avatar SDK. `realtime-avatar` and the route adapters hold your API key and are server-only; `realtime-avatar/react`, `/browser` and `/tools` never can — importing a server entry into a browser build throws at bundle time.",
5
5
  "license": "MIT",
6
6
  "author": "The Influence Company",