@burdenoff/fe-libs 2026.1028.2 → 2026.1028.4
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/turnTimeline.d.ts +18 -6
- package/dist/shared/assistant/turnTimeline.js +57 -39
- package/dist/shared/assistant/turnTimingReport.d.ts +10 -1
- package/dist/shared/assistant/turnTimingReport.js +19 -6
- package/dist/shared/assistant/ui/AssistantSpeechProvider.js +239 -237
- package/dist/shared/assistant/ui/useVoiceSession.js +218 -202
- package/dist/shared/assistant/voice/handsfree/handsFreeController.d.ts +3 -0
- package/dist/shared/assistant/voice/handsfree/handsFreeController.js +5 -1
- package/dist/shared/assistant/voice/handsfree/useHandsFreeInterrupt.d.ts +1 -0
- package/dist/shared/assistant/voice/handsfree/useHandsFreeInterrupt.js +1 -0
- package/dist/shared/assistant/voice/useVoiceInput.d.ts +3 -1
- package/dist/shared/assistant/voice/useVoiceInput.js +49 -43
- package/dist/shared/config/gatewayUrls.d.ts +13 -0
- package/dist/shared/config/gatewayUrls.js +7 -3
- package/dist/shared/config/index.d.ts +1 -1
- package/dist/shared-assistant.js +206 -206
- package/dist/shared-config.js +16 -16
- package/dist/shared.js +140 -140
- package/package.json +1 -1
|
@@ -4,33 +4,37 @@ import { readHandsFreeOverride as r, resolveHandsFreeEnabled as ee } from "../vo
|
|
|
4
4
|
import { silencePlaybackReference as te } from "../voice/handsfree/playbackReference.js";
|
|
5
5
|
import { primeHostedPlayback as ne } from "../speech/hostedTtsSpeechEngine.js";
|
|
6
6
|
import { speechFeed as re } from "../speech/speechFeed.js";
|
|
7
|
-
import { INITIAL_SESSION as ie, INTERRUPT_MAX_MS as ae, NOTHING_HEARD as
|
|
8
|
-
import { useHandsFreeInterrupt as
|
|
9
|
-
import { isTurnSuperseded as
|
|
10
|
-
import { captionChunks as
|
|
11
|
-
import { turnClockMs as
|
|
12
|
-
import { useAssistantSpeech as
|
|
13
|
-
import { useLiveAvatarOn as
|
|
14
|
-
import { useCallback as
|
|
7
|
+
import { INITIAL_SESSION as ie, INTERRUPT_MAX_MS as ae, NOTHING_HEARD as oe, advanceHeardClock as se, isCapturing as ce, isSubmittable as le, planListeningHold as ue, planReplyConsumption as de, planVoiceAnnouncement as fe, shouldRecognise as pe, spokenSoFar as i, voiceSessionReducer as me } from "../voice/voiceSession.js";
|
|
8
|
+
import { useHandsFreeInterrupt as he } from "../voice/handsfree/useHandsFreeInterrupt.js";
|
|
9
|
+
import { isTurnSuperseded as ge } from "../voice/supersededTurns.js";
|
|
10
|
+
import { captionChunks as _e } from "../voice/speechPlayback.js";
|
|
11
|
+
import { turnClockMs as a } from "../turnTimeline.js";
|
|
12
|
+
import { useAssistantSpeech as ve } from "./AssistantSpeechProvider.js";
|
|
13
|
+
import { useLiveAvatarOn as ye } from "./assistantLiveAvatarSeam.js";
|
|
14
|
+
import { useCallback as o, useEffect as s, useLayoutEffect as be, useMemo as xe, useReducer as Se, useRef as c, useState as l } from "react";
|
|
15
15
|
//#region src/shared/assistant/ui/useVoiceSession.ts
|
|
16
|
-
var
|
|
17
|
-
function
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}, [
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
var Ce = 4e3, we = 2500, Te = 5e3, Ee = 12e3;
|
|
17
|
+
function De({ onSubmit: De, latestReply: u, language: d, graceMs: Oe, onSupersede: ke, handsFreeInterrupt: Ae, handsFreeStreamNoticeAccepted: je, onAcceptHandsFreeStreamNotice: Me, onBargeIn: Ne }) {
|
|
18
|
+
let f = n(Oe), p = c(null), [m, h] = Se(o((e, t) => me(e, t, p.current), []), ie), g = ve(), { setSessionSpeaking: _, setSessionMicOpen: v, speakingMessageId: y, stop: b } = g, x = c(g.toggle), Pe = g.toggle;
|
|
19
|
+
be(() => {
|
|
20
|
+
x.current = Pe;
|
|
21
|
+
}, [Pe]);
|
|
22
|
+
let S = c(null), C = c({}), w = c(oe), T = c(void 0), E = c(void 0);
|
|
23
|
+
s(() => {
|
|
24
|
+
(m.phase === "idle" || m.phase === "ended") && (T.current = void 0, E.current = void 0);
|
|
25
|
+
}, [m.phase]);
|
|
26
|
+
let D = c(null), O = c(!1), k = c(null), [Fe, A] = l(null), [j, M] = l(!1), [Ie, N] = l(!1), [P, F] = l(!1), I = c(m);
|
|
27
|
+
I.current = m;
|
|
28
|
+
let L = c([]), R = c(null), z = c(!1), B = o(() => {
|
|
25
29
|
L.current = [], R.current = null, z.current = !1;
|
|
26
|
-
}, []), V =
|
|
30
|
+
}, []), V = o(() => {
|
|
27
31
|
for (;;) {
|
|
28
32
|
let e = L.current.shift();
|
|
29
33
|
if (!e) return !1;
|
|
30
34
|
if (!(e.stillWanted && !e.stillWanted())) {
|
|
31
|
-
R.current = e,
|
|
35
|
+
R.current = e, p.current = p.current ? `${p.current} ${e.text}` : e.text;
|
|
32
36
|
try {
|
|
33
|
-
|
|
37
|
+
x.current(e.id, e.text, e.lang);
|
|
34
38
|
} catch {
|
|
35
39
|
R.current = null;
|
|
36
40
|
continue;
|
|
@@ -38,50 +42,50 @@ function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersed
|
|
|
38
42
|
return !0;
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
|
-
}, []), H =
|
|
42
|
-
let t =
|
|
45
|
+
}, []), H = c(!1), Le = o((e) => {
|
|
46
|
+
let t = ue({
|
|
43
47
|
requested: e,
|
|
44
48
|
held: H.current,
|
|
45
49
|
muted: I.current.muted
|
|
46
50
|
});
|
|
47
|
-
H.current = t.held, t.mute !== null &&
|
|
51
|
+
H.current = t.held, t.mute !== null && h({
|
|
48
52
|
type: "mute",
|
|
49
53
|
muted: t.mute
|
|
50
54
|
});
|
|
51
|
-
}, []), U =
|
|
52
|
-
let t =
|
|
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,
|
|
54
|
-
}, []),
|
|
55
|
+
}, []), U = o((e) => {
|
|
56
|
+
let t = fe(I.current);
|
|
57
|
+
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, h({ type: "announce" }))));
|
|
58
|
+
}, []), Re = o((e) => {
|
|
55
59
|
if (I.current.phase !== "thinking") {
|
|
56
60
|
U(e);
|
|
57
61
|
return;
|
|
58
62
|
}
|
|
59
|
-
L.current.push(e), z.current = !0,
|
|
60
|
-
}, [U]),
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
+
L.current.push(e), z.current = !0, h({ type: "answered-locally" });
|
|
64
|
+
}, [U]), ze = c(u);
|
|
65
|
+
ze.current = u, s(() => {
|
|
66
|
+
if (m.phase === "speaking") {
|
|
63
67
|
let e = k.current;
|
|
64
|
-
|
|
68
|
+
p.current = e && D.current === e.id && u?.id !== e.id ? e.text : u?.text ?? null;
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
67
|
-
if (
|
|
71
|
+
if (p.current === null) return;
|
|
68
72
|
let e = setTimeout(() => {
|
|
69
|
-
|
|
70
|
-
},
|
|
73
|
+
p.current = null;
|
|
74
|
+
}, we);
|
|
71
75
|
return () => clearTimeout(e);
|
|
72
|
-
}, [
|
|
73
|
-
let W =
|
|
74
|
-
W.current =
|
|
75
|
-
let G =
|
|
76
|
-
|
|
76
|
+
}, [m.phase, u]);
|
|
77
|
+
let W = c(d);
|
|
78
|
+
W.current = d;
|
|
79
|
+
let G = o((e, t) => {
|
|
80
|
+
h({
|
|
77
81
|
type: "speech",
|
|
78
82
|
transcript: e,
|
|
79
83
|
final: t,
|
|
80
84
|
at: Date.now(),
|
|
81
85
|
language: W.current
|
|
82
86
|
});
|
|
83
|
-
}, []),
|
|
84
|
-
|
|
87
|
+
}, []), Be = o((e, t, n) => {
|
|
88
|
+
h({
|
|
85
89
|
type: "speech",
|
|
86
90
|
transcript: e,
|
|
87
91
|
final: t,
|
|
@@ -89,176 +93,185 @@ function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersed
|
|
|
89
93
|
language: n ?? W.current,
|
|
90
94
|
gated: !0
|
|
91
95
|
});
|
|
92
|
-
}, []), K =
|
|
93
|
-
B(),
|
|
94
|
-
}, [
|
|
95
|
-
onFinalize:
|
|
96
|
-
language:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
96
|
+
}, []), K = c(0), Ve = o(() => {
|
|
97
|
+
B(), b(), K.current = Date.now(), M(!0), h({ type: "interrupt" });
|
|
98
|
+
}, [b, B]), q = e({
|
|
99
|
+
onFinalize: o((e) => G(e, !0), [G]),
|
|
100
|
+
language: d,
|
|
101
|
+
onSpeechEnd: (e) => {
|
|
102
|
+
I.current.phase === "listening" && (E.current = e);
|
|
103
|
+
}
|
|
104
|
+
}), He = c(Ne);
|
|
105
|
+
He.current = Ne;
|
|
106
|
+
let Ue = o(() => {
|
|
107
|
+
B(), te(), b(), K.current = Date.now(), M(!0), h({ type: "interrupt" }), He.current?.();
|
|
108
|
+
}, [b, B]), We = ye(), J = he({
|
|
109
|
+
enabled: ee(Ae, r(), We),
|
|
110
|
+
phase: m.phase,
|
|
111
|
+
muted: m.muted,
|
|
112
|
+
language: d,
|
|
113
|
+
streamNoticeAccepted: je ?? null,
|
|
114
|
+
onAcceptStreamNotice: Me,
|
|
115
|
+
onBargeIn: Ue,
|
|
116
|
+
onTranscript: Be,
|
|
117
|
+
onSpeechFrame: (e) => {
|
|
118
|
+
I.current.phase === "listening" && !I.current.muted && (T.current = e);
|
|
119
|
+
}
|
|
120
|
+
}), Ge = J.mode === "notice", Y = q.isListening || J.suppressWebSpeech && J.mode === "active";
|
|
121
|
+
s(() => {
|
|
112
122
|
q.interim && G(q.interim, !1);
|
|
113
|
-
}, [q.interim, G]),
|
|
114
|
-
|
|
123
|
+
}, [q.interim, G]), s(() => {
|
|
124
|
+
S.current && clearTimeout(S.current), N(!1), w.current = se(w.current, m, a());
|
|
115
125
|
let e = t({
|
|
116
|
-
phase:
|
|
117
|
-
muted:
|
|
118
|
-
spoken:
|
|
126
|
+
phase: m.phase,
|
|
127
|
+
muted: m.muted,
|
|
128
|
+
spoken: i(m),
|
|
119
129
|
holding: P,
|
|
120
|
-
graceMs:
|
|
130
|
+
graceMs: f
|
|
121
131
|
});
|
|
122
132
|
if (!e.arm) return;
|
|
123
|
-
let n = setTimeout(() => N(!0), Math.min(800, e.delayMs)), r =
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
speechEnded:
|
|
133
|
+
let n = setTimeout(() => N(!0), Math.min(800, e.delayMs)), r = a();
|
|
134
|
+
return S.current = setTimeout(() => {
|
|
135
|
+
C.current = {
|
|
136
|
+
speechEnded: w.current.at ?? r,
|
|
137
|
+
lastSpeechFrame: T.current,
|
|
138
|
+
recognizerSpeechEnded: E.current,
|
|
139
|
+
selectedGraceMs: f,
|
|
127
140
|
graceStarted: r,
|
|
128
|
-
graceEnded:
|
|
129
|
-
},
|
|
141
|
+
graceEnded: a()
|
|
142
|
+
}, h({
|
|
130
143
|
type: "silence",
|
|
131
144
|
at: Date.now()
|
|
132
145
|
});
|
|
133
146
|
}, e.delayMs), () => {
|
|
134
|
-
clearTimeout(n),
|
|
147
|
+
clearTimeout(n), S.current && clearTimeout(S.current);
|
|
135
148
|
};
|
|
136
149
|
}, [
|
|
137
|
-
|
|
150
|
+
m,
|
|
138
151
|
P,
|
|
139
|
-
|
|
140
|
-
]),
|
|
141
|
-
(
|
|
142
|
-
}, [
|
|
152
|
+
f
|
|
153
|
+
]), s(() => {
|
|
154
|
+
(m.phase !== "listening" || m.muted) && F(!1);
|
|
155
|
+
}, [m.phase, m.muted]), s(() => {
|
|
143
156
|
if (!j) return;
|
|
144
157
|
let e = Date.now() - K.current, t = Y ? Math.max(0, 500 - e) : Math.max(0, ae - e), n = setTimeout(() => M(!1), t);
|
|
145
158
|
return () => clearTimeout(n);
|
|
146
159
|
}, [j, Y]);
|
|
147
|
-
let
|
|
148
|
-
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
let e =
|
|
152
|
-
e &&
|
|
153
|
-
}, [
|
|
154
|
-
let e =
|
|
155
|
-
e.claim && (D.current =
|
|
160
|
+
let Ke = c("");
|
|
161
|
+
m.phase === "listening" && (Ke.current = i(m)), s(() => {
|
|
162
|
+
if (m.phase !== "thinking") return;
|
|
163
|
+
v(!1), B(), A((e) => typeof e == "object" && e ? null : e);
|
|
164
|
+
let e = Ke.current.trim();
|
|
165
|
+
e && De(e, Q.current ?? `vs-orphan-${Date.now().toString(36)}`, C.current, { correction: I.current.correcting }), C.current = {}, w.current = oe, T.current = void 0, E.current = void 0;
|
|
166
|
+
}, [m.phase]), s(() => {
|
|
167
|
+
let e = de(u?.id, m.phase, D.current, ge(u?.id));
|
|
168
|
+
e.claim && (D.current = u?.id ?? null, e.advance && u && (u.refused ? (B(), A({
|
|
156
169
|
kind: "turn-refused",
|
|
157
|
-
text:
|
|
158
|
-
}),
|
|
170
|
+
text: u.text
|
|
171
|
+
}), h({ type: "turn-refused" })) : h({
|
|
159
172
|
type: "reply",
|
|
160
|
-
text:
|
|
173
|
+
text: u.text
|
|
161
174
|
})));
|
|
162
175
|
}, [
|
|
163
|
-
|
|
164
|
-
|
|
176
|
+
u,
|
|
177
|
+
m.phase,
|
|
165
178
|
B
|
|
166
|
-
]),
|
|
167
|
-
if (
|
|
168
|
-
if (
|
|
169
|
-
|
|
179
|
+
]), s(() => {
|
|
180
|
+
if (y === null || m.phase !== "thinking" || D.current === y) return;
|
|
181
|
+
if (ge(y)) {
|
|
182
|
+
b();
|
|
170
183
|
return;
|
|
171
184
|
}
|
|
172
|
-
let e = re.peek(
|
|
173
|
-
!e || e.channel !== "voice" || e.voiceSessionId === void 0 || e.voiceSessionId !== Q.current || (D.current =
|
|
174
|
-
id:
|
|
185
|
+
let e = re.peek(y);
|
|
186
|
+
!e || e.channel !== "voice" || e.voiceSessionId === void 0 || e.voiceSessionId !== Q.current || (D.current = y, k.current = {
|
|
187
|
+
id: y,
|
|
175
188
|
text: e.text
|
|
176
|
-
},
|
|
189
|
+
}, h({
|
|
177
190
|
type: "reply-audio-started",
|
|
178
|
-
replyId:
|
|
191
|
+
replyId: y
|
|
179
192
|
}));
|
|
180
193
|
}, [
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
]),
|
|
185
|
-
let e =
|
|
186
|
-
O.current && !e && (R.current = null, (I.current.phase !== "speaking" || !V()) &&
|
|
187
|
-
}, [
|
|
188
|
-
|
|
189
|
-
}, [
|
|
190
|
-
if (
|
|
194
|
+
y,
|
|
195
|
+
m.phase,
|
|
196
|
+
b
|
|
197
|
+
]), s(() => {
|
|
198
|
+
let e = y !== null;
|
|
199
|
+
O.current && !e && (R.current = null, (I.current.phase !== "speaking" || !V()) && h({ type: "speech-ended" })), O.current = e;
|
|
200
|
+
}, [y, V]), s(() => {
|
|
201
|
+
m.phase !== "listening" && A((e) => e === "nothing-heard" ? null : e);
|
|
202
|
+
}, [m.phase]), s(() => {
|
|
203
|
+
if (m.phase !== "speaking" || y !== null) return;
|
|
191
204
|
let e = setTimeout(() => {
|
|
192
|
-
O.current || (B(),
|
|
193
|
-
},
|
|
205
|
+
O.current || (B(), h({ type: "speech-ended" }));
|
|
206
|
+
}, Ce);
|
|
194
207
|
return () => clearTimeout(e);
|
|
195
208
|
}, [
|
|
196
|
-
|
|
197
|
-
|
|
209
|
+
m.phase,
|
|
210
|
+
y,
|
|
198
211
|
B
|
|
199
212
|
]);
|
|
200
|
-
let X =
|
|
201
|
-
|
|
213
|
+
let X = g.outputNotCleared;
|
|
214
|
+
s(() => {
|
|
202
215
|
if (X === null || D.current !== X.messageId) return;
|
|
203
216
|
let { phase: e } = I.current;
|
|
204
217
|
e === "idle" || e === "ended" || e === "thinking" || A("reply-not-heard");
|
|
205
|
-
}, [X]),
|
|
206
|
-
|
|
207
|
-
}, [
|
|
208
|
-
let Z =
|
|
209
|
-
Z.current =
|
|
210
|
-
let
|
|
211
|
-
|
|
212
|
-
let e =
|
|
213
|
-
e !==
|
|
218
|
+
}, [X]), s(() => {
|
|
219
|
+
m.phase === "listening" || m.phase === "thinking" || A((e) => e === "reply-not-heard" ? null : e);
|
|
220
|
+
}, [m.phase]);
|
|
221
|
+
let Z = c(ke);
|
|
222
|
+
Z.current = ke;
|
|
223
|
+
let qe = c(0);
|
|
224
|
+
s(() => {
|
|
225
|
+
let e = m.superseded;
|
|
226
|
+
e !== qe.current && (qe.current = e, e !== 0 && (B(), b(), Z.current?.()));
|
|
214
227
|
}, [
|
|
215
|
-
|
|
228
|
+
m.superseded,
|
|
216
229
|
B,
|
|
217
|
-
|
|
218
|
-
]),
|
|
219
|
-
|
|
230
|
+
b
|
|
231
|
+
]), s(() => {
|
|
232
|
+
v(ce(m)), pe(m) && !J.suppressWebSpeech ? q.isActive || q.start("auto") : q.isActive && q.stop();
|
|
220
233
|
}, [
|
|
221
|
-
|
|
234
|
+
m,
|
|
222
235
|
q,
|
|
223
|
-
|
|
236
|
+
v,
|
|
224
237
|
J.suppressWebSpeech
|
|
225
|
-
]),
|
|
226
|
-
|
|
227
|
-
}, [
|
|
228
|
-
q.error === "not-allowed" || q.error === "audio-capture" ?
|
|
238
|
+
]), s(() => {
|
|
239
|
+
m.phase !== "speaking" || !z.current || (z.current = !1, V() || h({ type: "speech-ended" }));
|
|
240
|
+
}, [m.phase, V]), s(() => {
|
|
241
|
+
q.error === "not-allowed" || q.error === "audio-capture" ? h({
|
|
229
242
|
type: "fail",
|
|
230
243
|
error: "mic-denied"
|
|
231
|
-
}) : q.error === "language-not-supported" ?
|
|
244
|
+
}) : q.error === "language-not-supported" ? h({
|
|
232
245
|
type: "fail",
|
|
233
246
|
error: "no-speech-support"
|
|
234
247
|
}) : q.error === "network" ? A("network") : q.error === "unknown" && A("recognition-failed");
|
|
235
|
-
}, [q.error]),
|
|
248
|
+
}, [q.error]), s(() => {
|
|
236
249
|
q.error !== null || !Y || A((e) => e === "network" || e === "recognition-failed" ? null : e);
|
|
237
|
-
}, [q.error, Y]),
|
|
238
|
-
if (Y && A((e) => e === "not-capturing" ? null : e), !
|
|
239
|
-
let e = setTimeout(() => A("not-capturing"),
|
|
250
|
+
}, [q.error, Y]), s(() => {
|
|
251
|
+
if (Y && A((e) => e === "not-capturing" ? null : e), !ce(m) || Y || Ge) return;
|
|
252
|
+
let e = setTimeout(() => A("not-capturing"), Te);
|
|
240
253
|
return () => clearTimeout(e);
|
|
241
254
|
}, [
|
|
242
|
-
|
|
255
|
+
m,
|
|
243
256
|
Y,
|
|
244
|
-
|
|
245
|
-
]),
|
|
246
|
-
if (
|
|
247
|
-
let e = setTimeout(() => A("nothing-heard"),
|
|
257
|
+
Ge
|
|
258
|
+
]), s(() => {
|
|
259
|
+
if (m.phase !== "listening" || m.muted || !Y || i(m) !== "") return;
|
|
260
|
+
let e = setTimeout(() => A("nothing-heard"), Ee);
|
|
248
261
|
return () => clearTimeout(e);
|
|
249
|
-
}, [
|
|
250
|
-
|
|
251
|
-
}, [
|
|
252
|
-
let Q =
|
|
262
|
+
}, [m, Y]), s(() => {
|
|
263
|
+
i(m) !== "" && A(null);
|
|
264
|
+
}, [m]);
|
|
265
|
+
let Q = c(null), Je = o(() => {
|
|
253
266
|
if (M(!1), N(!1), F(!1), B(), H.current = !1, !q.isSupported) {
|
|
254
|
-
|
|
267
|
+
h({
|
|
255
268
|
type: "fail",
|
|
256
269
|
error: "no-speech-support"
|
|
257
270
|
});
|
|
258
271
|
return;
|
|
259
272
|
}
|
|
260
|
-
if (
|
|
261
|
-
|
|
273
|
+
if (g.isBlocked) {
|
|
274
|
+
h({
|
|
262
275
|
type: "fail",
|
|
263
276
|
error: "no-voice-support"
|
|
264
277
|
});
|
|
@@ -271,54 +284,57 @@ function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersed
|
|
|
271
284
|
t.volume = 0, e.speak(t);
|
|
272
285
|
}
|
|
273
286
|
} catch {}
|
|
274
|
-
ne(),
|
|
287
|
+
ne(), _(!0), A(null), D.current = ze.current?.id ?? null, k.current = null, Q.current = `vs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, h({ type: "start" });
|
|
275
288
|
}, [
|
|
276
289
|
q.isSupported,
|
|
277
|
-
|
|
278
|
-
|
|
290
|
+
g.isBlocked,
|
|
291
|
+
_,
|
|
279
292
|
B
|
|
280
|
-
]),
|
|
281
|
-
M(!1), N(!1), F(!1), B(), H.current = !1, Q.current = null,
|
|
293
|
+
]), Ye = o(() => {
|
|
294
|
+
M(!1), N(!1), F(!1), B(), H.current = !1, Q.current = null, h({ type: "end" }), b(), _(!1);
|
|
282
295
|
}, [
|
|
283
|
-
|
|
284
|
-
|
|
296
|
+
b,
|
|
297
|
+
_,
|
|
285
298
|
B
|
|
286
|
-
]),
|
|
299
|
+
]), Xe = o((e) => Q.current === e, []), Ze = o(() => {
|
|
287
300
|
let e = I.current;
|
|
288
301
|
e.phase !== "listening" || e.muted || F(!0);
|
|
289
|
-
}, []),
|
|
302
|
+
}, []), Qe = o(() => {
|
|
290
303
|
F(!1);
|
|
291
304
|
let e = I.current;
|
|
292
|
-
if (e.phase !== "listening" || e.muted || !
|
|
293
|
-
let t =
|
|
294
|
-
|
|
295
|
-
speechEnded:
|
|
305
|
+
if (e.phase !== "listening" || e.muted || !le(i(e))) return;
|
|
306
|
+
let t = a();
|
|
307
|
+
C.current = {
|
|
308
|
+
speechEnded: w.current.at ?? t,
|
|
309
|
+
lastSpeechFrame: T.current,
|
|
310
|
+
recognizerSpeechEnded: E.current,
|
|
311
|
+
selectedGraceMs: f,
|
|
296
312
|
graceStarted: t,
|
|
297
313
|
graceEnded: t
|
|
298
|
-
},
|
|
314
|
+
}, h({
|
|
299
315
|
type: "silence",
|
|
300
316
|
at: Date.now()
|
|
301
317
|
});
|
|
302
|
-
}, []),
|
|
303
|
-
|
|
318
|
+
}, [f]), $e = o(() => {
|
|
319
|
+
h({
|
|
304
320
|
type: "mute",
|
|
305
321
|
muted: !I.current.muted
|
|
306
322
|
});
|
|
307
323
|
}, []);
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}, [
|
|
311
|
-
let $ =
|
|
312
|
-
return
|
|
313
|
-
session:
|
|
314
|
-
notice:
|
|
324
|
+
s(() => () => {
|
|
325
|
+
_(!1), v(!1);
|
|
326
|
+
}, [_, v]);
|
|
327
|
+
let $ = _e(g.spokenChunks, m.phase, y);
|
|
328
|
+
return xe(() => ({
|
|
329
|
+
session: m,
|
|
330
|
+
notice: Fe ?? (J.fallbackReason !== null && J.fallbackReason !== "notice-declined" ? "hands-free-fallback" : J.nothingHeard ? "hands-free-nothing-heard" : null),
|
|
315
331
|
spokenChunks: $,
|
|
316
332
|
capturing: Y,
|
|
317
|
-
active:
|
|
333
|
+
active: m.phase !== "idle" && (m.phase !== "ended" || m.error !== null),
|
|
318
334
|
interrupting: j,
|
|
319
|
-
awaitingEndpoint:
|
|
335
|
+
awaitingEndpoint: Ie,
|
|
320
336
|
holding: P,
|
|
321
|
-
correcting:
|
|
337
|
+
correcting: m.correcting,
|
|
322
338
|
handsFree: {
|
|
323
339
|
mode: J.mode,
|
|
324
340
|
nothingHeard: J.nothingHeard,
|
|
@@ -326,22 +342,22 @@ function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersed
|
|
|
326
342
|
fallbackReason: J.fallbackReason,
|
|
327
343
|
privacyNotice: J.privacyNotice
|
|
328
344
|
},
|
|
329
|
-
available: q.isSupported && !
|
|
330
|
-
start:
|
|
331
|
-
end:
|
|
332
|
-
isCurrentSession:
|
|
333
|
-
toggleMute:
|
|
334
|
-
interrupt:
|
|
335
|
-
letMeFinish:
|
|
336
|
-
imDone:
|
|
345
|
+
available: q.isSupported && !g.isBlocked,
|
|
346
|
+
start: Je,
|
|
347
|
+
end: Ye,
|
|
348
|
+
isCurrentSession: Xe,
|
|
349
|
+
toggleMute: $e,
|
|
350
|
+
interrupt: Ve,
|
|
351
|
+
letMeFinish: Ze,
|
|
352
|
+
imDone: Qe,
|
|
337
353
|
announce: U,
|
|
338
|
-
holdListening:
|
|
339
|
-
answerLocally:
|
|
354
|
+
holdListening: Le,
|
|
355
|
+
answerLocally: Re
|
|
340
356
|
}), [
|
|
341
|
-
|
|
342
|
-
|
|
357
|
+
m,
|
|
358
|
+
Fe,
|
|
343
359
|
j,
|
|
344
|
-
|
|
360
|
+
Ie,
|
|
345
361
|
P,
|
|
346
362
|
Y,
|
|
347
363
|
J.mode,
|
|
@@ -350,19 +366,19 @@ function Te({ onSubmit: Te, latestReply: f, language: p, graceMs: Ee, onSupersed
|
|
|
350
366
|
J.fallbackReason,
|
|
351
367
|
J.privacyNotice,
|
|
352
368
|
q.isSupported,
|
|
353
|
-
|
|
369
|
+
g.isBlocked,
|
|
354
370
|
$,
|
|
355
|
-
Ke,
|
|
356
|
-
qe,
|
|
357
371
|
Je,
|
|
358
|
-
Ze,
|
|
359
|
-
ze,
|
|
360
372
|
Ye,
|
|
361
373
|
Xe,
|
|
374
|
+
$e,
|
|
375
|
+
Ve,
|
|
376
|
+
Ze,
|
|
377
|
+
Qe,
|
|
362
378
|
U,
|
|
363
|
-
|
|
364
|
-
|
|
379
|
+
Le,
|
|
380
|
+
Re
|
|
365
381
|
]);
|
|
366
382
|
}
|
|
367
383
|
//#endregion
|
|
368
|
-
export {
|
|
384
|
+
export { De as useVoiceSession };
|
|
@@ -33,6 +33,9 @@ export interface ControllerDeps {
|
|
|
33
33
|
onFallback: (reason: FallbackReason) => void;
|
|
34
34
|
/** A barge-in that produced no words within `NOTHING_HEARD_MS`. */
|
|
35
35
|
onNothingHeard?: () => void;
|
|
36
|
+
/** Timing only: end of the latest echo-approved voiced capture frame. */
|
|
37
|
+
onSpeechFrame?: (at: number) => void;
|
|
38
|
+
now?: () => number;
|
|
36
39
|
isEnabled: () => boolean;
|
|
37
40
|
prerollMs?: number;
|
|
38
41
|
setTimer?: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;
|
|
@@ -66,7 +66,11 @@ function u(u) {
|
|
|
66
66
|
rmsDb: e.rmsDb,
|
|
67
67
|
zcr: e.zcr
|
|
68
68
|
}, t.pass);
|
|
69
|
-
r.event === "onset"
|
|
69
|
+
if (r.event === "onset" && M(t.reason), b && r.voiced && t.pass) {
|
|
70
|
+
let t = u.now?.() ?? performance.now(), n = _?.context ? Math.max(0, (_.context.currentTime - e.ctxTime) * 1e3) : 0;
|
|
71
|
+
u.onSpeechFrame?.(t - n);
|
|
72
|
+
}
|
|
73
|
+
r.event !== "onset" && b && r.inSegment && u.transport.pushFrame(n), r.event === "end" && j();
|
|
70
74
|
}, P = () => {
|
|
71
75
|
if (!v && !_) return;
|
|
72
76
|
v = !1, k(), b && u.transport.cancelUtterance(b.id), b = null, g.invalidate(), u.transport.stop(), h.clear(), p.reset(), m.reset();
|
|
@@ -24,6 +24,7 @@ export interface UseHandsFreeInterruptOptions {
|
|
|
24
24
|
language: string;
|
|
25
25
|
/** The user's voice started while the assistant was audible. */
|
|
26
26
|
onBargeIn: () => void;
|
|
27
|
+
onSpeechFrame?: (at: number) => void;
|
|
27
28
|
onTranscript: (text: string, final: boolean, language: string | undefined) => void;
|
|
28
29
|
/**
|
|
29
30
|
* The backend stream's privacy-notice version this person accepted (it is
|
|
@@ -94,6 +94,8 @@ export interface UseVoiceInputOptions {
|
|
|
94
94
|
* here) means the user can freely edit while dictation continues.
|
|
95
95
|
*/
|
|
96
96
|
onFinalize: (text: string) => void;
|
|
97
|
+
/** Browser endpoint notification time; not an acoustic timestamp. */
|
|
98
|
+
onSpeechEnd?: (at: number) => void;
|
|
97
99
|
/**
|
|
98
100
|
* BCP-47 tag to listen for. Omit to follow the app locale, which is what this
|
|
99
101
|
* hook did unconditionally before (BOFF-7108).
|
|
@@ -137,4 +139,4 @@ export interface UseVoiceInputResult {
|
|
|
137
139
|
toggle: () => void;
|
|
138
140
|
clearError: () => void;
|
|
139
141
|
}
|
|
140
|
-
export declare function useVoiceInput({ onFinalize, language: requested, }: UseVoiceInputOptions): UseVoiceInputResult;
|
|
142
|
+
export declare function useVoiceInput({ onFinalize, onSpeechEnd, language: requested, }: UseVoiceInputOptions): UseVoiceInputResult;
|