realtime-avatar 0.26.1 → 0.27.1

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.
@@ -177,6 +177,8 @@ function RealtimeAvatarLiveKitRoom(props) {
177
177
  LiveKitRoom,
178
178
  {
179
179
  ...roomProps,
180
+ // Keep the room and its in-room observers scoped to the grant, as on web.
181
+ key: grant?.session_id ?? "no-session",
180
182
  serverUrl: grant?.livekit_url,
181
183
  token: grant?.participant_token,
182
184
  connect: shouldConnect,
@@ -1416,6 +1418,9 @@ function useAvatarQualityGovernor(input) {
1416
1418
  }
1417
1419
 
1418
1420
  // ../client/src/react/avatar-video-surface.ts
1421
+ function liveBadgeLabel(dims, debug) {
1422
+ return debug && dims && dims.width > 0 && dims.height > 0 ? `live \xB7 ${dims.width}\xD7${dims.height}` : "live";
1423
+ }
1419
1424
  function resolveSurfaceLayers(input) {
1420
1425
  return {
1421
1426
  showPoster: Boolean(input.poster),
@@ -1491,6 +1496,7 @@ function AvatarVideoSurface(props) {
1491
1496
  style,
1492
1497
  children,
1493
1498
  showLiveBadge = true,
1499
+ debug = false,
1494
1500
  adaptivePlayout = false,
1495
1501
  onPlayoutDelayChange,
1496
1502
  testID
@@ -1533,7 +1539,7 @@ function AvatarVideoSurface(props) {
1533
1539
  });
1534
1540
  const resizeMode = fit === "cover" ? "cover" : "contain";
1535
1541
  const liveDims = videoTrack?.publication?.dimensions ?? null;
1536
- const liveLabel = showLive && liveDims && liveDims.width > 0 && liveDims.height > 0 ? `live \xB7 ${liveDims.width}\xD7${liveDims.height}` : "live";
1542
+ const liveLabel = liveBadgeLabel(liveDims, debug);
1537
1543
  const posterLayer = layers.showPoster ? createElement(Image, {
1538
1544
  key: "poster",
1539
1545
  source: { uri: poster },
@@ -2391,7 +2397,8 @@ function lifecyclePhaseFrom(args) {
2391
2397
  kind: "reconnectable",
2392
2398
  reconnecting: true,
2393
2399
  attempt: recovery.attempt,
2394
- strategy: "fresh-grant"
2400
+ strategy: "fresh-grant",
2401
+ ...recovery.reason ? { reason: recovery.reason } : {}
2395
2402
  };
2396
2403
  }
2397
2404
  if (recovery.kind === "failed") {
@@ -2399,11 +2406,16 @@ function lifecyclePhaseFrom(args) {
2399
2406
  kind: "reconnectable",
2400
2407
  reconnecting: false,
2401
2408
  attempt: MAX_RECONNECT_ATTEMPTS,
2402
- strategy: "fresh-grant"
2409
+ strategy: "fresh-grant",
2410
+ ...recovery.reason ? { reason: recovery.reason } : {}
2403
2411
  };
2404
2412
  }
2405
2413
  if (recovery.kind === "ended") {
2406
- return recovery.reason ? { kind: "ended", reason: recovery.reason } : { kind: "ended" };
2414
+ return {
2415
+ kind: "ended",
2416
+ ...recovery.reason ? { reason: recovery.reason } : {},
2417
+ ...recovery.code ? { code: recovery.code } : {}
2418
+ };
2407
2419
  }
2408
2420
  switch (capacity.kind) {
2409
2421
  case "idle":
@@ -2416,7 +2428,7 @@ function lifecyclePhaseFrom(args) {
2416
2428
  return { kind: "ended", reason: "error" };
2417
2429
  case "active": {
2418
2430
  if (!connected || !agentPresent) {
2419
- return { kind: "connecting", grant: capacity.grant };
2431
+ return { kind: "connecting", grant: capacity.grant, waitingFor: connected ? "agent" : "transport" };
2420
2432
  }
2421
2433
  if (idle.kind === "idle-warning") {
2422
2434
  return { kind: "idle-warning", secondsRemaining: idle.secondsRemaining, deadlineAt: idle.deadlineAt };
@@ -2444,6 +2456,7 @@ function resolveIdleTimeoutMs(args) {
2444
2456
  return DEFAULT_IDLE_SECONDS * 1e3;
2445
2457
  }
2446
2458
  var DEFAULT_CONNECT_WATCHDOG_SECONDS = 12;
2459
+ var DEFAULT_READY_TIMEOUT_SECONDS = 30;
2447
2460
  function resolveWarnBeforeMs(idleTimeoutMs, override, idleWarnLeadSeconds) {
2448
2461
  if (idleTimeoutMs <= 0) return 0;
2449
2462
  const maxLeadMs = Math.max(0, idleTimeoutMs - 1e3);
@@ -2469,6 +2482,7 @@ function useSessionLifecycle(input) {
2469
2482
  maxReconnectAttempts,
2470
2483
  autoRetryBusy = true,
2471
2484
  connectWatchdogSeconds,
2485
+ readyTimeoutSeconds,
2472
2486
  requestOptions,
2473
2487
  onBehaviorChange
2474
2488
  } = input;
@@ -2479,17 +2493,27 @@ function useSessionLifecycle(input) {
2479
2493
  const reconnectPolicyRef = useRef(reconnectPolicy);
2480
2494
  reconnectPolicyRef.current = reconnectPolicy;
2481
2495
  const connectWatchdogMs = typeof connectWatchdogSeconds === "number" && Number.isFinite(connectWatchdogSeconds) ? Math.max(0, Math.floor(connectWatchdogSeconds)) * 1e3 : DEFAULT_CONNECT_WATCHDOG_SECONDS * 1e3;
2496
+ const readyTimeoutMs = typeof readyTimeoutSeconds === "number" && Number.isFinite(readyTimeoutSeconds) && readyTimeoutSeconds > 0 ? Math.max(1, Math.floor(readyTimeoutSeconds)) * 1e3 : DEFAULT_READY_TIMEOUT_SECONDS * 1e3;
2482
2497
  const [userEnded, setUserEnded] = useState(false);
2483
2498
  const userEndedRef = useRef(false);
2499
+ const [readinessFailed, setReadinessFailed] = useState(false);
2500
+ const readinessFailureRef = useRef(null);
2501
+ const readinessRef = useRef(null);
2502
+ const readyGrantRef = useRef(null);
2503
+ const agentWaitRef = useRef(null);
2484
2504
  const phaseRef = useRef({ kind: "idle" });
2485
2505
  const grantState = useLiveKitAvatarGrant({
2486
2506
  client,
2487
2507
  session,
2488
- active: active && !userEnded,
2508
+ active: active && !userEnded && !readinessFailed,
2489
2509
  autoRetryBusy,
2490
2510
  requestOptions
2491
2511
  });
2492
2512
  const capacity = grantState.capacity;
2513
+ const grantId = grantState.grant?.session_id ?? null;
2514
+ const grantIdRef = useRef(null);
2515
+ grantIdRef.current = grantId;
2516
+ const retiredGrantRef = useRef(null);
2493
2517
  const [recovery, setRecovery] = useState({ kind: "connected" });
2494
2518
  const recoveryRef = useRef(recovery);
2495
2519
  recoveryRef.current = recovery;
@@ -2512,8 +2536,19 @@ function useSessionLifecycle(input) {
2512
2536
  grantIdleSeconds: grantState.grant?.idle_timeout_seconds ?? 0
2513
2537
  });
2514
2538
  const warnAtMs = resolveWarnBeforeMs(idleTimeoutMs, warnBeforeMs, idleWarnLeadSeconds);
2515
- const [agentPresent, setAgentPresentState] = useState(false);
2516
- const [connected, setConnected] = useState(false);
2539
+ const [agentObservation, setAgentObservation] = useState({ grantId: null, present: false });
2540
+ const [connectionObservation, setConnectionObservation] = useState({ grantId: null, connected: false });
2541
+ const agentPresent = agentObservation.grantId === grantId && agentObservation.present;
2542
+ const connected = connectionObservation.grantId === grantId && connectionObservation.connected;
2543
+ connectedRef.current = connected;
2544
+ const setAgentPresentState = useCallback((present) => {
2545
+ const owner = grantIdRef.current;
2546
+ setAgentObservation((prev) => prev.grantId === owner && prev.present === present ? prev : { grantId: owner, present });
2547
+ }, []);
2548
+ const setConnected = useCallback((value) => {
2549
+ const owner = grantIdRef.current;
2550
+ setConnectionObservation((prev) => prev.grantId === owner && prev.connected === value ? prev : { grantId: owner, connected: value });
2551
+ }, []);
2517
2552
  const lastActivityRef = useRef(Date.now());
2518
2553
  const leaveRoomRef = useRef(null);
2519
2554
  const [, setClockTick] = useState(0);
@@ -2530,7 +2565,11 @@ function useSessionLifecycle(input) {
2530
2565
  refreshBaselineCapacityRef.current = null;
2531
2566
  attemptRef.current = 0;
2532
2567
  setAttempt(0);
2533
- setRecovery(userEndedRef.current ? { kind: "ended", reason: "user" } : { kind: "connected" });
2568
+ readinessRef.current = null;
2569
+ readyGrantRef.current = null;
2570
+ agentWaitRef.current = null;
2571
+ retiredGrantRef.current = null;
2572
+ setRecovery(userEndedRef.current ? { kind: "ended", reason: "user" } : readinessFailureRef.current ? { kind: "ended", reason: "error", code: readinessFailureRef.current } : { kind: "connected" });
2534
2573
  setConnected(false);
2535
2574
  setAgentPresentState(false);
2536
2575
  lastActivityRef.current = Date.now();
@@ -2539,26 +2578,33 @@ function useSessionLifecycle(input) {
2539
2578
  if (active) return;
2540
2579
  userEndedRef.current = false;
2541
2580
  setUserEnded(false);
2581
+ readinessFailureRef.current = null;
2582
+ setReadinessFailed(false);
2542
2583
  reset();
2543
2584
  }, [active, reset]);
2544
2585
  const markActivity = useCallback(() => {
2586
+ if (grantId === null || grantIdRef.current !== grantId) return;
2545
2587
  lastActivityRef.current = Date.now();
2546
2588
  setClockTick((value) => value + 1);
2547
- }, []);
2589
+ }, [grantId]);
2548
2590
  const stayConnected = useCallback(() => {
2591
+ if (grantId === null || grantIdRef.current !== grantId) return;
2549
2592
  if (!connectedRef.current) return;
2550
2593
  lastActivityRef.current = Date.now();
2551
2594
  setClockTick((value) => value + 1);
2552
- }, []);
2595
+ }, [grantId]);
2553
2596
  const setAgentPresent = useCallback((present) => {
2554
- setAgentPresentState((prev) => {
2555
- if (present && !prev) lastActivityRef.current = Date.now();
2556
- return present;
2597
+ if (grantId === null || grantIdRef.current !== grantId) return;
2598
+ if (userEndedRef.current || readinessFailureRef.current || retiredGrantRef.current !== null && retiredGrantRef.current === grantIdRef.current) return;
2599
+ setAgentObservation((prev) => {
2600
+ if (present && (prev.grantId !== grantId || !prev.present)) lastActivityRef.current = Date.now();
2601
+ return prev.grantId === grantId && prev.present === present ? prev : { grantId, present };
2557
2602
  });
2558
- }, []);
2603
+ }, [grantId]);
2559
2604
  const registerLeaveRoom = useCallback((leave) => {
2605
+ if (grantId === null || grantIdRef.current !== grantId) return;
2560
2606
  leaveRoomRef.current = leave;
2561
- }, []);
2607
+ }, [grantId]);
2562
2608
  const endIdle = useCallback(() => {
2563
2609
  if (!connectedRef.current) return;
2564
2610
  clearTimer();
@@ -2570,24 +2616,25 @@ function useSessionLifecycle(input) {
2570
2616
  leaveRoomRef.current?.();
2571
2617
  }, [clearTimer]);
2572
2618
  const onConnected = useCallback(() => {
2573
- if (userEndedRef.current) return;
2619
+ if (grantId === null || grantIdRef.current !== grantId) return;
2620
+ if (userEndedRef.current || readinessFailureRef.current || retiredGrantRef.current !== null && retiredGrantRef.current === grantIdRef.current) return;
2574
2621
  clearTimer();
2575
2622
  connectedRef.current = true;
2576
2623
  manualReconnectPendingRef.current = false;
2577
2624
  refreshBaselineCapacityRef.current = null;
2578
2625
  setConnected(true);
2579
- attemptRef.current = 0;
2580
- setAttempt(0);
2626
+ if (readinessRef.current) readinessRef.current.sawTransport = true;
2581
2627
  setRecovery({ kind: "connected" });
2582
2628
  lastActivityRef.current = Date.now();
2583
- }, [clearTimer]);
2629
+ }, [clearTimer, grantId]);
2584
2630
  const onDisconnected = useCallback(
2585
2631
  (reason) => {
2586
- if (!active || userEndedRef.current) return;
2632
+ if (grantId === null || grantIdRef.current !== grantId) return;
2633
+ if (!active || userEndedRef.current || readinessFailureRef.current) return;
2587
2634
  const action = disconnectAction(reason, connectedRef.current);
2588
2635
  if (action === "noop") return;
2589
2636
  if (action === "reset") {
2590
- if (recoveryRef.current.kind === "refreshing") return;
2637
+ if (recoveryRef.current.kind === "refreshing" || recoveryRef.current.kind === "reconnecting") return;
2591
2638
  reset();
2592
2639
  return;
2593
2640
  }
@@ -2604,11 +2651,12 @@ function useSessionLifecycle(input) {
2604
2651
  refreshBaselineCapacityRef.current = null;
2605
2652
  setRecovery({ kind: "reconnecting", attempt: attemptRef.current });
2606
2653
  },
2607
- [active, reset]
2654
+ [active, reset, grantId]
2608
2655
  );
2609
2656
  const onConnectionError = useCallback(
2610
2657
  (error) => {
2611
- if (!active || userEndedRef.current || !isRecoverableConnectionError(error)) return;
2658
+ if (grantId === null || grantIdRef.current !== grantId) return;
2659
+ if (!active || userEndedRef.current || readinessFailureRef.current || !isRecoverableConnectionError(error)) return;
2612
2660
  connectedRef.current = false;
2613
2661
  setConnected(false);
2614
2662
  setAgentPresentState(false);
@@ -2619,7 +2667,7 @@ function useSessionLifecycle(input) {
2619
2667
  return { kind: "reconnecting", attempt: attemptRef.current };
2620
2668
  });
2621
2669
  },
2622
- [active]
2670
+ [active, grantId]
2623
2671
  );
2624
2672
  useEffect(() => {
2625
2673
  if (capacity.kind === "error" && recovery.kind === "connected") manualReconnectPendingRef.current = false;
@@ -2629,11 +2677,12 @@ function useSessionLifecycle(input) {
2629
2677
  const baseline = refreshBaselineCapacityRef.current;
2630
2678
  if (baseline?.kind === "error" && baseline.error === capacity.error) return;
2631
2679
  refreshBaselineCapacityRef.current = null;
2632
- setRecovery({ kind: "reconnecting", attempt: recovery.attempt });
2680
+ setRecovery({ kind: "reconnecting", attempt: recovery.attempt, ...recovery.reason ? { reason: recovery.reason } : {} });
2633
2681
  }, [capacity, recovery]);
2634
2682
  const onConnectionStateChange = useCallback(
2635
2683
  (state) => {
2636
- if (!active || userEndedRef.current) return;
2684
+ if (grantId === null || grantIdRef.current !== grantId) return;
2685
+ if (!active || userEndedRef.current || readinessFailureRef.current) return;
2637
2686
  if (state === "connected") {
2638
2687
  onConnected();
2639
2688
  return;
@@ -2647,14 +2696,78 @@ function useSessionLifecycle(input) {
2647
2696
  }
2648
2697
  }
2649
2698
  },
2650
- [active, onConnected]
2699
+ [active, onConnected, grantId]
2651
2700
  );
2701
+ useEffect(() => {
2702
+ if (!active || userEnded || readinessFailed || !connected || !agentPresent || !grantState.grant) return;
2703
+ readyGrantRef.current = grantState.grant.session_id;
2704
+ readinessRef.current = null;
2705
+ agentWaitRef.current = null;
2706
+ attemptRef.current = 0;
2707
+ setAttempt(0);
2708
+ }, [active, userEnded, readinessFailed, connected, agentPresent, grantState.grant]);
2709
+ const failReadiness = useCallback((code) => {
2710
+ if (userEndedRef.current || readinessFailureRef.current) return;
2711
+ readinessFailureRef.current = code;
2712
+ clearTimer();
2713
+ connectedRef.current = false;
2714
+ manualReconnectPendingRef.current = false;
2715
+ refreshBaselineCapacityRef.current = null;
2716
+ const ended = { kind: "ended", reason: "error", code };
2717
+ recoveryRef.current = ended;
2718
+ setRecovery(ended);
2719
+ setConnected(false);
2720
+ setAgentPresentState(false);
2721
+ releaseRef.current("disconnected");
2722
+ setReadinessFailed(true);
2723
+ leaveRoomRef.current?.();
2724
+ }, [clearTimer]);
2725
+ useEffect(() => {
2726
+ if (!active || userEnded || readinessFailed) return;
2727
+ if (recovery.kind === "ended" || recovery.kind === "failed") return;
2728
+ if (grantState.grant && readyGrantRef.current === grantState.grant.session_id) return;
2729
+ if (!readinessRef.current && grantState.grant) {
2730
+ readinessRef.current = { startedAt: Date.now(), sawTransport: connected };
2731
+ }
2732
+ const budget = readinessRef.current;
2733
+ if (!budget) return;
2734
+ const timer = window.setTimeout(() => {
2735
+ if (readinessRef.current !== budget) return;
2736
+ failReadiness(budget.sawTransport ? "agent_timeout" : "connection_timeout");
2737
+ }, Math.max(0, budget.startedAt + readyTimeoutMs - Date.now()));
2738
+ return () => window.clearTimeout(timer);
2739
+ }, [active, userEnded, readinessFailed, connected, agentPresent, grantState.grant, recovery.kind, readyTimeoutMs, failReadiness]);
2740
+ useEffect(() => {
2741
+ const sessionId = grantState.grant?.session_id;
2742
+ if (!active || userEnded || readinessFailed || !sessionId || readyGrantRef.current === sessionId || agentPresent) return;
2743
+ if (recovery.kind === "reconnecting" || recovery.kind === "failed" || recovery.kind === "ended") return;
2744
+ if (agentWaitRef.current?.sessionId !== sessionId) {
2745
+ if (!connected) return;
2746
+ agentWaitRef.current = { sessionId, startedAt: Date.now() };
2747
+ }
2748
+ const wait = agentWaitRef.current;
2749
+ if (connectWatchdogMs <= 0) return;
2750
+ const timer = window.setTimeout(() => {
2751
+ if (agentWaitRef.current !== wait || readinessFailureRef.current || userEndedRef.current) return;
2752
+ retiredGrantRef.current = sessionId;
2753
+ connectedRef.current = false;
2754
+ setConnected(false);
2755
+ setAgentPresentState(false);
2756
+ const next = { kind: "reconnecting", attempt: attemptRef.current, reason: "agent_timeout" };
2757
+ recoveryRef.current = next;
2758
+ setRecovery(next);
2759
+ releaseRef.current("disconnected");
2760
+ leaveRoomRef.current?.();
2761
+ }, Math.max(0, wait.startedAt + connectWatchdogMs - Date.now()));
2762
+ return () => window.clearTimeout(timer);
2763
+ }, [active, userEnded, readinessFailed, connected, agentPresent, grantState.grant, recovery.kind, connectWatchdogMs]);
2652
2764
  useEffect(() => {
2653
2765
  if (!active || connected || connectWatchdogMs <= 0) return;
2654
2766
  if (grantState.status !== "ready" || !grantState.grant) return;
2655
2767
  if (recovery.kind === "failed" || recovery.kind === "ended") return;
2656
2768
  if (recovery.kind === "in-place-reconnecting") return;
2657
2769
  const timer = window.setTimeout(() => {
2770
+ if (readinessFailureRef.current || userEndedRef.current) return;
2658
2771
  releaseRef.current("disconnected");
2659
2772
  setRecovery({ kind: "reconnecting", attempt: attemptRef.current });
2660
2773
  }, connectWatchdogMs);
@@ -2664,6 +2777,10 @@ function useSessionLifecycle(input) {
2664
2777
  if (!active || !needsFreshGrant(recovery)) return;
2665
2778
  const step2 = retryStep(attemptRef.current, reconnectPolicyRef.current);
2666
2779
  if (step2.kind === "give-up") {
2780
+ if (readinessRef.current) {
2781
+ failReadiness(readinessRef.current.sawTransport ? "agent_timeout" : "connection_timeout");
2782
+ return;
2783
+ }
2667
2784
  manualReconnectPendingRef.current = false;
2668
2785
  refreshBaselineCapacityRef.current = null;
2669
2786
  releaseRef.current("disconnected");
@@ -2672,15 +2789,16 @@ function useSessionLifecycle(input) {
2672
2789
  }
2673
2790
  timerRef.current = window.setTimeout(() => {
2674
2791
  timerRef.current = null;
2792
+ if (readinessFailureRef.current || userEndedRef.current) return;
2675
2793
  attemptRef.current = step2.attempt;
2676
2794
  setAttempt(step2.attempt);
2677
2795
  refreshBaselineCapacityRef.current = capacityRef.current;
2678
2796
  releaseRef.current("superseded");
2679
- setRecovery({ kind: "refreshing", attempt: step2.attempt });
2797
+ setRecovery({ kind: "refreshing", attempt: step2.attempt, ...recovery.reason ? { reason: recovery.reason } : {} });
2680
2798
  refreshRef.current();
2681
2799
  }, step2.delayMs);
2682
2800
  return clearTimer;
2683
- }, [recovery.kind, active, attempt, clearTimer]);
2801
+ }, [recovery.kind, active, attempt, clearTimer, failReadiness]);
2684
2802
  const end = useCallback(() => {
2685
2803
  if (!active || userEndedRef.current || phaseRef.current.kind === "ended") return;
2686
2804
  userEndedRef.current = true;
@@ -2697,6 +2815,13 @@ function useSessionLifecycle(input) {
2697
2815
  }, [active, clearTimer]);
2698
2816
  const reconnect = useCallback(() => {
2699
2817
  if (!active) return;
2818
+ if (readinessFailureRef.current) {
2819
+ readinessFailureRef.current = null;
2820
+ setReadinessFailed(false);
2821
+ reset();
2822
+ manualReconnectPendingRef.current = true;
2823
+ return;
2824
+ }
2700
2825
  if (userEndedRef.current) {
2701
2826
  userEndedRef.current = false;
2702
2827
  setUserEnded(false);
@@ -2707,6 +2832,12 @@ function useSessionLifecycle(input) {
2707
2832
  if (manualReconnectPendingRef.current || recovery.kind === "in-place-reconnecting") return;
2708
2833
  manualReconnectPendingRef.current = true;
2709
2834
  clearTimer();
2835
+ readinessRef.current = null;
2836
+ agentWaitRef.current = null;
2837
+ readyGrantRef.current = null;
2838
+ connectedRef.current = false;
2839
+ setConnected(false);
2840
+ setAgentPresentState(false);
2710
2841
  attemptRef.current = 0;
2711
2842
  setAttempt(0);
2712
2843
  refreshBaselineCapacityRef.current = capacityRef.current;
@@ -2826,6 +2957,7 @@ function SessionLifecycleRoomBridge({
2826
2957
  const assistant = useVoiceAssistant();
2827
2958
  const transcriptions = useTranscriptions();
2828
2959
  const room = useRoomContext();
2960
+ const roomMatchesGrant = lifecycle.grant === void 0 || lifecycle.grant !== null && room.name === lifecycle.grant.room_name;
2829
2961
  useCallMedia(room, {
2830
2962
  setMicrophoneFacts,
2831
2963
  setAudioPlayback,
@@ -2979,11 +3111,11 @@ function SessionLifecycleRoomBridge({
2979
3111
  detailsEnabled ? video?.publication : void 0
2980
3112
  ]);
2981
3113
  useEffect(() => {
2982
- onConnectionStateChange(connectionState);
2983
- }, [connectionState, onConnectionStateChange]);
3114
+ if (roomMatchesGrant) onConnectionStateChange(connectionState);
3115
+ }, [connectionState, onConnectionStateChange, roomMatchesGrant]);
2984
3116
  useEffect(() => {
2985
- setAgentPresent(agentPresent);
2986
- }, [agentPresent, setAgentPresent]);
3117
+ if (roomMatchesGrant) setAgentPresent(agentPresent);
3118
+ }, [agentPresent, setAgentPresent, roomMatchesGrant]);
2987
3119
  useEffect(() => {
2988
3120
  const active = isCallActivity({
2989
3121
  assistantState,
@@ -3691,4 +3823,4 @@ function createProxyClient(options) {
3691
3823
  };
3692
3824
  }
3693
3825
 
3694
- export { AdaptivePlayoutController, AvatarVideoSurface, DEFAULT_APPROACHING_END_LEAD_SECONDS, DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, DEFAULT_CREDITS_LOW_LEAD_SECONDS, DEFAULT_GOVERNOR_CONFIG, DEFAULT_GRACE_CEILING_SECONDS, DEFAULT_GRACE_WINDOW_LEAD_SECONDS, DEFAULT_IDLE_SECONDS, DEFAULT_IDLE_WARN_LEAD_SECONDS, DEFAULT_TURN_TIMEOUT_SECONDS, MAX_RECONNECT_ATTEMPTS, MAX_SESSION_INSTRUCTIONS_CHARS, RECONNECT_BACKOFF_MS, RealtimeAvatarLiveKitRoom, SessionLifecycleRoomBridge, capacityErrorFromBusy, capacityStateFromGrant, createProxyClient, isNativeLiveTrackSubscribed, knownBehaviorStates, mapTurnState, readInboundRtp, sessionBehaviorSchema, sessionClipSchema, splitCallTranscript, useAvatarAdaptivePlayoutDelay, useAvatarCamera, useAvatarPlayoutDelay, useAvatarQualityGovernor, useCallTranscript, useLiveKitAvatarGrant, useLiveTrackProducing, useMicLease, useRealtimeAvatarAudioSession, useRealtimeSession, useReleaseMicLeaseOnTrackEnded, useSessionLifecycle };
3826
+ export { AdaptivePlayoutController, AvatarVideoSurface, DEFAULT_APPROACHING_END_LEAD_SECONDS, DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, DEFAULT_CREDITS_LOW_LEAD_SECONDS, DEFAULT_GOVERNOR_CONFIG, DEFAULT_GRACE_CEILING_SECONDS, DEFAULT_GRACE_WINDOW_LEAD_SECONDS, DEFAULT_IDLE_SECONDS, DEFAULT_IDLE_WARN_LEAD_SECONDS, DEFAULT_READY_TIMEOUT_SECONDS, DEFAULT_TURN_TIMEOUT_SECONDS, MAX_RECONNECT_ATTEMPTS, MAX_SESSION_INSTRUCTIONS_CHARS, RECONNECT_BACKOFF_MS, RealtimeAvatarLiveKitRoom, SessionLifecycleRoomBridge, capacityErrorFromBusy, capacityStateFromGrant, createProxyClient, isNativeLiveTrackSubscribed, knownBehaviorStates, mapTurnState, readInboundRtp, sessionBehaviorSchema, sessionClipSchema, splitCallTranscript, useAvatarAdaptivePlayoutDelay, useAvatarCamera, useAvatarPlayoutDelay, useAvatarQualityGovernor, useCallTranscript, useLiveKitAvatarGrant, useLiveTrackProducing, useMicLease, useRealtimeAvatarAudioSession, useRealtimeSession, useReleaseMicLeaseOnTrackEnded, useSessionLifecycle };
package/dist/react.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ToolRegistration, RegisteredTool } from './tools.js';
2
- import { aj as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit, aQ as CallMicrophone, aR as CallAudioPlayback, aS as CallMicrophoneProblem } from './proxy-client-C8M_G_Kk.js';
3
- export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, aT as AvatarVideoSurface, aU 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_TURN_TIMEOUT_SECONDS, u as DeclaredInputSource, E as EndReason, v as EndedEvent, w as ExtendResult, F as FishTtsModel, x as FreezeReadingFn, G as Governor, y as GovernorAction, z as GovernorConfig, H as GovernorSignal, I as GovernorState, J as GraceWindowClosedEvent, K as GraceWindowOpenEvent, M as GraceWindowState, N as IdleWarningEvent, O as InboundRtpCursor, P as InboundRtpReading, Q as KnownBehaviorState, R as LLMProvider, S as LLMSelection, T as LiveKitAvatarGrantState, U as LiveKitAvatarGrantStatus, V as LiveKitCapacityState, W as LiveKitConnectionStatus, X as LiveKitSessionRequest, Y as LiveKitSessionStartResult, aV as LivePlaybackKeeper, Z as MAX_RECONNECT_ATTEMPTS, _ as MAX_SESSION_INSTRUCTIONS_CHARS, aW as MIC_LEASE_ENDED_TIMEOUT_MS, aX as MicLease, aY as NETWORK_QUALITY_POLICY, aZ as NetworkQualityStatus, a_ as PlayableVideoElement, $ as ProxyClientOptions, a0 as QualityCap, a1 as RECONNECT_BACKOFF_MS, a$ as RealtimeAvatarCapacityError, b0 as RealtimeAvatarLiveKitRoom, b1 as RealtimeAvatarLiveKitRoomProps, a2 as RealtimeAvatarRequestOptions, a3 as RealtimeSessionApi, a4 as RealtimeSessionMedia, a5 as RealtimeSessionRoomSinks, a6 as ReconnectPolicy, a7 as ReconnectingEvent, a8 as RecoveryState, a9 as RetryStep, aa as SendTurnOptions, ab as SessionBehavior, ac as SessionClip, ad as SessionClocks, ae as SessionEndReason, af as SessionLifecycleApi, ag as SessionLifecyclePhase, ah as SessionLifecyclePhaseKind, ai as SessionLifecycleRoomBridge, ak as SurfaceLayers, al as TranscriptSender, am as TranscriptSenderOptions, an as TurnState, ao as TurnTimeoutEvent, ap as UseAvatarQualityGovernorInput, aq as UseLiveKitAvatarGrantInput, ar as UseRealtimeSessionInput, as as UseSessionLifecycleInput, at as VoiceSpec, au as VoiceSpecInput, av as capacityErrorFromBusy, aw as capacityStateFromGrant, ax as createProxyClient, az as knownBehaviorStates, aA as mapTurnState, aB as readInboundRtp, aC as sessionBehaviorSchema, aD as sessionClipSchema, b2 as shouldReplayPendingTurn, aE as splitCallTranscript, aF as useAvatarAdaptivePlayoutDelay, aG as useAvatarCamera, aH as useAvatarPlayoutDelay, aI as useAvatarQualityGovernor, aJ as useCallTranscript, aK as useLiveKitAvatarGrant, aM as useMicLease, aN as useRealtimeSession, aO as useReleaseMicLeaseOnTrackEnded, aP as useSessionLifecycle } from './proxy-client-C8M_G_Kk.js';
2
+ import { ak as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit, aS as CallMicrophone, aT as CallAudioPlayback, aU as CallMicrophoneProblem } from './proxy-client-Cs2azqD1.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-Cs2azqD1.js';
4
4
  import { CSSProperties, ReactNode } from 'react';
5
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
6
6
  import { R as RecordingArtifact } from './recording-DjswTx5B.js';
@@ -138,6 +138,11 @@ type AvatarCallProps = Pick<SessionLifecycleRoomBridgeProps, "onConnectionDetail
138
138
  /** A still shown before the idle clip is playable. */
139
139
  poster?: string | null;
140
140
  fit?: AvatarVideoFit;
141
+ /**
142
+ * The small "live" badge over the video while she is live. Default true. Set false when your
143
+ * own UI already shows the call's status.
144
+ */
145
+ showLiveBadge?: boolean;
141
146
  /** Adds your classes to the call's box. The box sizes itself inline; to resize it, use `style`. */
142
147
  className?: string;
143
148
  /** Inline style for the call's box, spread over its own (`height`, `aspectRatio`, …). */