@burdenoff/fe-libs 2026.1016.2 → 2026.1017.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) 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 +29 -0
  16. package/dist/shared/assistant/i18nManifest.js +29 -0
  17. package/dist/shared/assistant/index.d.ts +8 -0
  18. package/dist/shared/assistant/speech/hostedTtsSpeechEngine.d.ts +6 -0
  19. package/dist/shared/assistant/speech/hostedTtsSpeechEngine.js +118 -101
  20. package/dist/shared/assistant/speech/notifyCancellationSpeechEngine.js +3 -0
  21. package/dist/shared/assistant/speech/replySpeechRequests.d.ts +84 -0
  22. package/dist/shared/assistant/speech/replySpeechRequests.js +70 -0
  23. package/dist/shared/assistant/speech/routingSpeechEngine.js +13 -9
  24. package/dist/shared/assistant/speech/types.d.ts +39 -1
  25. package/dist/shared/assistant/ui/AIAssistantPanel.js +128 -119
  26. package/dist/shared/assistant/ui/AssistantChatArea.js +1572 -1430
  27. package/dist/shared/assistant/ui/AssistantMessageItem.js +110 -100
  28. package/dist/shared/assistant/ui/AssistantSpeechProvider.d.ts +10 -0
  29. package/dist/shared/assistant/ui/AssistantSpeechProvider.js +286 -225
  30. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +40 -0
  31. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.d.ts +6 -0
  32. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +99 -52
  33. package/dist/shared/assistant/ui/avatar/AvatarTabAskBox.d.ts +8 -0
  34. package/dist/shared/assistant/ui/avatar/AvatarTabAskBox.js +87 -0
  35. package/dist/shared/assistant/ui/avatar/AvatarTabLastExchange.d.ts +13 -0
  36. package/dist/shared/assistant/ui/avatar/AvatarTabLastExchange.js +97 -0
  37. package/dist/shared/assistant/ui/avatar/avatarAskGate.d.ts +104 -0
  38. package/dist/shared/assistant/ui/avatar/avatarAskGate.js +39 -0
  39. package/dist/shared/assistant/ui/avatar/avatarTabDraft.d.ts +17 -0
  40. package/dist/shared/assistant/ui/avatar/avatarTabDraft.js +22 -0
  41. package/dist/shared/assistant/ui/avatar/avatarTabExchange.d.ts +47 -0
  42. package/dist/shared/assistant/ui/avatar/avatarTabExchange.js +50 -0
  43. package/dist/shared/assistant/ui/panelOverlays.d.ts +8 -1
  44. package/dist/shared/assistant/ui/panelOverlays.js +1 -1
  45. package/dist/shared/assistant/ui/useVoiceSession.js +222 -227
  46. package/dist/shared/assistant/voice/handsfree/playbackReference.d.ts +15 -4
  47. package/dist/shared/assistant/voice/handsfree/playbackReference.js +9 -6
  48. package/dist/shared-assistant.js +270 -262
  49. package/package.json +1 -1
@@ -2,262 +2,263 @@ import { useVoiceInput as e } from "../voice/useVoiceInput.js";
2
2
  import { planSubmitTimer as t, resolveVoiceGraceMs as n } from "../voice/handsFree.js";
3
3
  import { readHandsFreeOverride as r, resolveHandsFreeEnabled as ee } from "../voice/handsfree/handsFreeSetting.js";
4
4
  import { silencePlaybackReference as te } from "../voice/handsfree/playbackReference.js";
5
- import { speechFeed as ne } from "../speech/speechFeed.js";
6
- import { INITIAL_SESSION as re, INTERRUPT_MAX_MS as ie, NOTHING_HEARD as i, advanceHeardClock as ae, isCapturing as a, isSubmittable as oe, planListeningHold as se, planReplyConsumption as ce, planVoiceAnnouncement as le, shouldRecognise as ue, spokenSoFar as o, voiceSessionReducer as de } from "../voice/voiceSession.js";
7
- import { useHandsFreeInterrupt as fe } from "../voice/handsfree/useHandsFreeInterrupt.js";
8
- import { isTurnSuperseded as pe } from "../voice/supersededTurns.js";
9
- import { captionChunks as me } from "../voice/speechPlayback.js";
5
+ import { primeHostedPlayback as ne } from "../speech/hostedTtsSpeechEngine.js";
6
+ import { speechFeed as re } from "../speech/speechFeed.js";
7
+ import { INITIAL_SESSION as ie, INTERRUPT_MAX_MS as ae, NOTHING_HEARD as i, advanceHeardClock as oe, isCapturing as a, isSubmittable as se, planListeningHold as ce, planReplyConsumption as le, planVoiceAnnouncement as ue, shouldRecognise as de, spokenSoFar as o, voiceSessionReducer as fe } from "../voice/voiceSession.js";
8
+ import { useHandsFreeInterrupt as pe } from "../voice/handsfree/useHandsFreeInterrupt.js";
9
+ import { isTurnSuperseded as me } from "../voice/supersededTurns.js";
10
+ import { captionChunks as he } from "../voice/speechPlayback.js";
10
11
  import { turnClockMs as s } from "../turnTimeline.js";
11
- import { useAssistantSpeech as he } from "./AssistantSpeechProvider.js";
12
- import { useLiveAvatarOn as ge } from "./assistantLiveAvatarSeam.js";
13
- import { useCallback as c, useEffect as l, useLayoutEffect as _e, useMemo as ve, useReducer as ye, useRef as u, useState as d } from "react";
12
+ import { useAssistantSpeech as ge } from "./AssistantSpeechProvider.js";
13
+ import { useLiveAvatarOn as _e } from "./assistantLiveAvatarSeam.js";
14
+ import { useCallback as c, useEffect as l, useLayoutEffect as ve, useMemo as ye, useReducer as be, useRef as u, useState as d } from "react";
14
15
  //#region src/shared/assistant/ui/useVoiceSession.ts
15
- var be = 4e3, xe = 2500, Se = 5e3, Ce = 12e3, we = "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=";
16
- function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersede: De, handsFreeInterrupt: Oe, handsFreeStreamNoticeAccepted: ke, onAcceptHandsFreeStreamNotice: Ae, onBargeIn: m }) {
17
- let h = n(Ee), g = u(null), [_, v] = ye(c((e, t) => de(e, t, g.current), []), re), y = he(), { setSessionSpeaking: b, setSessionMicOpen: x, speakingMessageId: S, stop: C } = y, w = u(y.toggle), je = y.toggle;
18
- _e(() => {
19
- w.current = je;
20
- }, [je]);
21
- let T = u(null), E = u({}), D = u(i), O = u(null), k = u(!1), A = u(null), [Me, j] = d(null), [M, N] = d(!1), [Ne, P] = d(!1), [F, I] = d(!1), L = u(_);
22
- L.current = _;
23
- let R = u([]), z = u(null), B = u(!1), V = c(() => {
24
- R.current = [], z.current = null, B.current = !1;
25
- }, []), H = c(() => {
16
+ var xe = 4e3, Se = 2500, Ce = 5e3, we = 12e3;
17
+ function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersede: De, handsFreeInterrupt: Oe, handsFreeStreamNoticeAccepted: ke, onAcceptHandsFreeStreamNotice: Ae, onBargeIn: je }) {
18
+ let Me = n(Ee), m = u(null), [h, g] = be(c((e, t) => fe(e, t, m.current), []), ie), _ = ge(), { setSessionSpeaking: v, setSessionMicOpen: y, speakingMessageId: b, stop: x } = _, S = u(_.toggle), C = _.toggle;
19
+ ve(() => {
20
+ S.current = C;
21
+ }, [C]);
22
+ let w = u(null), T = u({}), E = u(i), D = u(null), O = u(!1), k = u(null), [Ne, A] = d(null), [j, M] = d(!1), [Pe, N] = d(!1), [P, F] = d(!1), I = u(h);
23
+ I.current = h;
24
+ let L = u([]), R = u(null), z = u(!1), B = c(() => {
25
+ L.current = [], R.current = null, z.current = !1;
26
+ }, []), V = c(() => {
26
27
  for (;;) {
27
- let e = R.current.shift();
28
+ let e = L.current.shift();
28
29
  if (!e) return !1;
29
30
  if (!(e.stillWanted && !e.stillWanted())) {
30
- z.current = e, g.current = g.current ? `${g.current} ${e.text}` : e.text;
31
+ R.current = e, m.current = m.current ? `${m.current} ${e.text}` : e.text;
31
32
  try {
32
- w.current(e.id, e.text, e.lang);
33
+ S.current(e.id, e.text, e.lang);
33
34
  } catch {
34
- z.current = null;
35
+ R.current = null;
35
36
  continue;
36
37
  }
37
38
  return !0;
38
39
  }
39
40
  }
40
- }, []), U = u(!1), Pe = c((e) => {
41
- let t = se({
41
+ }, []), H = u(!1), Fe = c((e) => {
42
+ let t = ce({
42
43
  requested: e,
43
- held: U.current,
44
- muted: L.current.muted
44
+ held: H.current,
45
+ muted: I.current.muted
45
46
  });
46
- U.current = t.held, t.mute !== null && v({
47
+ H.current = t.held, t.mute !== null && g({
47
48
  type: "mute",
48
49
  muted: t.mute
49
50
  });
50
- }, []), W = c((e) => {
51
- let t = le(L.current);
52
- t !== "drop" && (z.current?.id === e.id || R.current.some((t) => t.id === e.id) || (R.current.push(e), t === "start" && !B.current && (B.current = !0, v({ type: "announce" }))));
53
- }, []), Fe = c((e) => {
54
- if (L.current.phase !== "thinking") {
55
- W(e);
51
+ }, []), U = c((e) => {
52
+ let t = ue(I.current);
53
+ t !== "drop" && (R.current?.id === e.id || L.current.some((t) => t.id === e.id) || (L.current.push(e), t === "start" && !z.current && (z.current = !0, g({ type: "announce" }))));
54
+ }, []), Ie = c((e) => {
55
+ if (I.current.phase !== "thinking") {
56
+ U(e);
56
57
  return;
57
58
  }
58
- R.current.push(e), B.current = !0, v({ type: "answered-locally" });
59
- }, [W]), Ie = u(f);
60
- Ie.current = f, l(() => {
61
- if (_.phase === "speaking") {
62
- let e = A.current;
63
- g.current = e && O.current === e.id && f?.id !== e.id ? e.text : f?.text ?? null;
59
+ L.current.push(e), z.current = !0, g({ type: "answered-locally" });
60
+ }, [U]), Le = u(f);
61
+ Le.current = f, l(() => {
62
+ if (h.phase === "speaking") {
63
+ let e = k.current;
64
+ m.current = e && D.current === e.id && f?.id !== e.id ? e.text : f?.text ?? null;
64
65
  return;
65
66
  }
66
- if (g.current === null) return;
67
+ if (m.current === null) return;
67
68
  let e = setTimeout(() => {
68
- g.current = null;
69
- }, xe);
69
+ m.current = null;
70
+ }, Se);
70
71
  return () => clearTimeout(e);
71
- }, [_.phase, f]);
72
- let G = u(p);
73
- G.current = p;
74
- let K = c((e, t) => {
75
- v({
72
+ }, [h.phase, f]);
73
+ let W = u(p);
74
+ W.current = p;
75
+ let G = c((e, t) => {
76
+ g({
76
77
  type: "speech",
77
78
  transcript: e,
78
79
  final: t,
79
80
  at: Date.now(),
80
- language: G.current
81
+ language: W.current
81
82
  });
82
- }, []), Le = c((e, t, n) => {
83
- v({
83
+ }, []), Re = c((e, t, n) => {
84
+ g({
84
85
  type: "speech",
85
86
  transcript: e,
86
87
  final: t,
87
88
  at: Date.now(),
88
- language: n ?? G.current,
89
+ language: n ?? W.current,
89
90
  gated: !0
90
91
  });
91
- }, []), q = u(0), Re = c(() => {
92
- V(), C(), q.current = Date.now(), N(!0), v({ type: "interrupt" });
93
- }, [C, V]), J = e({
94
- onFinalize: c((e) => K(e, !0), [K]),
92
+ }, []), K = u(0), ze = c(() => {
93
+ B(), x(), K.current = Date.now(), M(!0), g({ type: "interrupt" });
94
+ }, [x, B]), q = e({
95
+ onFinalize: c((e) => G(e, !0), [G]),
95
96
  language: p
96
- }), ze = u(m);
97
- ze.current = m;
98
- let Be = c(() => {
99
- V(), te(), C(), q.current = Date.now(), N(!0), v({ type: "interrupt" }), ze.current?.();
100
- }, [C, V]), Ve = ge(), Y = fe({
101
- enabled: ee(Oe, r(), Ve),
102
- phase: _.phase,
103
- muted: _.muted,
97
+ }), Be = u(je);
98
+ Be.current = je;
99
+ let Ve = c(() => {
100
+ B(), te(), x(), K.current = Date.now(), M(!0), g({ type: "interrupt" }), Be.current?.();
101
+ }, [x, B]), He = _e(), J = pe({
102
+ enabled: ee(Oe, r(), He),
103
+ phase: h.phase,
104
+ muted: h.muted,
104
105
  language: p,
105
106
  streamNoticeAccepted: ke ?? null,
106
107
  onAcceptStreamNotice: Ae,
107
- onBargeIn: Be,
108
- onTranscript: Le
109
- }), He = Y.mode === "notice", X = J.isListening || Y.suppressWebSpeech && Y.mode === "active";
108
+ onBargeIn: Ve,
109
+ onTranscript: Re
110
+ }), Ue = J.mode === "notice", Y = q.isListening || J.suppressWebSpeech && J.mode === "active";
110
111
  l(() => {
111
- J.interim && K(J.interim, !1);
112
- }, [J.interim, K]), l(() => {
113
- T.current && clearTimeout(T.current), P(!1), D.current = ae(D.current, _, s());
112
+ q.interim && G(q.interim, !1);
113
+ }, [q.interim, G]), l(() => {
114
+ w.current && clearTimeout(w.current), N(!1), E.current = oe(E.current, h, s());
114
115
  let e = t({
115
- phase: _.phase,
116
- muted: _.muted,
117
- spoken: o(_),
118
- holding: F,
119
- graceMs: h
116
+ phase: h.phase,
117
+ muted: h.muted,
118
+ spoken: o(h),
119
+ holding: P,
120
+ graceMs: Me
120
121
  });
121
122
  if (!e.arm) return;
122
- let n = setTimeout(() => P(!0), Math.min(800, e.delayMs)), r = s();
123
- return T.current = setTimeout(() => {
124
- E.current = {
125
- speechEnded: D.current.at ?? r,
123
+ let n = setTimeout(() => N(!0), Math.min(800, e.delayMs)), r = s();
124
+ return w.current = setTimeout(() => {
125
+ T.current = {
126
+ speechEnded: E.current.at ?? r,
126
127
  graceStarted: r,
127
128
  graceEnded: s()
128
- }, v({
129
+ }, g({
129
130
  type: "silence",
130
131
  at: Date.now()
131
132
  });
132
133
  }, e.delayMs), () => {
133
- clearTimeout(n), T.current && clearTimeout(T.current);
134
+ clearTimeout(n), w.current && clearTimeout(w.current);
134
135
  };
135
136
  }, [
136
- _,
137
- F,
138
- h
137
+ h,
138
+ P,
139
+ Me
139
140
  ]), l(() => {
140
- (_.phase !== "listening" || _.muted) && I(!1);
141
- }, [_.phase, _.muted]), l(() => {
142
- if (!M) return;
143
- let e = Date.now() - q.current, t = X ? Math.max(0, 500 - e) : Math.max(0, ie - e), n = setTimeout(() => N(!1), t);
141
+ (h.phase !== "listening" || h.muted) && F(!1);
142
+ }, [h.phase, h.muted]), l(() => {
143
+ if (!j) return;
144
+ let e = Date.now() - K.current, t = Y ? Math.max(0, 500 - e) : Math.max(0, ae - e), n = setTimeout(() => M(!1), t);
144
145
  return () => clearTimeout(n);
145
- }, [M, X]);
146
- let Ue = u("");
147
- _.phase === "listening" && (Ue.current = o(_)), l(() => {
148
- if (_.phase !== "thinking") return;
149
- x(!1), V(), j((e) => typeof e == "object" && e ? null : e);
150
- let e = Ue.current.trim();
151
- e && Te(e, Q.current ?? `vs-orphan-${Date.now().toString(36)}`, E.current, { correction: L.current.correcting }), E.current = {}, D.current = i;
152
- }, [_.phase]), l(() => {
153
- let e = ce(f?.id, _.phase, O.current, pe(f?.id));
154
- e.claim && (O.current = f?.id ?? null, e.advance && f && (f.refused ? (V(), j({
146
+ }, [j, Y]);
147
+ let We = u("");
148
+ h.phase === "listening" && (We.current = o(h)), l(() => {
149
+ if (h.phase !== "thinking") return;
150
+ y(!1), B(), A((e) => typeof e == "object" && e ? null : e);
151
+ let e = We.current.trim();
152
+ e && Te(e, Q.current ?? `vs-orphan-${Date.now().toString(36)}`, T.current, { correction: I.current.correcting }), T.current = {}, E.current = i;
153
+ }, [h.phase]), l(() => {
154
+ let e = le(f?.id, h.phase, D.current, me(f?.id));
155
+ e.claim && (D.current = f?.id ?? null, e.advance && f && (f.refused ? (B(), A({
155
156
  kind: "turn-refused",
156
157
  text: f.text
157
- }), v({ type: "turn-refused" })) : v({
158
+ }), g({ type: "turn-refused" })) : g({
158
159
  type: "reply",
159
160
  text: f.text
160
161
  })));
161
162
  }, [
162
163
  f,
163
- _.phase,
164
- V
164
+ h.phase,
165
+ B
165
166
  ]), l(() => {
166
- if (S === null || _.phase !== "thinking" || O.current === S) return;
167
- if (pe(S)) {
168
- C();
167
+ if (b === null || h.phase !== "thinking" || D.current === b) return;
168
+ if (me(b)) {
169
+ x();
169
170
  return;
170
171
  }
171
- let e = ne.peek(S);
172
- !e || e.channel !== "voice" || e.voiceSessionId === void 0 || e.voiceSessionId !== Q.current || (O.current = S, A.current = {
173
- id: S,
172
+ let e = re.peek(b);
173
+ !e || e.channel !== "voice" || e.voiceSessionId === void 0 || e.voiceSessionId !== Q.current || (D.current = b, k.current = {
174
+ id: b,
174
175
  text: e.text
175
- }, v({
176
+ }, g({
176
177
  type: "reply-audio-started",
177
- replyId: S
178
+ replyId: b
178
179
  }));
179
180
  }, [
180
- S,
181
- _.phase,
182
- C
181
+ b,
182
+ h.phase,
183
+ x
183
184
  ]), l(() => {
184
- let e = S !== null;
185
- k.current && !e && (z.current = null, (L.current.phase !== "speaking" || !H()) && v({ type: "speech-ended" })), k.current = e;
186
- }, [S, H]), l(() => {
187
- _.phase !== "listening" && j((e) => e === "nothing-heard" ? null : e);
188
- }, [_.phase]), l(() => {
189
- if (_.phase !== "speaking" || S !== null) return;
185
+ let e = b !== null;
186
+ O.current && !e && (R.current = null, (I.current.phase !== "speaking" || !V()) && g({ type: "speech-ended" })), O.current = e;
187
+ }, [b, V]), l(() => {
188
+ h.phase !== "listening" && A((e) => e === "nothing-heard" ? null : e);
189
+ }, [h.phase]), l(() => {
190
+ if (h.phase !== "speaking" || b !== null) return;
190
191
  let e = setTimeout(() => {
191
- k.current || (V(), v({ type: "speech-ended" }));
192
- }, be);
192
+ O.current || (B(), g({ type: "speech-ended" }));
193
+ }, xe);
193
194
  return () => clearTimeout(e);
194
195
  }, [
195
- _.phase,
196
- S,
197
- V
196
+ h.phase,
197
+ b,
198
+ B
198
199
  ]);
199
- let Z = y.outputNotCleared;
200
+ let X = _.outputNotCleared;
200
201
  l(() => {
201
- if (Z === null || O.current !== Z.messageId) return;
202
- let { phase: e } = L.current;
203
- e === "idle" || e === "ended" || e === "thinking" || j("reply-not-heard");
204
- }, [Z]), l(() => {
205
- _.phase === "listening" || _.phase === "thinking" || j((e) => e === "reply-not-heard" ? null : e);
206
- }, [_.phase]);
207
- let We = u(De);
208
- We.current = De;
202
+ if (X === null || D.current !== X.messageId) return;
203
+ let { phase: e } = I.current;
204
+ e === "idle" || e === "ended" || e === "thinking" || A("reply-not-heard");
205
+ }, [X]), l(() => {
206
+ h.phase === "listening" || h.phase === "thinking" || A((e) => e === "reply-not-heard" ? null : e);
207
+ }, [h.phase]);
208
+ let Z = u(De);
209
+ Z.current = De;
209
210
  let Ge = u(0);
210
211
  l(() => {
211
- let e = _.superseded;
212
- e !== Ge.current && (Ge.current = e, e !== 0 && (V(), C(), We.current?.()));
212
+ let e = h.superseded;
213
+ e !== Ge.current && (Ge.current = e, e !== 0 && (B(), x(), Z.current?.()));
213
214
  }, [
214
- _.superseded,
215
- V,
216
- C
215
+ h.superseded,
216
+ B,
217
+ x
217
218
  ]), l(() => {
218
- x(a(_)), ue(_) && !Y.suppressWebSpeech ? J.isActive || J.start("auto") : J.isActive && J.stop();
219
+ y(a(h)), de(h) && !J.suppressWebSpeech ? q.isActive || q.start("auto") : q.isActive && q.stop();
219
220
  }, [
220
- _,
221
- J,
222
- x,
223
- Y.suppressWebSpeech
221
+ h,
222
+ q,
223
+ y,
224
+ J.suppressWebSpeech
224
225
  ]), l(() => {
225
- _.phase !== "speaking" || !B.current || (B.current = !1, H() || v({ type: "speech-ended" }));
226
- }, [_.phase, H]), l(() => {
227
- J.error === "not-allowed" || J.error === "audio-capture" ? v({
226
+ h.phase !== "speaking" || !z.current || (z.current = !1, V() || g({ type: "speech-ended" }));
227
+ }, [h.phase, V]), l(() => {
228
+ q.error === "not-allowed" || q.error === "audio-capture" ? g({
228
229
  type: "fail",
229
230
  error: "mic-denied"
230
- }) : J.error === "language-not-supported" ? v({
231
+ }) : q.error === "language-not-supported" ? g({
231
232
  type: "fail",
232
233
  error: "no-speech-support"
233
- }) : J.error === "network" ? j("network") : J.error === "unknown" && j("recognition-failed");
234
- }, [J.error]), l(() => {
235
- J.error !== null || !X || j((e) => e === "network" || e === "recognition-failed" ? null : e);
236
- }, [J.error, X]), l(() => {
237
- if (X && j((e) => e === "not-capturing" ? null : e), !a(_) || X || He) return;
238
- let e = setTimeout(() => j("not-capturing"), Se);
234
+ }) : q.error === "network" ? A("network") : q.error === "unknown" && A("recognition-failed");
235
+ }, [q.error]), l(() => {
236
+ q.error !== null || !Y || A((e) => e === "network" || e === "recognition-failed" ? null : e);
237
+ }, [q.error, Y]), l(() => {
238
+ if (Y && A((e) => e === "not-capturing" ? null : e), !a(h) || Y || Ue) return;
239
+ let e = setTimeout(() => A("not-capturing"), Ce);
239
240
  return () => clearTimeout(e);
240
241
  }, [
241
- _,
242
- X,
243
- He
242
+ h,
243
+ Y,
244
+ Ue
244
245
  ]), l(() => {
245
- if (_.phase !== "listening" || _.muted || !X || o(_) !== "") return;
246
- let e = setTimeout(() => j("nothing-heard"), Ce);
246
+ if (h.phase !== "listening" || h.muted || !Y || o(h) !== "") return;
247
+ let e = setTimeout(() => A("nothing-heard"), we);
247
248
  return () => clearTimeout(e);
248
- }, [_, X]), l(() => {
249
- o(_) !== "" && j(null);
250
- }, [_]);
249
+ }, [h, Y]), l(() => {
250
+ o(h) !== "" && A(null);
251
+ }, [h]);
251
252
  let Q = u(null), Ke = c(() => {
252
- if (N(!1), P(!1), I(!1), V(), U.current = !1, !J.isSupported) {
253
- v({
253
+ if (M(!1), N(!1), F(!1), B(), H.current = !1, !q.isSupported) {
254
+ g({
254
255
  type: "fail",
255
256
  error: "no-speech-support"
256
257
  });
257
258
  return;
258
259
  }
259
- if (y.isBlocked) {
260
- v({
260
+ if (_.isBlocked) {
261
+ g({
261
262
  type: "fail",
262
263
  error: "no-voice-support"
263
264
  });
@@ -270,103 +271,97 @@ function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersed
270
271
  t.volume = 0, e.speak(t);
271
272
  }
272
273
  } catch {}
273
- try {
274
- if (typeof window < "u" && typeof window.Audio == "function") {
275
- let e = new window.Audio(we);
276
- e.volume = 0, e.play().catch(() => {});
277
- }
278
- } catch {}
279
- b(!0), j(null), O.current = Ie.current?.id ?? null, A.current = null, Q.current = `vs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, v({ type: "start" });
274
+ ne(), v(!0), A(null), D.current = Le.current?.id ?? null, k.current = null, Q.current = `vs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, g({ type: "start" });
280
275
  }, [
281
- J.isSupported,
282
- y.isBlocked,
283
- b,
284
- V
276
+ q.isSupported,
277
+ _.isBlocked,
278
+ v,
279
+ B
285
280
  ]), qe = c(() => {
286
- N(!1), P(!1), I(!1), V(), U.current = !1, Q.current = null, v({ type: "end" }), C(), b(!1);
281
+ M(!1), N(!1), F(!1), B(), H.current = !1, Q.current = null, g({ type: "end" }), x(), v(!1);
287
282
  }, [
288
- C,
289
- b,
290
- V
283
+ x,
284
+ v,
285
+ B
291
286
  ]), Je = c((e) => Q.current === e, []), Ye = c(() => {
292
- let e = L.current;
293
- e.phase !== "listening" || e.muted || I(!0);
287
+ let e = I.current;
288
+ e.phase !== "listening" || e.muted || F(!0);
294
289
  }, []), Xe = c(() => {
295
- I(!1);
296
- let e = L.current;
297
- if (e.phase !== "listening" || e.muted || !oe(o(e))) return;
290
+ F(!1);
291
+ let e = I.current;
292
+ if (e.phase !== "listening" || e.muted || !se(o(e))) return;
298
293
  let t = s();
299
- E.current = {
300
- speechEnded: D.current.at ?? t,
294
+ T.current = {
295
+ speechEnded: E.current.at ?? t,
301
296
  graceStarted: t,
302
297
  graceEnded: t
303
- }, v({
298
+ }, g({
304
299
  type: "silence",
305
300
  at: Date.now()
306
301
  });
307
302
  }, []), Ze = c(() => {
308
- v({
303
+ g({
309
304
  type: "mute",
310
- muted: !L.current.muted
305
+ muted: !I.current.muted
311
306
  });
312
307
  }, []);
313
308
  l(() => () => {
314
- b(!1), x(!1);
315
- }, [b, x]);
316
- let $ = me(y.spokenChunks, _.phase, S);
317
- return ve(() => ({
318
- session: _,
319
- notice: Me ?? (Y.fallbackReason !== null && Y.fallbackReason !== "notice-declined" ? "hands-free-fallback" : Y.nothingHeard ? "hands-free-nothing-heard" : null),
309
+ v(!1), y(!1);
310
+ }, [v, y]);
311
+ let $ = he(_.spokenChunks, h.phase, b);
312
+ return ye(() => ({
313
+ session: h,
314
+ notice: Ne ?? (J.fallbackReason !== null && J.fallbackReason !== "notice-declined" ? "hands-free-fallback" : J.nothingHeard ? "hands-free-nothing-heard" : null),
320
315
  spokenChunks: $,
321
- capturing: X,
322
- active: _.phase !== "idle" && (_.phase !== "ended" || _.error !== null),
323
- interrupting: M,
324
- awaitingEndpoint: Ne,
325
- holding: F,
326
- correcting: _.correcting,
316
+ capturing: Y,
317
+ active: h.phase !== "idle" && (h.phase !== "ended" || h.error !== null),
318
+ interrupting: j,
319
+ awaitingEndpoint: Pe,
320
+ holding: P,
321
+ correcting: h.correcting,
327
322
  handsFree: {
328
- mode: Y.mode,
329
- nothingHeard: Y.nothingHeard,
330
- transportKind: Y.transportKind,
331
- fallbackReason: Y.fallbackReason,
332
- privacyNotice: Y.privacyNotice
323
+ mode: J.mode,
324
+ nothingHeard: J.nothingHeard,
325
+ transportKind: J.transportKind,
326
+ fallbackReason: J.fallbackReason,
327
+ privacyNotice: J.privacyNotice
333
328
  },
334
- available: J.isSupported && !y.isBlocked,
329
+ available: q.isSupported && !_.isBlocked,
335
330
  start: Ke,
336
331
  end: qe,
337
332
  isCurrentSession: Je,
338
333
  toggleMute: Ze,
339
- interrupt: Re,
334
+ interrupt: ze,
340
335
  letMeFinish: Ye,
341
336
  imDone: Xe,
342
- announce: W,
343
- holdListening: Pe,
344
- answerLocally: Fe
337
+ announce: U,
338
+ holdListening: Fe,
339
+ answerLocally: Ie
345
340
  }), [
346
- _,
347
- Me,
348
- M,
341
+ h,
349
342
  Ne,
350
- F,
351
- X,
352
- Y.mode,
353
- Y.nothingHeard,
354
- Y.transportKind,
355
- Y.fallbackReason,
356
- Y.privacyNotice,
357
- J.isSupported,
358
- y.isBlocked,
343
+ j,
344
+ Pe,
345
+ P,
346
+ Y,
347
+ J.mode,
348
+ J.nothingHeard,
349
+ J.transportKind,
350
+ J.fallbackReason,
351
+ J.privacyNotice,
352
+ q.isSupported,
353
+ _.isBlocked,
359
354
  $,
360
355
  Ke,
361
356
  qe,
362
357
  Je,
363
358
  Ze,
364
- Re,
359
+ ze,
365
360
  Ye,
366
361
  Xe,
367
- W,
368
- Pe,
369
- Fe
362
+ U,
363
+ Fe,
364
+ Ie
370
365
  ]);
371
366
  }
372
367
  //#endregion
@@ -3,10 +3,11 @@
3
3
  * available to the capture worklet's second input so the echo gate can meter it
4
4
  * on the microphone's clock.
5
5
  *
6
- * Process-wide registry, because the two sides are far apart: the hosted TTS
7
- * engine creates one `Audio` element per chunk and the capture graph lives in
8
- * the voice session. With nothing attached (hands-free off, or capture not yet
9
- * started) `tapPlaybackElement` is a no-op and playback is untouched.
6
+ * Process-wide registry, because the two sides are far apart: while capture is
7
+ * attached the hosted TTS engine creates one `Audio` element per chunk, and the
8
+ * capture graph lives in the voice session. With nothing attached (hands-free
9
+ * off, or capture not yet started) `tapPlaybackElement` is a no-op and the
10
+ * engine plays on its one reusable element instead (BOFF-9408).
10
11
  *
11
12
  * When attached, each element is routed
12
13
  * element -> MediaElementSource -> gain -> { reference bus, speakers }
@@ -26,6 +27,16 @@ export declare function attachPlaybackReference(context: AudioContext, reference
26
27
  * microphone never cuts a reply off mid-word.
27
28
  */
28
29
  export declare function detachPlaybackReference(closeContext: () => void): void;
30
+ /**
31
+ * Is a capture graph listening for the assistant's output right now?
32
+ *
33
+ * The hosted engine asks before each piece (BOFF-9408): while attached it plays
34
+ * a fresh element and taps it; otherwise it reuses its one unlocked element.
35
+ * A tapped element is bound to THIS context for good (`createMediaElementSource`
36
+ * works once per element), so reusing it after the context closes would play
37
+ * into nothing.
38
+ */
39
+ export declare function isPlaybackReferenceAttached(): boolean;
29
40
  /** Route a freshly created playback element through the reference bus. */
30
41
  export declare function tapPlaybackElement(element: HTMLMediaElement): void;
31
42
  /** Cut every tapped element's output at once (a barge-in). */