@burdenoff/fe-libs 2026.1024.1 → 2026.1024.2
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 +5 -1
- package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +28 -25
- package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +1 -1
- package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +61 -50
- package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +19 -4
- package/dist/shared/assistant/avatar/live/liveAvatarController.js +163 -155
- package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +5 -3
- package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +2 -2
- package/package.json +1 -1
|
@@ -115,7 +115,11 @@ var f = "inline-flex h-8 items-center gap-1.5 rounded-full border border-border-
|
|
|
115
115
|
}), /* @__PURE__ */ u("button", {
|
|
116
116
|
type: "button",
|
|
117
117
|
onClick: () => {
|
|
118
|
-
|
|
118
|
+
if (M) {
|
|
119
|
+
h.recheckRetry();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
w.current?.focus(), h.confirm();
|
|
119
123
|
},
|
|
120
124
|
"aria-describedby": M ? `${b} ${x}` : b,
|
|
121
125
|
"aria-disabled": M,
|
|
@@ -29,31 +29,34 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
29
29
|
}, [r]);
|
|
30
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
|
-
let B = E(),
|
|
32
|
+
let B = E(), V = P.retryAt !== null && P.retryAt > B, H = ne(P, D, B), U = P.selectedAvatarId, W = (e) => {
|
|
33
33
|
let t = P.avatars.indexOf(e);
|
|
34
34
|
return de?.[e] ?? (t === -1 ? D.chooserNone : `${D.chooserLabel} ${t + 1}`);
|
|
35
|
-
},
|
|
35
|
+
}, G = I === "ready" ? { kind: "turn-off" } : I === "ending" ? { kind: "ending" } : R || I === "starting" || I === "connecting" ? { kind: "starting" } : P.offered ? {
|
|
36
36
|
kind: "turn-on",
|
|
37
|
-
ready: !
|
|
38
|
-
} : null,
|
|
37
|
+
ready: !V && U !== null && (I === "choosing" || I === "off" && P.avatars.includes(U))
|
|
38
|
+
} : null, K = s(null), ge = G?.kind === "turn-off";
|
|
39
39
|
ie(() => {
|
|
40
|
-
|
|
41
|
-
}, [
|
|
40
|
+
K.current = ge ? E() : null;
|
|
41
|
+
}, [ge, E]);
|
|
42
42
|
let _e = i((e) => {
|
|
43
43
|
if (e !== null) return () => {
|
|
44
44
|
e.ownerDocument.activeElement === e && N.current?.focus();
|
|
45
45
|
};
|
|
46
46
|
}, []), ve = () => {
|
|
47
|
-
if (
|
|
47
|
+
if (G !== null) switch (G.kind) {
|
|
48
48
|
case "turn-on":
|
|
49
|
-
if (!
|
|
49
|
+
if (!G.ready || U === null) {
|
|
50
|
+
V && r.recheckRetry();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
50
53
|
z(!0), I === "choosing" ? r.confirm() : r.requestOn({
|
|
51
54
|
start: !0,
|
|
52
|
-
avatarId:
|
|
55
|
+
avatarId: U
|
|
53
56
|
});
|
|
54
57
|
return;
|
|
55
58
|
case "turn-off": {
|
|
56
|
-
let e =
|
|
59
|
+
let e = K.current;
|
|
57
60
|
if (e !== null && E() - e < 1e3) return;
|
|
58
61
|
r.switchOff();
|
|
59
62
|
return;
|
|
@@ -61,7 +64,7 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
61
64
|
case "starting":
|
|
62
65
|
case "ending": return;
|
|
63
66
|
}
|
|
64
|
-
}, ye = te(F, me), q =
|
|
67
|
+
}, ye = te(F, me), q = U === null ? null : pe.get(U) ?? 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
68
|
if (e === p || e === m) {
|
|
66
69
|
T.setVoiceMode(e);
|
|
67
70
|
return;
|
|
@@ -73,7 +76,7 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
73
76
|
}, []), ke = (e) => {
|
|
74
77
|
let t = fe?.get(e);
|
|
75
78
|
return t === void 0 || Ee.has(t) ? null : t;
|
|
76
|
-
}, X = !L &&
|
|
79
|
+
}, X = !L && U !== null ? ke(U) : 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
80
|
return $ !== Q && je($), /* @__PURE__ */ f("div", {
|
|
78
81
|
ref: N,
|
|
79
82
|
tabIndex: -1,
|
|
@@ -96,9 +99,9 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
96
99
|
playsInline: !0,
|
|
97
100
|
"data-testid": "assistant-avatar-tab-video"
|
|
98
101
|
}) : null,
|
|
99
|
-
X !== null &&
|
|
102
|
+
X !== null && U !== null ? /* @__PURE__ */ d(re, {
|
|
100
103
|
size: "frame",
|
|
101
|
-
name: U
|
|
104
|
+
name: W(U),
|
|
102
105
|
src: X,
|
|
103
106
|
onError: Oe
|
|
104
107
|
}) : null,
|
|
@@ -121,10 +124,10 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
121
124
|
})
|
|
122
125
|
}),
|
|
123
126
|
/* @__PURE__ */ d("p", {
|
|
124
|
-
className:
|
|
127
|
+
className: H === null ? "sr-only" : "text-center text-sm text-text-secondary",
|
|
125
128
|
role: "status",
|
|
126
129
|
"data-testid": "assistant-avatar-tab-status",
|
|
127
|
-
children:
|
|
130
|
+
children: H ?? ""
|
|
128
131
|
}),
|
|
129
132
|
P.avatars.length > 1 && !L ? /* @__PURE__ */ f("fieldset", {
|
|
130
133
|
className: "flex min-w-0 flex-col gap-1.5",
|
|
@@ -135,7 +138,7 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
135
138
|
}), /* @__PURE__ */ d("div", {
|
|
136
139
|
className: "flex flex-wrap gap-2",
|
|
137
140
|
children: P.avatars.map((e) => {
|
|
138
|
-
let t = e ===
|
|
141
|
+
let t = e === U, n = W(e);
|
|
139
142
|
return /* @__PURE__ */ f("label", {
|
|
140
143
|
className: `${oe} ${t ? "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"}`,
|
|
141
144
|
title: t ? D.chooserSelected(n) : D.chooserOption(n),
|
|
@@ -164,7 +167,7 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
164
167
|
}, e);
|
|
165
168
|
})
|
|
166
169
|
})]
|
|
167
|
-
}) : P.avatars.length > 0 &&
|
|
170
|
+
}) : P.avatars.length > 0 && U !== null ? /* @__PURE__ */ f("div", {
|
|
168
171
|
className: "flex flex-col gap-0.5",
|
|
169
172
|
"data-testid": "assistant-avatar-tab-chosen",
|
|
170
173
|
children: [/* @__PURE__ */ f("p", {
|
|
@@ -174,7 +177,7 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
174
177
|
children: D.chooserLabel
|
|
175
178
|
}), /* @__PURE__ */ d("span", {
|
|
176
179
|
className: "truncate text-text-primary",
|
|
177
|
-
children: U
|
|
180
|
+
children: W(U)
|
|
178
181
|
})]
|
|
179
182
|
}), L && P.avatars.length > 1 ? /* @__PURE__ */ d("p", {
|
|
180
183
|
className: b,
|
|
@@ -245,16 +248,16 @@ var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted"
|
|
|
245
248
|
"data-testid": "assistant-avatar-tab-disclosure",
|
|
246
249
|
children: D.consent
|
|
247
250
|
}),
|
|
248
|
-
|
|
251
|
+
G === null ? null : /* @__PURE__ */ d("button", {
|
|
249
252
|
ref: _e,
|
|
250
253
|
type: "button",
|
|
251
254
|
onClick: ve,
|
|
252
|
-
"aria-disabled":
|
|
253
|
-
"aria-describedby":
|
|
254
|
-
className:
|
|
255
|
+
"aria-disabled": G.kind === "turn-on" ? !G.ready : G.kind !== "turn-off",
|
|
256
|
+
"aria-describedby": G.kind === "turn-on" ? k : void 0,
|
|
257
|
+
className: G.kind === "turn-off" ? S : ae,
|
|
255
258
|
"data-testid": "assistant-avatar-tab-switch",
|
|
256
|
-
"data-slot":
|
|
257
|
-
children:
|
|
259
|
+
"data-slot": G.kind,
|
|
260
|
+
children: G.kind === "turn-off" ? D.stop : G.kind === "starting" ? D.turnOnStarting : G.kind === "ending" ? D.stateEnding : D.consentConfirm
|
|
258
261
|
}),
|
|
259
262
|
$ ? /* @__PURE__ */ d("button", {
|
|
260
263
|
type: "button",
|
|
@@ -45,7 +45,7 @@ export interface LiveAvatarSeamOptions {
|
|
|
45
45
|
readonly avatarVoiceStore?: LiveAvatarVoiceStore;
|
|
46
46
|
readonly scheduler?: AvatarScheduler;
|
|
47
47
|
readonly browserSupported?: () => boolean;
|
|
48
|
-
readonly controller?: Partial<Pick<LiveAvatarControllerDeps, 'createAudioElement' | 'releaseAudioElement' | 'startMeter' | 'decode' | 'now' | 'newIdempotencyKey' | 'subscribeWorkspaceChange' | 'subscribePageHide' | 'idleMs' | 'endBeforeDeadlineMs' | 'connectTimeoutMs' | 'trackerConfig' | 'maxDrainMs' | 'stallMs' | 'readinessProbe'>>;
|
|
48
|
+
readonly controller?: Partial<Pick<LiveAvatarControllerDeps, 'createAudioElement' | 'releaseAudioElement' | 'startMeter' | 'decode' | 'now' | 'newIdempotencyKey' | 'subscribeWorkspaceChange' | 'subscribePageHide' | 'subscribePageShow' | 'idleMs' | 'endBeforeDeadlineMs' | 'connectTimeoutMs' | 'trackerConfig' | 'maxDrainMs' | 'stallMs' | 'readinessProbe'>>;
|
|
49
49
|
}
|
|
50
50
|
/** The seam, plus what a host or a test may need of it. */
|
|
51
51
|
export interface LiveAvatarSeamHandle extends AssistantLiveAvatarSeam {
|
|
@@ -98,8 +98,18 @@ function j() {
|
|
|
98
98
|
function M(e) {
|
|
99
99
|
if (!(typeof window > "u")) return (t) => (window.addEventListener(e, t), () => window.removeEventListener(e, t));
|
|
100
100
|
}
|
|
101
|
-
function N(
|
|
102
|
-
|
|
101
|
+
function N() {
|
|
102
|
+
if (!(typeof window > "u" || typeof document > "u")) return (e) => {
|
|
103
|
+
let t = window, n = document, r = () => {
|
|
104
|
+
n.visibilityState === "visible" && e();
|
|
105
|
+
};
|
|
106
|
+
return n.addEventListener("visibilitychange", r), t.addEventListener("pageshow", e), () => {
|
|
107
|
+
n.removeEventListener("visibilitychange", r), t.removeEventListener("pageshow", e);
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function P(e = {}) {
|
|
112
|
+
let t = e.preferences ?? O, n = e.controller ?? {}, r = n.subscribeWorkspaceChange ?? M("burdenoff:workspace-changed"), i = n.subscribePageHide ?? M("pagehide"), a = n.subscribePageShow ?? N(), o = n.now ?? (() => Date.now()), u = p({
|
|
103
113
|
client: e.client ?? D(),
|
|
104
114
|
openRoom: e.openRoom ?? m,
|
|
105
115
|
scheduler: e.scheduler ?? s,
|
|
@@ -111,10 +121,11 @@ function N(e = {}) {
|
|
|
111
121
|
}),
|
|
112
122
|
startMeter: n.startMeter ?? ((e, t) => typeof MediaStream > "u" ? null : c(new MediaStream([e]), t)),
|
|
113
123
|
browserSupported: e.browserSupported ?? (() => w()),
|
|
114
|
-
now:
|
|
124
|
+
now: o,
|
|
115
125
|
newIdempotencyKey: n.newIdempotencyKey ?? (() => d()),
|
|
116
126
|
...r ? { subscribeWorkspaceChange: r } : {},
|
|
117
127
|
...i ? { subscribePageHide: i } : {},
|
|
128
|
+
...a ? { subscribePageShow: a } : {},
|
|
118
129
|
...n.idleMs === void 0 ? {} : { idleMs: n.idleMs },
|
|
119
130
|
...n.endBeforeDeadlineMs === void 0 ? {} : { endBeforeDeadlineMs: n.endBeforeDeadlineMs },
|
|
120
131
|
...n.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: n.connectTimeoutMs },
|
|
@@ -123,53 +134,53 @@ function N(e = {}) {
|
|
|
123
134
|
...n.stallMs === void 0 ? {} : { stallMs: n.stallMs },
|
|
124
135
|
...n.readinessProbe === void 0 ? {} : { readinessProbe: n.readinessProbe },
|
|
125
136
|
...e.onLog ? { onLog: e.onLog } : {}
|
|
126
|
-
}),
|
|
127
|
-
let i = v(
|
|
128
|
-
avatarVoices:
|
|
137
|
+
}), f = b(e.avatarVoices), T = e.avatarImages === void 0 ? null : _(e.avatarImages), E = e.avatarVoiceStore ?? k, P = () => E.getState(), F = (n, r) => {
|
|
138
|
+
let i = v(P(), {
|
|
139
|
+
avatarVoices: f,
|
|
129
140
|
avatarId: t.getState().avatarId,
|
|
130
141
|
...e.defaultAvatarVoiceMode ? { hostDefault: e.defaultAvatarVoiceMode } : {}
|
|
131
142
|
});
|
|
132
143
|
return i === null ? null : y(i, n, r.listVoices());
|
|
133
|
-
},
|
|
144
|
+
}, I = (e, t) => {
|
|
134
145
|
let n = {
|
|
135
146
|
...e,
|
|
136
147
|
voice: t
|
|
137
148
|
};
|
|
138
|
-
return n.audioOutput = x(
|
|
149
|
+
return n.audioOutput = x(u.createOutput(), () => {
|
|
139
150
|
n.voice = e.voice;
|
|
140
|
-
}, (e) =>
|
|
141
|
-
|
|
151
|
+
}, (e) => u.subscribe(() => {
|
|
152
|
+
u.getSnapshot().phase !== "ready" && e();
|
|
142
153
|
})), n;
|
|
143
154
|
};
|
|
144
155
|
return {
|
|
145
156
|
decorateSpeechEngine: (e) => {
|
|
146
157
|
let t = (t) => {
|
|
147
|
-
let n =
|
|
158
|
+
let n = F(t.lang, e), r = n === null ? t : {
|
|
148
159
|
...t,
|
|
149
160
|
voice: n
|
|
150
161
|
};
|
|
151
162
|
return {
|
|
152
163
|
voice: n,
|
|
153
|
-
target:
|
|
164
|
+
target: u.route(r, e)
|
|
154
165
|
};
|
|
155
166
|
}, n = null, r = () => {
|
|
156
167
|
let e = n;
|
|
157
168
|
n = null, e?.();
|
|
158
169
|
}, i = (t, r, i) => {
|
|
159
|
-
let
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
!
|
|
170
|
+
let a = !0, o = !1, s = () => void 0, c = () => {
|
|
171
|
+
a && (a = !1, n === c && (n = null), s());
|
|
172
|
+
}, l = () => o ? !1 : (o = !0, c(), !0), d = () => {
|
|
173
|
+
!a || !l() || (e.cancel(), i?.onError?.("output-left"));
|
|
163
174
|
};
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}), n =
|
|
175
|
+
s = u.subscribe(() => {
|
|
176
|
+
u.getSnapshot().phase !== "ready" && d();
|
|
177
|
+
}), n = c, r.audioOutput &&= A(r.audioOutput, d), r.fallback = !1, e.speak(t, r, {
|
|
167
178
|
...i,
|
|
168
179
|
onEnd: () => {
|
|
169
|
-
|
|
180
|
+
l() && i?.onEnd?.();
|
|
170
181
|
},
|
|
171
182
|
onError: (e) => {
|
|
172
|
-
|
|
183
|
+
l() && i?.onError?.(e);
|
|
173
184
|
}
|
|
174
185
|
});
|
|
175
186
|
};
|
|
@@ -179,26 +190,26 @@ function N(e = {}) {
|
|
|
179
190
|
listVoices: () => e.listVoices(),
|
|
180
191
|
subscribeVoices: (t) => e.subscribeVoices(t),
|
|
181
192
|
wouldSpeakOnAvatar: (e) => t(e).target === "avatar",
|
|
182
|
-
speak: (n,
|
|
193
|
+
speak: (n, a, o) => {
|
|
183
194
|
r();
|
|
184
|
-
let { voice:
|
|
185
|
-
if (
|
|
186
|
-
if (
|
|
187
|
-
e.cancel(),
|
|
195
|
+
let { voice: s, target: c } = t(a);
|
|
196
|
+
if (c !== "avatar") {
|
|
197
|
+
if (a.avatarOnly === !0) {
|
|
198
|
+
e.cancel(), o?.onError?.("output-left");
|
|
188
199
|
return;
|
|
189
200
|
}
|
|
190
|
-
e.speak(n,
|
|
201
|
+
e.speak(n, a, o);
|
|
191
202
|
return;
|
|
192
203
|
}
|
|
193
|
-
let
|
|
194
|
-
...
|
|
195
|
-
audioOutput:
|
|
196
|
-
} :
|
|
197
|
-
if (
|
|
198
|
-
i(n,
|
|
204
|
+
let l = s === null ? {
|
|
205
|
+
...a,
|
|
206
|
+
audioOutput: u.createOutput()
|
|
207
|
+
} : I(a, s);
|
|
208
|
+
if (a.avatarOnly === !0) {
|
|
209
|
+
i(n, l, o);
|
|
199
210
|
return;
|
|
200
211
|
}
|
|
201
|
-
e.speak(n,
|
|
212
|
+
e.speak(n, l, o);
|
|
202
213
|
},
|
|
203
214
|
cancel: () => {
|
|
204
215
|
r(), e.cancel();
|
|
@@ -206,7 +217,7 @@ function N(e = {}) {
|
|
|
206
217
|
};
|
|
207
218
|
},
|
|
208
219
|
Stage: ({ expanded: t, fallback: n }) => /* @__PURE__ */ C(g, {
|
|
209
|
-
controller:
|
|
220
|
+
controller: u,
|
|
210
221
|
expanded: t,
|
|
211
222
|
fallback: n,
|
|
212
223
|
...e.avatarNames ? { avatarNames: e.avatarNames } : {}
|
|
@@ -214,33 +225,33 @@ function N(e = {}) {
|
|
|
214
225
|
enabled: {
|
|
215
226
|
subscribe: (e) => t.subscribe(e),
|
|
216
227
|
getSnapshot: () => t.getState().avatarEnabled,
|
|
217
|
-
switchOff: () =>
|
|
218
|
-
voiceLive: (e) =>
|
|
228
|
+
switchOff: () => u.switchOff(),
|
|
229
|
+
voiceLive: (e) => u.setVoiceLive(e)
|
|
219
230
|
},
|
|
220
231
|
tab: {
|
|
221
232
|
offered: {
|
|
222
|
-
subscribe: (e) =>
|
|
223
|
-
getSnapshot: () =>
|
|
233
|
+
subscribe: (e) => u.subscribe(e),
|
|
234
|
+
getSnapshot: () => u.getSnapshot().tabOffered
|
|
224
235
|
},
|
|
225
236
|
ask: {
|
|
226
|
-
subscribe: (e) =>
|
|
227
|
-
getSnapshot: () => h(
|
|
237
|
+
subscribe: (e) => u.subscribe(e),
|
|
238
|
+
getSnapshot: () => h(u.getSnapshot(), o())
|
|
228
239
|
},
|
|
229
|
-
setOpen: (e) =>
|
|
230
|
-
heldByVoice: () =>
|
|
240
|
+
setOpen: (e) => u.setTabOpen(e),
|
|
241
|
+
heldByVoice: () => u.getSnapshot().heldByVoice,
|
|
231
242
|
View: (t) => /* @__PURE__ */ C(S, {
|
|
232
243
|
...t,
|
|
233
|
-
controller:
|
|
234
|
-
avatarVoices:
|
|
235
|
-
voiceStore:
|
|
244
|
+
controller: u,
|
|
245
|
+
avatarVoices: f,
|
|
246
|
+
voiceStore: E,
|
|
236
247
|
...e.avatarNames ? { avatarNames: e.avatarNames } : {},
|
|
237
|
-
...
|
|
248
|
+
...T ? { avatarImages: T } : {},
|
|
238
249
|
...e.defaultAvatarVoiceMode ? { defaultAvatarVoiceMode: e.defaultAvatarVoiceMode } : {}
|
|
239
250
|
})
|
|
240
251
|
},
|
|
241
|
-
controller:
|
|
242
|
-
dispose: () =>
|
|
252
|
+
controller: u,
|
|
253
|
+
dispose: () => u.dispose()
|
|
243
254
|
};
|
|
244
255
|
}
|
|
245
256
|
//#endregion
|
|
246
|
-
export {
|
|
257
|
+
export { P as createLiveAvatarSeam, w as isLiveAvatarBrowserSupported, T as liveAvatarAuthorization, E as liveAvatarWorkspaceAuthorization };
|
|
@@ -46,9 +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
|
|
50
|
-
*
|
|
51
|
-
* earlier.
|
|
49
|
+
* It goes with `problem`: cleared together, and both clear by themselves when the time comes (its timer, or `recheckRetry` when the
|
|
50
|
+
* page is shown again after a system sleep made that timer late). A newer refusal (including a question that failed or was answered
|
|
51
|
+
* "not offered"), a workspace switch and a voice conversation starting outside the Avatar tab clear them earlier.
|
|
52
52
|
*/
|
|
53
53
|
readonly retryAt: number | null;
|
|
54
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. */
|
|
@@ -117,11 +117,19 @@ export interface LiveAvatarControllerDeps {
|
|
|
117
117
|
readonly startMeter: (track: MediaStreamTrack, onChange: (audible: boolean) => void) => AvatarMeterHandle | null;
|
|
118
118
|
/** The browser can show the avatar at all (desktop Chromium first: GATE1B §10). */
|
|
119
119
|
readonly browserSupported: () => boolean;
|
|
120
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Wall-clock milliseconds: a refusal's retry time (`retryAt`), its lock and its re-check (`recheckRetry`); also the fallback for
|
|
122
|
+
* `endsAt` when the server announced no session length (the server's clock may differ).
|
|
123
|
+
*/
|
|
121
124
|
readonly now: () => number;
|
|
122
125
|
readonly newIdempotencyKey: () => string;
|
|
123
126
|
readonly subscribeWorkspaceChange?: (listener: () => void) => () => void;
|
|
124
127
|
readonly subscribePageHide?: (listener: () => void) => () => void;
|
|
128
|
+
/**
|
|
129
|
+
* The page is shown again (its document visible again, or shown from the back/forward cache): the listener is `recheckRetry`. The
|
|
130
|
+
* scheduler's monotonic clock does not count a system sleep, so after one a refusal's timer fires late; the wall clock (`now`) does.
|
|
131
|
+
*/
|
|
132
|
+
readonly subscribePageShow?: (listener: () => void) => () => void;
|
|
125
133
|
readonly idleMs?: number;
|
|
126
134
|
readonly endBeforeDeadlineMs?: number;
|
|
127
135
|
readonly connectTimeoutMs?: number;
|
|
@@ -157,6 +165,13 @@ export interface LiveAvatarController {
|
|
|
157
165
|
* time (`retryAt`) that has not come.
|
|
158
166
|
*/
|
|
159
167
|
confirm(): void;
|
|
168
|
+
/**
|
|
169
|
+
* BOFF-9333 UI-11: once the wall clock has reached a refusal's retry time (`retryAt`), clear the refusal now, as its timer does: the
|
|
170
|
+
* problem, whose budget it was, the time, and the timer with them. That timer runs on the scheduler's monotonic clock, which a system
|
|
171
|
+
* sleep stops (macOS, Linux), so after one it fires late. Runs when the page is shown again (`subscribePageShow`); a host with its own
|
|
172
|
+
* wake signal may call it too. With no time, or before it, it does nothing: nothing is cleared early, and the timer stays as it is.
|
|
173
|
+
*/
|
|
174
|
+
recheckRetry(): void;
|
|
160
175
|
/**
|
|
161
176
|
* The Avatar tab opened or closed (BOFF-9341). Open: the avatar may be asked for with no voice conversation, and an avatar already on
|
|
162
177
|
* is adopted; a question already in flight is asked again, choose only. Close: any question, choice or session ends as the person's
|
|
@@ -3,7 +3,7 @@ import { createAvatarTransportPort as n } from "./avatarTransportPort.js";
|
|
|
3
3
|
import { createAvatarReplyOutputs as r } from "./avatarReplyOutputs.js";
|
|
4
4
|
import { AvatarApiError as i, avatarAvailabilityRefusal as a, avatarRefusalFor as o } from "./avatarSessionClient.js";
|
|
5
5
|
//#region src/shared/assistant/avatar/live/liveAvatarController.ts
|
|
6
|
-
var s = 12e4, c = 1e4, l = 8e3, ee = 5e3, u = 2e4,
|
|
6
|
+
var s = 12e4, c = 1e4, l = 8e3, ee = 5e3, u = 2e4, te = 1e3, d = {
|
|
7
7
|
voiceLive: !1,
|
|
8
8
|
offered: !1,
|
|
9
9
|
tabOpen: !1,
|
|
@@ -21,7 +21,7 @@ var s = 12e4, c = 1e4, l = 8e3, ee = 5e3, u = 2e4, d = 1e3, f = {
|
|
|
21
21
|
audioBlocked: !1,
|
|
22
22
|
videoReady: !1
|
|
23
23
|
};
|
|
24
|
-
function
|
|
24
|
+
function f(e) {
|
|
25
25
|
switch (e) {
|
|
26
26
|
case "permission_revoked": return {
|
|
27
27
|
problem: "unavailable",
|
|
@@ -57,53 +57,53 @@ function p(e) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
var
|
|
60
|
+
var p = new Set(["playback-blocked", "needs-hosted-voice"]), m = {
|
|
61
61
|
problem: "unavailable",
|
|
62
62
|
hide: !0
|
|
63
63
|
};
|
|
64
|
-
function
|
|
65
|
-
let { scheduler: c, preferences: l } = s, u = s.idleMs ?? 12e4,
|
|
66
|
-
...
|
|
64
|
+
function h(s) {
|
|
65
|
+
let { scheduler: c, preferences: l } = s, u = s.idleMs ?? 12e4, te = s.endBeforeDeadlineMs ?? 1e4, h = s.connectTimeoutMs ?? 2e4, g = s.requestTimeoutMs ?? 8e3, _ = {
|
|
66
|
+
...d,
|
|
67
67
|
tabOffered: s.browserSupported()
|
|
68
|
-
},
|
|
68
|
+
}, v = /* @__PURE__ */ new Set(), y = !1, b = null, x = 0, S = !1, C = !1, w = 0, T = null, E = null, D = [], O = 0, k = !1, A = "user", j = !1, M = !1, N = null, P = null, F = null, I = null, L = !1;
|
|
69
69
|
function R(e) {
|
|
70
70
|
try {
|
|
71
71
|
s.onLog?.(e);
|
|
72
72
|
} catch {}
|
|
73
73
|
}
|
|
74
74
|
function z() {
|
|
75
|
-
return !
|
|
75
|
+
return !y && s.browserSupported();
|
|
76
76
|
}
|
|
77
77
|
function ne(e, t) {
|
|
78
78
|
return (e || t) && z();
|
|
79
79
|
}
|
|
80
80
|
function B() {
|
|
81
|
-
return
|
|
81
|
+
return _.retryAt !== null && s.now() < _.retryAt;
|
|
82
82
|
}
|
|
83
83
|
function V(e) {
|
|
84
84
|
let t = {
|
|
85
|
-
...
|
|
85
|
+
..._,
|
|
86
86
|
...e
|
|
87
87
|
};
|
|
88
|
-
e.problem !== void 0 && e.problem !==
|
|
88
|
+
e.problem !== void 0 && e.problem !== _.problem && (e.retryAt === void 0 && (t.retryAt = null), e.budgetScope === void 0 && (t.budgetScope = null));
|
|
89
89
|
let n = {
|
|
90
90
|
...t,
|
|
91
91
|
offered: ne(t.voiceLive, t.tabOpen),
|
|
92
92
|
tabOffered: z(),
|
|
93
93
|
heldByVoice: M && t.voiceLive
|
|
94
94
|
};
|
|
95
|
-
if (!Object.keys(n).every((e) => n[e] ===
|
|
96
|
-
n.retryAt !==
|
|
97
|
-
for (let e of [...
|
|
95
|
+
if (!Object.keys(n).every((e) => n[e] === _[e])) {
|
|
96
|
+
n.retryAt !== _.retryAt && (I?.(), I = null), _ = n;
|
|
97
|
+
for (let e of [...v]) try {
|
|
98
98
|
e();
|
|
99
99
|
} catch {}
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
function H() {
|
|
103
|
-
return
|
|
103
|
+
return k && !j ? "voice_end" : "user";
|
|
104
104
|
}
|
|
105
105
|
function U() {
|
|
106
|
-
l.getState().avatarEnabled !==
|
|
106
|
+
l.getState().avatarEnabled !== S && l.setAvatarEnabled(S);
|
|
107
107
|
}
|
|
108
108
|
let re = {
|
|
109
109
|
positionSec: null,
|
|
@@ -111,15 +111,15 @@ function g(s) {
|
|
|
111
111
|
rpc: "none"
|
|
112
112
|
}, W = r({
|
|
113
113
|
port: {
|
|
114
|
-
isActive: () =>
|
|
115
|
-
isAudioBlocked: () =>
|
|
116
|
-
connectionId: () =>
|
|
117
|
-
isMeterHealthy: () =>
|
|
118
|
-
isMeterAudible: () =>
|
|
119
|
-
openSegment: () =>
|
|
120
|
-
clearBuffer: (e) =>
|
|
121
|
-
setOutputMuted: (e) =>
|
|
122
|
-
subscribe: (e) =>
|
|
114
|
+
isActive: () => E !== null && E.port.isActive(),
|
|
115
|
+
isAudioBlocked: () => E?.port.isAudioBlocked() ?? !1,
|
|
116
|
+
connectionId: () => E?.port.connectionId() ?? 0,
|
|
117
|
+
isMeterHealthy: () => E?.port.isMeterHealthy() ?? !1,
|
|
118
|
+
isMeterAudible: () => E?.port.isMeterAudible() ?? !1,
|
|
119
|
+
openSegment: () => E ? E.port.openSegment() : Promise.reject(new e("connection")),
|
|
120
|
+
clearBuffer: (e) => E ? E.port.clearBuffer(e) : Promise.resolve(re),
|
|
121
|
+
setOutputMuted: (e) => E?.port.setOutputMuted(e),
|
|
122
|
+
subscribe: (e) => E ? E.port.subscribe(e) : () => void 0
|
|
123
123
|
},
|
|
124
124
|
scheduler: c,
|
|
125
125
|
decode: s.decode,
|
|
@@ -135,7 +135,7 @@ function g(s) {
|
|
|
135
135
|
V({ notice: "interrupted" });
|
|
136
136
|
return;
|
|
137
137
|
case "voice-fallback":
|
|
138
|
-
|
|
138
|
+
S && V({ notice: "voice-only" });
|
|
139
139
|
return;
|
|
140
140
|
case "audio-blocked":
|
|
141
141
|
V({
|
|
@@ -151,7 +151,7 @@ function g(s) {
|
|
|
151
151
|
P?.(), P = null;
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
|
-
if (
|
|
154
|
+
if (C) {
|
|
155
155
|
K("user", { notice: "time-limit" });
|
|
156
156
|
return;
|
|
157
157
|
}
|
|
@@ -159,8 +159,8 @@ function g(s) {
|
|
|
159
159
|
}
|
|
160
160
|
});
|
|
161
161
|
function G() {
|
|
162
|
-
P?.(), P = null,
|
|
163
|
-
P = null,
|
|
162
|
+
P?.(), P = null, _.phase === "ready" && (P = c.setTimeout(() => {
|
|
163
|
+
P = null, _.phase === "ready" && !W.isBusy() && K("idle", {});
|
|
164
164
|
}, u));
|
|
165
165
|
}
|
|
166
166
|
function ie() {
|
|
@@ -168,8 +168,8 @@ function g(s) {
|
|
|
168
168
|
}
|
|
169
169
|
function ae() {
|
|
170
170
|
W.leave();
|
|
171
|
-
let e =
|
|
172
|
-
if (
|
|
171
|
+
let e = E;
|
|
172
|
+
if (E = null, e !== null) {
|
|
173
173
|
if (e.unsubscribe?.(), e.meter !== null) {
|
|
174
174
|
if (e.meterAudible) {
|
|
175
175
|
e.meterAudible = !1;
|
|
@@ -181,9 +181,9 @@ function g(s) {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
function K(e, t) {
|
|
184
|
-
|
|
185
|
-
let n =
|
|
186
|
-
|
|
184
|
+
w += 1, A = e, S = !1, C = !1, M = !1, N = null, ae(), ie();
|
|
185
|
+
let n = T;
|
|
186
|
+
T = null, U(), n !== null && (R({
|
|
187
187
|
event: "session-ended",
|
|
188
188
|
reason: e
|
|
189
189
|
}), s.client()?.end(n, {
|
|
@@ -194,32 +194,39 @@ function g(s) {
|
|
|
194
194
|
closing: !1,
|
|
195
195
|
audioBlocked: !1,
|
|
196
196
|
videoReady: !1,
|
|
197
|
-
...t.problem === void 0 ?
|
|
197
|
+
...t.problem === void 0 ? _.problem !== null && p.has(_.problem) ? { problem: null } : {} : { problem: t.problem },
|
|
198
198
|
...t.notice === void 0 ? {} : { notice: t.notice }
|
|
199
199
|
});
|
|
200
200
|
}
|
|
201
201
|
function q(e) {
|
|
202
202
|
let t = o(e);
|
|
203
|
-
t.hide && (
|
|
203
|
+
t.hide && (y = !0), e instanceof i && R({
|
|
204
204
|
event: "refused",
|
|
205
205
|
status: e.status,
|
|
206
206
|
code: e.code
|
|
207
|
-
}),
|
|
207
|
+
}), S = !1, M = !1, N = null, U();
|
|
208
208
|
let n = t.retryAfterSeconds !== void 0 && t.retryAfterSeconds > 0 ? t.retryAfterSeconds * 1e3 : 0, r = n > 0 ? s.now() + n : null;
|
|
209
209
|
return V({
|
|
210
210
|
phase: "off",
|
|
211
211
|
problem: t.problem,
|
|
212
212
|
retryAt: r,
|
|
213
213
|
budgetScope: t.scope ?? null
|
|
214
|
-
}), r !== null &&
|
|
215
|
-
I = null,
|
|
216
|
-
problem: null,
|
|
217
|
-
retryAt: null
|
|
218
|
-
});
|
|
214
|
+
}), r !== null && _.retryAt === r && (I = c.setTimeout(() => {
|
|
215
|
+
I = null, J(r);
|
|
219
216
|
}, n)), t;
|
|
220
217
|
}
|
|
221
|
-
function J(e
|
|
222
|
-
|
|
218
|
+
function J(e) {
|
|
219
|
+
_.retryAt === e && V({
|
|
220
|
+
problem: null,
|
|
221
|
+
retryAt: null
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function Y() {
|
|
225
|
+
let e = _.retryAt;
|
|
226
|
+
L || e === null || B() || J(e);
|
|
227
|
+
}
|
|
228
|
+
function X(e = m) {
|
|
229
|
+
e.hide && (y = !0), S = !1, M = !1, N = null, U(), V({
|
|
223
230
|
phase: "off",
|
|
224
231
|
problem: e.problem,
|
|
225
232
|
retryAt: null,
|
|
@@ -229,7 +236,7 @@ function g(s) {
|
|
|
229
236
|
function oe(e) {
|
|
230
237
|
switch (e.type) {
|
|
231
238
|
case "audioPlayback":
|
|
232
|
-
e.canPlay ?
|
|
239
|
+
e.canPlay ? _.problem === "playback-blocked" ? V({
|
|
233
240
|
audioBlocked: !1,
|
|
234
241
|
problem: null
|
|
235
242
|
}) : V({ audioBlocked: !1 }) : V({
|
|
@@ -245,22 +252,22 @@ function g(s) {
|
|
|
245
252
|
}
|
|
246
253
|
}
|
|
247
254
|
async function se() {
|
|
248
|
-
let e =
|
|
249
|
-
if (e === null ||
|
|
250
|
-
let t =
|
|
255
|
+
let e = T;
|
|
256
|
+
if (e === null || _.phase === "ending" || _.phase === "off") return;
|
|
257
|
+
let t = w;
|
|
251
258
|
V({ phase: "ending" });
|
|
252
259
|
let n = null, r = !1;
|
|
253
260
|
try {
|
|
254
|
-
let t = s.client(), i = t === null ? void 0 : await
|
|
261
|
+
let t = s.client(), i = t === null ? void 0 : await le((n) => t.status(e, n));
|
|
255
262
|
n = i?.endedReason ?? null, r = i !== void 0 && [
|
|
256
263
|
"ended",
|
|
257
264
|
"failed",
|
|
258
265
|
"canceled"
|
|
259
266
|
].includes(i.state);
|
|
260
267
|
} catch {}
|
|
261
|
-
if (
|
|
262
|
-
let i =
|
|
263
|
-
i.hide && (
|
|
268
|
+
if (w !== t) return;
|
|
269
|
+
let i = f(n);
|
|
270
|
+
i.hide && (y = !0), r && (T = null, R({
|
|
264
271
|
event: "session-ended",
|
|
265
272
|
reason: "server"
|
|
266
273
|
})), K("client_error", i);
|
|
@@ -268,10 +275,10 @@ function g(s) {
|
|
|
268
275
|
async function ce(e, r) {
|
|
269
276
|
let i = s.client();
|
|
270
277
|
if (i === null) {
|
|
271
|
-
|
|
278
|
+
X();
|
|
272
279
|
return;
|
|
273
280
|
}
|
|
274
|
-
let a = ++
|
|
281
|
+
let a = ++w;
|
|
275
282
|
V({
|
|
276
283
|
phase: "starting",
|
|
277
284
|
problem: null,
|
|
@@ -281,103 +288,103 @@ function g(s) {
|
|
|
281
288
|
try {
|
|
282
289
|
o = await s.openRoom();
|
|
283
290
|
} catch {
|
|
284
|
-
|
|
291
|
+
w === a && X();
|
|
285
292
|
return;
|
|
286
293
|
}
|
|
287
294
|
let l = () => {
|
|
288
295
|
o.disconnect().catch(() => void 0);
|
|
289
296
|
};
|
|
290
|
-
if (
|
|
297
|
+
if (w !== a) {
|
|
291
298
|
l();
|
|
292
299
|
return;
|
|
293
300
|
}
|
|
294
|
-
let u = c.now(),
|
|
301
|
+
let u = c.now(), d;
|
|
295
302
|
try {
|
|
296
|
-
|
|
303
|
+
d = await i.create({
|
|
297
304
|
avatarId: e,
|
|
298
305
|
consentVersion: r,
|
|
299
306
|
idempotencyKey: s.newIdempotencyKey()
|
|
300
307
|
});
|
|
301
308
|
} catch (e) {
|
|
302
|
-
if (l(),
|
|
303
|
-
q(e).showDisclosure && (
|
|
309
|
+
if (l(), w !== a) return;
|
|
310
|
+
q(e).showDisclosure && (b = null, $.requestOn({ start: !1 }));
|
|
304
311
|
return;
|
|
305
312
|
}
|
|
306
|
-
if (
|
|
307
|
-
l(), i.end(
|
|
313
|
+
if (w !== a) {
|
|
314
|
+
l(), i.end(d.sessionId, { reason: A === "voice_end" ? "voice_end" : "user" }).catch(() => void 0);
|
|
308
315
|
return;
|
|
309
316
|
}
|
|
310
|
-
|
|
311
|
-
let
|
|
317
|
+
T = d.sessionId, V({ phase: "connecting" });
|
|
318
|
+
let f = new t({
|
|
312
319
|
room: o,
|
|
313
|
-
avatarIdentity:
|
|
320
|
+
avatarIdentity: d.avatarIdentity,
|
|
314
321
|
scheduler: c,
|
|
315
322
|
...s.readinessProbe === void 0 ? {} : { readinessProbe: s.readinessProbe }
|
|
316
|
-
}),
|
|
317
|
-
session:
|
|
318
|
-
transport:
|
|
323
|
+
}), p = /* @__PURE__ */ new Set(), m = {
|
|
324
|
+
session: d,
|
|
325
|
+
transport: f,
|
|
319
326
|
audioElement: s.createAudioElement(),
|
|
320
327
|
meter: null,
|
|
321
328
|
meterAudible: !1,
|
|
322
|
-
meterListeners:
|
|
329
|
+
meterListeners: p,
|
|
323
330
|
videoTrack: null,
|
|
324
331
|
unsubscribe: null,
|
|
325
332
|
port: n({
|
|
326
|
-
transport:
|
|
327
|
-
isEnabled: () =>
|
|
328
|
-
subscribeMeter: (e) => (
|
|
329
|
-
|
|
333
|
+
transport: f,
|
|
334
|
+
isEnabled: () => S && !C,
|
|
335
|
+
subscribeMeter: (e) => (p.add(e), () => {
|
|
336
|
+
p.delete(e);
|
|
330
337
|
}),
|
|
331
|
-
isMeterHealthy: () =>
|
|
338
|
+
isMeterHealthy: () => m.meter?.isRunning() ?? !1
|
|
332
339
|
})
|
|
333
340
|
};
|
|
334
|
-
|
|
341
|
+
E = m, m.unsubscribe = f.subscribe(oe);
|
|
335
342
|
try {
|
|
336
|
-
await
|
|
337
|
-
url:
|
|
338
|
-
token:
|
|
339
|
-
audioElement:
|
|
343
|
+
await ue(f.connect({
|
|
344
|
+
url: d.livekitUrl,
|
|
345
|
+
token: d.participantToken,
|
|
346
|
+
audioElement: m.audioElement,
|
|
340
347
|
onAvatarAudio: (e) => {
|
|
341
|
-
if (!(e.mediaStreamTrack === null ||
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
for (let t of [...
|
|
348
|
+
if (!(e.mediaStreamTrack === null || m.meter !== null)) try {
|
|
349
|
+
m.meter = s.startMeter(e.mediaStreamTrack, (e) => {
|
|
350
|
+
m.meterAudible = e;
|
|
351
|
+
for (let t of [...p]) t(e);
|
|
345
352
|
});
|
|
346
353
|
} catch {
|
|
347
|
-
|
|
354
|
+
m.meter = null;
|
|
348
355
|
}
|
|
349
356
|
},
|
|
350
357
|
onAvatarVideo: (e) => {
|
|
351
|
-
|
|
352
|
-
let t =
|
|
353
|
-
t !== null && e.attach(t),
|
|
358
|
+
m.videoTrack = e;
|
|
359
|
+
let t = Z();
|
|
360
|
+
t !== null && e.attach(t), E === m && V({ videoReady: !0 });
|
|
354
361
|
}
|
|
355
|
-
}),
|
|
362
|
+
}), h);
|
|
356
363
|
} catch (e) {
|
|
357
|
-
if (
|
|
364
|
+
if (w !== a) return;
|
|
358
365
|
R({
|
|
359
366
|
event: "connect-failed",
|
|
360
367
|
error: e instanceof Error ? e.name : "error"
|
|
361
368
|
}), K("client_error", { problem: "connection" });
|
|
362
369
|
return;
|
|
363
370
|
}
|
|
364
|
-
if (
|
|
371
|
+
if (w !== a || E !== m) return;
|
|
365
372
|
V({
|
|
366
373
|
phase: "ready",
|
|
367
|
-
audioBlocked:
|
|
368
|
-
...
|
|
374
|
+
audioBlocked: f.audioBlocked,
|
|
375
|
+
...f.audioBlocked ? { problem: "playback-blocked" } : {}
|
|
369
376
|
}), G();
|
|
370
|
-
let
|
|
377
|
+
let g = x > 0 ? u + x - ee - c.now() : d.endsAt - s.now(), _ = Math.max(0, g - te);
|
|
371
378
|
F = c.setTimeout(() => {
|
|
372
|
-
F = null,
|
|
373
|
-
},
|
|
379
|
+
F = null, w === a && (C = !0, V({ closing: !0 }), W.isBusy() || K("user", { notice: "time-limit" }));
|
|
380
|
+
}, _);
|
|
374
381
|
}
|
|
375
|
-
function
|
|
382
|
+
function le(t) {
|
|
376
383
|
let n = new AbortController();
|
|
377
384
|
return new Promise((r, i) => {
|
|
378
385
|
let a = c.setTimeout(() => {
|
|
379
386
|
n.abort(), i(new e("connection"));
|
|
380
|
-
},
|
|
387
|
+
}, g);
|
|
381
388
|
t(n.signal).then((e) => {
|
|
382
389
|
a(), r(e);
|
|
383
390
|
}, (e) => {
|
|
@@ -385,7 +392,7 @@ function g(s) {
|
|
|
385
392
|
});
|
|
386
393
|
});
|
|
387
394
|
}
|
|
388
|
-
function
|
|
395
|
+
function ue(t, n) {
|
|
389
396
|
return new Promise((r, i) => {
|
|
390
397
|
let a = c.setTimeout(() => i(new e("connection")), n);
|
|
391
398
|
t.then((e) => {
|
|
@@ -395,32 +402,32 @@ function g(s) {
|
|
|
395
402
|
});
|
|
396
403
|
});
|
|
397
404
|
}
|
|
398
|
-
function
|
|
399
|
-
return
|
|
405
|
+
function Z() {
|
|
406
|
+
return D[D.length - 1] ?? null;
|
|
400
407
|
}
|
|
401
|
-
function
|
|
402
|
-
let t =
|
|
408
|
+
function de(e) {
|
|
409
|
+
let t = D.indexOf(e);
|
|
403
410
|
if (t === -1) return;
|
|
404
|
-
let n = t ===
|
|
405
|
-
|
|
406
|
-
let r =
|
|
411
|
+
let n = t === D.length - 1;
|
|
412
|
+
D.splice(t, 1);
|
|
413
|
+
let r = E?.videoTrack ?? null;
|
|
407
414
|
if (!n || r === null) return;
|
|
408
415
|
r.detach();
|
|
409
|
-
let i =
|
|
416
|
+
let i = Z();
|
|
410
417
|
i !== null && r.attach(i);
|
|
411
418
|
}
|
|
412
419
|
let Q = [];
|
|
413
420
|
Q.push(l.subscribe(() => {
|
|
414
421
|
let e = l.getState().avatarEnabled;
|
|
415
|
-
if (e !==
|
|
422
|
+
if (e !== S) {
|
|
416
423
|
if (e) {
|
|
417
424
|
l.setAvatarEnabled(!1);
|
|
418
425
|
return;
|
|
419
426
|
}
|
|
420
|
-
|
|
427
|
+
_.phase !== "off" && _.phase !== "checking" && _.phase !== "choosing" ? K(H(), {}) : S = !1;
|
|
421
428
|
}
|
|
422
429
|
})), s.subscribeWorkspaceChange && Q.push(s.subscribeWorkspaceChange(() => {
|
|
423
|
-
if (
|
|
430
|
+
if (y = !1, _.phase === "off" ? V({
|
|
424
431
|
problem: null,
|
|
425
432
|
avatars: [],
|
|
426
433
|
consentVersion: null
|
|
@@ -428,26 +435,26 @@ function g(s) {
|
|
|
428
435
|
avatars: [],
|
|
429
436
|
consentVersion: null
|
|
430
437
|
})), j) {
|
|
431
|
-
let e =
|
|
438
|
+
let e = w;
|
|
432
439
|
c.setTimeout(() => {
|
|
433
|
-
j &&
|
|
440
|
+
j && w === e && $.requestOn({ start: !1 });
|
|
434
441
|
}, 0);
|
|
435
442
|
}
|
|
436
443
|
})), s.subscribePageHide && Q.push(s.subscribePageHide(() => {
|
|
437
|
-
(
|
|
438
|
-
}));
|
|
444
|
+
(T !== null || _.phase !== "off") && K("page_hide", { keepalive: !0 });
|
|
445
|
+
})), s.subscribePageShow && Q.push(s.subscribePageShow(Y));
|
|
439
446
|
let $ = {
|
|
440
447
|
subscribe(e) {
|
|
441
|
-
return
|
|
442
|
-
|
|
448
|
+
return v.add(e), () => {
|
|
449
|
+
v.delete(e);
|
|
443
450
|
};
|
|
444
451
|
},
|
|
445
|
-
getSnapshot: () =>
|
|
452
|
+
getSnapshot: () => _,
|
|
446
453
|
setVoiceLive(e) {
|
|
447
|
-
if (
|
|
454
|
+
if (k = !e, e) {
|
|
448
455
|
V({
|
|
449
456
|
voiceLive: !0,
|
|
450
|
-
problem:
|
|
457
|
+
problem: y || j && B() ? _.problem : null,
|
|
451
458
|
notice: null
|
|
452
459
|
});
|
|
453
460
|
return;
|
|
@@ -456,33 +463,33 @@ function g(s) {
|
|
|
456
463
|
V({ voiceLive: !1 });
|
|
457
464
|
return;
|
|
458
465
|
}
|
|
459
|
-
(
|
|
466
|
+
(T !== null || _.phase !== "off") && (_.phase === "checking" || _.phase === "choosing" ? (w += 1, V({ phase: "off" })) : K("voice_end", {})), V({
|
|
460
467
|
voiceLive: !1,
|
|
461
468
|
notice: null
|
|
462
469
|
});
|
|
463
470
|
},
|
|
464
471
|
requestOn(e = {}) {
|
|
465
|
-
if (L || !
|
|
472
|
+
if (L || !_.offered || _.phase !== "off") return;
|
|
466
473
|
let t = e.start === void 0 ? "auto" : e.start ? "start" : "choose";
|
|
467
474
|
if (t === "start" && B()) return;
|
|
468
|
-
let n = e.avatarId ??
|
|
475
|
+
let n = e.avatarId ?? _.selectedAvatarId, r = _.consentVersion, i = s.client();
|
|
469
476
|
if (i === null) {
|
|
470
|
-
|
|
477
|
+
X();
|
|
471
478
|
return;
|
|
472
479
|
}
|
|
473
480
|
t === "start" && (N = c.now());
|
|
474
|
-
let o = ++
|
|
481
|
+
let o = ++w;
|
|
475
482
|
V({
|
|
476
483
|
phase: "checking",
|
|
477
|
-
problem: B() ?
|
|
484
|
+
problem: B() ? _.problem : null,
|
|
478
485
|
notice: null
|
|
479
|
-
}),
|
|
480
|
-
if (
|
|
486
|
+
}), le((e) => i.availability(e)).then((e) => {
|
|
487
|
+
if (w !== o) return;
|
|
481
488
|
if (!e.available) {
|
|
482
|
-
|
|
489
|
+
X(a(e.reason));
|
|
483
490
|
return;
|
|
484
491
|
}
|
|
485
|
-
|
|
492
|
+
x = e.maxSessionSeconds * 1e3;
|
|
486
493
|
let i = t === "start" && n !== null && e.avatars.includes(n), s = l.getState().avatarId, c = i ? n : s !== null && e.avatars.includes(s) ? s : e.avatars[0] ?? null;
|
|
487
494
|
switch (V({
|
|
488
495
|
phase: "choosing",
|
|
@@ -491,7 +498,7 @@ function g(s) {
|
|
|
491
498
|
consentVersion: e.consentVersion
|
|
492
499
|
}), t) {
|
|
493
500
|
case "auto":
|
|
494
|
-
|
|
501
|
+
b === e.consentVersion && e.avatars.length === 1 && $.confirm();
|
|
495
502
|
return;
|
|
496
503
|
case "start":
|
|
497
504
|
i && r === e.consentVersion ? $.confirm() : N = null;
|
|
@@ -499,45 +506,46 @@ function g(s) {
|
|
|
499
506
|
case "choose": return;
|
|
500
507
|
}
|
|
501
508
|
}, (e) => {
|
|
502
|
-
|
|
509
|
+
w === o && q(e);
|
|
503
510
|
});
|
|
504
511
|
},
|
|
505
512
|
confirm() {
|
|
506
|
-
if (
|
|
507
|
-
let e =
|
|
508
|
-
e === null || t === null || (
|
|
513
|
+
if (_.phase !== "choosing" || !(_.voiceLive || j) || B()) return;
|
|
514
|
+
let e = _.selectedAvatarId, t = _.consentVersion;
|
|
515
|
+
e === null || t === null || (b = t, l.setAvatarId(e), S = !0, M = !j, j && (N ??= c.now()), U(), ce(e, t));
|
|
509
516
|
},
|
|
517
|
+
recheckRetry: Y,
|
|
510
518
|
cancelChoice() {
|
|
511
|
-
|
|
519
|
+
_.phase === "choosing" && (w += 1, N = null, V({ phase: "off" }));
|
|
512
520
|
},
|
|
513
521
|
setTabOpen(e) {
|
|
514
522
|
if (L || e === j) return;
|
|
515
523
|
if (j = e, e) {
|
|
516
|
-
V({ tabOpen: !0 }),
|
|
524
|
+
V({ tabOpen: !0 }), _.phase === "checking" && (w += 1, V({ phase: "off" }), $.requestOn({ start: !1 }));
|
|
517
525
|
return;
|
|
518
526
|
}
|
|
519
527
|
N = null;
|
|
520
|
-
let t =
|
|
528
|
+
let t = _.phase;
|
|
521
529
|
if (t === "checking" || t === "choosing") {
|
|
522
|
-
|
|
530
|
+
w += 1, V({
|
|
523
531
|
tabOpen: !1,
|
|
524
532
|
phase: "off"
|
|
525
533
|
});
|
|
526
534
|
return;
|
|
527
535
|
}
|
|
528
|
-
(
|
|
536
|
+
(T !== null || t !== "off") && !_.heldByVoice && K("user", {}), V({ tabOpen: !1 });
|
|
529
537
|
},
|
|
530
538
|
selectAvatar(e) {
|
|
531
|
-
|
|
539
|
+
_.avatars.includes(e) && V({ selectedAvatarId: e });
|
|
532
540
|
},
|
|
533
541
|
switchOff() {
|
|
534
|
-
let { phase: e } =
|
|
542
|
+
let { phase: e } = _;
|
|
535
543
|
if (!(j && N !== null && (e === "checking" || e === "starting" || e === "connecting") && c.now() - N < 1e3)) {
|
|
536
544
|
if (e === "checking" || e === "choosing") {
|
|
537
|
-
|
|
545
|
+
w += 1, N = null, V({ phase: "off" });
|
|
538
546
|
return;
|
|
539
547
|
}
|
|
540
|
-
if (e === "off" && !
|
|
548
|
+
if (e === "off" && !S) {
|
|
541
549
|
U();
|
|
542
550
|
return;
|
|
543
551
|
}
|
|
@@ -545,41 +553,41 @@ function g(s) {
|
|
|
545
553
|
}
|
|
546
554
|
},
|
|
547
555
|
resumeAudio() {
|
|
548
|
-
let e =
|
|
556
|
+
let e = E;
|
|
549
557
|
e !== null && (e.transport.resumeAudio().catch(() => void 0), e.meter?.resume().catch(() => void 0));
|
|
550
558
|
},
|
|
551
559
|
setStageMounted(e) {
|
|
552
|
-
|
|
560
|
+
O = Math.max(0, O + (e ? 1 : -1));
|
|
553
561
|
},
|
|
554
562
|
attachVideo(e) {
|
|
555
563
|
if (e === null) {
|
|
556
|
-
let e =
|
|
557
|
-
e !== null &&
|
|
564
|
+
let e = Z();
|
|
565
|
+
e !== null && de(e);
|
|
558
566
|
return;
|
|
559
567
|
}
|
|
560
|
-
let t =
|
|
561
|
-
t !== -1 &&
|
|
562
|
-
let n =
|
|
563
|
-
return n !== null && (n.detach(), n.attach(e)), () =>
|
|
568
|
+
let t = D.indexOf(e);
|
|
569
|
+
t !== -1 && D.splice(t, 1), D.push(e);
|
|
570
|
+
let n = E?.videoTrack ?? null;
|
|
571
|
+
return n !== null && (n.detach(), n.attach(e)), () => de(e);
|
|
564
572
|
},
|
|
565
573
|
route(e, t) {
|
|
566
|
-
if (
|
|
574
|
+
if (_.phase !== "ready" || C || !S || O === 0 || E === null || E.port.isAudioBlocked() || e.fallback === !1) return "speakers";
|
|
567
575
|
let n = e.voice;
|
|
568
576
|
return n !== null && n.source !== "hosted" || !t.listVoices().some((e) => e.source === "hosted") ? (R({
|
|
569
577
|
event: "notice",
|
|
570
578
|
notice: { kind: "needs-hosted-voice" }
|
|
571
|
-
}), V({ problem: "needs-hosted-voice" }), "speakers") : (
|
|
579
|
+
}), V({ problem: "needs-hosted-voice" }), "speakers") : (_.problem === "needs-hosted-voice" && V({ problem: null }), "avatar");
|
|
572
580
|
},
|
|
573
581
|
createOutput: () => W.createOutput(),
|
|
574
582
|
dispose() {
|
|
575
583
|
if (!L) {
|
|
576
|
-
L = !0, (
|
|
584
|
+
L = !0, (T !== null || _.phase !== "off") && K("user", {}), I?.(), I = null;
|
|
577
585
|
for (let e of Q.splice(0)) e();
|
|
578
|
-
|
|
586
|
+
v.clear();
|
|
579
587
|
}
|
|
580
588
|
}
|
|
581
589
|
};
|
|
582
590
|
return $;
|
|
583
591
|
}
|
|
584
592
|
//#endregion
|
|
585
|
-
export { u as LIVE_AVATAR_CONNECT_TIMEOUT_MS, c as LIVE_AVATAR_END_BEFORE_DEADLINE_MS, s as LIVE_AVATAR_IDLE_MS, l as LIVE_AVATAR_REQUEST_TIMEOUT_MS,
|
|
593
|
+
export { u as LIVE_AVATAR_CONNECT_TIMEOUT_MS, c as LIVE_AVATAR_END_BEFORE_DEADLINE_MS, s as LIVE_AVATAR_IDLE_MS, l as LIVE_AVATAR_REQUEST_TIMEOUT_MS, te as LIVE_AVATAR_TAB_OFF_GUARD_MS, h as createLiveAvatarController };
|
|
@@ -3,16 +3,18 @@ import { AvatarCopy } from '../useAvatarCopy';
|
|
|
3
3
|
import { LiveAvatarSnapshot } from './liveAvatarController';
|
|
4
4
|
/**
|
|
5
5
|
* ★ BOFF-9360: whether the avatar can take a typed question's answer now, as the Avatar tab's ask box reads it
|
|
6
|
-
* (`LiveAvatarTabSeam.ask`).
|
|
6
|
+
* (`LiveAvatarTabSeam.ask`). From the snapshot and the time (`now`) alone: it never asks `route()`, which has side effects.
|
|
7
7
|
* - `ready`: on, and nothing in the way;
|
|
8
8
|
* - `sound-blocked`: on, but the browser blocks its sound until a tap on the frame lifts it (critic m1);
|
|
9
9
|
* - `ending`: stopping, or near its time limit (`closing`), so no new reply goes to it (critic m1);
|
|
10
10
|
* - `starting`: being started or joined;
|
|
11
|
-
* - `locked`: off after a "not now" refusal that said when (`retryAt`): Turn on waits for
|
|
11
|
+
* - `locked`: off after a "not now" refusal that said when (`retryAt`), until that time (`now`, wall-clock ms): Turn on waits for it
|
|
12
|
+
* (critic m11). From that time on it is `off`, even while the snapshot still carries the time: the timer that clears it runs late
|
|
13
|
+
* after a system sleep (BOFF-9333 UI-11);
|
|
12
14
|
* - `off`: off, or being set up (checking, choosing).
|
|
13
15
|
* A device voice (no hosted voice to animate) is not predicted here: the route says so per reply, and the tab's
|
|
14
16
|
* status line already explains it (`needs-hosted-voice`).
|
|
15
17
|
*/
|
|
16
|
-
export declare function liveAvatarAskState(snapshot: LiveAvatarSnapshot): LiveAvatarAskState;
|
|
18
|
+
export declare function liveAvatarAskState(snapshot: LiveAvatarSnapshot, now?: number): LiveAvatarAskState;
|
|
17
19
|
/** The words for the avatar's state line: what went wrong, what it is doing, or null. `now`: wall-clock ms, for the retry time. */
|
|
18
20
|
export declare function liveAvatarStatusText(snapshot: LiveAvatarSnapshot, copy: AvatarCopy, now?: number): string | null;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
//#region src/shared/assistant/avatar/live/liveAvatarStatus.ts
|
|
2
|
-
function e(e) {
|
|
2
|
+
function e(e, t = Date.now()) {
|
|
3
3
|
switch (e.phase) {
|
|
4
4
|
case "ready": return e.closing ? "ending" : e.audioBlocked ? "sound-blocked" : "ready";
|
|
5
5
|
case "starting":
|
|
6
6
|
case "connecting": return "starting";
|
|
7
7
|
case "ending": return "ending";
|
|
8
|
-
default: return e.retryAt
|
|
8
|
+
default: return e.retryAt !== null && e.retryAt > t ? "locked" : "off";
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
function t(e, t, n = Date.now()) {
|