@burdenoff/fe-libs 2026.1016.1 → 2026.1016.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/shared/assistant/avatar/live/AvatarFace.d.ts +13 -0
  2. package/dist/shared/assistant/avatar/live/AvatarFace.js +46 -0
  3. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +2 -0
  4. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +172 -145
  5. package/dist/shared/assistant/avatar/live/avatarFaces.d.ts +31 -0
  6. package/dist/shared/assistant/avatar/live/avatarFaces.js +33 -0
  7. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +12 -0
  8. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +142 -75
  9. package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +5 -0
  10. package/dist/shared/assistant/avatar/live/liveAvatarController.js +3 -1
  11. package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +14 -0
  12. package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -2
  13. package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +42 -0
  14. package/dist/shared/assistant/avatar/useAvatarCopy.js +113 -75
  15. package/dist/shared/assistant/i18nManifest.d.ts +28 -0
  16. package/dist/shared/assistant/i18nManifest.js +28 -0
  17. package/dist/shared/assistant/index.d.ts +8 -0
  18. package/dist/shared/assistant/speech/replySpeechRequests.d.ts +84 -0
  19. package/dist/shared/assistant/speech/replySpeechRequests.js +70 -0
  20. package/dist/shared/assistant/speech/routingSpeechEngine.js +2 -1
  21. package/dist/shared/assistant/speech/types.d.ts +28 -1
  22. package/dist/shared/assistant/ui/AIAssistantPanel.js +128 -119
  23. package/dist/shared/assistant/ui/AssistantChatArea.js +1572 -1430
  24. package/dist/shared/assistant/ui/AssistantSpeechProvider.js +235 -191
  25. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +40 -0
  26. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.d.ts +6 -0
  27. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +99 -52
  28. package/dist/shared/assistant/ui/avatar/AvatarTabAskBox.d.ts +8 -0
  29. package/dist/shared/assistant/ui/avatar/AvatarTabAskBox.js +87 -0
  30. package/dist/shared/assistant/ui/avatar/AvatarTabLastExchange.d.ts +13 -0
  31. package/dist/shared/assistant/ui/avatar/AvatarTabLastExchange.js +97 -0
  32. package/dist/shared/assistant/ui/avatar/avatarAskGate.d.ts +104 -0
  33. package/dist/shared/assistant/ui/avatar/avatarAskGate.js +39 -0
  34. package/dist/shared/assistant/ui/avatar/avatarTabDraft.d.ts +17 -0
  35. package/dist/shared/assistant/ui/avatar/avatarTabDraft.js +22 -0
  36. package/dist/shared/assistant/ui/avatar/avatarTabExchange.d.ts +47 -0
  37. package/dist/shared/assistant/ui/avatar/avatarTabExchange.js +50 -0
  38. package/dist/shared/assistant/ui/panelOverlays.d.ts +8 -1
  39. package/dist/shared/assistant/ui/panelOverlays.js +1 -1
  40. package/dist/shared/providers/shell/I18nProvider.js +3 -3
  41. package/dist/shared-assistant.js +260 -252
  42. package/package.json +1 -1
@@ -1,18 +1,21 @@
1
1
  import { ensureWorkspaceToken as e, getStoredAuthToken as t, getStoredWorkspaceId as n, resolveGatewayUrl as r } from "../../../graphql/fetch.js";
2
- import { readSpeechEngineConfig as i } from "../../speech/createSpeechEngine.js";
3
- import { useAvatarPreferencesStore as a } from "../avatarPreferences.js";
4
- import { realAvatarScheduler as o } from "./avatarPorts.js";
5
- import { startAvatarAudioMeter as s } from "./avatarAudioMeter.js";
6
- import { decodeMp3ToPcm16k as c } from "./avatarAudioDecode.js";
7
- import { avatarApiBaseFromTtsEndpoint as l, avatarIdempotencyKey as u, createAvatarSessionClient as d } from "./avatarSessionClient.js";
8
- import { createLiveAvatarController as f } from "./liveAvatarController.js";
9
- import { openLiveKitAvatarRoom as p } from "./liveKitAvatarRoom.js";
10
- import { LiveAvatarStageView as m } from "./LiveAvatarStage.js";
11
- import { avatarSpeakerFor as h, hostedAvatarVoice as g, liveAvatarVoicesFrom as _, restoreVoiceWhenOutputLeaves as v } from "./avatarVoice.js";
12
- import { LiveAvatarTabView as y } from "./LiveAvatarTabView.js";
13
- import { jsx as b } from "react/jsx-runtime";
2
+ import { isOutputRefusal as i } from "../../speech/pieceSink.js";
3
+ import { readSpeechEngineConfig as a } from "../../speech/createSpeechEngine.js";
4
+ import { useAvatarPreferencesStore as o } from "../avatarPreferences.js";
5
+ import { realAvatarScheduler as s } from "./avatarPorts.js";
6
+ import { startAvatarAudioMeter as c } from "./avatarAudioMeter.js";
7
+ import { decodeMp3ToPcm16k as l } from "./avatarAudioDecode.js";
8
+ import { avatarApiBaseFromTtsEndpoint as u, avatarIdempotencyKey as d, createAvatarSessionClient as f } from "./avatarSessionClient.js";
9
+ import { createLiveAvatarController as p } from "./liveAvatarController.js";
10
+ import { openLiveKitAvatarRoom as m } from "./liveKitAvatarRoom.js";
11
+ import { liveAvatarAskState as h } from "./liveAvatarStatus.js";
12
+ import { LiveAvatarStageView as g } from "./LiveAvatarStage.js";
13
+ import { sanitizeAvatarImages as _ } from "./avatarFaces.js";
14
+ import { avatarSpeakerFor as v, hostedAvatarVoice as y, liveAvatarVoicesFrom as b, restoreVoiceWhenOutputLeaves as x } from "./avatarVoice.js";
15
+ import { LiveAvatarTabView as S } from "./LiveAvatarTabView.js";
16
+ import { jsx as C } from "react/jsx-runtime";
14
17
  //#region src/shared/assistant/avatar/live/createLiveAvatarSeam.tsx
15
- function x(e = globalThis) {
18
+ function w(e = globalThis) {
16
19
  let t = e;
17
20
  if (typeof t.RTCPeerConnection != "function" || typeof t.OfflineAudioContext != "function" || typeof t.AudioContext != "function" || typeof t.MediaStream != "function") return !1;
18
21
  let n = t.navigator, r = n?.userAgentData;
@@ -23,14 +26,14 @@ function x(e = globalThis) {
23
26
  let i = n?.userAgent ?? "";
24
27
  return /\b(?:Chrome|Chromium)\//.test(i) && !/\b(?:Edg|OPR|SamsungBrowser)\/|Mobile|Android|iPhone|iPad/.test(i);
25
28
  }
26
- function S() {
27
- let e = i().getHeaders?.() ?? {};
29
+ function T() {
30
+ let e = a().getHeaders?.() ?? {};
28
31
  for (let [t, n] of Object.entries(e)) if (t.toLowerCase() === "authorization" && typeof n == "string" && n.trim() !== "") return n.trim();
29
32
  let n = t();
30
33
  return n ? `Bearer ${n}` : null;
31
34
  }
32
- async function C(t) {
33
- let i = n(), a = S(), o = a?.replace(/^Bearer\s+/i, "") ?? null;
35
+ async function E(t) {
36
+ let i = n(), a = T(), o = a?.replace(/^Bearer\s+/i, "") ?? null;
34
37
  if (!i || !o) return null;
35
38
  let s = t.refused?.replace(/^Bearer\s+/i, ""), c = await e({
36
39
  url: r("workspace"),
@@ -39,60 +42,77 @@ async function C(t) {
39
42
  currentQuery: "liveAvatarSession",
40
43
  ...t.fresh ? { issuanceGuard: {
41
44
  identityKey: "liveAvatarSession",
42
- isCurrent: () => n() === i && S() === a,
45
+ isCurrent: () => n() === i && T() === a,
43
46
  accepts: (e, t) => e !== s && t === i
44
47
  } } : {}
45
48
  });
46
49
  return c ? `Bearer ${c}` : null;
47
50
  }
48
- function w() {
51
+ function D() {
49
52
  let e = null;
50
53
  return () => {
51
- let t = typeof location > "u" ? void 0 : location.href, n = l(i().endpoint, t);
54
+ let t = typeof location > "u" ? void 0 : location.href, n = u(a().endpoint, t);
52
55
  return n === null ? null : (e?.base !== n && (e = {
53
56
  base: n,
54
- client: d({
57
+ client: f({
55
58
  baseUrl: n,
56
- getAuthorization: S,
57
- getWorkspaceAuthorization: C
59
+ getAuthorization: T,
60
+ getWorkspaceAuthorization: E
58
61
  })
59
62
  }), e.client);
60
63
  };
61
64
  }
62
- var T = {
63
- getState: () => a.getState(),
64
- setAvatarEnabled: (e) => a.getState().setAvatarEnabled(e),
65
- setAvatarId: (e) => a.getState().setAvatarId(e),
66
- subscribe: (e) => a.subscribe(() => e())
67
- }, E = {
68
- getState: () => a.getState(),
69
- subscribe: (e) => a.subscribe(() => e()),
70
- setVoiceMode: (e) => a.getState().setVoiceMode(e),
71
- setAvatarSpeaker: (e) => a.getState().setAvatarSpeaker(e)
65
+ var O = {
66
+ getState: () => o.getState(),
67
+ setAvatarEnabled: (e) => o.getState().setAvatarEnabled(e),
68
+ setAvatarId: (e) => o.getState().setAvatarId(e),
69
+ subscribe: (e) => o.subscribe(() => e())
70
+ }, k = {
71
+ getState: () => o.getState(),
72
+ subscribe: (e) => o.subscribe(() => e()),
73
+ setVoiceMode: (e) => o.getState().setVoiceMode(e),
74
+ setAvatarSpeaker: (e) => o.getState().setAvatarSpeaker(e)
72
75
  };
73
- function D() {
76
+ function A(e, t) {
77
+ let n = (e) => {
78
+ i(e) || t();
79
+ };
80
+ return {
81
+ write: (t, r, i) => {
82
+ let a = e.write(t, r, i);
83
+ return a.then(void 0, n), a;
84
+ },
85
+ drain: () => {
86
+ let t = e.drain();
87
+ return t.then(void 0, n), t;
88
+ },
89
+ retract: (t) => e.retract(t),
90
+ cancel: () => e.cancel()
91
+ };
92
+ }
93
+ function j() {
74
94
  if (typeof document > "u") return null;
75
95
  let e = document.createElement("audio");
76
96
  return e.autoplay = !0, e.hidden = !0, e.setAttribute("data-testid", "assistant-live-avatar-audio"), document.body.appendChild(e), e;
77
97
  }
78
- function O(e) {
98
+ function M(e) {
79
99
  if (!(typeof window > "u")) return (t) => (window.addEventListener(e, t), () => window.removeEventListener(e, t));
80
100
  }
81
- function k(e = {}) {
82
- let t = e.preferences ?? T, n = e.controller ?? {}, r = n.subscribeWorkspaceChange ?? O("burdenoff:workspace-changed"), i = n.subscribePageHide ?? O("pagehide"), a = f({
83
- client: e.client ?? w(),
84
- openRoom: e.openRoom ?? p,
85
- scheduler: e.scheduler ?? o,
101
+ function N(e = {}) {
102
+ let t = e.preferences ?? O, n = e.controller ?? {}, r = n.subscribeWorkspaceChange ?? M("burdenoff:workspace-changed"), i = n.subscribePageHide ?? M("pagehide"), a = p({
103
+ client: e.client ?? D(),
104
+ openRoom: e.openRoom ?? m,
105
+ scheduler: e.scheduler ?? s,
86
106
  preferences: t,
87
- decode: n.decode ?? ((e, t) => c(e, { signal: t })),
88
- createAudioElement: n.createAudioElement ?? D,
107
+ decode: n.decode ?? ((e, t) => l(e, { signal: t })),
108
+ createAudioElement: n.createAudioElement ?? j,
89
109
  releaseAudioElement: n.releaseAudioElement ?? ((e) => {
90
110
  e.srcObject = null, e.remove();
91
111
  }),
92
- startMeter: n.startMeter ?? ((e, t) => typeof MediaStream > "u" ? null : s(new MediaStream([e]), t)),
93
- browserSupported: e.browserSupported ?? (() => x()),
112
+ startMeter: n.startMeter ?? ((e, t) => typeof MediaStream > "u" ? null : c(new MediaStream([e]), t)),
113
+ browserSupported: e.browserSupported ?? (() => w()),
94
114
  now: n.now ?? (() => Date.now()),
95
- newIdempotencyKey: n.newIdempotencyKey ?? (() => u()),
115
+ newIdempotencyKey: n.newIdempotencyKey ?? (() => d()),
96
116
  ...r ? { subscribeWorkspaceChange: r } : {},
97
117
  ...i ? { subscribePageHide: i } : {},
98
118
  ...n.idleMs === void 0 ? {} : { idleMs: n.idleMs },
@@ -103,47 +123,89 @@ function k(e = {}) {
103
123
  ...n.stallMs === void 0 ? {} : { stallMs: n.stallMs },
104
124
  ...n.readinessProbe === void 0 ? {} : { readinessProbe: n.readinessProbe },
105
125
  ...e.onLog ? { onLog: e.onLog } : {}
106
- }), l = _(e.avatarVoices), d = e.avatarVoiceStore ?? E, S = () => d.getState(), C = (n, r) => {
107
- let i = h(S(), {
108
- avatarVoices: l,
126
+ }), o = b(e.avatarVoices), u = e.avatarImages === void 0 ? null : _(e.avatarImages), f = e.avatarVoiceStore ?? k, T = () => f.getState(), E = (n, r) => {
127
+ let i = v(T(), {
128
+ avatarVoices: o,
109
129
  avatarId: t.getState().avatarId,
110
130
  ...e.defaultAvatarVoiceMode ? { hostDefault: e.defaultAvatarVoiceMode } : {}
111
131
  });
112
- return i === null ? null : g(i, n, r.listVoices());
113
- }, k = (e, t) => {
132
+ return i === null ? null : y(i, n, r.listVoices());
133
+ }, N = (e, t) => {
114
134
  let n = {
115
135
  ...e,
116
136
  voice: t
117
137
  };
118
- return n.audioOutput = v(a.createOutput(), () => {
138
+ return n.audioOutput = x(a.createOutput(), () => {
119
139
  n.voice = e.voice;
120
140
  }, (e) => a.subscribe(() => {
121
141
  a.getSnapshot().phase !== "ready" && e();
122
142
  })), n;
123
143
  };
124
144
  return {
125
- decorateSpeechEngine: (e) => ({
126
- id: e.id,
127
- isSupported: () => e.isSupported(),
128
- listVoices: () => e.listVoices(),
129
- subscribeVoices: (t) => e.subscribeVoices(t),
130
- speak: (t, n, r) => {
131
- let i = C(n.lang, e), o = i === null ? n : {
132
- ...n,
133
- voice: i
145
+ decorateSpeechEngine: (e) => {
146
+ let t = (t) => {
147
+ let n = E(t.lang, e), r = n === null ? t : {
148
+ ...t,
149
+ voice: n
134
150
  };
135
- if (a.route(o, e) !== "avatar") {
136
- e.speak(t, n, r);
137
- return;
151
+ return {
152
+ voice: n,
153
+ target: a.route(r, e)
154
+ };
155
+ }, n = null, r = () => {
156
+ let e = n;
157
+ n = null, e?.();
158
+ }, i = (t, r, i) => {
159
+ let o = !0, s = !1, c = () => void 0, l = () => {
160
+ o && (o = !1, n === l && (n = null), c());
161
+ }, u = () => s ? !1 : (s = !0, l(), !0), d = () => {
162
+ !o || !u() || (e.cancel(), i?.onError?.("output-left"));
163
+ };
164
+ c = a.subscribe(() => {
165
+ a.getSnapshot().phase !== "ready" && d();
166
+ }), n = l, r.audioOutput &&= A(r.audioOutput, d), r.fallback = !1, e.speak(t, r, {
167
+ ...i,
168
+ onEnd: () => {
169
+ u() && i?.onEnd?.();
170
+ },
171
+ onError: (e) => {
172
+ u() && i?.onError?.(e);
173
+ }
174
+ });
175
+ };
176
+ return {
177
+ id: e.id,
178
+ isSupported: () => e.isSupported(),
179
+ listVoices: () => e.listVoices(),
180
+ subscribeVoices: (t) => e.subscribeVoices(t),
181
+ wouldSpeakOnAvatar: (e) => t(e).target === "avatar",
182
+ speak: (n, o, s) => {
183
+ r();
184
+ let { voice: c, target: l } = t(o);
185
+ if (l !== "avatar") {
186
+ if (o.avatarOnly === !0) {
187
+ e.cancel(), s?.onError?.("output-left");
188
+ return;
189
+ }
190
+ e.speak(n, o, s);
191
+ return;
192
+ }
193
+ let u = c === null ? {
194
+ ...o,
195
+ audioOutput: a.createOutput()
196
+ } : N(o, c);
197
+ if (o.avatarOnly === !0) {
198
+ i(n, u, s);
199
+ return;
200
+ }
201
+ e.speak(n, u, s);
202
+ },
203
+ cancel: () => {
204
+ r(), e.cancel();
138
205
  }
139
- e.speak(t, i === null ? {
140
- ...n,
141
- audioOutput: a.createOutput()
142
- } : k(n, i), r);
143
- },
144
- cancel: () => e.cancel()
145
- }),
146
- Stage: ({ expanded: t, fallback: n }) => /* @__PURE__ */ b(m, {
206
+ };
207
+ },
208
+ Stage: ({ expanded: t, fallback: n }) => /* @__PURE__ */ C(g, {
147
209
  controller: a,
148
210
  expanded: t,
149
211
  fallback: n,
@@ -160,14 +222,19 @@ function k(e = {}) {
160
222
  subscribe: (e) => a.subscribe(e),
161
223
  getSnapshot: () => a.getSnapshot().tabOffered
162
224
  },
225
+ ask: {
226
+ subscribe: (e) => a.subscribe(e),
227
+ getSnapshot: () => h(a.getSnapshot())
228
+ },
163
229
  setOpen: (e) => a.setTabOpen(e),
164
230
  heldByVoice: () => a.getSnapshot().heldByVoice,
165
- View: (t) => /* @__PURE__ */ b(y, {
231
+ View: (t) => /* @__PURE__ */ C(S, {
166
232
  ...t,
167
233
  controller: a,
168
- avatarVoices: l,
169
- voiceStore: d,
234
+ avatarVoices: o,
235
+ voiceStore: f,
170
236
  ...e.avatarNames ? { avatarNames: e.avatarNames } : {},
237
+ ...u ? { avatarImages: u } : {},
171
238
  ...e.defaultAvatarVoiceMode ? { defaultAvatarVoiceMode: e.defaultAvatarVoiceMode } : {}
172
239
  })
173
240
  },
@@ -176,4 +243,4 @@ function k(e = {}) {
176
243
  };
177
244
  }
178
245
  //#endregion
179
- export { k as createLiveAvatarSeam, x as isLiveAvatarBrowserSupported, S as liveAvatarAuthorization, C as liveAvatarWorkspaceAuthorization };
246
+ export { N as createLiveAvatarSeam, w as isLiveAvatarBrowserSupported, T as liveAvatarAuthorization, E as liveAvatarWorkspaceAuthorization };
@@ -52,6 +52,11 @@ export interface LiveAvatarSnapshot {
52
52
  /** The avatar was turned on outside the tab during the voice conversation that is still live: closing the tab hands it back to Voice. */
53
53
  readonly heldByVoice: boolean;
54
54
  readonly phase: LiveAvatarPhase;
55
+ /**
56
+ * ★ BOFF-9360 (critic m1): the session's time limit is near (10 s before the server's end). The phase is still `ready` and a reply
57
+ * in flight finishes on the avatar, but no new reply goes to it, and the session ends between replies. False in every other phase.
58
+ */
59
+ readonly closing: boolean;
55
60
  /** The avatars the server offers, from the last availability answer. */
56
61
  readonly avatars: readonly string[];
57
62
  /** The avatar the next session starts with. */
@@ -11,6 +11,7 @@ var s = 12e4, c = 1e4, l = 8e3, ee = 5e3, u = 2e4, d = 1e3, f = {
11
11
  retryAt: null,
12
12
  heldByVoice: !1,
13
13
  phase: "off",
14
+ closing: !1,
14
15
  avatars: [],
15
16
  selectedAvatarId: null,
16
17
  consentVersion: null,
@@ -189,6 +190,7 @@ function g(s) {
189
190
  keepalive: t.keepalive === !0
190
191
  }).catch(() => void 0)), V({
191
192
  phase: "off",
193
+ closing: !1,
192
194
  audioBlocked: !1,
193
195
  videoReady: !1,
194
196
  ...t.problem === void 0 ? v.problem !== null && m.has(v.problem) ? { problem: null } : {} : { problem: t.problem },
@@ -364,7 +366,7 @@ function g(s) {
364
366
  }), G();
365
367
  let _ = S > 0 ? u + S - ee - c.now() : f.endsAt - s.now(), v = Math.max(0, _ - d);
366
368
  F = c.setTimeout(() => {
367
- F = null, T === a && (w = !0, W.isBusy() || K("user", { notice: "time-limit" }));
369
+ F = null, T === a && (w = !0, V({ closing: !0 }), W.isBusy() || K("user", { notice: "time-limit" }));
368
370
  }, v);
369
371
  }
370
372
  function Y(t) {
@@ -1,4 +1,18 @@
1
+ import { LiveAvatarAskState } from '../../ui/assistantLiveAvatarSeam';
1
2
  import { AvatarCopy } from '../useAvatarCopy';
2
3
  import { LiveAvatarSnapshot } from './liveAvatarController';
4
+ /**
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`). Pure, from the snapshot alone: it never asks `route()`, which has side effects.
7
+ * - `ready`: on, and nothing in the way;
8
+ * - `sound-blocked`: on, but the browser blocks its sound until a tap on the frame lifts it (critic m1);
9
+ * - `ending`: stopping, or near its time limit (`closing`), so no new reply goes to it (critic m1);
10
+ * - `starting`: being started or joined;
11
+ * - `locked`: off after a "not now" refusal that said when (`retryAt`): Turn on waits for that time (critic m11);
12
+ * - `off`: off, or being set up (checking, choosing).
13
+ * A device voice (no hosted voice to animate) is not predicted here: the route says so per reply, and the tab's
14
+ * status line already explains it (`needs-hosted-voice`).
15
+ */
16
+ export declare function liveAvatarAskState(snapshot: LiveAvatarSnapshot): LiveAvatarAskState;
3
17
  /** 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. */
4
18
  export declare function liveAvatarStatusText(snapshot: LiveAvatarSnapshot, copy: AvatarCopy, now?: number): string | null;
@@ -1,5 +1,14 @@
1
1
  //#region src/shared/assistant/avatar/live/liveAvatarStatus.ts
2
- function e(e, t, n = Date.now()) {
2
+ function e(e) {
3
+ switch (e.phase) {
4
+ case "ready": return e.closing ? "ending" : e.audioBlocked ? "sound-blocked" : "ready";
5
+ case "starting":
6
+ case "connecting": return "starting";
7
+ case "ending": return "ending";
8
+ default: return e.retryAt === null ? "off" : "locked";
9
+ }
10
+ }
11
+ function t(e, t, n = Date.now()) {
3
12
  if (e.problem === "playback-blocked" && e.audioBlocked) return null;
4
13
  let r = e.voiceLive;
5
14
  if (e.problem !== null) {
@@ -18,4 +27,4 @@ function e(e, t, n = Date.now()) {
18
27
  }
19
28
  }
20
29
  //#endregion
21
- export { e as liveAvatarStatusText };
30
+ export { e as liveAvatarAskState, t as liveAvatarStatusText };
@@ -1,3 +1,4 @@
1
+ import { AvatarAskBlocker } from '../ui/avatar/avatarAskGate';
1
2
  import { VoiceStartBlocker } from '../ui/voice/voiceStartGate';
2
3
  /** What the person can be told went wrong; the adapter maps server codes onto these. */
3
4
  export type AvatarProblem = 'unavailable' | 'not-ready' | 'already-running' | 'capacity' | 'rate-limited' | 'paused'
@@ -62,6 +63,47 @@ export interface AvatarCopy {
62
63
  voiceStartBlocked: (blocker: VoiceStartBlocker) => string;
63
64
  /** Said with a lasting reason: the avatar can be turned on, but it is asked by voice only. */
64
65
  askNeedsVoice: string;
66
+ /**
67
+ * ★ BOFF-9360: the Avatar tab's typed questions. Said while the avatar is off with voice blocked for a reason that
68
+ * lasts, and typing open: the line `askNeedsVoice` was before typing existed (critic M3).
69
+ */
70
+ askByTyping: string;
71
+ /** Said while the avatar is off, when neither typing nor voice can ask here (critic M3): turning it on is of no use. */
72
+ askNothing: string;
73
+ /** The ask box's textarea: its label, placeholder and keyboard hint. */
74
+ askLabel: string;
75
+ askPlaceholder: string;
76
+ askHint: string;
77
+ /** Send, as the composer names it (`chatInput.send`). */
78
+ askSend: string;
79
+ /** Why Send is off, in the gate's order (`avatarAskBlocker`); null for a live voice conversation (the box is hidden). */
80
+ askBlocked: (blocker: AvatarAskBlocker) => string | null;
81
+ /** A file dropped or pasted on the box is not taken. */
82
+ askNoFiles: string;
83
+ /** Before the first question of this opening. */
84
+ askEmpty: string;
85
+ /** The last exchange: its section's name and its two visible labels. */
86
+ askExchangeLabel: string;
87
+ askYouAsked: string;
88
+ askAnswer: string;
89
+ /** Which conversation the question went into (critic m9). */
90
+ askAskedIn: (title: string) => string;
91
+ askShowAll: string;
92
+ askShowLess: string;
93
+ /** While the answer is on its way: the voice session's own "Thinking…". */
94
+ askThinking: string;
95
+ /** What became of the answer's audio: never a claim that it was heard. */
96
+ askNotSpoken: string;
97
+ /** The avatar stopped part way (Turn off, the time limit, the avatar leaving): part of it was read, not all. */
98
+ askCut: string;
99
+ askNotHeard: string;
100
+ askFailed: string;
101
+ /** A failed turn whose question could not go back into the box (newer words were typed there): it is in Chat. */
102
+ askFailedInChat: string;
103
+ askAlsoInChat: string;
104
+ /** Stop reading the answer: the transcript's own words (`message.stopSpeaking*`). */
105
+ askStop: string;
106
+ askStopShort: string;
65
107
  /** Why hands-free interrupt is off while the avatar is on (the pilot rule), and that it stays off after: the session's notice, and its control's reason. */
66
108
  handsFreeOff: string;
67
109
  /**