@burdenoff/fe-libs 2026.1024.3 → 2026.1026.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.
Files changed (33) hide show
  1. package/dist/shared/assistant/avatar/live/LiveAvatarMinutes.d.ts +24 -0
  2. package/dist/shared/assistant/avatar/live/LiveAvatarMinutes.js +161 -0
  3. package/dist/shared/assistant/avatar/live/LiveAvatarStage.js +95 -74
  4. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +6 -0
  5. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +195 -172
  6. package/dist/shared/assistant/avatar/live/avatarPorts.d.ts +9 -0
  7. package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +77 -1
  8. package/dist/shared/assistant/avatar/live/avatarSessionClient.js +193 -72
  9. package/dist/shared/assistant/avatar/live/avatarTransport.d.ts +25 -3
  10. package/dist/shared/assistant/avatar/live/avatarTransport.js +29 -2
  11. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +35 -4
  12. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +136 -79
  13. package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +79 -3
  14. package/dist/shared/assistant/avatar/live/liveAvatarController.js +424 -245
  15. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.d.ts +127 -0
  16. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.js +168 -0
  17. package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +3 -0
  18. package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -7
  19. package/dist/shared/assistant/avatar/live/liveKitAvatarRoom.js +6 -1
  20. package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +127 -2
  21. package/dist/shared/assistant/avatar/useAvatarCopy.js +116 -7
  22. package/dist/shared/assistant/i18nManifest.d.ts +32 -1
  23. package/dist/shared/assistant/i18nManifest.js +55 -24
  24. package/dist/shared/assistant/index.d.ts +2 -0
  25. package/dist/shared/assistant/ui/AssistantChatArea.js +17 -17
  26. package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts +5 -2
  27. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +11 -3
  28. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +78 -76
  29. package/dist/shared/assistant/ui/avatar/avatarAskGate.d.ts +2 -0
  30. package/dist/shared/assistant/ui/avatar/avatarAskGate.js +1 -0
  31. package/dist/shared/events/index.d.ts +2 -1
  32. package/dist/shared-assistant.js +227 -225
  33. package/package.json +1 -1
@@ -19,6 +19,63 @@ export type AvatarAvailabilityReason = (typeof AVAILABILITY_REASONS)[number];
19
19
  */
20
20
  declare const BUDGET_SCOPES: readonly ["person", "workspace", "everyone"];
21
21
  export type AvatarBudgetScope = (typeof BUDGET_SCOPES)[number];
22
+ /** BOFF-9446: the only `minutes.version` this client reads (contract v2). A block of any other version reads as no block. */
23
+ export declare const AVATAR_MINUTES_CONTRACT_VERSION = 2;
24
+ /** BOFF-9446: the states of an availability `minutes` block. Any other value reads as no block. */
25
+ export declare const AVATAR_MINUTES_STATES: readonly ["AVAILABLE", "IN_USE", "EXHAUSTED", "UNAVAILABLE"];
26
+ export type AvatarMinutesState = (typeof AVATAR_MINUTES_STATES)[number];
27
+ /**
28
+ * BOFF-9446: why no credits are left for this person. `FREE_CEILING`: their free credits for the month are used (plan credits would
29
+ * still work); `ZERO_SESSION_LIMIT`: too many sessions today that charged nothing (a 402 only, until the next UTC midnight; a plan does
30
+ * not lift it); `NOT_INCLUDED`: this workspace has no avatar credits at all; `OWN_SESSION`: the credits left are held by this person's
31
+ * own session on another device (a block's IN_USE only). Any other value reads as none.
32
+ */
33
+ export declare const AVATAR_MINUTES_REASONS: readonly ["FREE_CEILING", "ZERO_SESSION_LIMIT", "NOT_INCLUDED", "OWN_SESSION"];
34
+ export type AvatarMinutesReason = (typeof AVATAR_MINUTES_REASONS)[number];
35
+ /**
36
+ * BOFF-9446: the balance of an AVAILABLE, IN_USE or EXHAUSTED block, the workspace's. Counts are whole credits (1 credit = 1 minute of
37
+ * avatar time), 0 or more.
38
+ */
39
+ export interface AvatarMinutesBalance {
40
+ /** Credits left for this person now: carried, then free within their ceiling, then plan. */
41
+ readonly availableMinutes: number;
42
+ /** Reserved by open sessions in the workspace. */
43
+ readonly heldMinutes: number;
44
+ /** The workspace's free credits each month; 0 without a free allowance. */
45
+ readonly freeMinutesPerMonth: number;
46
+ /** The workspace's free credits left this month, before the person's ceiling. */
47
+ readonly freeRemaining: number;
48
+ /** The credits the workspace's counted plans give each month, summed. */
49
+ readonly planMinutesPerMonth: number;
50
+ readonly planRemaining: number;
51
+ /** Credits given back by a full-session credit, still unused. */
52
+ readonly carriedRemaining: number;
53
+ /** The free credits one person may use each month, across workspaces; null while billing has the ceiling off. */
54
+ readonly personFreeCeiling: number | null;
55
+ /** This person's free credits left this month; null while billing has the ceiling off. */
56
+ readonly personFreeRemaining: number | null;
57
+ /** When the free and plan credits reset (the next 1st, 00:00 UTC), as UTC epoch milliseconds: shown as a UTC date, never local. */
58
+ readonly resetsAt: number;
59
+ /** A plan with more avatar credits exists for this app: display only (it offers "See plans"), never an entitlement. */
60
+ readonly morePlanCreditsAvailable: boolean;
61
+ }
62
+ /** BOFF-9446: the availability `minutes` block, as the client keeps it. */
63
+ export interface AvatarMinutes {
64
+ readonly state: AvatarMinutesState;
65
+ /** When the server gave one the client knows, where it may stand: OWN_SESSION with IN_USE, FREE_CEILING or NOT_INCLUDED with EXHAUSTED. */
66
+ readonly reason: AvatarMinutesReason | null;
67
+ /** With AVAILABLE, IN_USE and EXHAUSTED; null with UNAVAILABLE, which carries no numbers. */
68
+ readonly balance: AvatarMinutesBalance | null;
69
+ /** W, the seconds of the stale-presence margin the counting rules name (20 to 120); null when the answer had none in that range. */
70
+ readonly staleSeconds: number | null;
71
+ }
72
+ /** BOFF-9446: what a start answer says about the session's minutes (enforce only). */
73
+ export interface AvatarSessionMinutes {
74
+ /** The minutes reserved for this session, 1 or more. */
75
+ readonly reserved: number;
76
+ /** The session's length D' is shorter than the server's D: a session that reaches D' ends because its minutes ran out. */
77
+ readonly limitedByBalance: boolean;
78
+ }
22
79
  export interface AvatarAvailability {
23
80
  readonly available: boolean;
24
81
  readonly reason: AvatarAvailabilityReason | null;
@@ -28,6 +85,8 @@ export interface AvatarAvailability {
28
85
  readonly consentVersion: string;
29
86
  /** The server's session cap, D. */
30
87
  readonly maxSessionSeconds: number;
88
+ /** ★ BOFF-9446: the avatar-minutes block (enforce only). Absent when the answer had none, or none the client can read. */
89
+ readonly minutes?: AvatarMinutes;
31
90
  }
32
91
  export interface AvatarSessionStart {
33
92
  readonly sessionId: string;
@@ -40,6 +99,10 @@ export interface AvatarSessionStart {
40
99
  readonly connectBy: number;
41
100
  /** Wall-clock milliseconds: the server's hard end (create + D - 5 s). */
42
101
  readonly endsAt: number;
102
+ /** ★ BOFF-9446: D', this session's own length in seconds (enforce only): its hard end is create + D' - 5 s. */
103
+ readonly maxSessionSeconds?: number;
104
+ /** ★ BOFF-9446: the session's minutes (enforce only). */
105
+ readonly minutes?: AvatarSessionMinutes;
43
106
  }
44
107
  export type AvatarSessionState = 'starting' | 'ready' | 'ending' | 'ended' | 'failed' | 'canceled';
45
108
  export interface AvatarSessionStatus {
@@ -58,11 +121,15 @@ export declare class AvatarApiError extends Error {
58
121
  readonly retryAfterSeconds: number | null;
59
122
  /** On `budget_exhausted` only, whose budget the body names when it is one of the three we know; null in every other answer. */
60
123
  readonly scope: AvatarBudgetScope | null;
124
+ /** ★ BOFF-9446: on `minutes_exhausted` only, the body's reason when the client knows it; null otherwise. */
125
+ readonly minutesReason: AvatarMinutesReason | null;
61
126
  constructor(status: number,
62
127
  /** The server's or the gateway's code, `invalid_response` for a body we refused, `network` for no answer. */
63
128
  code: string, retryAfterSeconds: number | null,
64
129
  /** On `budget_exhausted` only, whose budget the body names when it is one of the three we know; null in every other answer. */
65
- scope?: AvatarBudgetScope | null);
130
+ scope?: AvatarBudgetScope | null,
131
+ /** ★ BOFF-9446: on `minutes_exhausted` only, the body's reason when the client knows it; null otherwise. */
132
+ minutesReason?: AvatarMinutesReason | null);
66
133
  }
67
134
  export interface AvatarSessionClientOptions {
68
135
  /** `https://<gateway>/api/avatar/v1` (avatarApiBaseFromTtsEndpoint). */
@@ -120,6 +187,8 @@ export interface AvatarRefusal {
120
187
  * Absent when it named none (today's server) and with every other refusal: the person is then told the general line.
121
188
  */
122
189
  readonly scope?: AvatarBudgetScope;
190
+ /** ★ BOFF-9446: with `minutes-exhausted`, the reason the server gave, when the client knows it. Absent otherwise. */
191
+ readonly minutesReason?: AvatarMinutesReason;
123
192
  }
124
193
  /**
125
194
  * The person's side of every failure (§3.4): the normal voice always continues; this says what to show. 404 means "not here" (the module
@@ -132,4 +201,11 @@ export declare function avatarRefusalFor(error: unknown): AvatarRefusal;
132
201
  * enabled, not permitted), or only not now when the gateway could not check the permission (`unavailable`): the toggle stays.
133
202
  */
134
203
  export declare function avatarAvailabilityRefusal(reason: AvatarAvailabilityReason | null): AvatarRefusal;
204
+ /**
205
+ * ★ BOFF-9446: what a minutes block means for a start, as the server's create would answer it: EXHAUSTED and IN_USE cannot start a
206
+ * session, so the person is told why instead of sending a create that would be refused (and, for IN_USE with OWN_SESSION, that would
207
+ * meet the person's own session on another device). AVAILABLE can, and UNAVAILABLE stays startable as the retry (the server decides).
208
+ * No block, today's answers: nothing in the way. Never a reason to hide, and no time (an availability answer carries no Retry-After).
209
+ */
210
+ export declare function avatarMinutesRefusal(minutes: AvatarMinutes | null | undefined): AvatarRefusal | null;
135
211
  export {};
@@ -23,16 +23,34 @@ var o = [
23
23
  "person",
24
24
  "workspace",
25
25
  "everyone"
26
- ], c = class extends Error {
26
+ ], c = 2, l = [
27
+ "AVAILABLE",
28
+ "IN_USE",
29
+ "EXHAUSTED",
30
+ "UNAVAILABLE"
31
+ ], u = [
32
+ "FREE_CEILING",
33
+ "ZERO_SESSION_LIMIT",
34
+ "NOT_INCLUDED",
35
+ "OWN_SESSION"
36
+ ], d = {
37
+ IN_USE: ["OWN_SESSION"],
38
+ EXHAUSTED: ["FREE_CEILING", "NOT_INCLUDED"]
39
+ }, f = [
40
+ "FREE_CEILING",
41
+ "ZERO_SESSION_LIMIT",
42
+ "NOT_INCLUDED"
43
+ ], p = class extends Error {
27
44
  status;
28
45
  code;
29
46
  retryAfterSeconds;
30
47
  scope;
31
- constructor(e, t, n, r = null) {
32
- super(`avatar api ${e} ${t}`), this.status = e, this.code = t, this.retryAfterSeconds = n, this.scope = r, this.name = "AvatarApiError";
48
+ minutesReason;
49
+ constructor(e, t, n, r = null, i = null) {
50
+ super(`avatar api ${e} ${t}`), this.status = e, this.code = t, this.retryAfterSeconds = n, this.scope = r, this.minutesReason = i, this.name = "AvatarApiError";
33
51
  }
34
52
  };
35
- function l(e = globalThis.crypto) {
53
+ function m(e = globalThis.crypto) {
36
54
  if (e && typeof e.randomUUID == "function") return e.randomUUID();
37
55
  let t = new Uint8Array(16);
38
56
  if (e && typeof e.getRandomValues == "function") e.getRandomValues(t);
@@ -41,43 +59,96 @@ function l(e = globalThis.crypto) {
41
59
  let n = [...t].map((e) => e.toString(16).padStart(2, "0")).join("");
42
60
  return `${n.slice(0, 8)}-${n.slice(8, 12)}-${n.slice(12, 16)}-${n.slice(16, 20)}-${n.slice(20)}`;
43
61
  }
44
- function u(e) {
62
+ function h(e) {
45
63
  return typeof e == "object" && !!e && !Array.isArray(e);
46
64
  }
47
- function d(e, t) {
65
+ function g(e, t) {
48
66
  let n = e.headers.get("retry-after"), r = n === null || n.trim() === "" ? NaN : Number(n);
49
67
  if (Number.isFinite(r) && r >= 0) return r;
50
- let i = u(t) && u(t.error) ? Number(t.error.retryAfterSeconds) : NaN;
68
+ let i = h(t) && h(t.error) ? Number(t.error.retryAfterSeconds) : NaN;
51
69
  return Number.isFinite(i) && i >= 0 ? i : null;
52
70
  }
53
- async function f(e) {
71
+ async function _(e) {
54
72
  try {
55
73
  return await e.json();
56
74
  } catch {
57
75
  return null;
58
76
  }
59
77
  }
60
- function p(e, t) {
61
- if (e !== "budget_exhausted" || !u(t) || !u(t.error)) return null;
78
+ function v(e, t) {
79
+ if (e !== "budget_exhausted" || !h(t) || !h(t.error)) return null;
62
80
  let { scope: n } = t.error;
63
81
  return s.find((e) => e === n) ?? null;
64
82
  }
65
- async function m(e) {
66
- let t = await f(e), n = u(t) && u(t.error) && typeof t.error.code == "string" ? t.error.code : "http_error";
67
- return new c(e.status, n, d(e, t), p(n, t));
83
+ async function y(e) {
84
+ let t = await _(e), n = h(t) && h(t.error) && typeof t.error.code == "string" ? t.error.code : "http_error", r = n === "minutes_exhausted" && h(t) && h(t.error) ? b(t.error.reason, f) : null;
85
+ return new p(e.status, n, g(e, t), v(n, t), r);
68
86
  }
69
- function h(e) {
70
- if (!u(e) || typeof e.available != "boolean") throw new c(200, "invalid_response", null);
71
- let t = Array.isArray(e.avatars) ? e.avatars.flatMap((e) => u(e) && typeof e.id == "string" && r.test(e.id) ? [e.id] : []) : [], n = o.find((t) => t === e.reason) ?? null, i = typeof e.consentVersion == "string" ? e.consentVersion : "", a = typeof e.maxSessionSeconds == "number" && Number.isFinite(e.maxSessionSeconds) ? e.maxSessionSeconds : 0, s = e.available && t.length > 0 && i !== "" && a > 0;
87
+ function b(e, t) {
88
+ return t.find((t) => t === e) ?? null;
89
+ }
90
+ function x(e) {
91
+ return typeof e == "number" && Number.isSafeInteger(e) && e >= 0 ? e : null;
92
+ }
93
+ function S(e) {
94
+ return e === null ? null : x(e) ?? void 0;
95
+ }
96
+ function C(e) {
97
+ if (typeof e != "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(e)) return null;
98
+ let t = Date.parse(e);
99
+ return Number.isFinite(t) && new Date(t).toISOString() === e ? t : null;
100
+ }
101
+ function w(e) {
102
+ let t = x(e.availableMinutes), n = x(e.heldMinutes), r = x(e.freeMinutesPerMonth), i = x(e.freeRemaining), a = x(e.planMinutesPerMonth), o = x(e.planRemaining), s = x(e.carriedRemaining), c = S(e.personFreeCeiling), l = S(e.personFreeRemaining), u = C(e.resetsAt), { morePlanCreditsAvailable: d } = e;
103
+ return t === null || n === null || r === null || i === null || a === null || o === null || s === null || c === void 0 || l === void 0 || u === null || typeof d != "boolean" ? null : {
104
+ availableMinutes: t,
105
+ heldMinutes: n,
106
+ freeMinutesPerMonth: r,
107
+ freeRemaining: i,
108
+ planMinutesPerMonth: a,
109
+ planRemaining: o,
110
+ carriedRemaining: s,
111
+ personFreeCeiling: c,
112
+ personFreeRemaining: l,
113
+ resetsAt: u,
114
+ morePlanCreditsAvailable: d
115
+ };
116
+ }
117
+ function T(e) {
118
+ if (!h(e) || e.version !== 2) return null;
119
+ let t = l.find((t) => t === e.state);
120
+ if (t === void 0) return null;
121
+ let n = null;
122
+ if (t !== "UNAVAILABLE" && (n = w(e), n === null || (t === "AVAILABLE" ? n.availableMinutes < 1 : n.availableMinutes !== 0))) return null;
123
+ let r = e.staleSeconds;
124
+ return {
125
+ state: t,
126
+ reason: b(e.reason, d[t] ?? []),
127
+ balance: n,
128
+ staleSeconds: typeof r == "number" && Number.isSafeInteger(r) && r >= 20 && r <= 120 ? r : null
129
+ };
130
+ }
131
+ function E(e) {
132
+ if (!h(e)) return null;
133
+ let t = x(e.reserved);
134
+ return t === null || t < 1 || typeof e.limitedByBalance != "boolean" ? null : {
135
+ reserved: t,
136
+ limitedByBalance: e.limitedByBalance
137
+ };
138
+ }
139
+ function D(e) {
140
+ if (!h(e) || typeof e.available != "boolean") throw new p(200, "invalid_response", null);
141
+ let t = Array.isArray(e.avatars) ? e.avatars.flatMap((e) => h(e) && typeof e.id == "string" && r.test(e.id) ? [e.id] : []) : [], n = o.find((t) => t === e.reason) ?? null, i = typeof e.consentVersion == "string" ? e.consentVersion : "", a = typeof e.maxSessionSeconds == "number" && Number.isFinite(e.maxSessionSeconds) ? e.maxSessionSeconds : 0, s = e.available && t.length > 0 && i !== "" && a > 0, c = T(e.minutes);
72
142
  return {
73
143
  available: s,
74
144
  reason: s ? null : n ?? "not_enabled",
75
145
  avatars: t,
76
146
  consentVersion: i,
77
- maxSessionSeconds: a
147
+ maxSessionSeconds: a,
148
+ ...c === null ? {} : { minutes: c }
78
149
  };
79
150
  }
80
- function g(e) {
151
+ function O(e) {
81
152
  try {
82
153
  let n = new URL(e);
83
154
  return n.protocol === "wss:" ? !0 : n.protocol === "ws:" && t.has(n.hostname.toLowerCase());
@@ -85,20 +156,23 @@ function g(e) {
85
156
  return !1;
86
157
  }
87
158
  }
88
- function _(e) {
89
- if (!u(e)) throw new c(201, "invalid_response", null);
90
- let { sessionId: t, livekitUrl: r, participantToken: i, avatarIdentity: a, connectBy: o, endsAt: s } = e, l = typeof o == "string" ? Date.parse(o) : NaN, d = typeof s == "string" ? Date.parse(s) : NaN;
91
- if (typeof t != "string" || !n.test(t) || typeof r != "string" || !g(r) || typeof i != "string" || i === "" || typeof a != "string" || a === "" || !Number.isFinite(l) || !Number.isFinite(d)) throw new c(201, "invalid_response", null);
159
+ function k(e) {
160
+ if (!h(e)) throw new p(201, "invalid_response", null);
161
+ let { sessionId: t, livekitUrl: r, participantToken: i, avatarIdentity: a, connectBy: o, endsAt: s } = e, c = typeof o == "string" ? Date.parse(o) : NaN, l = typeof s == "string" ? Date.parse(s) : NaN;
162
+ if (typeof t != "string" || !n.test(t) || typeof r != "string" || !O(r) || typeof i != "string" || i === "" || typeof a != "string" || a === "" || !Number.isFinite(c) || !Number.isFinite(l)) throw new p(201, "invalid_response", null);
163
+ let { maxSessionSeconds: u } = e, d = typeof u == "number" && Number.isSafeInteger(u) && u > 0 ? u : null, f = E(e.minutes);
92
164
  return {
93
165
  sessionId: t,
94
166
  livekitUrl: r,
95
167
  participantToken: i,
96
168
  avatarIdentity: a,
97
- connectBy: l,
98
- endsAt: d
169
+ connectBy: c,
170
+ endsAt: l,
171
+ ...d === null ? {} : { maxSessionSeconds: d },
172
+ ...f === null ? {} : { minutes: f }
99
173
  };
100
174
  }
101
- var v = [
175
+ var A = [
102
176
  "starting",
103
177
  "ready",
104
178
  "ending",
@@ -106,22 +180,22 @@ var v = [
106
180
  "failed",
107
181
  "canceled"
108
182
  ];
109
- function y(e, t) {
110
- if (!u(e) || !v.includes(e.state)) throw new c(200, "invalid_response", null);
183
+ function j(e, t) {
184
+ if (!h(e) || !A.includes(e.state)) throw new p(200, "invalid_response", null);
111
185
  return {
112
186
  sessionId: t,
113
187
  state: e.state,
114
188
  endedReason: typeof e.endedReason == "string" ? e.endedReason : null
115
189
  };
116
190
  }
117
- var b = (e) => new Promise((t) => {
191
+ var M = (e) => new Promise((t) => {
118
192
  setTimeout(t, e);
119
193
  });
120
- function x(e) {
121
- let t = e.fetchImpl ?? ((e, t) => fetch(e, t)), a = e.sleep ?? b, o = e.now ?? (() => Date.now()), s = e.startInProgressBudgetMs ?? 15e3, l = e.baseUrl.replace(/\/+$/, "");
122
- async function u(t, n = null) {
194
+ function N(e) {
195
+ let t = e.fetchImpl ?? ((e, t) => fetch(e, t)), a = e.sleep ?? M, o = e.now ?? (() => Date.now()), s = e.startInProgressBudgetMs ?? 15e3, c = e.baseUrl.replace(/\/+$/, "");
196
+ async function l(t, n = null) {
123
197
  let r = e.getAuthorization();
124
- if (!r) throw new c(401, "unauthenticated", null);
198
+ if (!r) throw new p(401, "unauthenticated", null);
125
199
  let i = { authorization: r };
126
200
  if (t) {
127
201
  let t = await e.getWorkspaceAuthorization(n === null ? { fresh: !1 } : {
@@ -133,7 +207,7 @@ function x(e) {
133
207
  }
134
208
  return i;
135
209
  }
136
- async function d(e, n) {
210
+ async function u(e, n) {
137
211
  try {
138
212
  return await t(e, {
139
213
  ...n,
@@ -142,20 +216,20 @@ function x(e) {
142
216
  redirect: "error"
143
217
  });
144
218
  } catch (e) {
145
- throw n.signal?.aborted ? e : new c(0, "network", null);
219
+ throw n.signal?.aborted ? e : new p(0, "network", null);
146
220
  }
147
221
  }
148
222
  return {
149
223
  async availability(e) {
150
224
  let t = null;
151
225
  for (;;) {
152
- let n = await u(!0, t), r = await d(`${l}/availability`, {
226
+ let n = await l(!0, t), r = await u(`${c}/availability`, {
153
227
  method: "GET",
154
228
  headers: n,
155
229
  signal: e
156
230
  });
157
- if (r.ok) return h(await f(r));
158
- let i = await m(r);
231
+ if (r.ok) return D(await _(r));
232
+ let i = await y(r);
159
233
  if (r.status === 401 && t === null) {
160
234
  t = {
161
235
  error: i,
@@ -167,16 +241,16 @@ function x(e) {
167
241
  }
168
242
  },
169
243
  async create(e, t) {
170
- if (!r.test(e.avatarId) || !i.test(e.idempotencyKey)) throw new c(400, "invalid_request", null);
244
+ if (!r.test(e.avatarId) || !i.test(e.idempotencyKey)) throw new p(400, "invalid_request", null);
171
245
  let n = JSON.stringify({
172
246
  avatarId: e.avatarId,
173
247
  consentVersion: e.consentVersion
174
- }), p = o(), h = null, g = !1, v = !1, y = null;
248
+ }), d = o(), f = null, m = !1, h = !1, g = null;
175
249
  for (;;) {
176
- let r = await u(!0, h);
177
- if (h = null, y !== null && o() - p > s) throw y;
178
- if (r["x-workspace-authorization"] === void 0) throw new c(403, "workspace_context_required", null);
179
- let i = await d(`${l}/sessions`, {
250
+ let r = await l(!0, f);
251
+ if (f = null, g !== null && o() - d > s) throw g;
252
+ if (r["x-workspace-authorization"] === void 0) throw new p(403, "workspace_context_required", null);
253
+ let i = await u(`${c}/sessions`, {
180
254
  method: "POST",
181
255
  headers: {
182
256
  ...r,
@@ -186,41 +260,41 @@ function x(e) {
186
260
  body: n,
187
261
  signal: t
188
262
  });
189
- if (i.status === 201) return _(await f(i));
190
- let b = await m(i);
191
- if (i.status === 401 && !g) {
192
- g = !0, h = {
193
- error: b,
263
+ if (i.status === 201) return k(await _(i));
264
+ let v = await y(i);
265
+ if (i.status === 401 && !m) {
266
+ m = !0, f = {
267
+ error: v,
194
268
  workspace: r["x-workspace-authorization"]
195
269
  };
196
270
  continue;
197
271
  }
198
- if (i.status === 409 && b.code === "start_in_progress") {
199
- let e = Math.max(250, (b.retryAfterSeconds ?? 1) * 1e3);
200
- if (o() - p + e > s || (await a(e), t?.aborted)) throw b;
201
- y = b;
272
+ if (i.status === 409 && v.code === "start_in_progress") {
273
+ let e = Math.max(250, (v.retryAfterSeconds ?? 1) * 1e3);
274
+ if (o() - d + e > s || (await a(e), t?.aborted)) throw v;
275
+ g = v;
202
276
  continue;
203
277
  }
204
- if (i.status === 502 && !v) {
205
- v = !0;
278
+ if (i.status === 502 && !h) {
279
+ h = !0;
206
280
  continue;
207
281
  }
208
- throw b;
282
+ throw v;
209
283
  }
210
284
  },
211
285
  async status(e, t) {
212
- if (!n.test(e)) throw new c(400, "invalid_request", null);
213
- let r = await u(!1), i = await d(`${l}/sessions/${e}`, {
286
+ if (!n.test(e)) throw new p(400, "invalid_request", null);
287
+ let r = await l(!1), i = await u(`${c}/sessions/${e}`, {
214
288
  method: "GET",
215
289
  headers: r,
216
290
  signal: t
217
291
  });
218
- if (!i.ok) throw await m(i);
219
- return y(await f(i), e);
292
+ if (!i.ok) throw await y(i);
293
+ return j(await _(i), e);
220
294
  },
221
295
  async end(e, { reason: t, keepalive: r = !1 }) {
222
- if (!n.test(e)) throw new c(400, "invalid_request", null);
223
- let i = await u(!1), a = await d(`${l}/sessions/${e}/end`, {
296
+ if (!n.test(e)) throw new p(400, "invalid_request", null);
297
+ let i = await l(!1), a = await u(`${c}/sessions/${e}/end`, {
224
298
  method: "POST",
225
299
  headers: {
226
300
  ...i,
@@ -229,28 +303,34 @@ function x(e) {
229
303
  body: JSON.stringify({ reason: t }),
230
304
  keepalive: r
231
305
  });
232
- if (!a.ok) throw await m(a);
233
- return y(await f(a), e);
306
+ if (!a.ok) throw await y(a);
307
+ return j(await _(a), e);
234
308
  }
235
309
  };
236
310
  }
237
- var S = new Set([
311
+ var P = new Set([
238
312
  "capacity",
239
313
  "rate-limited",
240
- "budget-exhausted"
314
+ "budget-exhausted",
315
+ "minutes-in-use",
316
+ "minutes-in-use-self",
317
+ "minutes-unavailable"
241
318
  ]);
242
- function C(e) {
243
- if (!(e instanceof c)) return {
319
+ function F(e) {
320
+ return e.problem === null ? !1 : P.has(e.problem) ? !0 : e.problem === "minutes-exhausted" && e.minutesReason === "ZERO_SESSION_LIMIT";
321
+ }
322
+ function I(e) {
323
+ if (!(e instanceof p)) return {
244
324
  problem: "connection",
245
325
  hide: !1
246
326
  };
247
- let t = w(e);
248
- return t.problem !== null && S.has(t.problem) && e.retryAfterSeconds !== null ? {
327
+ let t = L(e);
328
+ return F(t) && e.retryAfterSeconds !== null ? {
249
329
  ...t,
250
330
  retryAfterSeconds: e.retryAfterSeconds
251
331
  } : t;
252
332
  }
253
- function w(e) {
333
+ function L(e) {
254
334
  if (e.status === 0) return {
255
335
  problem: "connection",
256
336
  hide: !1
@@ -312,6 +392,26 @@ function w(e) {
312
392
  hide: !1,
313
393
  scope: e.scope
314
394
  };
395
+ case "minutes_exhausted": return e.minutesReason === null ? {
396
+ problem: "minutes-exhausted",
397
+ hide: !1
398
+ } : {
399
+ problem: "minutes-exhausted",
400
+ hide: !1,
401
+ minutesReason: e.minutesReason
402
+ };
403
+ case "minutes_in_use": return {
404
+ problem: "minutes-in-use",
405
+ hide: !1
406
+ };
407
+ case "minutes_in_use_self": return {
408
+ problem: "minutes-in-use-self",
409
+ hide: !1
410
+ };
411
+ case "minutes_unavailable": return {
412
+ problem: "minutes-unavailable",
413
+ hide: !1
414
+ };
315
415
  case "avatar_unavailable": return {
316
416
  problem: "unavailable",
317
417
  hide: !1
@@ -345,7 +445,7 @@ function w(e) {
345
445
  };
346
446
  }
347
447
  }
348
- function T(e) {
448
+ function R(e) {
349
449
  return e === "unavailable" ? {
350
450
  problem: "capacity",
351
451
  hide: !1
@@ -354,5 +454,26 @@ function T(e) {
354
454
  hide: !0
355
455
  };
356
456
  }
457
+ function z(e) {
458
+ if (e == null) return null;
459
+ switch (e.state) {
460
+ case "EXHAUSTED": return e.reason === null ? {
461
+ problem: "minutes-exhausted",
462
+ hide: !1
463
+ } : {
464
+ problem: "minutes-exhausted",
465
+ hide: !1,
466
+ minutesReason: e.reason
467
+ };
468
+ case "IN_USE": return e.reason === "OWN_SESSION" ? {
469
+ problem: "minutes-in-use-self",
470
+ hide: !1
471
+ } : {
472
+ problem: "minutes-in-use",
473
+ hide: !1
474
+ };
475
+ default: return null;
476
+ }
477
+ }
357
478
  //#endregion
358
- export { e as AVATAR_API_PATH, c as AvatarApiError, a as avatarApiBaseFromTtsEndpoint, T as avatarAvailabilityRefusal, l as avatarIdempotencyKey, C as avatarRefusalFor, x as createAvatarSessionClient };
479
+ export { e as AVATAR_API_PATH, c as AVATAR_MINUTES_CONTRACT_VERSION, u as AVATAR_MINUTES_REASONS, l as AVATAR_MINUTES_STATES, p as AvatarApiError, a as avatarApiBaseFromTtsEndpoint, R as avatarAvailabilityRefusal, m as avatarIdempotencyKey, z as avatarMinutesRefusal, I as avatarRefusalFor, N as createAvatarSessionClient };
@@ -51,6 +51,11 @@ export type AvatarTransportEvent = {
51
51
  readonly type: 'audioPlayback';
52
52
  readonly canPlay: boolean;
53
53
  }
54
+ /** ★ BOFF-9696: the browser refused (or allowed again) the avatar's video, e.g. iOS in Low Power Mode until a user gesture. */
55
+ | {
56
+ readonly type: 'videoPlayback';
57
+ readonly canPlay: boolean;
58
+ }
54
59
  /** Result of the readiness probe at connect (see AvatarReadinessProbeOptions). */
55
60
  | {
56
61
  readonly type: 'readiness';
@@ -168,6 +173,8 @@ export declare class LiveKitAvatarTransport {
168
173
  private videoPresent;
169
174
  private degraded;
170
175
  private audioElement;
176
+ /** ★ BOFF-9696 (S1): the browser paused the avatar's audio element on its own (see watchAudioElement). */
177
+ private elementPaused;
171
178
  private disposed;
172
179
  private rejectReady;
173
180
  private mediaReady;
@@ -218,15 +225,30 @@ export declare class LiveKitAvatarTransport {
218
225
  get transportState(): AvatarTransportState;
219
226
  /** The readiness probe's result once connect() has resolved; null before. */
220
227
  get readiness(): AvatarReadinessStatus | null;
221
- /** True while the browser blocks the avatar's audio (autoplay policy): resumeAudio() from a user gesture is needed. */
228
+ /**
229
+ * True while the browser blocks the avatar's audio: by autoplay policy, or by pausing its element on its own (an interruption, the lock
230
+ * screen). resumeAudio() from a user gesture is needed.
231
+ */
222
232
  get audioBlocked(): boolean;
233
+ /** ★ BOFF-9696: true while the browser refuses the avatar's video (resumeVideo() from a user gesture plays it). */
234
+ get videoBlocked(): boolean;
223
235
  subscribe(handler: (event: AvatarTransportEvent) => void): AvatarUnsubscribe;
224
236
  private emit;
225
237
  /**
226
- * Resume audio playback. Call it synchronously from the user's click on the Avatar toggle (a user gesture is required by
227
- * the browser's autoplay policy), and again from a "tap to enable sound" control when audioPlayback reports canPlay=false.
238
+ * Resume audio playback. Call it synchronously from a user gesture (the browser's autoplay policy requires one): today the
239
+ * "tap to enable sound" control (`controller.resumeAudio()`), shown when audioPlayback reports canPlay=false.
228
240
  */
229
241
  resumeAudio(): Promise<void>;
242
+ /** ★ BOFF-9696: play a refused video. Call it synchronously from a user gesture, like resumeAudio(). */
243
+ resumeVideo(): Promise<void>;
244
+ /**
245
+ * ★ BOFF-9696 (S1): an element the browser PAUSED on its own while the avatar is on (an interruption such as a call or Siri, the lock
246
+ * screen, or WebKit pausing it when it is unmuted). The SDK only learns of a blocked playback from a failed play(), so without this the
247
+ * avatar would lip-sync with no voice and no way back. The pause reports the sound blocked; the existing tap (resumeAudio: the SDK's
248
+ * startAudio plays every attached element) plays it again, and its 'playing' reports it back. Only while ready and fed: a release
249
+ * (srcObject null) or a teardown is not a block.
250
+ */
251
+ private watchAudioElement;
230
252
  /** Mute or unmute the avatar's audio element instantly (used to silence output on cancel before the provider confirms). */
231
253
  setOutputMuted(muted: boolean): void;
232
254
  connect(params: AvatarConnectParams): Promise<void>;