realtime-avatar 0.11.1 → 0.11.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.
@@ -218,7 +218,7 @@ function isTransient(cause) {
218
218
 
219
219
  // ../http-client/src/client.ts
220
220
  var DEFAULT_BASE_URL = "https://realtimeavatar.ai/api/v1";
221
- var SDK_VERSION = "0.11.1";
221
+ var SDK_VERSION = "0.11.3";
222
222
  var RealtimeAvatar = class {
223
223
  #apiKey;
224
224
  #baseUrl;
@@ -1,4 +1,4 @@
1
- import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-X3XXLAJJ.js';
1
+ import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-K726WCDN.js';
2
2
 
3
3
  // ../proxy/src/config.ts
4
4
  var json = (body, status = 200) => new Response(JSON.stringify(body), {
package/dist/express.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-2TQVKQJJ.js';
2
- import './chunk-X3XXLAJJ.js';
1
+ import { createProxyHandler } from './chunk-OVAQBIZ2.js';
2
+ import './chunk-K726WCDN.js';
3
3
 
4
4
  // ../proxy/src/express.ts
5
5
  function realtimeAvatarExpress(config) {
package/dist/hono.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-2TQVKQJJ.js';
2
- import './chunk-X3XXLAJJ.js';
1
+ import { createProxyHandler } from './chunk-OVAQBIZ2.js';
2
+ import './chunk-K726WCDN.js';
3
3
 
4
4
  // ../proxy/src/hono.ts
5
5
  function realtimeAvatarHono(config) {
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-X3XXLAJJ.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-K726WCDN.js';
package/dist/nextjs.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-2TQVKQJJ.js';
2
- import './chunk-X3XXLAJJ.js';
1
+ import { createProxyHandler } from './chunk-OVAQBIZ2.js';
2
+ import './chunk-K726WCDN.js';
3
3
 
4
4
  // ../proxy/src/nextjs.ts
5
5
  function createRealtimeAvatarRoute(config) {
@@ -378,8 +378,24 @@ declare const DEFAULT_GOVERNOR_CONFIG: GovernorConfig;
378
378
  interface Governor {
379
379
  state: GovernorState;
380
380
  cap: QualityCap;
381
- /** Consecutive failed up-probes — drives the exponential dwell backoff. */
381
+ /** Consecutive failed up-probes. Drives the exponential dwell backoff.
382
+ *
383
+ * NOT a signal about the low rung. It counts attempts to reach HIGH that did not
384
+ * survive, which says nothing about whether the rung below HIGH is holdable. Using it
385
+ * to pick WHICH rung "low" means was a real regression: see `lowUnhealthy`. */
382
386
  failures: number;
387
+ /** Unhealthy ticks observed while ALREADY on the low cap.
388
+ *
389
+ * This is the only honest evidence that the low rung itself is not affordable, and it
390
+ * is what steps the cap down to the bottom of the ladder. It exists because `failures`
391
+ * looked like it would do the job and does not: a starved OPENING reports the wait for
392
+ * the first frame as a freeze (`firstFrameWaitFreezeMs`), so any session whose first
393
+ * frame is slower than about a second fails its opening probation and lands on
394
+ * `failures = 1` before the link has been asked to carry anything. Measured on prod,
395
+ * first frames ran 2.4 s to 28 s, i.e. essentially every call. Keying the rung step on
396
+ * `failures` therefore sent the FIRST demote straight to the bottom rung, including on
397
+ * a call that recorded zero freezes. Reset whenever the cap returns to high. */
398
+ lowUnhealthy: number;
383
399
  /** Wall-clock ms the current state was entered (for dwell/clean/probe timing). */
384
400
  enteredAtMs: number;
385
401
  /** Wall-clock ms of the last healthy tick in the current low period (clean-window
@@ -1,8 +1,8 @@
1
1
  import { LiveKitRoomProps } from '@livekit/react-native';
2
2
  export { AudioSession, VideoTrack, VideoTrackProps, registerGlobals } from '@livekit/react-native';
3
3
  import { ReactNode, ReactElement } from 'react';
4
- import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-D1Zm7reb.js';
5
- export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, at as isNativeLiveTrackSubscribed, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aF as useLiveTrackProducing, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-D1Zm7reb.js';
4
+ import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-8cQOLLE_.js';
5
+ export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, at as isNativeLiveTrackSubscribed, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aF as useLiveTrackProducing, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-8cQOLLE_.js';
6
6
  import { StyleProp, ViewStyle } from 'react-native';
7
7
  export { useChat, useConnectionState, useLocalParticipant, useRoomContext, useTrackToggle, useTranscriptions, useVoiceAssistant } from '@livekit/components-react';
8
8
  export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
@@ -747,6 +747,7 @@ var initGovernor = (nowMs, openingCap = "low") => ({
747
747
  state: openingCap === "high" ? "opening_high" : "opening",
748
748
  cap: openingCap,
749
749
  failures: 0,
750
+ lowUnhealthy: 0,
750
751
  enteredAtMs: nowMs,
751
752
  healthySinceMs: null
752
753
  });
@@ -794,12 +795,14 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
794
795
  const healthy = isHealthy(s);
795
796
  const healthySince = healthy ? g.healthySinceMs ?? nowMs : null;
796
797
  const resetFailures = healthy && healthySince !== null && nowMs - healthySince >= cfg.healthyResetMs;
798
+ const lowUnhealthy = healthy ? g.lowUnhealthy : g.lowUnhealthy + 1;
797
799
  const dwellDone = nowMs - g.enteredAtMs >= dwellMs(g.failures, cfg);
798
800
  if (dwellDone && healthy) {
799
801
  return {
800
802
  governor: {
801
803
  ...enter(g, "cap_low_eligible", "low", nowMs),
802
804
  failures: resetFailures ? 0 : g.failures,
805
+ lowUnhealthy: resetFailures ? 0 : lowUnhealthy,
803
806
  healthySinceMs: nowMs
804
807
  }
805
808
  };
@@ -808,13 +811,14 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
808
811
  governor: {
809
812
  ...g,
810
813
  healthySinceMs: healthySince,
811
- failures: resetFailures ? 0 : g.failures
814
+ failures: resetFailures ? 0 : g.failures,
815
+ lowUnhealthy: resetFailures ? 0 : lowUnhealthy
812
816
  }
813
817
  };
814
818
  }
815
819
  case "cap_low_eligible": {
816
820
  if (!isHealthy(s)) {
817
- return { governor: { ...g, healthySinceMs: null } };
821
+ return { governor: { ...g, healthySinceMs: null, lowUnhealthy: g.lowUnhealthy + 1 } };
818
822
  }
819
823
  const cleanSince = g.healthySinceMs ?? nowMs;
820
824
  if (nowMs - cleanSince >= cfg.cleanMs) {
@@ -827,7 +831,9 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
827
831
  }
828
832
  case "probing_up": {
829
833
  if (nowMs - g.enteredAtMs >= cfg.probeMs) {
830
- return { governor: { ...enter(g, "cap_high_stable", "high", nowMs), failures: 0 } };
834
+ return {
835
+ governor: { ...enter(g, "cap_high_stable", "high", nowMs), failures: 0, lowUnhealthy: 0 }
836
+ };
831
837
  }
832
838
  return { governor: g };
833
839
  }
@@ -837,9 +843,11 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
837
843
  return { governor: g };
838
844
  }
839
845
  };
840
- var resolveLowCapQuality = (declaredLayerQualities) => {
846
+ var LOW_CAP_STEP_AFTER_UNHEALTHY = 2;
847
+ var resolveLowCapQuality = (declaredLayerQualities, lowUnhealthy = 0) => {
841
848
  const sorted = [...declaredLayerQualities].sort((a, b) => a - b);
842
- return sorted.length >= 2 ? sorted[sorted.length - 2] : 1;
849
+ if (sorted.length < 2) return 1;
850
+ return lowUnhealthy >= LOW_CAP_STEP_AFTER_UNHEALTHY ? sorted[0] : sorted[sorted.length - 2];
843
851
  };
844
852
 
845
853
  // ../client/src/react/use-quality-governor.ts
@@ -959,10 +967,11 @@ function useAvatarQualityGovernor(input) {
959
967
  return { freezeMs: 0, jitterRising: false };
960
968
  }
961
969
  };
962
- const applyCap = (cap) => {
970
+ const applyCap = (cap, lowUnhealthy = 0) => {
963
971
  try {
964
972
  const lowCap = resolveLowCapQuality(
965
- (targetPublication?.trackInfo?.layers ?? []).map((l) => l.quality)
973
+ (targetPublication?.trackInfo?.layers ?? []).map((l) => l.quality),
974
+ lowUnhealthy
966
975
  );
967
976
  targetPublication?.setVideoQuality?.(cap === "low" ? lowCap : VideoQuality.HIGH);
968
977
  } catch {
@@ -987,13 +996,13 @@ function useAvatarQualityGovernor(input) {
987
996
  pausedSinceTick = false;
988
997
  const { governor, action } = step(gov, signal, Date.now(), config);
989
998
  gov = governor;
990
- if (action) applyCap(action.setCap);
999
+ if (action) applyCap(action.setCap, gov.lowUnhealthy);
991
1000
  } catch {
992
1001
  } finally {
993
1002
  tickRunning = false;
994
1003
  }
995
1004
  };
996
- applyCap(gov.cap);
1005
+ applyCap(gov.cap, gov.lowUnhealthy);
997
1006
  const handle = setInterval(() => void tick(), tickMs);
998
1007
  return () => {
999
1008
  disposed = true;
package/dist/react.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { RegisteredTool, ToolRegistration } from './tools.js';
2
2
  import { ReactNode } from 'react';
3
- import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-D1Zm7reb.js';
4
- export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, aK as AvatarVideoSurface, aL as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, aM as LivePlaybackKeeper, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, aN as MIC_LEASE_ENDED_TIMEOUT_MS, aO as MicLease, aP as PlayableVideoElement, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, aQ as RealtimeAvatarCapacityError, aR as RealtimeAvatarLiveKitRoom, aS as RealtimeAvatarLiveKitRoomProps, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, aT as shouldReplayPendingTurn, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-D1Zm7reb.js';
3
+ import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-8cQOLLE_.js';
4
+ export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, aK as AvatarVideoSurface, aL as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, aM as LivePlaybackKeeper, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, aN as MIC_LEASE_ENDED_TIMEOUT_MS, aO as MicLease, aP as PlayableVideoElement, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, aQ as RealtimeAvatarCapacityError, aR as RealtimeAvatarLiveKitRoom, aS as RealtimeAvatarLiveKitRoomProps, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, aT as shouldReplayPendingTurn, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-8cQOLLE_.js';
5
5
  export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
6
6
  import 'zod';
7
7
  import '@livekit/components-react';
package/dist/react.js CHANGED
@@ -948,6 +948,7 @@ var initGovernor = (nowMs, openingCap = "low") => ({
948
948
  state: openingCap === "high" ? "opening_high" : "opening",
949
949
  cap: openingCap,
950
950
  failures: 0,
951
+ lowUnhealthy: 0,
951
952
  enteredAtMs: nowMs,
952
953
  healthySinceMs: null
953
954
  });
@@ -995,12 +996,14 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
995
996
  const healthy = isHealthy(s);
996
997
  const healthySince = healthy ? g.healthySinceMs ?? nowMs : null;
997
998
  const resetFailures = healthy && healthySince !== null && nowMs - healthySince >= cfg.healthyResetMs;
999
+ const lowUnhealthy = healthy ? g.lowUnhealthy : g.lowUnhealthy + 1;
998
1000
  const dwellDone = nowMs - g.enteredAtMs >= dwellMs(g.failures, cfg);
999
1001
  if (dwellDone && healthy) {
1000
1002
  return {
1001
1003
  governor: {
1002
1004
  ...enter(g, "cap_low_eligible", "low", nowMs),
1003
1005
  failures: resetFailures ? 0 : g.failures,
1006
+ lowUnhealthy: resetFailures ? 0 : lowUnhealthy,
1004
1007
  healthySinceMs: nowMs
1005
1008
  }
1006
1009
  };
@@ -1009,13 +1012,14 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
1009
1012
  governor: {
1010
1013
  ...g,
1011
1014
  healthySinceMs: healthySince,
1012
- failures: resetFailures ? 0 : g.failures
1015
+ failures: resetFailures ? 0 : g.failures,
1016
+ lowUnhealthy: resetFailures ? 0 : lowUnhealthy
1013
1017
  }
1014
1018
  };
1015
1019
  }
1016
1020
  case "cap_low_eligible": {
1017
1021
  if (!isHealthy(s)) {
1018
- return { governor: { ...g, healthySinceMs: null } };
1022
+ return { governor: { ...g, healthySinceMs: null, lowUnhealthy: g.lowUnhealthy + 1 } };
1019
1023
  }
1020
1024
  const cleanSince = g.healthySinceMs ?? nowMs;
1021
1025
  if (nowMs - cleanSince >= cfg.cleanMs) {
@@ -1028,7 +1032,9 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
1028
1032
  }
1029
1033
  case "probing_up": {
1030
1034
  if (nowMs - g.enteredAtMs >= cfg.probeMs) {
1031
- return { governor: { ...enter(g, "cap_high_stable", "high", nowMs), failures: 0 } };
1035
+ return {
1036
+ governor: { ...enter(g, "cap_high_stable", "high", nowMs), failures: 0, lowUnhealthy: 0 }
1037
+ };
1032
1038
  }
1033
1039
  return { governor: g };
1034
1040
  }
@@ -1038,9 +1044,11 @@ var step = (g, s, nowMs, cfg = DEFAULT_GOVERNOR_CONFIG) => {
1038
1044
  return { governor: g };
1039
1045
  }
1040
1046
  };
1041
- var resolveLowCapQuality = (declaredLayerQualities) => {
1047
+ var LOW_CAP_STEP_AFTER_UNHEALTHY = 2;
1048
+ var resolveLowCapQuality = (declaredLayerQualities, lowUnhealthy = 0) => {
1042
1049
  const sorted = [...declaredLayerQualities].sort((a, b) => a - b);
1043
- return sorted.length >= 2 ? sorted[sorted.length - 2] : 1;
1050
+ if (sorted.length < 2) return 1;
1051
+ return lowUnhealthy >= LOW_CAP_STEP_AFTER_UNHEALTHY ? sorted[0] : sorted[sorted.length - 2];
1044
1052
  };
1045
1053
 
1046
1054
  // ../client/src/react/use-quality-governor.ts
@@ -1160,10 +1168,11 @@ function useAvatarQualityGovernor(input) {
1160
1168
  return { freezeMs: 0, jitterRising: false };
1161
1169
  }
1162
1170
  };
1163
- const applyCap = (cap) => {
1171
+ const applyCap = (cap, lowUnhealthy = 0) => {
1164
1172
  try {
1165
1173
  const lowCap = resolveLowCapQuality(
1166
- (targetPublication?.trackInfo?.layers ?? []).map((l) => l.quality)
1174
+ (targetPublication?.trackInfo?.layers ?? []).map((l) => l.quality),
1175
+ lowUnhealthy
1167
1176
  );
1168
1177
  targetPublication?.setVideoQuality?.(cap === "low" ? lowCap : VideoQuality.HIGH);
1169
1178
  } catch {
@@ -1188,13 +1197,13 @@ function useAvatarQualityGovernor(input) {
1188
1197
  pausedSinceTick = false;
1189
1198
  const { governor, action } = step(gov, signal, Date.now(), config);
1190
1199
  gov = governor;
1191
- if (action) applyCap(action.setCap);
1200
+ if (action) applyCap(action.setCap, gov.lowUnhealthy);
1192
1201
  } catch {
1193
1202
  } finally {
1194
1203
  tickRunning = false;
1195
1204
  }
1196
1205
  };
1197
- applyCap(gov.cap);
1206
+ applyCap(gov.cap, gov.lowUnhealthy);
1198
1207
  const handle = setInterval(() => void tick(), tickMs);
1199
1208
  return () => {
1200
1209
  disposed = true;
@@ -1476,7 +1485,7 @@ function isFrameFlowingAt(snapshot) {
1476
1485
  }
1477
1486
  function freezeMsFromFrameGap(gapMs) {
1478
1487
  if (!Number.isFinite(gapMs) || gapMs <= AVATAR_FRAME_GAP_FREEZE_FLOOR_MS) return 0;
1479
- return gapMs;
1488
+ return gapMs - AVATAR_FRAME_GAP_FREEZE_FLOOR_MS;
1480
1489
  }
1481
1490
  function isFrameFreezeInhibited(snapshot) {
1482
1491
  return snapshot.hidden || snapshot.resumePending || !snapshot.trackProducing || !snapshot.seenFrame || snapshot.lastFrameAtMs === null;
package/dist/server.js CHANGED
@@ -1 +1 @@
1
- export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-X3XXLAJJ.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-K726WCDN.js';
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-2TQVKQJJ.js';
2
- import './chunk-X3XXLAJJ.js';
1
+ import { createProxyHandler } from './chunk-OVAQBIZ2.js';
2
+ import './chunk-K726WCDN.js';
3
3
 
4
4
  // src/tanstack-start.ts
5
5
  function realtimeAvatarServerRoute(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "realtime-avatar",
3
- "version": "0.11.1",
3
+ "version": "0.11.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",