realtime-avatar 0.17.0 → 0.18.0

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.
@@ -71,9 +71,27 @@ var clipListSchema = z.array(motionIdSchema).min(1).max(32).superRefine((clips,
71
71
  seen.add(clip);
72
72
  });
73
73
  });
74
+ var idleWeightSchema = z.number().min(0).max(100);
74
75
  var idleSchema = z.strictObject({
75
76
  clips: clipListSchema,
76
- weight: z.number().min(0).max(100).optional()
77
+ /** Legacy aggregate weight. Mutually exclusive with member weights. */
78
+ weight: idleWeightSchema.optional(),
79
+ /** Per-member weights, including `primary`; omitted members default to 1. */
80
+ weights: motionRecord(idleWeightSchema).optional()
81
+ }).superRefine((idle, ctx) => {
82
+ if (idle.weights === void 0) return;
83
+ if (idle.weight !== void 0) {
84
+ ctx.addIssue({ code: "custom", path: ["weight"], message: "Use weight or weights, not both" });
85
+ }
86
+ const members = /* @__PURE__ */ new Set(["primary", ...idle.clips]);
87
+ for (const clip of Object.keys(idle.weights)) {
88
+ if (!members.has(clip)) {
89
+ ctx.addIssue({ code: "custom", path: ["weights", clip], message: "Unknown idle member" });
90
+ }
91
+ }
92
+ if (![...members].some((clip) => (idle.weights?.[clip] ?? 1) > 0)) {
93
+ ctx.addIssue({ code: "custom", path: ["weights"], message: "Idle pool needs a positive member" });
94
+ }
77
95
  });
78
96
  var clipActionSchema = z.strictObject({
79
97
  description: z.string().trim().min(1).max(512),
@@ -600,7 +618,7 @@ z.enum([
600
618
 
601
619
  // ../http-client/src/client.ts
602
620
  var DEFAULT_BASE_URL = "https://realtimeavatar.ai/api/v1";
603
- var SDK_VERSION = "0.17.0";
621
+ var SDK_VERSION = "0.18.0";
604
622
  var RealtimeAvatar = class {
605
623
  #apiKey;
606
624
  #baseUrl;
@@ -1,4 +1,4 @@
1
- import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-OESXLLQB.js';
1
+ import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-27XNZHSF.js';
2
2
 
3
3
  // ../proxy/src/config.ts
4
4
  var json = (body, status = 200) => new Response(JSON.stringify(body), {
package/dist/express.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig } from './types-CFkiNV3f.js';
2
- import './types-BnlnrYgN.js';
1
+ import { P as ProxyConfig } from './types-Bzn5PJ0g.js';
2
+ import './types-CxkszIDK.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-27BB4DQ2.js';
2
- import './chunk-OESXLLQB.js';
1
+ import { createProxyHandler } from './chunk-RKIEGOGJ.js';
2
+ import './chunk-27XNZHSF.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-CFkiNV3f.js';
2
- import './types-BnlnrYgN.js';
1
+ import { P as ProxyConfig } from './types-Bzn5PJ0g.js';
2
+ import './types-CxkszIDK.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/hono.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-27BB4DQ2.js';
2
- import './chunk-OESXLLQB.js';
1
+ import { createProxyHandler } from './chunk-RKIEGOGJ.js';
2
+ import './chunk-27XNZHSF.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-BnlnrYgN.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 RecordingArtifactStatus, x as RecordingMode, V as VideoPolicy, y as isQueued, z as listRecordingsResponseSchema, B as recordingAccessResponseSchema, D as recordingArtifactSchema, F as recordingModeSchema } from './types-BnlnrYgN.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-CxkszIDK.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 RecordingArtifactStatus, x as RecordingMode, V as VideoPolicy, y as isQueued, z as listRecordingsResponseSchema, B as recordingAccessResponseSchema, D as recordingArtifactSchema, F as recordingModeSchema } from './types-CxkszIDK.js';
3
3
  import { ConnectionHistoryResponse } from './connection-history.js';
4
4
  import { z } from 'zod';
5
5
 
@@ -344,6 +344,7 @@ declare const clipLibraryDeclarationSchema: z.ZodObject<{
344
344
  idle: z.ZodOptional<z.ZodObject<{
345
345
  clips: z.ZodArray<z.ZodString>;
346
346
  weight: z.ZodOptional<z.ZodNumber>;
347
+ weights: z.ZodOptional<z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodNumber>>>;
347
348
  }, z.core.$strict>>;
348
349
  on: z.ZodOptional<z.ZodObject<{
349
350
  userSpeechStarted: z.ZodOptional<z.ZodObject<{
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-OESXLLQB.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-27XNZHSF.js';
2
2
  import './chunk-MCIXU6XP.js';
package/dist/nextjs.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig } from './types-CFkiNV3f.js';
2
- import './types-BnlnrYgN.js';
1
+ import { P as ProxyConfig } from './types-Bzn5PJ0g.js';
2
+ import './types-CxkszIDK.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
package/dist/nextjs.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-27BB4DQ2.js';
2
- import './chunk-OESXLLQB.js';
1
+ import { createProxyHandler } from './chunk-RKIEGOGJ.js';
2
+ import './chunk-27XNZHSF.js';
3
3
  import './chunk-MCIXU6XP.js';
4
4
 
5
5
  // ../proxy/src/nextjs.ts
@@ -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-DmJVj18Z.js';
5
- 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_TURN_TIMEOUT_SECONDS, E as EndReason, u as EndedEvent, v as ExtendResult, F as FishTtsModel, w as FreezeReadingFn, G as Governor, x as GovernorAction, y as GovernorConfig, z as GovernorSignal, H as GovernorState, I as GraceWindowClosedEvent, J as GraceWindowOpenEvent, K as GraceWindowState, M as IdleWarningEvent, N as InboundRtpCursor, O as InboundRtpReading, P as KnownBehaviorState, Q as LLMProvider, R as LLMSelection, S as LiveKitAvatarGrantState, T as LiveKitAvatarGrantStatus, U as LiveKitCapacityState, V as LiveKitConnectionStatus, W as LiveKitSessionRequest, X as LiveKitSessionStartResult, Y as MAX_RECONNECT_ATTEMPTS, Z as MAX_SESSION_INSTRUCTIONS_CHARS, _ as ProxyClientOptions, $ as QualityCap, a0 as RECONNECT_BACKOFF_MS, a1 as RealtimeAvatarRequestOptions, a2 as RealtimeSessionApi, a3 as RealtimeSessionMedia, a4 as RealtimeSessionRoomSinks, a5 as ReconnectPolicy, a6 as ReconnectingEvent, a7 as RecoveryState, a8 as RetryStep, a9 as SessionBehavior, aa as SessionClip, ab as SessionClocks, ac as SessionEndReason, ad as SessionLifecycleApi, ae as SessionLifecyclePhase, af as SessionLifecyclePhaseKind, ag as SessionLifecycleRoomBridge, ah as SessionLifecycleRoomBridgeProps, ai as SurfaceLayers, aj as TurnState, ak as TurnTimeoutEvent, al as UseAvatarQualityGovernorInput, am as UseLiveKitAvatarGrantInput, an as UseRealtimeSessionInput, ao as UseSessionLifecycleInput, ap as VoiceSpec, aq as VoiceSpecInput, ar as capacityErrorFromBusy, as as capacityStateFromGrant, at as createProxyClient, au as isNativeLiveTrackSubscribed, av as knownBehaviorStates, aw as mapTurnState, ax as readInboundRtp, ay as sessionBehaviorSchema, az as sessionClipSchema, aA as splitCallTranscript, aB as useAvatarAdaptivePlayoutDelay, aC as useAvatarPlayoutDelay, aD as useAvatarQualityGovernor, aE as useCallTranscript, aF as useLiveKitAvatarGrant, aG as useLiveTrackProducing, aH as useMicLease, aI as useRealtimeSession, aJ as useReleaseMicLeaseOnTrackEnded, aK as useSessionLifecycle } from './proxy-client-DmJVj18Z.js';
4
+ import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-w6prrzGW.js';
5
+ 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_TURN_TIMEOUT_SECONDS, E as EndReason, u as EndedEvent, v as ExtendResult, F as FishTtsModel, w as FreezeReadingFn, G as Governor, x as GovernorAction, y as GovernorConfig, z as GovernorSignal, H as GovernorState, I as GraceWindowClosedEvent, J as GraceWindowOpenEvent, K as GraceWindowState, M as IdleWarningEvent, N as InboundRtpCursor, O as InboundRtpReading, P as KnownBehaviorState, Q as LLMProvider, R as LLMSelection, S as LiveKitAvatarGrantState, T as LiveKitAvatarGrantStatus, U as LiveKitCapacityState, V as LiveKitConnectionStatus, W as LiveKitSessionRequest, X as LiveKitSessionStartResult, Y as MAX_RECONNECT_ATTEMPTS, Z as MAX_SESSION_INSTRUCTIONS_CHARS, _ as ProxyClientOptions, $ as QualityCap, a0 as RECONNECT_BACKOFF_MS, a1 as RealtimeAvatarRequestOptions, a2 as RealtimeSessionApi, a3 as RealtimeSessionMedia, a4 as RealtimeSessionRoomSinks, a5 as ReconnectPolicy, a6 as ReconnectingEvent, a7 as RecoveryState, a8 as RetryStep, a9 as SessionBehavior, aa as SessionClip, ab as SessionClocks, ac as SessionEndReason, ad as SessionLifecycleApi, ae as SessionLifecyclePhase, af as SessionLifecyclePhaseKind, ag as SessionLifecycleRoomBridge, ah as SessionLifecycleRoomBridgeProps, ai as SurfaceLayers, aj as TurnState, ak as TurnTimeoutEvent, al as UseAvatarQualityGovernorInput, am as UseLiveKitAvatarGrantInput, an as UseRealtimeSessionInput, ao as UseSessionLifecycleInput, ap as VoiceSpec, aq as VoiceSpecInput, ar as capacityErrorFromBusy, as as capacityStateFromGrant, at as createProxyClient, au as isNativeLiveTrackSubscribed, av as knownBehaviorStates, aw as mapTurnState, ax as readInboundRtp, ay as sessionBehaviorSchema, az as sessionClipSchema, aA as splitCallTranscript, aB as useAvatarAdaptivePlayoutDelay, aC as useAvatarPlayoutDelay, aD as useAvatarQualityGovernor, aE as useCallTranscript, aF as useLiveKitAvatarGrant, aG as useLiveTrackProducing, aH as useMicLease, aI as useRealtimeSession, aJ as useReleaseMicLeaseOnTrackEnded, aK as useSessionLifecycle } from './proxy-client-w6prrzGW.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';
@@ -148,6 +148,131 @@ function playoutDelayTarget(track) {
148
148
  return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
149
149
  }
150
150
 
151
+ // ../http-client/src/generated/error-semantics.ts
152
+ var ERROR_SEMANTICS = {
153
+ "bad_request": { statuses: [400], message: "Check the request and try again.", retryable: false },
154
+ "unauthorized": { statuses: [401], message: "Authentication is required to use Realtime Avatar.", retryable: false },
155
+ "payment_required": { statuses: [402], message: "Add credits to continue using realtime avatars.", retryable: false },
156
+ "forbidden": { statuses: [403], message: "This API key does not have access to that action.", retryable: false },
157
+ "not_found": { statuses: [404], message: "We could not find that avatar or resource.", retryable: false },
158
+ "method_not_allowed": { statuses: [405], message: "This request method is not supported.", retryable: false },
159
+ "not_acceptable": { statuses: [406], message: "The requested response format is not supported.", retryable: false },
160
+ "request_aborted": { statuses: [408], message: "The session request was cancelled.", retryable: false },
161
+ "conflict": { statuses: [409], message: "That request is already in progress. Try again in a moment.", retryable: true },
162
+ "length_required": { statuses: [411], message: "A request content length is required.", retryable: false },
163
+ "payload_too_large": { statuses: [413], message: "The request is too large.", retryable: false },
164
+ "unprocessable_entity": { statuses: [422], message: "Check the request and try again.", retryable: false },
165
+ "rate_limited": { statuses: [429], message: "Too many realtime requests. Please slow down and retry.", retryable: true },
166
+ "internal_error": { statuses: [500], message: "The session service encountered an error. Try again.", retryable: true },
167
+ "unavailable": { statuses: [503], message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true },
168
+ "insufficient_scope": { statuses: [403], message: "This API key is missing a required permission.", retryable: false },
169
+ "loop_pending": { statuses: [409], message: "An avatar loop update is still in progress. Try again shortly.", retryable: true },
170
+ "rate_limit_exceeded": { statuses: [429], message: "Too many realtime requests. Please slow down and retry.", retryable: true },
171
+ "upstream_failed": { statuses: [500, 502, 503], message: "The session service could not complete the request. Try again.", retryable: true },
172
+ "upstream_unreachable": { statuses: [502, 503], message: "The session service could not be reached. Try again.", retryable: true },
173
+ "invalid_upstream_response": { statuses: [502], message: "The session service returned an invalid response. Try again.", retryable: true },
174
+ "upstream_timeout": { statuses: [504], message: "The session service took too long to respond. Try again.", retryable: true },
175
+ "capacity_unavailable": { statuses: [503], message: "Session availability could not be checked. Try again.", retryable: true },
176
+ "session_dispatch_failed": { statuses: [503], message: "The avatar session could not be started. Try again.", retryable: true },
177
+ "admission_unavailable": { statuses: [503], message: "Session admission is temporarily unavailable. Try again.", retryable: true },
178
+ "service_warming": { statuses: [503], message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true },
179
+ "avatar_not_ready": { statuses: [409], message: "This avatar is still being built \u2014 it is usually ready within a minute. Try again then.", retryable: true },
180
+ "clip_library_not_ready": { statuses: [409], message: "This avatar's motion clips are still rendering. Try again in a minute.", retryable: true },
181
+ "recording_unsupported": { statuses: [501], message: "Session recording is not enabled for this deployment.", retryable: false },
182
+ "recording_requires_new_room": { statuses: [422], message: "Recording requires a new session room. Start a fresh session to record it.", retryable: false },
183
+ "recording_unavailable": { statuses: [503], message: "Session recording is temporarily unavailable. Try again in a moment.", retryable: true }
184
+ };
185
+ var UNCOPIED_ERROR_CODES = [
186
+ "capacity_busy",
187
+ "concurrency_limit_reached",
188
+ "insufficient_credits",
189
+ "spend_limit_exceeded",
190
+ "billing_required",
191
+ "network_error"
192
+ ];
193
+ var MESSAGE_SHAPE_PATTERNS = { "machine": { "source": "[_=<>{}\\[\\]|\\\\$`]|\\w+\\.\\w+\\.\\w|\\S:\\d|\\w\\(\\)|^\\s*(?:at|File)\\b|\\bTraceback \\(most recent call last\\)", "flags": "mu" }, "locator": { "source": "https?://|/[\\w.-]+/|[\\w.+-]+@[\\w.-]+\\.[a-z]{2,}|\\bbearer\\s|[\\r\\n]", "flags": "i" } };
194
+ var MESSAGE_MAX_LENGTH = 180;
195
+ var SHAPE_TESTS = Object.values(MESSAGE_SHAPE_PATTERNS).map(
196
+ (pattern) => new RegExp(pattern.source, pattern.flags)
197
+ );
198
+ function hasInternalDetail(message) {
199
+ return SHAPE_TESTS.some((pattern) => pattern.test(message));
200
+ }
201
+ function userSafeMessage(message) {
202
+ const trimmed = message.trim();
203
+ if (!trimmed || hasInternalDetail(trimmed)) return null;
204
+ return trimmed.length > MESSAGE_MAX_LENGTH ? `${trimmed.slice(0, MESSAGE_MAX_LENGTH - 3)}...` : trimmed;
205
+ }
206
+ function isCopiedErrorCode(code) {
207
+ return Object.hasOwn(ERROR_SEMANTICS, code);
208
+ }
209
+ function isKnownErrorCode(code) {
210
+ return isCopiedErrorCode(code) || UNCOPIED_ERROR_CODES.some((known) => known === code);
211
+ }
212
+ function semanticsFor(code) {
213
+ return isCopiedErrorCode(code) ? ERROR_SEMANTICS[code] : void 0;
214
+ }
215
+ function semanticsForStatus(status) {
216
+ return Object.values(ERROR_SEMANTICS).find(
217
+ (entry) => entry.statuses.some((allowed) => allowed === status)
218
+ );
219
+ }
220
+ function defaultCodeForStatus(status) {
221
+ const match = Object.entries(ERROR_SEMANTICS).find(
222
+ ([, entry]) => entry.statuses.some((allowed) => allowed === status)
223
+ );
224
+ return match ? match[0] : "request_failed";
225
+ }
226
+ function normalizeError(input, overrides) {
227
+ const status = input.status ?? 0;
228
+ const rawCode = input.code?.trim() || null;
229
+ const rawMessage = input.message?.trim() || "";
230
+ if (rawCode) {
231
+ const semantics = semanticsFor(rawCode);
232
+ if (semantics?.statuses.some((allowed) => allowed === status)) {
233
+ return { code: rawCode, message: semantics.message, retryable: semantics.retryable };
234
+ }
235
+ }
236
+ if (status === 402) {
237
+ return {
238
+ code: rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" ? rawCode : "billing_required",
239
+ message: userSafeMessage(rawMessage) ?? ERROR_SEMANTICS.payment_required.message,
240
+ retryable: false
241
+ };
242
+ }
243
+ if (status === 429 && rawCode === "concurrency_limit_reached") {
244
+ return {
245
+ code: "concurrency_limit_reached",
246
+ message: userSafeMessage(rawMessage) ?? overrides.concurrencyLimitFallback,
247
+ retryable: true
248
+ };
249
+ }
250
+ if (status === 400 || status === 422) {
251
+ return {
252
+ code: "invalid_request",
253
+ message: userSafeMessage(rawMessage) ?? ERROR_SEMANTICS.bad_request.message,
254
+ retryable: false
255
+ };
256
+ }
257
+ const recognised = !rawCode || isKnownErrorCode(rawCode);
258
+ const byStatus = recognised && status < 500 ? semanticsForStatus(status) : void 0;
259
+ if (byStatus) {
260
+ return {
261
+ code: status === 503 ? "service_unavailable" : defaultCodeForStatus(status),
262
+ message: byStatus.message,
263
+ retryable: byStatus.retryable
264
+ };
265
+ }
266
+ if (status >= 500) {
267
+ return { code: "service_unavailable", message: ERROR_SEMANTICS.unavailable.message, retryable: true };
268
+ }
269
+ return {
270
+ code: status === 0 && rawCode === "network_error" ? "network_error" : "request_failed",
271
+ message: userSafeMessage(rawMessage) ?? overrides.requestFailedFallback,
272
+ retryable: status === 0 || status >= 500
273
+ };
274
+ }
275
+
151
276
  // ../client/src/errors.ts
152
277
  var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
153
278
  constructor(message, status, body, response, metadata = {}) {
@@ -206,72 +331,27 @@ var RealtimeAvatarCapacityError = class extends Error {
206
331
  return this.busy.recommended_retry_ms;
207
332
  }
208
333
  };
334
+ function readRecord(value) {
335
+ return typeof value === "object" && value !== null ? { ...value } : null;
336
+ }
209
337
  function extractErrorMessage(body) {
210
- if (!body || typeof body !== "object") return typeof body === "string" && body ? body : null;
211
- const record = body;
212
- const detail = readMessageValue(record.detail);
213
- if (detail) return detail;
214
- const error = readMessageValue(record.error);
215
- if (error) return error;
216
- const message = readMessageValue(record.message);
217
- if (message) return message;
218
- return null;
338
+ if (typeof body === "string") return body || null;
339
+ const record = readRecord(body);
340
+ if (!record) return null;
341
+ return readMessageValue(record.detail) ?? readMessageValue(record.error) ?? readMessageValue(record.message);
219
342
  }
220
343
  function normalizeRealtimeAvatarError(input) {
221
- const status = input.status ?? 0;
222
- const rawCode = input.code?.trim() || null;
223
- const rawMessage = input.message?.trim() || "";
224
- const blob = `${rawCode ?? ""} ${rawMessage}`.toLowerCase();
225
- if (rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" || status === 402) {
226
- return {
227
- code: rawCode ?? "billing_required",
228
- message: rawMessage && !hasInternalDetail(rawMessage) ? rawMessage : "Add credits to continue using realtime avatars.",
229
- retryable: false
230
- };
231
- }
232
- if (status === 401) {
233
- return { code: "unauthorized", message: "Authentication is required to use Realtime Avatar.", retryable: false };
234
- }
235
- if (status === 403) {
236
- return { code: "forbidden", message: "This API key does not have access to that action.", retryable: false };
237
- }
238
- if (status === 404 || blob.includes("unknown avatar_id") || blob.includes("avatar not found")) {
239
- return { code: "not_found", message: "We could not find that avatar or resource.", retryable: false };
240
- }
241
- if (status === 409) {
242
- return { code: "conflict", message: "That request is already in progress. Try again in a moment.", retryable: true };
243
- }
244
- if (status === 429 && rawCode === "concurrency_limit_reached") {
245
- return {
246
- code: rawCode,
247
- message: userSafeMessage(rawMessage) ?? "The concurrent session limit is reached. Active and starting sessions count. End a session or wait for pending starts to clear, then retry.",
248
- retryable: true
249
- };
250
- }
251
- if (status === 429) {
252
- return { code: "rate_limited", message: "Too many realtime requests. Please slow down and retry.", retryable: true };
253
- }
254
- if (isWarmingMessage(blob)) {
255
- return { code: "service_unavailable", message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true };
256
- }
257
- if (status === 400 || status === 422) {
258
- return { code: "invalid_request", message: userSafeMessage(rawMessage) ?? "Check the request and try again.", retryable: false };
259
- }
260
- if (status >= 500) {
261
- return { code: "service_unavailable", message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true };
262
- }
263
- return {
264
- code: isPublicErrorCode(rawCode) ? rawCode : "request_failed",
265
- message: userSafeMessage(rawMessage) ?? "Realtime Avatar request failed. Try again.",
266
- retryable: status === 0 || status >= 500
267
- };
344
+ return normalizeError(input, {
345
+ concurrencyLimitFallback: CONCURRENCY_LIMIT_FALLBACK_MESSAGE,
346
+ requestFailedFallback: "Realtime Avatar request failed. Try again."
347
+ });
268
348
  }
269
349
  function extractErrorMetadata(body, status, rawMessage) {
270
- if (!body || typeof body !== "object") {
350
+ const record = readRecord(body);
351
+ if (!record) {
271
352
  const normalized2 = normalizeRealtimeAvatarError({ status, message: rawMessage });
272
353
  return { ...normalized2, rawMessage };
273
354
  }
274
- const record = body;
275
355
  const code = typeof record.code === "string" ? record.code : null;
276
356
  const normalized = normalizeRealtimeAvatarError({ status, code, message: rawMessage });
277
357
  return {
@@ -282,38 +362,13 @@ function extractErrorMetadata(body, status, rawMessage) {
282
362
  message: normalized.message
283
363
  };
284
364
  }
285
- var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
286
- "billing_required",
287
- "invalid_request",
288
- "unauthorized",
289
- "forbidden",
290
- "not_found",
291
- "conflict",
292
- "rate_limited",
293
- "service_unavailable",
294
- "request_failed",
295
- "network_error"
296
- ]);
297
- function isPublicErrorCode(code) {
298
- return Boolean(code && PUBLIC_ERROR_CODES.has(code));
299
- }
365
+ var CONCURRENCY_LIMIT_FALLBACK_MESSAGE = "The concurrent session limit is reached. Active and starting sessions count. End a session or wait for pending starts to clear, then retry.";
300
366
  function readMessageValue(value) {
301
367
  if (typeof value === "string") return value.trim() || null;
302
- if (!value || typeof value !== "object") return null;
303
- const record = value;
368
+ const record = readRecord(value);
369
+ if (!record) return null;
304
370
  return readMessageValue(record.message) ?? readMessageValue(record.error) ?? readMessageValue(record.detail);
305
371
  }
306
- function userSafeMessage(message) {
307
- if (!message || hasInternalDetail(message)) return null;
308
- return message.length > 180 ? `${message.slice(0, 177)}...` : message;
309
- }
310
- function isWarmingMessage(blob) {
311
- return blob.includes("warming") || blob.includes("not ready") || blob.includes("starting up");
312
- }
313
- var MACHINE_SHAPED = /[_=<>{}\[\]|\\$`]|\w+\.\w+\.\w|\S:\d|\w\(\)|^\s*(?:at|File)\b/mu;
314
- function hasInternalDetail(message) {
315
- return MACHINE_SHAPED.test(message);
316
- }
317
372
  function splitCallTranscript(all, localIdentity) {
318
373
  if (!localIdentity) return { user: [], agent: all };
319
374
  const user = [];
@@ -2383,7 +2438,7 @@ function SessionLifecycleRoomBridge({
2383
2438
  }, [connectionHistoryGrant, detailsSessionId]);
2384
2439
  useEffect(() => {
2385
2440
  const collector = connectionHistoryRef.current;
2386
- if (!collector || typeof window === "undefined") return;
2441
+ if (!collector || typeof window === "undefined" || typeof window.addEventListener !== "function" || typeof window.removeEventListener !== "function") return;
2387
2442
  const flush = () => {
2388
2443
  void collector.flush();
2389
2444
  };
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 { ah as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-DmJVj18Z.js';
4
- export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, aL as AvatarVideoSurface, aM 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, E as EndReason, u as EndedEvent, v as ExtendResult, F as FishTtsModel, w as FreezeReadingFn, G as Governor, x as GovernorAction, y as GovernorConfig, z as GovernorSignal, H as GovernorState, I as GraceWindowClosedEvent, J as GraceWindowOpenEvent, K as GraceWindowState, M as IdleWarningEvent, N as InboundRtpCursor, O as InboundRtpReading, P as KnownBehaviorState, Q as LLMProvider, R as LLMSelection, S as LiveKitAvatarGrantState, T as LiveKitAvatarGrantStatus, U as LiveKitCapacityState, V as LiveKitConnectionStatus, W as LiveKitSessionRequest, X as LiveKitSessionStartResult, aN as LivePlaybackKeeper, Y as MAX_RECONNECT_ATTEMPTS, Z as MAX_SESSION_INSTRUCTIONS_CHARS, aO as MIC_LEASE_ENDED_TIMEOUT_MS, aP as MicLease, aQ as NETWORK_QUALITY_POLICY, aR as NetworkQualityStatus, aS as PlayableVideoElement, _ as ProxyClientOptions, $ as QualityCap, a0 as RECONNECT_BACKOFF_MS, aT as RealtimeAvatarCapacityError, aU as RealtimeAvatarLiveKitRoom, aV as RealtimeAvatarLiveKitRoomProps, a1 as RealtimeAvatarRequestOptions, a2 as RealtimeSessionApi, a3 as RealtimeSessionMedia, a4 as RealtimeSessionRoomSinks, a5 as ReconnectPolicy, a6 as ReconnectingEvent, a7 as RecoveryState, a8 as RetryStep, a9 as SessionBehavior, aa as SessionClip, ab as SessionClocks, ac as SessionEndReason, ad as SessionLifecycleApi, ae as SessionLifecyclePhase, af as SessionLifecyclePhaseKind, ag as SessionLifecycleRoomBridge, ai as SurfaceLayers, aj as TurnState, ak as TurnTimeoutEvent, al as UseAvatarQualityGovernorInput, am as UseLiveKitAvatarGrantInput, an as UseRealtimeSessionInput, ao as UseSessionLifecycleInput, ap as VoiceSpec, aq as VoiceSpecInput, ar as capacityErrorFromBusy, as as capacityStateFromGrant, at as createProxyClient, av as knownBehaviorStates, aw as mapTurnState, ax as readInboundRtp, ay as sessionBehaviorSchema, az as sessionClipSchema, aW as shouldReplayPendingTurn, aA as splitCallTranscript, aB as useAvatarAdaptivePlayoutDelay, aC as useAvatarPlayoutDelay, aD as useAvatarQualityGovernor, aE as useCallTranscript, aF as useLiveKitAvatarGrant, aH as useMicLease, aI as useRealtimeSession, aJ as useReleaseMicLeaseOnTrackEnded, aK as useSessionLifecycle } from './proxy-client-DmJVj18Z.js';
3
+ import { ah as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-w6prrzGW.js';
4
+ export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, aL as AvatarVideoSurface, aM 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, E as EndReason, u as EndedEvent, v as ExtendResult, F as FishTtsModel, w as FreezeReadingFn, G as Governor, x as GovernorAction, y as GovernorConfig, z as GovernorSignal, H as GovernorState, I as GraceWindowClosedEvent, J as GraceWindowOpenEvent, K as GraceWindowState, M as IdleWarningEvent, N as InboundRtpCursor, O as InboundRtpReading, P as KnownBehaviorState, Q as LLMProvider, R as LLMSelection, S as LiveKitAvatarGrantState, T as LiveKitAvatarGrantStatus, U as LiveKitCapacityState, V as LiveKitConnectionStatus, W as LiveKitSessionRequest, X as LiveKitSessionStartResult, aN as LivePlaybackKeeper, Y as MAX_RECONNECT_ATTEMPTS, Z as MAX_SESSION_INSTRUCTIONS_CHARS, aO as MIC_LEASE_ENDED_TIMEOUT_MS, aP as MicLease, aQ as NETWORK_QUALITY_POLICY, aR as NetworkQualityStatus, aS as PlayableVideoElement, _ as ProxyClientOptions, $ as QualityCap, a0 as RECONNECT_BACKOFF_MS, aT as RealtimeAvatarCapacityError, aU as RealtimeAvatarLiveKitRoom, aV as RealtimeAvatarLiveKitRoomProps, a1 as RealtimeAvatarRequestOptions, a2 as RealtimeSessionApi, a3 as RealtimeSessionMedia, a4 as RealtimeSessionRoomSinks, a5 as ReconnectPolicy, a6 as ReconnectingEvent, a7 as RecoveryState, a8 as RetryStep, a9 as SessionBehavior, aa as SessionClip, ab as SessionClocks, ac as SessionEndReason, ad as SessionLifecycleApi, ae as SessionLifecyclePhase, af as SessionLifecyclePhaseKind, ag as SessionLifecycleRoomBridge, ai as SurfaceLayers, aj as TurnState, ak as TurnTimeoutEvent, al as UseAvatarQualityGovernorInput, am as UseLiveKitAvatarGrantInput, an as UseRealtimeSessionInput, ao as UseSessionLifecycleInput, ap as VoiceSpec, aq as VoiceSpecInput, ar as capacityErrorFromBusy, as as capacityStateFromGrant, at as createProxyClient, av as knownBehaviorStates, aw as mapTurnState, ax as readInboundRtp, ay as sessionBehaviorSchema, az as sessionClipSchema, aW as shouldReplayPendingTurn, aA as splitCallTranscript, aB as useAvatarAdaptivePlayoutDelay, aC as useAvatarPlayoutDelay, aD as useAvatarQualityGovernor, aE as useCallTranscript, aF as useLiveKitAvatarGrant, aH as useMicLease, aI as useRealtimeSession, aJ as useReleaseMicLeaseOnTrackEnded, aK as useSessionLifecycle } from './proxy-client-w6prrzGW.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
@@ -301,6 +301,131 @@ function playoutDelayTarget(track) {
301
301
  return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
302
302
  }
303
303
 
304
+ // ../http-client/src/generated/error-semantics.ts
305
+ var ERROR_SEMANTICS = {
306
+ "bad_request": { statuses: [400], message: "Check the request and try again.", retryable: false },
307
+ "unauthorized": { statuses: [401], message: "Authentication is required to use Realtime Avatar.", retryable: false },
308
+ "payment_required": { statuses: [402], message: "Add credits to continue using realtime avatars.", retryable: false },
309
+ "forbidden": { statuses: [403], message: "This API key does not have access to that action.", retryable: false },
310
+ "not_found": { statuses: [404], message: "We could not find that avatar or resource.", retryable: false },
311
+ "method_not_allowed": { statuses: [405], message: "This request method is not supported.", retryable: false },
312
+ "not_acceptable": { statuses: [406], message: "The requested response format is not supported.", retryable: false },
313
+ "request_aborted": { statuses: [408], message: "The session request was cancelled.", retryable: false },
314
+ "conflict": { statuses: [409], message: "That request is already in progress. Try again in a moment.", retryable: true },
315
+ "length_required": { statuses: [411], message: "A request content length is required.", retryable: false },
316
+ "payload_too_large": { statuses: [413], message: "The request is too large.", retryable: false },
317
+ "unprocessable_entity": { statuses: [422], message: "Check the request and try again.", retryable: false },
318
+ "rate_limited": { statuses: [429], message: "Too many realtime requests. Please slow down and retry.", retryable: true },
319
+ "internal_error": { statuses: [500], message: "The session service encountered an error. Try again.", retryable: true },
320
+ "unavailable": { statuses: [503], message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true },
321
+ "insufficient_scope": { statuses: [403], message: "This API key is missing a required permission.", retryable: false },
322
+ "loop_pending": { statuses: [409], message: "An avatar loop update is still in progress. Try again shortly.", retryable: true },
323
+ "rate_limit_exceeded": { statuses: [429], message: "Too many realtime requests. Please slow down and retry.", retryable: true },
324
+ "upstream_failed": { statuses: [500, 502, 503], message: "The session service could not complete the request. Try again.", retryable: true },
325
+ "upstream_unreachable": { statuses: [502, 503], message: "The session service could not be reached. Try again.", retryable: true },
326
+ "invalid_upstream_response": { statuses: [502], message: "The session service returned an invalid response. Try again.", retryable: true },
327
+ "upstream_timeout": { statuses: [504], message: "The session service took too long to respond. Try again.", retryable: true },
328
+ "capacity_unavailable": { statuses: [503], message: "Session availability could not be checked. Try again.", retryable: true },
329
+ "session_dispatch_failed": { statuses: [503], message: "The avatar session could not be started. Try again.", retryable: true },
330
+ "admission_unavailable": { statuses: [503], message: "Session admission is temporarily unavailable. Try again.", retryable: true },
331
+ "service_warming": { statuses: [503], message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true },
332
+ "avatar_not_ready": { statuses: [409], message: "This avatar is still being built \u2014 it is usually ready within a minute. Try again then.", retryable: true },
333
+ "clip_library_not_ready": { statuses: [409], message: "This avatar's motion clips are still rendering. Try again in a minute.", retryable: true },
334
+ "recording_unsupported": { statuses: [501], message: "Session recording is not enabled for this deployment.", retryable: false },
335
+ "recording_requires_new_room": { statuses: [422], message: "Recording requires a new session room. Start a fresh session to record it.", retryable: false },
336
+ "recording_unavailable": { statuses: [503], message: "Session recording is temporarily unavailable. Try again in a moment.", retryable: true }
337
+ };
338
+ var UNCOPIED_ERROR_CODES = [
339
+ "capacity_busy",
340
+ "concurrency_limit_reached",
341
+ "insufficient_credits",
342
+ "spend_limit_exceeded",
343
+ "billing_required",
344
+ "network_error"
345
+ ];
346
+ var MESSAGE_SHAPE_PATTERNS = { "machine": { "source": "[_=<>{}\\[\\]|\\\\$`]|\\w+\\.\\w+\\.\\w|\\S:\\d|\\w\\(\\)|^\\s*(?:at|File)\\b|\\bTraceback \\(most recent call last\\)", "flags": "mu" }, "locator": { "source": "https?://|/[\\w.-]+/|[\\w.+-]+@[\\w.-]+\\.[a-z]{2,}|\\bbearer\\s|[\\r\\n]", "flags": "i" } };
347
+ var MESSAGE_MAX_LENGTH = 180;
348
+ var SHAPE_TESTS = Object.values(MESSAGE_SHAPE_PATTERNS).map(
349
+ (pattern) => new RegExp(pattern.source, pattern.flags)
350
+ );
351
+ function hasInternalDetail(message) {
352
+ return SHAPE_TESTS.some((pattern) => pattern.test(message));
353
+ }
354
+ function userSafeMessage(message) {
355
+ const trimmed = message.trim();
356
+ if (!trimmed || hasInternalDetail(trimmed)) return null;
357
+ return trimmed.length > MESSAGE_MAX_LENGTH ? `${trimmed.slice(0, MESSAGE_MAX_LENGTH - 3)}...` : trimmed;
358
+ }
359
+ function isCopiedErrorCode(code) {
360
+ return Object.hasOwn(ERROR_SEMANTICS, code);
361
+ }
362
+ function isKnownErrorCode(code) {
363
+ return isCopiedErrorCode(code) || UNCOPIED_ERROR_CODES.some((known) => known === code);
364
+ }
365
+ function semanticsFor(code) {
366
+ return isCopiedErrorCode(code) ? ERROR_SEMANTICS[code] : void 0;
367
+ }
368
+ function semanticsForStatus(status) {
369
+ return Object.values(ERROR_SEMANTICS).find(
370
+ (entry) => entry.statuses.some((allowed) => allowed === status)
371
+ );
372
+ }
373
+ function defaultCodeForStatus(status) {
374
+ const match = Object.entries(ERROR_SEMANTICS).find(
375
+ ([, entry]) => entry.statuses.some((allowed) => allowed === status)
376
+ );
377
+ return match ? match[0] : "request_failed";
378
+ }
379
+ function normalizeError(input, overrides) {
380
+ const status = input.status ?? 0;
381
+ const rawCode = input.code?.trim() || null;
382
+ const rawMessage = input.message?.trim() || "";
383
+ if (rawCode) {
384
+ const semantics = semanticsFor(rawCode);
385
+ if (semantics?.statuses.some((allowed) => allowed === status)) {
386
+ return { code: rawCode, message: semantics.message, retryable: semantics.retryable };
387
+ }
388
+ }
389
+ if (status === 402) {
390
+ return {
391
+ code: rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" ? rawCode : "billing_required",
392
+ message: userSafeMessage(rawMessage) ?? ERROR_SEMANTICS.payment_required.message,
393
+ retryable: false
394
+ };
395
+ }
396
+ if (status === 429 && rawCode === "concurrency_limit_reached") {
397
+ return {
398
+ code: "concurrency_limit_reached",
399
+ message: userSafeMessage(rawMessage) ?? overrides.concurrencyLimitFallback,
400
+ retryable: true
401
+ };
402
+ }
403
+ if (status === 400 || status === 422) {
404
+ return {
405
+ code: "invalid_request",
406
+ message: userSafeMessage(rawMessage) ?? ERROR_SEMANTICS.bad_request.message,
407
+ retryable: false
408
+ };
409
+ }
410
+ const recognised = !rawCode || isKnownErrorCode(rawCode);
411
+ const byStatus = recognised && status < 500 ? semanticsForStatus(status) : void 0;
412
+ if (byStatus) {
413
+ return {
414
+ code: status === 503 ? "service_unavailable" : defaultCodeForStatus(status),
415
+ message: byStatus.message,
416
+ retryable: byStatus.retryable
417
+ };
418
+ }
419
+ if (status >= 500) {
420
+ return { code: "service_unavailable", message: ERROR_SEMANTICS.unavailable.message, retryable: true };
421
+ }
422
+ return {
423
+ code: status === 0 && rawCode === "network_error" ? "network_error" : "request_failed",
424
+ message: userSafeMessage(rawMessage) ?? overrides.requestFailedFallback,
425
+ retryable: status === 0 || status >= 500
426
+ };
427
+ }
428
+
304
429
  // ../client/src/errors.ts
305
430
  var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
306
431
  constructor(message, status, body, response, metadata = {}) {
@@ -359,72 +484,27 @@ var RealtimeAvatarCapacityError = class extends Error {
359
484
  return this.busy.recommended_retry_ms;
360
485
  }
361
486
  };
487
+ function readRecord(value) {
488
+ return typeof value === "object" && value !== null ? { ...value } : null;
489
+ }
362
490
  function extractErrorMessage(body) {
363
- if (!body || typeof body !== "object") return typeof body === "string" && body ? body : null;
364
- const record = body;
365
- const detail = readMessageValue(record.detail);
366
- if (detail) return detail;
367
- const error = readMessageValue(record.error);
368
- if (error) return error;
369
- const message = readMessageValue(record.message);
370
- if (message) return message;
371
- return null;
491
+ if (typeof body === "string") return body || null;
492
+ const record = readRecord(body);
493
+ if (!record) return null;
494
+ return readMessageValue(record.detail) ?? readMessageValue(record.error) ?? readMessageValue(record.message);
372
495
  }
373
496
  function normalizeRealtimeAvatarError(input) {
374
- const status = input.status ?? 0;
375
- const rawCode = input.code?.trim() || null;
376
- const rawMessage = input.message?.trim() || "";
377
- const blob = `${rawCode ?? ""} ${rawMessage}`.toLowerCase();
378
- if (rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" || status === 402) {
379
- return {
380
- code: rawCode ?? "billing_required",
381
- message: rawMessage && !hasInternalDetail(rawMessage) ? rawMessage : "Add credits to continue using realtime avatars.",
382
- retryable: false
383
- };
384
- }
385
- if (status === 401) {
386
- return { code: "unauthorized", message: "Authentication is required to use Realtime Avatar.", retryable: false };
387
- }
388
- if (status === 403) {
389
- return { code: "forbidden", message: "This API key does not have access to that action.", retryable: false };
390
- }
391
- if (status === 404 || blob.includes("unknown avatar_id") || blob.includes("avatar not found")) {
392
- return { code: "not_found", message: "We could not find that avatar or resource.", retryable: false };
393
- }
394
- if (status === 409) {
395
- return { code: "conflict", message: "That request is already in progress. Try again in a moment.", retryable: true };
396
- }
397
- if (status === 429 && rawCode === "concurrency_limit_reached") {
398
- return {
399
- code: rawCode,
400
- message: userSafeMessage(rawMessage) ?? "The concurrent session limit is reached. Active and starting sessions count. End a session or wait for pending starts to clear, then retry.",
401
- retryable: true
402
- };
403
- }
404
- if (status === 429) {
405
- return { code: "rate_limited", message: "Too many realtime requests. Please slow down and retry.", retryable: true };
406
- }
407
- if (isWarmingMessage(blob)) {
408
- return { code: "service_unavailable", message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true };
409
- }
410
- if (status === 400 || status === 422) {
411
- return { code: "invalid_request", message: userSafeMessage(rawMessage) ?? "Check the request and try again.", retryable: false };
412
- }
413
- if (status >= 500) {
414
- return { code: "service_unavailable", message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true };
415
- }
416
- return {
417
- code: isPublicErrorCode(rawCode) ? rawCode : "request_failed",
418
- message: userSafeMessage(rawMessage) ?? "Realtime Avatar request failed. Try again.",
419
- retryable: status === 0 || status >= 500
420
- };
497
+ return normalizeError(input, {
498
+ concurrencyLimitFallback: CONCURRENCY_LIMIT_FALLBACK_MESSAGE,
499
+ requestFailedFallback: "Realtime Avatar request failed. Try again."
500
+ });
421
501
  }
422
502
  function extractErrorMetadata(body, status, rawMessage) {
423
- if (!body || typeof body !== "object") {
503
+ const record = readRecord(body);
504
+ if (!record) {
424
505
  const normalized2 = normalizeRealtimeAvatarError({ status, message: rawMessage });
425
506
  return { ...normalized2, rawMessage };
426
507
  }
427
- const record = body;
428
508
  const code = typeof record.code === "string" ? record.code : null;
429
509
  const normalized = normalizeRealtimeAvatarError({ status, code, message: rawMessage });
430
510
  return {
@@ -435,38 +515,13 @@ function extractErrorMetadata(body, status, rawMessage) {
435
515
  message: normalized.message
436
516
  };
437
517
  }
438
- var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
439
- "billing_required",
440
- "invalid_request",
441
- "unauthorized",
442
- "forbidden",
443
- "not_found",
444
- "conflict",
445
- "rate_limited",
446
- "service_unavailable",
447
- "request_failed",
448
- "network_error"
449
- ]);
450
- function isPublicErrorCode(code) {
451
- return Boolean(code && PUBLIC_ERROR_CODES.has(code));
452
- }
518
+ var CONCURRENCY_LIMIT_FALLBACK_MESSAGE = "The concurrent session limit is reached. Active and starting sessions count. End a session or wait for pending starts to clear, then retry.";
453
519
  function readMessageValue(value) {
454
520
  if (typeof value === "string") return value.trim() || null;
455
- if (!value || typeof value !== "object") return null;
456
- const record = value;
521
+ const record = readRecord(value);
522
+ if (!record) return null;
457
523
  return readMessageValue(record.message) ?? readMessageValue(record.error) ?? readMessageValue(record.detail);
458
524
  }
459
- function userSafeMessage(message) {
460
- if (!message || hasInternalDetail(message)) return null;
461
- return message.length > 180 ? `${message.slice(0, 177)}...` : message;
462
- }
463
- function isWarmingMessage(blob) {
464
- return blob.includes("warming") || blob.includes("not ready") || blob.includes("starting up");
465
- }
466
- var MACHINE_SHAPED = /[_=<>{}\[\]|\\$`]|\w+\.\w+\.\w|\S:\d|\w\(\)|^\s*(?:at|File)\b/mu;
467
- function hasInternalDetail(message) {
468
- return MACHINE_SHAPED.test(message);
469
- }
470
525
  function splitCallTranscript(all, localIdentity) {
471
526
  if (!localIdentity) return { user: [], agent: all };
472
527
  const user = [];
@@ -2922,7 +2977,7 @@ function SessionLifecycleRoomBridge({
2922
2977
  }, [connectionHistoryGrant, detailsSessionId]);
2923
2978
  useEffect(() => {
2924
2979
  const collector = connectionHistoryRef.current;
2925
- if (!collector || typeof window === "undefined") return;
2980
+ if (!collector || typeof window === "undefined" || typeof window.addEventListener !== "function" || typeof window.removeEventListener !== "function") return;
2926
2981
  const flush = () => {
2927
2982
  void collector.flush();
2928
2983
  };
@@ -61,8 +61,8 @@ declare const recordingArtifactSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
61
61
  retainedUntil: z.ZodString;
62
62
  status: z.ZodLiteral<"failed">;
63
63
  errorCode: z.ZodEnum<{
64
- recording_failed: "recording_failed";
65
64
  recording_unavailable: "recording_unavailable";
65
+ recording_failed: "recording_failed";
66
66
  }>;
67
67
  }, z.core.$strict>], "status">;
68
68
  type RecordingArtifact = z.infer<typeof recordingArtifactSchema>;
@@ -118,8 +118,8 @@ declare const listRecordingsResponseSchema: z.ZodObject<{
118
118
  retainedUntil: z.ZodString;
119
119
  status: z.ZodLiteral<"failed">;
120
120
  errorCode: z.ZodEnum<{
121
- recording_failed: "recording_failed";
122
121
  recording_unavailable: "recording_unavailable";
122
+ recording_failed: "recording_failed";
123
123
  }>;
124
124
  }, z.core.$strict>], "status">>;
125
125
  nextCursor: z.ZodNullable<z.ZodString>;
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, k as ListRecordingsQuery, l as ListRecordingsResponse, i as ListSessionsOptions, L as LoopRedirect, m as RecordingAccessResponse, R as RecordingArtifact, w as RecordingArtifactStatus, x as RecordingMode, S as StartCallResult, T as TranscriptPayload, j as UsageSession, U as UsageSessionPage, V as VideoPolicy, y as isQueued, z as listRecordingsResponseSchema, B as recordingAccessResponseSchema, D as recordingArtifactSchema, F as recordingModeSchema } from './types-BnlnrYgN.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, k as ListRecordingsQuery, l as ListRecordingsResponse, i as ListSessionsOptions, L as LoopRedirect, m as RecordingAccessResponse, R as RecordingArtifact, w as RecordingArtifactStatus, x as RecordingMode, S as StartCallResult, T as TranscriptPayload, j as UsageSession, U as UsageSessionPage, V as VideoPolicy, y as isQueued, z as listRecordingsResponseSchema, B as recordingAccessResponseSchema, D as recordingArtifactSchema, F as recordingModeSchema } from './types-CxkszIDK.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-OESXLLQB.js';
1
+ export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-27XNZHSF.js';
2
2
  import './chunk-MCIXU6XP.js';
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig } from './types-CFkiNV3f.js';
2
- import './types-BnlnrYgN.js';
1
+ import { P as ProxyConfig } from './types-Bzn5PJ0g.js';
2
+ import './types-CxkszIDK.js';
3
3
  import 'zod';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { createProxyHandler } from './chunk-27BB4DQ2.js';
2
- import './chunk-OESXLLQB.js';
1
+ import { createProxyHandler } from './chunk-RKIEGOGJ.js';
2
+ import './chunk-27XNZHSF.js';
3
3
  import './chunk-MCIXU6XP.js';
4
4
 
5
5
  // src/tanstack-start.ts
@@ -1,4 +1,4 @@
1
- import { C as CallMode, a as CallPolicy } from './types-BnlnrYgN.js';
1
+ import { C as CallMode, a as CallPolicy } from './types-CxkszIDK.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";
@@ -110,6 +110,9 @@ interface components {
110
110
  idle?: {
111
111
  clips: string[];
112
112
  weight?: number;
113
+ weights?: {
114
+ [key: string]: number;
115
+ };
113
116
  };
114
117
  on?: {
115
118
  userSpeechStarted?: {
@@ -172,6 +175,19 @@ interface components {
172
175
  require_ready_clip_library?: boolean;
173
176
  };
174
177
  LiveKitSessionGrant: {
178
+ /** @description Returned only for X-RTA-Release-Evidence: 1. Omitted by default for strict SDK compatibility. Missing deployment or worker evidence remains null. */
179
+ readonly releaseIdentity?: {
180
+ deploymentId: string | null;
181
+ workerRevision: string | null;
182
+ provenance: {
183
+ /** @constant */
184
+ deploymentSource: "cloudflare-worker-version";
185
+ workerSource: "authenticated-worker-heartbeat" | null;
186
+ workerId: string | null;
187
+ podStartedAtMs: number | null;
188
+ workerObservedAt: string | null;
189
+ };
190
+ };
175
191
  /**
176
192
  * @default ready
177
193
  * @constant
@@ -471,6 +487,19 @@ interface components {
471
487
  ListUsageSessionsResponse: {
472
488
  data: {
473
489
  sessionId: string;
490
+ /** @description Returned only for X-RTA-Release-Evidence: 1. Omitted by default for strict client compatibility. Evidence is stored against this session; missing evidence remains null. */
491
+ readonly releaseIdentity?: {
492
+ deploymentId: string | null;
493
+ workerRevision: string | null;
494
+ provenance: {
495
+ /** @constant */
496
+ deploymentSource: "cloudflare-worker-version";
497
+ workerSource: "authenticated-worker-heartbeat" | null;
498
+ workerId: string | null;
499
+ podStartedAtMs: number | null;
500
+ workerObservedAt: string | null;
501
+ };
502
+ };
474
503
  avatarId: string | null;
475
504
  avatarName: string | null;
476
505
  /** @enum {string} */
@@ -667,6 +696,9 @@ interface components {
667
696
  idle?: {
668
697
  clips: string[];
669
698
  weight?: number;
699
+ weights?: {
700
+ [key: string]: number;
701
+ };
670
702
  };
671
703
  on?: {
672
704
  userSpeechStarted?: {
@@ -703,6 +735,9 @@ interface components {
703
735
  idle?: {
704
736
  clips: string[];
705
737
  weight?: number;
738
+ weights?: {
739
+ [key: string]: number;
740
+ };
706
741
  };
707
742
  on?: {
708
743
  userSpeechStarted?: {
@@ -763,6 +798,9 @@ interface components {
763
798
  idle?: {
764
799
  clips: string[];
765
800
  weight?: number;
801
+ weights?: {
802
+ [key: string]: number;
803
+ };
766
804
  };
767
805
  on?: {
768
806
  userSpeechStarted?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "realtime-avatar",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
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",
@@ -268,8 +268,8 @@ declare const liveKitSessionGrantSchema: z.ZodObject<{
268
268
  retainedUntil: z.ZodString;
269
269
  status: z.ZodLiteral<"failed">;
270
270
  errorCode: z.ZodEnum<{
271
- recording_failed: "recording_failed";
272
271
  recording_unavailable: "recording_unavailable";
272
+ recording_failed: "recording_failed";
273
273
  }>;
274
274
  }, z.core.$strict>], "status">>;
275
275
  connection_history: z.ZodOptional<z.ZodObject<{