@burdenoff/fe-libs 2026.1023.15 → 2026.1024.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.
- package/dist/shared/assistant/avatar/live/LiveAvatarStage.js +33 -31
- package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +51 -51
- package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +17 -1
- package/dist/shared/assistant/avatar/live/avatarSessionClient.js +81 -64
- package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +15 -3
- package/dist/shared/assistant/avatar/live/liveAvatarController.js +7 -4
- package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +2 -1
- package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +9 -3
- package/dist/shared/assistant/avatar/useAvatarCopy.js +7 -1
- package/dist/shared/assistant/i18nManifest.d.ts +4 -0
- package/dist/shared/assistant/i18nManifest.js +4 -0
- package/package.json +1 -1
|
@@ -4,39 +4,39 @@ import { useCallback as n, useEffect as r, useId as i, useRef as a, useSyncExter
|
|
|
4
4
|
import { Video as s, VideoOff as c, Volume2 as l } from "lucide-react";
|
|
5
5
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
6
6
|
//#region src/shared/assistant/avatar/live/LiveAvatarStage.tsx
|
|
7
|
-
var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-default bg-bg-elevated px-3 text-xs font-medium text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong aria-disabled:cursor-default aria-disabled:opacity-60", p = "inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border px-3 text-xs font-medium transition-colors focus-within:ring-2 focus-within:ring-border-strong", m = "inline-flex h-8 items-center rounded-full bg-action-primary-bg px-3 text-xs font-medium text-action-primary-text transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong", h = ({ controller: h, expanded: g, fallback: _, avatarNames: v }) => {
|
|
8
|
-
let y = e(), b = i(), x = i(), S =
|
|
7
|
+
var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-default bg-bg-elevated px-3 text-xs font-medium text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong aria-disabled:cursor-default aria-disabled:opacity-60", p = "inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border px-3 text-xs font-medium transition-colors focus-within:ring-2 focus-within:ring-border-strong", m = "inline-flex h-8 items-center rounded-full bg-action-primary-bg px-3 text-xs font-medium text-action-primary-text transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong aria-disabled:cursor-default aria-disabled:opacity-60", h = ({ controller: h, expanded: g, fallback: _, avatarNames: v }) => {
|
|
8
|
+
let y = e(), b = i(), x = i(), S = i(), C = a(null), w = a(null), T = n((e) => {
|
|
9
9
|
e?.focus();
|
|
10
|
-
}, []),
|
|
11
|
-
|
|
12
|
-
}), []),
|
|
10
|
+
}, []), E = n((e) => (w.current = e, () => {
|
|
11
|
+
w.current = null, document.activeElement === e && C.current?.focus();
|
|
12
|
+
}), []), D = o(h.subscribe, h.getSnapshot, h.getSnapshot);
|
|
13
13
|
r(() => (h.setStageMounted(!0), () => h.setStageMounted(!1)), [h]);
|
|
14
|
-
let { phase:
|
|
14
|
+
let { phase: O } = D, k = O === "starting" || O === "connecting" || O === "ready" || O === "ending", A = O === "checking" || O === "ending", j = Date.now(), M = D.retryAt !== null && D.retryAt > j, N = t(D, y, j), P = (e, t) => v?.[e] ?? `${y.chooserLabel} ${t + 1}`;
|
|
15
15
|
return /* @__PURE__ */ d("div", {
|
|
16
|
-
ref:
|
|
16
|
+
ref: C,
|
|
17
17
|
tabIndex: -1,
|
|
18
18
|
className: "flex flex-col items-center gap-2 focus:outline-none",
|
|
19
19
|
"data-testid": "assistant-live-avatar",
|
|
20
20
|
children: [
|
|
21
|
-
|
|
21
|
+
k ? /* @__PURE__ */ d("div", {
|
|
22
22
|
className: `relative overflow-hidden rounded-2xl border border-border-subtle bg-bg-sunken ${g ? "size-64" : "size-40"}`,
|
|
23
23
|
"data-testid": "assistant-live-avatar-frame",
|
|
24
|
-
"data-phase":
|
|
24
|
+
"data-phase": O,
|
|
25
25
|
children: [
|
|
26
26
|
/* @__PURE__ */ u("video", {
|
|
27
27
|
ref: h.attachVideo,
|
|
28
|
-
className: `size-full object-cover ${
|
|
28
|
+
className: `size-full object-cover ${D.videoReady ? "" : "invisible"}`,
|
|
29
29
|
"aria-label": y.stageLabel,
|
|
30
30
|
autoPlay: !0,
|
|
31
31
|
muted: !0,
|
|
32
32
|
playsInline: !0,
|
|
33
33
|
"data-testid": "assistant-live-avatar-video"
|
|
34
34
|
}),
|
|
35
|
-
|
|
35
|
+
D.videoReady ? null : /* @__PURE__ */ u("p", {
|
|
36
36
|
className: "absolute inset-0 flex items-center justify-center p-3 text-center text-xs text-text-muted",
|
|
37
|
-
children:
|
|
37
|
+
children: O === "ending" ? y.stateEnding : O === "connecting" ? y.stageWaiting : y.stateStarting
|
|
38
38
|
}),
|
|
39
|
-
|
|
39
|
+
D.audioBlocked ? /* @__PURE__ */ d("button", {
|
|
40
40
|
type: "button",
|
|
41
41
|
onClick: () => h.resumeAudio(),
|
|
42
42
|
className: "absolute inset-x-2 bottom-2 inline-flex items-center justify-center gap-1.5 rounded-xl bg-bg-elevated px-2 py-1.5 text-xs font-medium text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong",
|
|
@@ -48,23 +48,23 @@ var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-
|
|
|
48
48
|
}) : null
|
|
49
49
|
]
|
|
50
50
|
}) : _,
|
|
51
|
-
|
|
52
|
-
ref:
|
|
51
|
+
D.offered || k ? /* @__PURE__ */ d("button", {
|
|
52
|
+
ref: E,
|
|
53
53
|
type: "button",
|
|
54
54
|
onClick: () => {
|
|
55
|
-
|
|
55
|
+
A || (k ? h.switchOff() : O === "choosing" ? h.cancelChoice() : h.requestOn());
|
|
56
56
|
},
|
|
57
57
|
className: f,
|
|
58
|
-
"aria-pressed":
|
|
58
|
+
"aria-pressed": k,
|
|
59
59
|
title: y.toggleHint,
|
|
60
|
-
"aria-disabled":
|
|
60
|
+
"aria-disabled": A,
|
|
61
61
|
"data-testid": "assistant-live-avatar-toggle",
|
|
62
|
-
children: [u(
|
|
62
|
+
children: [u(k ? c : s, {
|
|
63
63
|
className: "size-4",
|
|
64
64
|
"aria-hidden": "true"
|
|
65
65
|
}), /* @__PURE__ */ u("span", { children: y.toggle })]
|
|
66
66
|
}) : null,
|
|
67
|
-
|
|
67
|
+
O === "choosing" ? /* @__PURE__ */ d("fieldset", {
|
|
68
68
|
className: "flex max-w-xs flex-col gap-2 rounded-xl border border-border-default bg-bg-elevated p-3 text-start",
|
|
69
69
|
"data-testid": "assistant-live-avatar-consent",
|
|
70
70
|
children: [
|
|
@@ -72,20 +72,20 @@ var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-
|
|
|
72
72
|
className: "sr-only",
|
|
73
73
|
children: y.toggle
|
|
74
74
|
}),
|
|
75
|
-
|
|
75
|
+
D.avatars.length > 1 ? /* @__PURE__ */ d("fieldset", {
|
|
76
76
|
className: "flex flex-wrap gap-1.5",
|
|
77
77
|
"data-testid": "assistant-live-avatar-chooser",
|
|
78
78
|
children: [/* @__PURE__ */ u("legend", {
|
|
79
79
|
className: "sr-only",
|
|
80
80
|
children: y.chooserTitle
|
|
81
|
-
}),
|
|
82
|
-
let n = e ===
|
|
81
|
+
}), D.avatars.map((e, t) => {
|
|
82
|
+
let n = e === D.selectedAvatarId, r = P(e, t);
|
|
83
83
|
return /* @__PURE__ */ d("label", {
|
|
84
84
|
className: `${p} ${n ? "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-default bg-bg-elevated text-text-secondary hover:bg-bg-raised hover:text-text-primary"}`,
|
|
85
85
|
title: n ? y.chooserSelected(r) : y.chooserOption(r),
|
|
86
86
|
children: [/* @__PURE__ */ u("input", {
|
|
87
87
|
type: "radio",
|
|
88
|
-
name:
|
|
88
|
+
name: S,
|
|
89
89
|
value: e,
|
|
90
90
|
checked: n,
|
|
91
91
|
onChange: () => h.selectAvatar(e),
|
|
@@ -96,7 +96,7 @@ var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-
|
|
|
96
96
|
})]
|
|
97
97
|
}) : null,
|
|
98
98
|
/* @__PURE__ */ u("p", {
|
|
99
|
-
ref:
|
|
99
|
+
ref: T,
|
|
100
100
|
id: b,
|
|
101
101
|
tabIndex: -1,
|
|
102
102
|
className: "text-xs text-text-secondary focus:outline-none",
|
|
@@ -108,18 +108,19 @@ var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-
|
|
|
108
108
|
children: [/* @__PURE__ */ u("button", {
|
|
109
109
|
type: "button",
|
|
110
110
|
onClick: () => {
|
|
111
|
-
|
|
111
|
+
w.current?.focus(), h.cancelChoice();
|
|
112
112
|
},
|
|
113
113
|
className: f,
|
|
114
114
|
children: y.consentCancel
|
|
115
115
|
}), /* @__PURE__ */ u("button", {
|
|
116
116
|
type: "button",
|
|
117
117
|
onClick: () => {
|
|
118
|
-
|
|
118
|
+
M || (w.current?.focus(), h.confirm());
|
|
119
119
|
},
|
|
120
|
-
"aria-describedby": b,
|
|
120
|
+
"aria-describedby": M ? `${b} ${x}` : b,
|
|
121
|
+
"aria-disabled": M,
|
|
121
122
|
className: m,
|
|
122
|
-
disabled:
|
|
123
|
+
disabled: D.selectedAvatarId === null,
|
|
123
124
|
"data-testid": "assistant-live-avatar-confirm",
|
|
124
125
|
children: y.consentConfirm
|
|
125
126
|
})]
|
|
@@ -127,10 +128,11 @@ var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-
|
|
|
127
128
|
]
|
|
128
129
|
}) : null,
|
|
129
130
|
/* @__PURE__ */ u("p", {
|
|
130
|
-
|
|
131
|
+
id: x,
|
|
132
|
+
className: N === null ? "sr-only" : "max-w-xs text-center text-xs text-text-muted",
|
|
131
133
|
role: "status",
|
|
132
134
|
"data-testid": "assistant-live-avatar-status",
|
|
133
|
-
children:
|
|
135
|
+
children: N ?? ""
|
|
134
136
|
})
|
|
135
137
|
]
|
|
136
138
|
});
|
|
@@ -4,10 +4,10 @@ import { useAssistantSpeech as ee } from "../../ui/AssistantSpeechProvider.js";
|
|
|
4
4
|
import { resolveAvatarVoiceMode as te } from "../avatarPreferences.js";
|
|
5
5
|
import "./liveAvatarController.js";
|
|
6
6
|
import { liveAvatarStatusText as ne } from "./liveAvatarStatus.js";
|
|
7
|
-
import { AvatarFace as
|
|
8
|
-
import { hostedAvatarVoice as
|
|
9
|
-
import { useCallback as
|
|
10
|
-
import { Volume2 as
|
|
7
|
+
import { AvatarFace as re } from "./AvatarFace.js";
|
|
8
|
+
import { hostedAvatarVoice as r } from "./avatarVoice.js";
|
|
9
|
+
import { useCallback as i, useEffect as a, useId as o, useLayoutEffect as ie, useRef as s, useState as c, useSyncExternalStore as l } from "react";
|
|
10
|
+
import { Volume2 as u } from "lucide-react";
|
|
11
11
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
12
12
|
//#region src/shared/assistant/avatar/live/LiveAvatarTabView.tsx
|
|
13
13
|
var p = "mine", m = "paired", h = "speaker:", g = /-Chirp3-HD-([A-Za-z]+)$/;
|
|
@@ -17,17 +17,17 @@ function _(e) {
|
|
|
17
17
|
}
|
|
18
18
|
function v(e, t) {
|
|
19
19
|
let n = t.find((e) => e.source === "hosted")?.lang;
|
|
20
|
-
return n === void 0 ? null :
|
|
20
|
+
return n === void 0 ? null : r(e, n, t);
|
|
21
21
|
}
|
|
22
|
-
var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted", x = "focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong", S = `inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl border border-border-default bg-bg-elevated px-4 text-sm font-medium text-text-primary transition-colors hover:bg-bg-raised aria-disabled:cursor-default aria-disabled:opacity-60 aria-disabled:hover:bg-bg-elevated ${x}`, ae = `inline-flex min-h-11 w-full items-center justify-center rounded-xl bg-action-primary-bg px-4 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover aria-disabled:cursor-default aria-disabled:opacity-60 aria-disabled:hover:bg-action-primary-bg ${x}`, oe = "relative inline-flex min-h-11 min-w-0 max-w-full cursor-pointer items-center gap-2 rounded-full border ps-1.5 pe-4 text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-border-strong", se = "absolute inset-0 m-0 size-full cursor-pointer appearance-none rounded-full opacity-0", C = ({ controller:
|
|
23
|
-
let D = n(), O = ee(), k =
|
|
24
|
-
|
|
25
|
-
if (g) return
|
|
26
|
-
}, [
|
|
27
|
-
let e =
|
|
28
|
-
e.phase === "off" && e.avatars.length === 0 &&
|
|
29
|
-
}, [
|
|
30
|
-
let { phase: I } = P, L = I === "starting" || I === "connecting" || I === "ready" || I === "ending", [R, z] =
|
|
22
|
+
var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted", x = "focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong", S = `inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl border border-border-default bg-bg-elevated px-4 text-sm font-medium text-text-primary transition-colors hover:bg-bg-raised aria-disabled:cursor-default aria-disabled:opacity-60 aria-disabled:hover:bg-bg-elevated ${x}`, ae = `inline-flex min-h-11 w-full items-center justify-center rounded-xl bg-action-primary-bg px-4 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover aria-disabled:cursor-default aria-disabled:opacity-60 aria-disabled:hover:bg-action-primary-bg ${x}`, oe = "relative inline-flex min-h-11 min-w-0 max-w-full cursor-pointer items-center gap-2 rounded-full border ps-1.5 pe-4 text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-border-strong", se = "absolute inset-0 m-0 size-full cursor-pointer appearance-none rounded-full opacity-0", C = ({ controller: r, shown: g, voice: C, handsFreeOff: ce, leaveHintId: le, startVoiceRef: ue, askLine: w, avatarNames: de, avatarImages: fe, avatarVoices: pe, defaultAvatarVoiceMode: me, voiceStore: T, now: E = Date.now }) => {
|
|
23
|
+
let D = n(), O = ee(), k = o(), he = o(), A = o(), j = o(), M = o(), N = s(null), P = l(r.subscribe, r.getSnapshot, r.getSnapshot), F = l(T.subscribe, T.getState, T.getState);
|
|
24
|
+
a(() => {
|
|
25
|
+
if (g) return r.setStageMounted(!0), () => r.setStageMounted(!1);
|
|
26
|
+
}, [r, g]), a(() => {
|
|
27
|
+
let e = r.getSnapshot();
|
|
28
|
+
e.phase === "off" && e.avatars.length === 0 && r.requestOn({ start: !1 });
|
|
29
|
+
}, [r]);
|
|
30
|
+
let { phase: I } = P, L = I === "starting" || I === "connecting" || I === "ready" || I === "ending", [R, z] = c(!1);
|
|
31
31
|
R && (I === "off" || I === "choosing" || I === "ready" || I === "ending") && z(!1);
|
|
32
32
|
let B = E(), ge = P.retryAt !== null && P.retryAt > B, V = ne(P, D, B), H = P.selectedAvatarId, U = (e) => {
|
|
33
33
|
let t = P.avatars.indexOf(e);
|
|
@@ -35,11 +35,11 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
35
35
|
}, W = I === "ready" ? { kind: "turn-off" } : I === "ending" ? { kind: "ending" } : R || I === "starting" || I === "connecting" ? { kind: "starting" } : P.offered ? {
|
|
36
36
|
kind: "turn-on",
|
|
37
37
|
ready: !ge && H !== null && (I === "choosing" || I === "off" && P.avatars.includes(H))
|
|
38
|
-
} : null, G =
|
|
39
|
-
|
|
38
|
+
} : null, G = s(null), K = W?.kind === "turn-off";
|
|
39
|
+
ie(() => {
|
|
40
40
|
G.current = K ? E() : null;
|
|
41
41
|
}, [K, E]);
|
|
42
|
-
let _e =
|
|
42
|
+
let _e = i((e) => {
|
|
43
43
|
if (e !== null) return () => {
|
|
44
44
|
e.ownerDocument.activeElement === e && N.current?.focus();
|
|
45
45
|
};
|
|
@@ -47,7 +47,7 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
47
47
|
if (W !== null) switch (W.kind) {
|
|
48
48
|
case "turn-on":
|
|
49
49
|
if (!W.ready || H === null) return;
|
|
50
|
-
z(!0), I === "choosing" ?
|
|
50
|
+
z(!0), I === "choosing" ? r.confirm() : r.requestOn({
|
|
51
51
|
start: !0,
|
|
52
52
|
avatarId: H
|
|
53
53
|
});
|
|
@@ -55,26 +55,26 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
55
55
|
case "turn-off": {
|
|
56
56
|
let e = G.current;
|
|
57
57
|
if (e !== null && E() - e < 1e3) return;
|
|
58
|
-
|
|
58
|
+
r.switchOff();
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
case "starting":
|
|
62
62
|
case "ending": return;
|
|
63
63
|
}
|
|
64
|
-
},
|
|
64
|
+
}, ye = te(F, me), q = H === null ? null : pe.get(H) ?? null, J = ye === "speaker" && F.avatarSpeaker !== null ? `${h}${F.avatarSpeaker}` : ye === "paired" && q !== null ? m : p, be = J === m ? q : J.startsWith(h) ? F.avatarSpeaker : null, Y = J === p ? O.activeVoiceName : be === null ? null : v(be, O.voiceChoices)?.name ?? null, xe = O.speakingMessageId !== null || O.spokenChunks !== null, Se = Y !== null && !C.live && !xe, Ce = O.activeVoiceName ? D.voiceModeMineNamed(_(O.activeVoiceName)) : D.voiceModeMine, we = (e) => {
|
|
65
65
|
if (e === p || e === m) {
|
|
66
66
|
T.setVoiceMode(e);
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
let n = e.slice(8);
|
|
70
70
|
e.startsWith(h) && t(n) && T.setAvatarSpeaker(n);
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
}, []),
|
|
71
|
+
}, Te = I === "ending" ? D.stateEnding : I === "connecting" || I === "ready" ? D.stageWaiting : I === "starting" ? D.stateStarting : D.stateOff, [Ee, De] = c(() => /* @__PURE__ */ new Set()), Oe = i((e) => {
|
|
72
|
+
De((t) => t.has(e) ? t : new Set(t).add(e));
|
|
73
|
+
}, []), ke = (e) => {
|
|
74
74
|
let t = fe?.get(e);
|
|
75
|
-
return t === void 0 ||
|
|
76
|
-
},
|
|
77
|
-
return /* @__PURE__ */ f("div", {
|
|
75
|
+
return t === void 0 || Ee.has(t) ? null : t;
|
|
76
|
+
}, X = !L && H !== null ? ke(H) : null, Z = L ? null : w === void 0 ? C.impossible ? D.askNeedsVoice : null : w === "by-typing" ? D.askByTyping : w === "needs-voice" ? D.askNeedsVoice : w === "nothing" ? D.askNothing : null, Ae = I === "off" && P.avatars.length === 0 && P.offered, [Q, je] = c(!1), $ = Ae && (Q || P.problem !== null);
|
|
77
|
+
return $ !== Q && je($), /* @__PURE__ */ f("div", {
|
|
78
78
|
ref: N,
|
|
79
79
|
tabIndex: -1,
|
|
80
80
|
className: "flex min-h-full shrink-0 flex-col gap-3 focus:outline-none",
|
|
@@ -88,7 +88,7 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
88
88
|
"data-phase": I,
|
|
89
89
|
children: [
|
|
90
90
|
L ? /* @__PURE__ */ d("video", {
|
|
91
|
-
ref:
|
|
91
|
+
ref: r.attachVideo,
|
|
92
92
|
className: `size-full object-cover ${P.videoReady ? "" : "invisible"}`,
|
|
93
93
|
"aria-label": D.stageLabel,
|
|
94
94
|
autoPlay: !0,
|
|
@@ -96,23 +96,23 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
96
96
|
playsInline: !0,
|
|
97
97
|
"data-testid": "assistant-avatar-tab-video"
|
|
98
98
|
}) : null,
|
|
99
|
-
|
|
99
|
+
X !== null && H !== null ? /* @__PURE__ */ d(re, {
|
|
100
100
|
size: "frame",
|
|
101
101
|
name: U(H),
|
|
102
|
-
src:
|
|
103
|
-
onError:
|
|
102
|
+
src: X,
|
|
103
|
+
onError: Oe
|
|
104
104
|
}) : null,
|
|
105
105
|
L && P.videoReady ? null : /* @__PURE__ */ d("p", {
|
|
106
|
-
className:
|
|
106
|
+
className: X === null ? "absolute inset-0 flex items-center justify-center p-3 text-center text-sm text-text-secondary" : "absolute inset-x-0 bottom-0 bg-bg-elevated/90 px-3 py-1.5 text-center text-xs text-text-secondary",
|
|
107
107
|
"data-testid": "assistant-avatar-tab-frame-text",
|
|
108
|
-
children:
|
|
108
|
+
children: Te
|
|
109
109
|
}),
|
|
110
110
|
P.audioBlocked ? /* @__PURE__ */ f("button", {
|
|
111
111
|
type: "button",
|
|
112
|
-
onClick: () =>
|
|
112
|
+
onClick: () => r.resumeAudio(),
|
|
113
113
|
className: `absolute inset-x-2 bottom-2 inline-flex min-h-11 items-center justify-center gap-1.5 rounded-xl bg-bg-elevated px-3 text-xs font-medium text-text-primary ${x}`,
|
|
114
114
|
"data-testid": "assistant-avatar-tab-sound",
|
|
115
|
-
children: [/* @__PURE__ */ d(
|
|
115
|
+
children: [/* @__PURE__ */ d(u, {
|
|
116
116
|
className: "size-4 shrink-0",
|
|
117
117
|
"aria-hidden": "true"
|
|
118
118
|
}), /* @__PURE__ */ d("span", { children: D.problem("playback-blocked") })]
|
|
@@ -145,16 +145,16 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
145
145
|
name: he,
|
|
146
146
|
value: e,
|
|
147
147
|
checked: t,
|
|
148
|
-
onChange: () =>
|
|
148
|
+
onChange: () => r.selectAvatar(e),
|
|
149
149
|
className: se,
|
|
150
150
|
"data-avatar-id": e
|
|
151
151
|
}),
|
|
152
|
-
/* @__PURE__ */ d(
|
|
152
|
+
/* @__PURE__ */ d(re, {
|
|
153
153
|
size: "pill",
|
|
154
154
|
name: n,
|
|
155
|
-
src:
|
|
155
|
+
src: ke(e),
|
|
156
156
|
selected: t,
|
|
157
|
-
onError:
|
|
157
|
+
onError: Oe
|
|
158
158
|
}),
|
|
159
159
|
/* @__PURE__ */ d("span", {
|
|
160
160
|
className: "truncate",
|
|
@@ -197,19 +197,19 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
197
197
|
className: "flex items-center gap-2",
|
|
198
198
|
children: [/* @__PURE__ */ f("select", {
|
|
199
199
|
id: A,
|
|
200
|
-
value:
|
|
201
|
-
onChange: (e) =>
|
|
200
|
+
value: J,
|
|
201
|
+
onChange: (e) => we(e.target.value),
|
|
202
202
|
"aria-describedby": j,
|
|
203
203
|
className: `min-h-11 min-w-0 flex-1 rounded-xl border border-border-default bg-bg-elevated px-3 text-sm text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary ${x}`,
|
|
204
204
|
"data-testid": "assistant-avatar-tab-voice",
|
|
205
205
|
children: [
|
|
206
206
|
/* @__PURE__ */ d("option", {
|
|
207
207
|
value: p,
|
|
208
|
-
children:
|
|
208
|
+
children: Ce
|
|
209
209
|
}),
|
|
210
|
-
|
|
210
|
+
q === null ? null : /* @__PURE__ */ d("option", {
|
|
211
211
|
value: m,
|
|
212
|
-
children: D.voiceModeDefaultNamed(
|
|
212
|
+
children: D.voiceModeDefaultNamed(q)
|
|
213
213
|
}),
|
|
214
214
|
e.map((e) => /* @__PURE__ */ d("option", {
|
|
215
215
|
value: `${h}${e}`,
|
|
@@ -219,14 +219,14 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
219
219
|
}), /* @__PURE__ */ d("button", {
|
|
220
220
|
type: "button",
|
|
221
221
|
onClick: () => {
|
|
222
|
-
|
|
222
|
+
Se && Y !== null && O.previewVoice(Y);
|
|
223
223
|
},
|
|
224
|
-
"aria-disabled": !
|
|
224
|
+
"aria-disabled": !Se,
|
|
225
225
|
"aria-label": D.hearVoice,
|
|
226
226
|
title: D.hearVoice,
|
|
227
227
|
className: `inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-border-default bg-bg-elevated text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary aria-disabled:cursor-default aria-disabled:opacity-60 ${x}`,
|
|
228
228
|
"data-testid": "assistant-avatar-tab-hear",
|
|
229
|
-
children: /* @__PURE__ */ d(
|
|
229
|
+
children: /* @__PURE__ */ d(u, {
|
|
230
230
|
className: "size-5",
|
|
231
231
|
"aria-hidden": "true"
|
|
232
232
|
})
|
|
@@ -256,9 +256,9 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
256
256
|
"data-slot": W.kind,
|
|
257
257
|
children: W.kind === "turn-off" ? D.stop : W.kind === "starting" ? D.turnOnStarting : W.kind === "ending" ? D.stateEnding : D.consentConfirm
|
|
258
258
|
}),
|
|
259
|
-
|
|
259
|
+
$ ? /* @__PURE__ */ d("button", {
|
|
260
260
|
type: "button",
|
|
261
|
-
onClick: () =>
|
|
261
|
+
onClick: () => r.requestOn({ start: !1 }),
|
|
262
262
|
className: S,
|
|
263
263
|
"data-testid": "assistant-avatar-tab-retry",
|
|
264
264
|
children: D.chooserRetry
|
|
@@ -276,15 +276,15 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
276
276
|
className: S,
|
|
277
277
|
"data-testid": "assistant-avatar-tab-start-voice",
|
|
278
278
|
children: D.startVoice
|
|
279
|
-
}), C.blocked === null ?
|
|
279
|
+
}), C.blocked === null ? Z === null ? null : /* @__PURE__ */ d("p", {
|
|
280
280
|
className: b,
|
|
281
281
|
"data-testid": "assistant-avatar-tab-ask-note",
|
|
282
|
-
children:
|
|
282
|
+
children: Z
|
|
283
283
|
}) : /* @__PURE__ */ d("p", {
|
|
284
284
|
id: M,
|
|
285
285
|
className: b,
|
|
286
286
|
"data-testid": "assistant-avatar-tab-voice-blocked",
|
|
287
|
-
children:
|
|
287
|
+
children: Z === null ? C.blocked : `${C.blocked} ${Z}`
|
|
288
288
|
})]
|
|
289
289
|
}),
|
|
290
290
|
ce && !P.voiceLive ? /* @__PURE__ */ d("p", {
|
|
@@ -12,6 +12,13 @@ export declare function avatarApiBaseFromTtsEndpoint(ttsEndpoint: string | null
|
|
|
12
12
|
*/
|
|
13
13
|
declare const AVAILABILITY_REASONS: readonly ["workspace_context_required", "not_enabled", "not_permitted", "unavailable"];
|
|
14
14
|
export type AvatarAvailabilityReason = (typeof AVAILABILITY_REASONS)[number];
|
|
15
|
+
/**
|
|
16
|
+
* Whose budget a used-up credit cap names, in the error body's `scope` (the speech service's `BudgetScope`): the person's own, their
|
|
17
|
+
* workspace's, or everyone's together (the environment's daily or monthly budget). Only 429 `budget_exhausted` carries one, and a
|
|
18
|
+
* server that does not send it yet (today's) leaves it out.
|
|
19
|
+
*/
|
|
20
|
+
declare const BUDGET_SCOPES: readonly ["person", "workspace", "everyone"];
|
|
21
|
+
export type AvatarBudgetScope = (typeof BUDGET_SCOPES)[number];
|
|
15
22
|
export interface AvatarAvailability {
|
|
16
23
|
readonly available: boolean;
|
|
17
24
|
readonly reason: AvatarAvailabilityReason | null;
|
|
@@ -49,9 +56,13 @@ export declare class AvatarApiError extends Error {
|
|
|
49
56
|
/** The server's or the gateway's code, `invalid_response` for a body we refused, `network` for no answer. */
|
|
50
57
|
readonly code: string;
|
|
51
58
|
readonly retryAfterSeconds: number | null;
|
|
59
|
+
/** On `budget_exhausted` only, whose budget the body names when it is one of the three we know; null in every other answer. */
|
|
60
|
+
readonly scope: AvatarBudgetScope | null;
|
|
52
61
|
constructor(status: number,
|
|
53
62
|
/** The server's or the gateway's code, `invalid_response` for a body we refused, `network` for no answer. */
|
|
54
|
-
code: string, retryAfterSeconds: number | null
|
|
63
|
+
code: string, retryAfterSeconds: number | null,
|
|
64
|
+
/** 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);
|
|
55
66
|
}
|
|
56
67
|
export interface AvatarSessionClientOptions {
|
|
57
68
|
/** `https://<gateway>/api/avatar/v1` (avatarApiBaseFromTtsEndpoint). */
|
|
@@ -104,6 +115,11 @@ export interface AvatarRefusal {
|
|
|
104
115
|
* in seconds. Absent when it said nothing: the person is then told no time, and nothing waits for one.
|
|
105
116
|
*/
|
|
106
117
|
readonly retryAfterSeconds?: number;
|
|
118
|
+
/**
|
|
119
|
+
* With `budget-exhausted` only: whose budget is used up, when the server said (the person's own, their workspace's, or everyone's).
|
|
120
|
+
* Absent when it named none (today's server) and with every other refusal: the person is then told the general line.
|
|
121
|
+
*/
|
|
122
|
+
readonly scope?: AvatarBudgetScope;
|
|
107
123
|
}
|
|
108
124
|
/**
|
|
109
125
|
* 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
|
|
@@ -19,15 +19,20 @@ var o = [
|
|
|
19
19
|
"not_enabled",
|
|
20
20
|
"not_permitted",
|
|
21
21
|
"unavailable"
|
|
22
|
-
], s =
|
|
22
|
+
], s = [
|
|
23
|
+
"person",
|
|
24
|
+
"workspace",
|
|
25
|
+
"everyone"
|
|
26
|
+
], c = class extends Error {
|
|
23
27
|
status;
|
|
24
28
|
code;
|
|
25
29
|
retryAfterSeconds;
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
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";
|
|
28
33
|
}
|
|
29
34
|
};
|
|
30
|
-
function
|
|
35
|
+
function l(e = globalThis.crypto) {
|
|
31
36
|
if (e && typeof e.randomUUID == "function") return e.randomUUID();
|
|
32
37
|
let t = new Uint8Array(16);
|
|
33
38
|
if (e && typeof e.getRandomValues == "function") e.getRandomValues(t);
|
|
@@ -36,38 +41,43 @@ function c(e = globalThis.crypto) {
|
|
|
36
41
|
let n = [...t].map((e) => e.toString(16).padStart(2, "0")).join("");
|
|
37
42
|
return `${n.slice(0, 8)}-${n.slice(8, 12)}-${n.slice(12, 16)}-${n.slice(16, 20)}-${n.slice(20)}`;
|
|
38
43
|
}
|
|
39
|
-
function
|
|
44
|
+
function u(e) {
|
|
40
45
|
return typeof e == "object" && !!e && !Array.isArray(e);
|
|
41
46
|
}
|
|
42
|
-
function
|
|
47
|
+
function d(e, t) {
|
|
43
48
|
let n = e.headers.get("retry-after"), r = n === null || n.trim() === "" ? NaN : Number(n);
|
|
44
49
|
if (Number.isFinite(r) && r >= 0) return r;
|
|
45
|
-
let i =
|
|
50
|
+
let i = u(t) && u(t.error) ? Number(t.error.retryAfterSeconds) : NaN;
|
|
46
51
|
return Number.isFinite(i) && i >= 0 ? i : null;
|
|
47
52
|
}
|
|
48
|
-
async function
|
|
53
|
+
async function f(e) {
|
|
49
54
|
try {
|
|
50
55
|
return await e.json();
|
|
51
56
|
} catch {
|
|
52
57
|
return null;
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
function p(e, t) {
|
|
61
|
+
if (e !== "budget_exhausted" || !u(t) || !u(t.error)) return null;
|
|
62
|
+
let { scope: n } = t.error;
|
|
63
|
+
return s.find((e) => e === n) ?? null;
|
|
58
64
|
}
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
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));
|
|
68
|
+
}
|
|
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;
|
|
62
72
|
return {
|
|
63
|
-
available:
|
|
64
|
-
reason:
|
|
73
|
+
available: s,
|
|
74
|
+
reason: s ? null : n ?? "not_enabled",
|
|
65
75
|
avatars: t,
|
|
66
76
|
consentVersion: i,
|
|
67
77
|
maxSessionSeconds: a
|
|
68
78
|
};
|
|
69
79
|
}
|
|
70
|
-
function
|
|
80
|
+
function g(e) {
|
|
71
81
|
try {
|
|
72
82
|
let n = new URL(e);
|
|
73
83
|
return n.protocol === "wss:" ? !0 : n.protocol === "ws:" && t.has(n.hostname.toLowerCase());
|
|
@@ -75,20 +85,20 @@ function m(e) {
|
|
|
75
85
|
return !1;
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
|
-
function
|
|
79
|
-
if (!
|
|
80
|
-
let { sessionId: t, livekitUrl: r, participantToken: i, avatarIdentity: a, connectBy: o, endsAt:
|
|
81
|
-
if (typeof t != "string" || !n.test(t) || typeof r != "string" || !
|
|
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);
|
|
82
92
|
return {
|
|
83
93
|
sessionId: t,
|
|
84
94
|
livekitUrl: r,
|
|
85
95
|
participantToken: i,
|
|
86
96
|
avatarIdentity: a,
|
|
87
|
-
connectBy:
|
|
97
|
+
connectBy: l,
|
|
88
98
|
endsAt: d
|
|
89
99
|
};
|
|
90
100
|
}
|
|
91
|
-
var
|
|
101
|
+
var v = [
|
|
92
102
|
"starting",
|
|
93
103
|
"ready",
|
|
94
104
|
"ending",
|
|
@@ -96,22 +106,22 @@ var g = [
|
|
|
96
106
|
"failed",
|
|
97
107
|
"canceled"
|
|
98
108
|
];
|
|
99
|
-
function
|
|
100
|
-
if (!
|
|
109
|
+
function y(e, t) {
|
|
110
|
+
if (!u(e) || !v.includes(e.state)) throw new c(200, "invalid_response", null);
|
|
101
111
|
return {
|
|
102
112
|
sessionId: t,
|
|
103
113
|
state: e.state,
|
|
104
114
|
endedReason: typeof e.endedReason == "string" ? e.endedReason : null
|
|
105
115
|
};
|
|
106
116
|
}
|
|
107
|
-
var
|
|
117
|
+
var b = (e) => new Promise((t) => {
|
|
108
118
|
setTimeout(t, e);
|
|
109
119
|
});
|
|
110
|
-
function
|
|
111
|
-
let t = e.fetchImpl ?? ((e, t) => fetch(e, t)), a = e.sleep ??
|
|
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(/\/+$/, "");
|
|
112
122
|
async function u(t, n = null) {
|
|
113
123
|
let r = e.getAuthorization();
|
|
114
|
-
if (!r) throw new
|
|
124
|
+
if (!r) throw new c(401, "unauthenticated", null);
|
|
115
125
|
let i = { authorization: r };
|
|
116
126
|
if (t) {
|
|
117
127
|
let t = await e.getWorkspaceAuthorization(n === null ? { fresh: !1 } : {
|
|
@@ -123,7 +133,7 @@ function y(e) {
|
|
|
123
133
|
}
|
|
124
134
|
return i;
|
|
125
135
|
}
|
|
126
|
-
async function
|
|
136
|
+
async function d(e, n) {
|
|
127
137
|
try {
|
|
128
138
|
return await t(e, {
|
|
129
139
|
...n,
|
|
@@ -132,20 +142,20 @@ function y(e) {
|
|
|
132
142
|
redirect: "error"
|
|
133
143
|
});
|
|
134
144
|
} catch (e) {
|
|
135
|
-
throw n.signal?.aborted ? e : new
|
|
145
|
+
throw n.signal?.aborted ? e : new c(0, "network", null);
|
|
136
146
|
}
|
|
137
147
|
}
|
|
138
148
|
return {
|
|
139
149
|
async availability(e) {
|
|
140
150
|
let t = null;
|
|
141
151
|
for (;;) {
|
|
142
|
-
let n = await u(!0, t), r = await
|
|
152
|
+
let n = await u(!0, t), r = await d(`${l}/availability`, {
|
|
143
153
|
method: "GET",
|
|
144
154
|
headers: n,
|
|
145
155
|
signal: e
|
|
146
156
|
});
|
|
147
|
-
if (r.ok) return
|
|
148
|
-
let i = await
|
|
157
|
+
if (r.ok) return h(await f(r));
|
|
158
|
+
let i = await m(r);
|
|
149
159
|
if (r.status === 401 && t === null) {
|
|
150
160
|
t = {
|
|
151
161
|
error: i,
|
|
@@ -157,16 +167,16 @@ function y(e) {
|
|
|
157
167
|
}
|
|
158
168
|
},
|
|
159
169
|
async create(e, t) {
|
|
160
|
-
if (!r.test(e.avatarId) || !i.test(e.idempotencyKey)) throw new
|
|
170
|
+
if (!r.test(e.avatarId) || !i.test(e.idempotencyKey)) throw new c(400, "invalid_request", null);
|
|
161
171
|
let n = JSON.stringify({
|
|
162
172
|
avatarId: e.avatarId,
|
|
163
173
|
consentVersion: e.consentVersion
|
|
164
|
-
}), p = o(),
|
|
174
|
+
}), p = o(), h = null, g = !1, v = !1, y = null;
|
|
165
175
|
for (;;) {
|
|
166
|
-
let r = await u(!0,
|
|
167
|
-
if (
|
|
168
|
-
if (r["x-workspace-authorization"] === void 0) throw new
|
|
169
|
-
let i = await
|
|
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`, {
|
|
170
180
|
method: "POST",
|
|
171
181
|
headers: {
|
|
172
182
|
...r,
|
|
@@ -176,10 +186,10 @@ function y(e) {
|
|
|
176
186
|
body: n,
|
|
177
187
|
signal: t
|
|
178
188
|
});
|
|
179
|
-
if (i.status === 201) return
|
|
180
|
-
let b = await
|
|
181
|
-
if (i.status === 401 && !
|
|
182
|
-
|
|
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 = {
|
|
183
193
|
error: b,
|
|
184
194
|
workspace: r["x-workspace-authorization"]
|
|
185
195
|
};
|
|
@@ -187,7 +197,7 @@ function y(e) {
|
|
|
187
197
|
}
|
|
188
198
|
if (i.status === 409 && b.code === "start_in_progress") {
|
|
189
199
|
let e = Math.max(250, (b.retryAfterSeconds ?? 1) * 1e3);
|
|
190
|
-
if (o() - p + e >
|
|
200
|
+
if (o() - p + e > s || (await a(e), t?.aborted)) throw b;
|
|
191
201
|
y = b;
|
|
192
202
|
continue;
|
|
193
203
|
}
|
|
@@ -199,18 +209,18 @@ function y(e) {
|
|
|
199
209
|
}
|
|
200
210
|
},
|
|
201
211
|
async status(e, t) {
|
|
202
|
-
if (!n.test(e)) throw new
|
|
203
|
-
let r = await u(!1), i = await
|
|
212
|
+
if (!n.test(e)) throw new c(400, "invalid_request", null);
|
|
213
|
+
let r = await u(!1), i = await d(`${l}/sessions/${e}`, {
|
|
204
214
|
method: "GET",
|
|
205
215
|
headers: r,
|
|
206
216
|
signal: t
|
|
207
217
|
});
|
|
208
|
-
if (!i.ok) throw await
|
|
209
|
-
return
|
|
218
|
+
if (!i.ok) throw await m(i);
|
|
219
|
+
return y(await f(i), e);
|
|
210
220
|
},
|
|
211
221
|
async end(e, { reason: t, keepalive: r = !1 }) {
|
|
212
|
-
if (!n.test(e)) throw new
|
|
213
|
-
let i = await u(!1), a = await
|
|
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`, {
|
|
214
224
|
method: "POST",
|
|
215
225
|
headers: {
|
|
216
226
|
...i,
|
|
@@ -219,28 +229,28 @@ function y(e) {
|
|
|
219
229
|
body: JSON.stringify({ reason: t }),
|
|
220
230
|
keepalive: r
|
|
221
231
|
});
|
|
222
|
-
if (!a.ok) throw await
|
|
223
|
-
return
|
|
232
|
+
if (!a.ok) throw await m(a);
|
|
233
|
+
return y(await f(a), e);
|
|
224
234
|
}
|
|
225
235
|
};
|
|
226
236
|
}
|
|
227
|
-
var
|
|
237
|
+
var S = new Set([
|
|
228
238
|
"capacity",
|
|
229
239
|
"rate-limited",
|
|
230
240
|
"budget-exhausted"
|
|
231
241
|
]);
|
|
232
|
-
function
|
|
233
|
-
if (!(e instanceof
|
|
242
|
+
function C(e) {
|
|
243
|
+
if (!(e instanceof c)) return {
|
|
234
244
|
problem: "connection",
|
|
235
245
|
hide: !1
|
|
236
246
|
};
|
|
237
|
-
let t =
|
|
238
|
-
return t.problem !== null &&
|
|
247
|
+
let t = w(e);
|
|
248
|
+
return t.problem !== null && S.has(t.problem) && e.retryAfterSeconds !== null ? {
|
|
239
249
|
...t,
|
|
240
250
|
retryAfterSeconds: e.retryAfterSeconds
|
|
241
251
|
} : t;
|
|
242
252
|
}
|
|
243
|
-
function
|
|
253
|
+
function w(e) {
|
|
244
254
|
if (e.status === 0) return {
|
|
245
255
|
problem: "connection",
|
|
246
256
|
hide: !1
|
|
@@ -294,19 +304,26 @@ function S(e) {
|
|
|
294
304
|
problem: "paused",
|
|
295
305
|
hide: !1
|
|
296
306
|
};
|
|
297
|
-
case "budget_exhausted": return {
|
|
307
|
+
case "budget_exhausted": return e.scope === null ? {
|
|
298
308
|
problem: "budget-exhausted",
|
|
299
309
|
hide: !1
|
|
310
|
+
} : {
|
|
311
|
+
problem: "budget-exhausted",
|
|
312
|
+
hide: !1,
|
|
313
|
+
scope: e.scope
|
|
300
314
|
};
|
|
301
315
|
case "avatar_unavailable": return {
|
|
302
316
|
problem: "unavailable",
|
|
303
317
|
hide: !1
|
|
304
318
|
};
|
|
305
|
-
case "provider_unavailable":
|
|
306
|
-
case "provider_out_of_credits": return {
|
|
319
|
+
case "provider_unavailable": return {
|
|
307
320
|
problem: "provider",
|
|
308
321
|
hide: !1
|
|
309
322
|
};
|
|
323
|
+
case "provider_out_of_credits": return {
|
|
324
|
+
problem: "provider-out-of-credits",
|
|
325
|
+
hide: !1
|
|
326
|
+
};
|
|
310
327
|
case "authentication_required":
|
|
311
328
|
case "unauthenticated":
|
|
312
329
|
case "untrusted_caller": return {
|
|
@@ -328,7 +345,7 @@ function S(e) {
|
|
|
328
345
|
};
|
|
329
346
|
}
|
|
330
347
|
}
|
|
331
|
-
function
|
|
348
|
+
function T(e) {
|
|
332
349
|
return e === "unavailable" ? {
|
|
333
350
|
problem: "capacity",
|
|
334
351
|
hide: !1
|
|
@@ -338,4 +355,4 @@ function C(e) {
|
|
|
338
355
|
};
|
|
339
356
|
}
|
|
340
357
|
//#endregion
|
|
341
|
-
export { e as AVATAR_API_PATH,
|
|
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 };
|
|
@@ -4,7 +4,7 @@ import { AvatarMeterHandle } from './avatarAudioMeter';
|
|
|
4
4
|
import { AvatarRoom, AvatarScheduler } from './avatarPorts';
|
|
5
5
|
import { AvatarTrackerConfig } from './avatarPlaybackTracker';
|
|
6
6
|
import { AvatarDecodedPiece, AvatarNotice, AvatarReplyOutputs } from './avatarReplyOutputs';
|
|
7
|
-
import { AvatarClientEndReason, AvatarSessionClient } from './avatarSessionClient';
|
|
7
|
+
import { AvatarBudgetScope, AvatarClientEndReason, AvatarSessionClient } from './avatarSessionClient';
|
|
8
8
|
import { AvatarReadinessProbeOptions } from './avatarTransport';
|
|
9
9
|
/** End after this long without a reply on the avatar while it is on (OD8, BACKEND-DESIGN §8.7). */
|
|
10
10
|
export declare const LIVE_AVATAR_IDLE_MS = 120000;
|
|
@@ -46,7 +46,9 @@ export interface LiveAvatarSnapshot {
|
|
|
46
46
|
readonly tabOffered: boolean;
|
|
47
47
|
/**
|
|
48
48
|
* After a "not now" refusal that said when (the server's Retry-After): the wall-clock ms a start may be tried again. Null otherwise.
|
|
49
|
-
* It goes with `problem`: cleared together, and both clear by themselves when the time comes.
|
|
49
|
+
* It goes with `problem`: cleared together, and both clear by themselves when the time comes. A newer refusal (including a question
|
|
50
|
+
* that failed or was answered "not offered"), a workspace switch and a voice conversation starting outside the Avatar tab clear them
|
|
51
|
+
* earlier.
|
|
50
52
|
*/
|
|
51
53
|
readonly retryAt: number | null;
|
|
52
54
|
/** The avatar was turned on outside the tab during the voice conversation that is still live: closing the tab hands it back to Voice. */
|
|
@@ -64,6 +66,11 @@ export interface LiveAvatarSnapshot {
|
|
|
64
66
|
/** What the person must read before the avatar starts; null until the server said which version. */
|
|
65
67
|
readonly consentVersion: string | null;
|
|
66
68
|
readonly problem: AvatarProblem | null;
|
|
69
|
+
/**
|
|
70
|
+
* With the problem `budget-exhausted`, whose budget is used up when the server said (BOFF-9333): the person's own, their workspace's,
|
|
71
|
+
* or everyone's. Null otherwise: no scope sent (today's server), or another problem. It goes with `problem`, as `retryAt` does.
|
|
72
|
+
*/
|
|
73
|
+
readonly budgetScope: AvatarBudgetScope | null;
|
|
67
74
|
readonly notice: LiveAvatarNotice | null;
|
|
68
75
|
/** The browser blocks the avatar's sound; a tap on the stage (`resumeAudio`) lifts it. */
|
|
69
76
|
readonly audioBlocked: boolean;
|
|
@@ -132,6 +139,8 @@ export interface LiveAvatarRequestOptions {
|
|
|
132
139
|
* and there is nothing to choose. `false`: the chooser only, never a start (the tab opening, "Try again", a workspace switch).
|
|
133
140
|
* `true`: the person's Turn on, with the disclosure on screen: start with `avatarId` (else the one selected) once the answer comes,
|
|
134
141
|
* only if it is still offered and the disclosure version is the one shown at the click; otherwise the chooser.
|
|
142
|
+
* While the snapshot carries a refusal's retry time (`retryAt`) that has not come, no mode starts: `true` asks nothing then; the other
|
|
143
|
+
* modes still ask (it is free) and keep the refusal with its time, and the chooser they reach waits for that time.
|
|
135
144
|
*/
|
|
136
145
|
readonly start?: boolean;
|
|
137
146
|
readonly avatarId?: string;
|
|
@@ -143,7 +152,10 @@ export interface LiveAvatarController {
|
|
|
143
152
|
setVoiceLive(live: boolean): void;
|
|
144
153
|
/** The person asked for the avatar: ask the server whether it is offered, then show the chooser and the disclosure (`options`). */
|
|
145
154
|
requestOn(options?: LiveAvatarRequestOptions): void;
|
|
146
|
-
/**
|
|
155
|
+
/**
|
|
156
|
+
* The person confirmed the disclosure: start a session with the chosen avatar. Does nothing while the snapshot carries a refusal's retry
|
|
157
|
+
* time (`retryAt`) that has not come.
|
|
158
|
+
*/
|
|
147
159
|
confirm(): void;
|
|
148
160
|
/**
|
|
149
161
|
* The Avatar tab opened or closed (BOFF-9341). Open: the avatar may be asked for with no voice conversation, and an avatar already on
|
|
@@ -16,6 +16,7 @@ var s = 12e4, c = 1e4, l = 8e3, ee = 5e3, u = 2e4, d = 1e3, f = {
|
|
|
16
16
|
selectedAvatarId: null,
|
|
17
17
|
consentVersion: null,
|
|
18
18
|
problem: null,
|
|
19
|
+
budgetScope: null,
|
|
19
20
|
notice: null,
|
|
20
21
|
audioBlocked: !1,
|
|
21
22
|
videoReady: !1
|
|
@@ -84,7 +85,7 @@ function g(s) {
|
|
|
84
85
|
...v,
|
|
85
86
|
...e
|
|
86
87
|
};
|
|
87
|
-
e.problem !== void 0 && e.retryAt === void 0 &&
|
|
88
|
+
e.problem !== void 0 && e.problem !== v.problem && (e.retryAt === void 0 && (t.retryAt = null), e.budgetScope === void 0 && (t.budgetScope = null));
|
|
88
89
|
let n = {
|
|
89
90
|
...t,
|
|
90
91
|
offered: ne(t.voiceLive, t.tabOpen),
|
|
@@ -208,7 +209,8 @@ function g(s) {
|
|
|
208
209
|
return V({
|
|
209
210
|
phase: "off",
|
|
210
211
|
problem: t.problem,
|
|
211
|
-
retryAt: r
|
|
212
|
+
retryAt: r,
|
|
213
|
+
budgetScope: t.scope ?? null
|
|
212
214
|
}), r !== null && v.retryAt === r && (I = c.setTimeout(() => {
|
|
213
215
|
I = null, v.retryAt === r && V({
|
|
214
216
|
problem: null,
|
|
@@ -220,6 +222,7 @@ function g(s) {
|
|
|
220
222
|
e.hide && (b = !0), C = !1, M = !1, N = null, U(), V({
|
|
221
223
|
phase: "off",
|
|
222
224
|
problem: e.problem,
|
|
225
|
+
retryAt: null,
|
|
223
226
|
avatars: []
|
|
224
227
|
});
|
|
225
228
|
}
|
|
@@ -471,7 +474,7 @@ function g(s) {
|
|
|
471
474
|
let o = ++T;
|
|
472
475
|
V({
|
|
473
476
|
phase: "checking",
|
|
474
|
-
problem: null,
|
|
477
|
+
problem: B() ? v.problem : null,
|
|
475
478
|
notice: null
|
|
476
479
|
}), Y((e) => i.availability(e)).then((e) => {
|
|
477
480
|
if (T !== o) return;
|
|
@@ -500,7 +503,7 @@ function g(s) {
|
|
|
500
503
|
});
|
|
501
504
|
},
|
|
502
505
|
confirm() {
|
|
503
|
-
if (v.phase !== "choosing" || !(v.voiceLive || j)) return;
|
|
506
|
+
if (v.phase !== "choosing" || !(v.voiceLive || j) || B()) return;
|
|
504
507
|
let e = v.selectedAvatarId, t = v.consentVersion;
|
|
505
508
|
e === null || t === null || (x = t, l.setAvatarId(e), C = !0, M = !j, j && (N ??= c.now()), U(), ce(e, t));
|
|
506
509
|
},
|
|
@@ -14,7 +14,8 @@ function t(e, t, n = Date.now()) {
|
|
|
14
14
|
if (e.problem !== null) {
|
|
15
15
|
let i = e.retryAt, a = i !== null && i > n, o = t.problem(e.problem, {
|
|
16
16
|
voiceLive: r,
|
|
17
|
-
timed: a
|
|
17
|
+
timed: a,
|
|
18
|
+
budgetScope: e.budgetScope
|
|
18
19
|
});
|
|
19
20
|
return a ? `${o} ${t.availableAgain(t.retryTime(i, n))}` : o;
|
|
20
21
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { AvatarAskBlocker } from '../ui/avatar/avatarAskGate';
|
|
2
2
|
import { VoiceStartBlocker } from '../ui/voice/voiceStartGate';
|
|
3
|
+
import { AvatarBudgetScope } from './live/avatarSessionClient';
|
|
3
4
|
/** What the person can be told went wrong; the adapter maps server codes onto these. */
|
|
4
5
|
export type AvatarProblem = 'unavailable' | 'not-ready' | 'already-running' | 'capacity' | 'rate-limited' | 'paused'
|
|
5
|
-
/** The avatar time allowed for now (
|
|
6
|
-
| 'budget-exhausted' | 'start-timeout' | 'provider'
|
|
6
|
+
/** The avatar time allowed for now (a credit cap) is used up; the server says when it comes back, and whose it is (`budgetScope`). */
|
|
7
|
+
| 'budget-exhausted' | 'start-timeout' | 'provider'
|
|
8
|
+
/** The avatar vendor's balance is at its floor: nobody's fault, it affects everyone, and no time is said for it. */
|
|
9
|
+
| 'provider-out-of-credits' | 'connection' | 'needs-hosted-voice' | 'unsupported-browser' | 'playback-blocked';
|
|
7
10
|
export interface AvatarCopy {
|
|
8
11
|
toggle: string;
|
|
9
12
|
toggleHint: string;
|
|
@@ -108,7 +111,8 @@ export interface AvatarCopy {
|
|
|
108
111
|
handsFreeOff: string;
|
|
109
112
|
/**
|
|
110
113
|
* What went wrong. `voiceLive` false: no voice conversation is live, so nothing is said to "continue" (BOFF-9341). `timed`: the next
|
|
111
|
-
* sentence says when it can start again, so the wording does not also say "in a moment".
|
|
114
|
+
* sentence says when it can start again, so the wording does not also say "in a moment". `budgetScope`: whose time is used up
|
|
115
|
+
* (`budget-exhausted` only; BOFF-9333).
|
|
112
116
|
*/
|
|
113
117
|
problem: (problem: AvatarProblem, options?: AvatarProblemOptions) => string;
|
|
114
118
|
}
|
|
@@ -117,6 +121,8 @@ export interface AvatarProblemOptions {
|
|
|
117
121
|
readonly voiceLive?: boolean;
|
|
118
122
|
/** A "You can start it again at …" sentence follows. */
|
|
119
123
|
readonly timed?: boolean;
|
|
124
|
+
/** With `budget-exhausted`, whose budget is used up. Absent or null: the server named none, and the general line is said. */
|
|
125
|
+
readonly budgetScope?: AvatarBudgetScope | null;
|
|
120
126
|
}
|
|
121
127
|
/**
|
|
122
128
|
* `at` as the reader's locale writes a time, with the date when `at` is not on `now`'s day (a daily limit comes back tomorrow).
|
|
@@ -122,9 +122,15 @@ function s() {
|
|
|
122
122
|
case "capacity": return r ? i("avatar.problemCapacityTimed", "Avatars are busy right now.") : i("avatar.problemCapacity", "Avatars are busy right now. Try again in a moment.");
|
|
123
123
|
case "rate-limited": return r ? i("avatar.problemRateLimitedTimed", "You started avatars too quickly.") : i("avatar.problemRateLimited", "You started avatars too quickly. Wait a minute and try again.");
|
|
124
124
|
case "paused": return i("avatar.problemPaused", "Avatars are paused for now.");
|
|
125
|
-
case "budget-exhausted":
|
|
125
|
+
case "budget-exhausted": switch (t.budgetScope) {
|
|
126
|
+
case "person": return i("avatar.problemBudgetExhaustedPerson", "Your avatar time is used up for now.");
|
|
127
|
+
case "workspace": return i("avatar.problemBudgetExhaustedWorkspace", "This workspace’s avatar time is used up for now.");
|
|
128
|
+
case "everyone": return i("avatar.problemBudgetExhaustedEveryone", "Avatar time is used up for everyone for now.");
|
|
129
|
+
default: return i("avatar.problemBudgetExhausted", "Avatar time is used up for now.");
|
|
130
|
+
}
|
|
126
131
|
case "start-timeout": return i("avatar.problemStartTimeout", "The avatar took too long to start. Try again in a moment.");
|
|
127
132
|
case "provider": return n ? i("avatar.problemProvider", "The avatar had a problem. Voice continues without it.") : i("avatar.problemProviderNoVoice", "The avatar had a problem.");
|
|
133
|
+
case "provider-out-of-credits": return i("avatar.problemProviderOutOfCredits", "Avatars aren’t available right now. Try again later.");
|
|
128
134
|
case "connection": return i("avatar.problemConnection", "The connection to the avatar was lost.");
|
|
129
135
|
case "needs-hosted-voice": return i("avatar.problemNeedsHostedVoice", "The avatar needs one of the assistant’s own voices. Choose one in the voice settings.");
|
|
130
136
|
case "unsupported-browser": return i("avatar.problemUnsupportedBrowser", "This browser can’t show the avatar.");
|
|
@@ -261,6 +261,9 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
|
|
|
261
261
|
readonly "avatar.handsFreeOff": "Hands-free interrupt is off, so the avatar's voice can't interrupt itself. It stays off after you turn the avatar off; turn it back on in More options. You can still tap to interrupt.";
|
|
262
262
|
readonly "avatar.problemAlreadyRunning": "An avatar is already running for you.";
|
|
263
263
|
readonly "avatar.problemBudgetExhausted": "Avatar time is used up for now.";
|
|
264
|
+
readonly "avatar.problemBudgetExhaustedEveryone": "Avatar time is used up for everyone for now.";
|
|
265
|
+
readonly "avatar.problemBudgetExhaustedPerson": "Your avatar time is used up for now.";
|
|
266
|
+
readonly "avatar.problemBudgetExhaustedWorkspace": "This workspace’s avatar time is used up for now.";
|
|
264
267
|
readonly "avatar.problemCapacity": "Avatars are busy right now. Try again in a moment.";
|
|
265
268
|
readonly "avatar.problemCapacityTimed": "Avatars are busy right now.";
|
|
266
269
|
readonly "avatar.problemConnection": "The connection to the avatar was lost.";
|
|
@@ -270,6 +273,7 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
|
|
|
270
273
|
readonly "avatar.problemPlaybackBlocked": "The browser blocked the avatar’s sound. Tap the avatar to turn it on.";
|
|
271
274
|
readonly "avatar.problemProvider": "The avatar had a problem. Voice continues without it.";
|
|
272
275
|
readonly "avatar.problemProviderNoVoice": "The avatar had a problem.";
|
|
276
|
+
readonly "avatar.problemProviderOutOfCredits": "Avatars aren’t available right now. Try again later.";
|
|
273
277
|
readonly "avatar.problemRateLimited": "You started avatars too quickly. Wait a minute and try again.";
|
|
274
278
|
readonly "avatar.problemRateLimitedTimed": "You started avatars too quickly.";
|
|
275
279
|
readonly "avatar.problemStartTimeout": "The avatar took too long to start. Try again in a moment.";
|
|
@@ -155,6 +155,9 @@ var m = {
|
|
|
155
155
|
"avatar.handsFreeOff": "Hands-free interrupt is off, so the avatar's voice can't interrupt itself. It stays off after you turn the avatar off; turn it back on in More options. You can still tap to interrupt.",
|
|
156
156
|
"avatar.problemAlreadyRunning": "An avatar is already running for you.",
|
|
157
157
|
"avatar.problemBudgetExhausted": "Avatar time is used up for now.",
|
|
158
|
+
"avatar.problemBudgetExhaustedEveryone": "Avatar time is used up for everyone for now.",
|
|
159
|
+
"avatar.problemBudgetExhaustedPerson": "Your avatar time is used up for now.",
|
|
160
|
+
"avatar.problemBudgetExhaustedWorkspace": "This workspace’s avatar time is used up for now.",
|
|
158
161
|
"avatar.problemCapacity": "Avatars are busy right now. Try again in a moment.",
|
|
159
162
|
"avatar.problemCapacityTimed": "Avatars are busy right now.",
|
|
160
163
|
"avatar.problemConnection": "The connection to the avatar was lost.",
|
|
@@ -164,6 +167,7 @@ var m = {
|
|
|
164
167
|
"avatar.problemPlaybackBlocked": "The browser blocked the avatar’s sound. Tap the avatar to turn it on.",
|
|
165
168
|
"avatar.problemProvider": "The avatar had a problem. Voice continues without it.",
|
|
166
169
|
"avatar.problemProviderNoVoice": "The avatar had a problem.",
|
|
170
|
+
"avatar.problemProviderOutOfCredits": "Avatars aren’t available right now. Try again later.",
|
|
167
171
|
"avatar.problemRateLimited": "You started avatars too quickly. Wait a minute and try again.",
|
|
168
172
|
"avatar.problemRateLimitedTimed": "You started avatars too quickly.",
|
|
169
173
|
"avatar.problemStartTimeout": "The avatar took too long to start. Try again in a moment.",
|