@burdenoff/fe-libs 2026.904.5 → 2026.904.7

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.
@@ -69,4 +69,7 @@ export * from './store';
69
69
  export * from './turnState';
70
70
  export * from './gatewayLimits';
71
71
  export * from './documentText';
72
+ export * from './ui/AssistantToolActivity';
73
+ export * from './ui/AssistantSpeechProvider';
74
+ export * from './ui/useVoiceSession';
72
75
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AAGxB,cAAc,wBAAwB,CAAC;AAKvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAK/B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAG3C,cAAc,QAAQ,CAAC;AAEvB,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/assistant/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,cAAc,gCAAgC,CAAC;AAC/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AAGxB,cAAc,wBAAwB,CAAC;AAKvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAK/B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAG3C,cAAc,QAAQ,CAAC;AAEvB,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import "./browserSpeechEngine.js";
2
+ import "./hostedVoiceCatalogue.js";
3
+ import "./hostedTtsSpeechEngine.js";
4
+ import "./routingSpeechEngine.js";
5
+ import "./createSpeechEngine.js";
@@ -0,0 +1,103 @@
1
+ import { FC, ReactNode } from 'react';
2
+ import { SpeechEngine, SpeechVoiceLike } from '../speech';
3
+ import { VoiceAvailability } from '../voice/speechVoices';
4
+ export interface AssistantSpeechContextValue {
5
+ /** False when the platform has no speech output — hide the control entirely. */
6
+ isSupported: boolean;
7
+ availability: VoiceAvailability;
8
+ /**
9
+ * True when nothing can be spoken in the app language, so every control that
10
+ * starts playback — the per-message button AND the header toggle — must be
11
+ * inert. `pending` is not blocked: see `speechGate`.
12
+ */
13
+ isBlocked: boolean;
14
+ /** Explains a disabled control (no installed voice for the app language). */
15
+ disabledReason: string | null;
16
+ /**
17
+ * Why THIS message cannot be spoken, when the app language is fine but the
18
+ * reply is not in it — a Tamil answer in an English UI on a device with no
19
+ * Tamil voice. `isBlocked` cannot see that: it is computed before any reply
20
+ * exists. Without this the control looks enabled and silently refuses, which
21
+ * is the dead-button bug all over again (BOFF-7106).
22
+ */
23
+ blockedFor: (messageId: string) => string | null;
24
+ /**
25
+ * Every installed voice that can read the language the user speaks, best
26
+ * first. Empty when there is no choice to offer — the picker hides itself
27
+ * rather than showing a one-item dropdown.
28
+ */
29
+ voiceChoices: readonly SpeechVoiceLike[];
30
+ /** The voice that will actually be used, resolved from the pick + ranking. */
31
+ /**
32
+ * The reply as it is being SPOKEN: every chunk, plus the one sounding now.
33
+ *
34
+ * `null` when nothing is playing. `index: -1` means chunked but not yet
35
+ * audible. Consumers reveal `chunks[0..index]` — anything beyond that is the
36
+ * caption running ahead of the voice.
37
+ */
38
+ spokenChunks: {
39
+ chunks: string[];
40
+ index: number;
41
+ } | null;
42
+ activeVoiceName: string | null;
43
+ /**
44
+ * The RAW stored pick: `null` means "use the ranked default".
45
+ *
46
+ * The picker must be driven by this rather than by `activeVoiceName`, or the
47
+ * two states collapse — with nothing stored, the ranked winner is already the
48
+ * shown option, so choosing it fires no change event and the pick can never
49
+ * be made. The user's voice would then silently move the next time the device
50
+ * installs a higher-ranked one. Same trap as the language selector.
51
+ */
52
+ preferredVoiceName: string | null;
53
+ /** `null` restores the ranked default for the current language. */
54
+ setVoice: (voiceName: string | null) => void;
55
+ /** The message currently being spoken, if any. */
56
+ speakingMessageId: string | null;
57
+ isSpeaking: (messageId: string) => boolean;
58
+ /**
59
+ * Speak this message, or stop if it is already the one speaking.
60
+ *
61
+ * `turnLocale` is the language the reply was GENERATED under, from the
62
+ * message's own metadata. It matters because script detection cannot separate
63
+ * Latin-script languages, so without it an old French reply would be read
64
+ * with whatever voice the CURRENT preference selects.
65
+ */
66
+ toggle: (messageId: string, markdown: string, turnLocale?: string) => void;
67
+ stop: () => void;
68
+ autoSpeakEnabled: boolean;
69
+ setAutoSpeak: (enabled: boolean) => void;
70
+ /**
71
+ * Speak replies for the duration of a live voice session, WITHOUT touching the
72
+ * user's saved preference.
73
+ *
74
+ * ★ Live Voice used to force `autoSpeakEnabled` true and restore it on exit.
75
+ * That field is PERSISTED (assistantStore partialize), and the restore only
76
+ * ran on a clean `end()` — so a reload, a closed tab or a backgrounded webview
77
+ * during a session left auto-speak switched ON for the user permanently, in
78
+ * normal typed chat, with the toggle showing a state they never chose. The
79
+ * default was never wrong; the session was overwriting it.
80
+ *
81
+ * Session-scoped React state instead: it cannot outlive the page, so there is
82
+ * nothing to restore and nothing to leak.
83
+ */
84
+ sessionSpeaking: boolean;
85
+ setSessionSpeaking: (enabled: boolean) => void;
86
+ /** Play a short sample in a named voice. No model call, no message, no tokens. */
87
+ previewVoice: (voiceName: string) => void;
88
+ }
89
+ export declare function useAssistantSpeech(): AssistantSpeechContextValue;
90
+ interface Props {
91
+ children: ReactNode;
92
+ /** Panel visibility. Hiding the panel stops the voice. */
93
+ visible?: boolean;
94
+ /**
95
+ * Injectable for tests and local experiments. The default routes between the
96
+ * device engine and the hosted one; with hosted TTS unconfigured it IS the
97
+ * device engine, unchanged.
98
+ */
99
+ engine?: SpeechEngine;
100
+ }
101
+ export declare const AssistantSpeechProvider: FC<Props>;
102
+ export {};
103
+ //# sourceMappingURL=AssistantSpeechProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssistantSpeechProvider.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantSpeechProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EASL,KAAK,EAAE,EACP,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAKf,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AA2B/D,OAAO,EAQL,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAc/B,MAAM,WAAW,2BAA2B;IAC1C,gFAAgF;IAChF,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,iBAAiB,CAAC;IAChC;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;;OAMG;IACH,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD;;;;OAIG;IACH,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,8EAA8E;IAC9E;;;;;;OAMG;IACH,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;;OAQG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C;;;;;;;OAOG;IACH,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3E,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC;;;;;;;;;;;;;OAaG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,kFAAkF;IAClF,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AA2BD,wBAAgB,kBAAkB,IAAI,2BAA2B,CAEhE;AAED,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,eAAO,MAAM,uBAAuB,EAAE,EAAE,CAAC,KAAK,CAmpB7C,CAAC"}
@@ -0,0 +1,294 @@
1
+ import { useI18n as e } from "../../providers/shell/I18nProvider.js";
2
+ import { isVoiceTurn as t } from "../voice/turnVisibility.js";
3
+ import { getSpeechEngine as n } from "../speech/createSpeechEngine.js";
4
+ import { VOICE_LIST_TIMEOUT_MS as r, languageDisplayName as i, selectVoiceForLocale as a, speechGate as o, speechLangForLocale as s, voiceAvailability as c, voicesForLocale as l } from "../voice/speechVoices.js";
5
+ import { chunkForSpeech as ee, toSpeakableText as u } from "../voice/speakableText.js";
6
+ import { voicePreviewText as d } from "../voice/voicePreview.js";
7
+ import { findAssistantLanguage as f } from "../voice/languages.js";
8
+ import { resolveLanguagePair as te } from "../voice/languagePair.js";
9
+ import { useAssistantTr as ne } from "../i18n.js";
10
+ import { speechLabelsFor as re } from "../voice/speechLabels.js";
11
+ import { resolveSpeechLocale as ie } from "../voice/replyLanguage.js";
12
+ import { IDLE_PLAYBACK as ae, isSpeakingMessage as p, speechPlaybackReducer as oe } from "../voice/speechPlayback.js";
13
+ import { useAssistantStore as m } from "../store.js";
14
+ import { isTurnInFlight as se } from "../turnState.js";
15
+ import { parseToolActivity as ce } from "./AssistantToolActivity.js";
16
+ import "../speech/index.js";
17
+ import { createContext as h, useCallback as g, useContext as _, useEffect as v, useMemo as y, useReducer as le, useRef as b, useState as x } from "react";
18
+ import { jsx as ue } from "react/jsx-runtime";
19
+ //#region src/shared/assistant/ui/AssistantSpeechProvider.tsx
20
+ var de = {
21
+ codeBlock: "",
22
+ image: "",
23
+ link: "",
24
+ largeTable: "",
25
+ truncated: "",
26
+ emailAt: "at",
27
+ emailDot: "dot",
28
+ nestedItem: ""
29
+ }, S = h({
30
+ isSupported: !1,
31
+ availability: "unsupported",
32
+ isBlocked: !0,
33
+ disabledReason: null,
34
+ blockedFor: () => null,
35
+ voiceChoices: [],
36
+ spokenChunks: null,
37
+ activeVoiceName: null,
38
+ preferredVoiceName: null,
39
+ setVoice: () => {},
40
+ speakingMessageId: null,
41
+ isSpeaking: () => !1,
42
+ toggle: () => {},
43
+ stop: () => {},
44
+ autoSpeakEnabled: !1,
45
+ setAutoSpeak: () => {},
46
+ sessionSpeaking: !1,
47
+ setSessionSpeaking: () => {},
48
+ previewVoice: () => {}
49
+ });
50
+ function C() {
51
+ return _(S);
52
+ }
53
+ var w = ({ children: h, visible: _ = !0, engine: C = n() }) => {
54
+ let w = ne(), { locale: T } = e(), [E, D] = le(oe, ae), [O, k] = x(() => C.listVoices()), [A, fe] = x(!1), [pe, j] = x(() => /* @__PURE__ */ new Map()), M = b(0), [N, P] = x(null), F = b(null), I = b(() => {}), L = g(() => {
55
+ F.current?.(), F.current = null;
56
+ }, []), R = m((e) => e.autoSpeakEnabled), z = m((e) => e.setAutoSpeak), [B, me] = x(!1), he = m((e) => e.activeConversationId), V = y(() => C.isSupported(), [C]);
57
+ v(() => {
58
+ if (!V) return;
59
+ let e = () => {
60
+ k(C.listVoices()), j((e) => e.size === 0 ? e : /* @__PURE__ */ new Map());
61
+ };
62
+ return e(), C.subscribeVoices(e);
63
+ }, [C, V]);
64
+ let H = m((e) => e.assistantLanguage), U = m((e) => e.replyLanguage), W = m((e) => e.preferredVoices), G = m((e) => e.setPreferredVoice), K = y(() => te(H, U, T).reply, [
65
+ H,
66
+ U,
67
+ T
68
+ ]), q = K.code, J = g((e) => f(e)?.tag ?? s(e), []), ge = g((e) => f(e)?.rate ?? .9, []), Y = y(() => c(V, O, K.tag, A), [
69
+ V,
70
+ O,
71
+ K,
72
+ A
73
+ ]), _e = y(() => ({
74
+ codeBlock: w("speech.codeBlock", "Code block"),
75
+ image: w("speech.image", "image"),
76
+ link: w("speech.link", "link"),
77
+ largeTable: w("speech.largeTable", "Table with {{rows}} rows and {{columns}} columns"),
78
+ truncated: w("speech.truncated", "The rest of this message is not read aloud"),
79
+ nestedItem: w("speech.nestedItem", "Sub-item {{ordinal}}"),
80
+ emailAt: w("speech.emailAt", "at"),
81
+ emailDot: w("speech.emailDot", "dot")
82
+ }), [w]), X = g(() => {
83
+ L(), C.cancel(), D({ type: "stop" });
84
+ }, [L, C]), ve = g((e) => {
85
+ let t = C.listVoices().find((t) => t.name === e) ?? null;
86
+ t && (X(), C.speak([d(q)], {
87
+ voice: t,
88
+ lang: J(q)
89
+ }));
90
+ }, [
91
+ C,
92
+ q,
93
+ X,
94
+ J
95
+ ]), ye = g((e) => pe.get(e) ?? null, [pe]), be = g((e, t, n) => {
96
+ L();
97
+ let i = !1, a = () => {}, o = (r) => {
98
+ if (!i) {
99
+ if (i = !0, F.current = null, clearTimeout(s), a(), r.length === 0) {
100
+ fe(!0);
101
+ return;
102
+ }
103
+ k(r), I.current(e, t, r, n);
104
+ }
105
+ }, s = setTimeout(() => o(C.listVoices()), r);
106
+ a = C.subscribeVoices(() => o(C.listVoices())), F.current = () => {
107
+ i || (i = !0, clearTimeout(s), a(), F.current = null);
108
+ };
109
+ }, [L, C]), Z = g((e, t, n, r) => {
110
+ let s = n;
111
+ if (s.length === 0) {
112
+ let e = C.listVoices();
113
+ e.length > 0 && (s = e, k(e));
114
+ }
115
+ let l = J(r), u = o(c(!0, s, r, A));
116
+ if (u === "block") {
117
+ let t = w("speech.noVoiceForReply", "This reply is in {{language}}, and no {{language}} voice is installed on this device.", { language: i(l, T) });
118
+ j((n) => new Map(n).set(e, t));
119
+ return;
120
+ }
121
+ if (u === "wait-for-voices") {
122
+ be(e, t, r);
123
+ return;
124
+ }
125
+ j((t) => {
126
+ if (!t.has(e)) return t;
127
+ let n = new Map(t);
128
+ return n.delete(e), n;
129
+ });
130
+ let d = M.current + 1;
131
+ M.current = d, D({
132
+ type: "start",
133
+ messageId: e,
134
+ requestId: d
135
+ });
136
+ let f = ee(t);
137
+ P({
138
+ requestId: d,
139
+ chunks: f,
140
+ index: -1
141
+ }), C.speak(f, {
142
+ voice: a(s, l, W[r]),
143
+ lang: l,
144
+ rate: ge(r)
145
+ }, {
146
+ onChunkStart: (e) => P((t) => t?.requestId === d ? {
147
+ ...t,
148
+ index: e
149
+ } : t),
150
+ onEnd: () => D({
151
+ type: "finished",
152
+ requestId: d
153
+ }),
154
+ onError: () => D({
155
+ type: "failed",
156
+ requestId: d
157
+ })
158
+ });
159
+ }, [
160
+ C,
161
+ T,
162
+ W,
163
+ ge,
164
+ J,
165
+ w,
166
+ A,
167
+ be
168
+ ]);
169
+ v(() => {
170
+ I.current = Z;
171
+ }, [Z]);
172
+ let Q = g((e, t, n) => {
173
+ if (!V) return;
174
+ let r = ie(u(t, { labels: de }), n ?? q), i = u(t, { labels: re(r, _e) });
175
+ i && Z(e, i, O, r);
176
+ }, [
177
+ V,
178
+ _e,
179
+ q,
180
+ Z,
181
+ O
182
+ ]), xe = g((e, t, n) => {
183
+ if (p(E, e)) {
184
+ X();
185
+ return;
186
+ }
187
+ Q(e, t, n);
188
+ }, [
189
+ E,
190
+ Q,
191
+ X
192
+ ]);
193
+ v(() => {
194
+ _ || X();
195
+ }, [_, X]), v(() => {
196
+ X(), j(/* @__PURE__ */ new Map());
197
+ }, [he, X]), v(() => {
198
+ j((e) => e.size === 0 ? e : /* @__PURE__ */ new Map()), X();
199
+ }, [q, X]), v(() => () => {
200
+ F.current?.(), F.current = null, C.cancel();
201
+ }, [C]), v(() => {
202
+ if (typeof window > "u") return;
203
+ let e = () => C.cancel();
204
+ return window.addEventListener("pagehide", e), () => window.removeEventListener("pagehide", e);
205
+ }, [C]), v(() => {
206
+ if (typeof document > "u" || !V) return;
207
+ let e = () => {
208
+ if (document.visibilityState !== "visible") return;
209
+ let e = C.listVoices();
210
+ e.length > 0 && L(), k(e), fe(!1), j((e) => e.size === 0 ? e : /* @__PURE__ */ new Map());
211
+ };
212
+ return document.addEventListener("visibilitychange", e), window.addEventListener("focus", e), () => {
213
+ document.removeEventListener("visibilitychange", e), window.removeEventListener("focus", e);
214
+ };
215
+ }, [
216
+ L,
217
+ C,
218
+ V
219
+ ]);
220
+ let Se = b(Q);
221
+ v(() => {
222
+ Se.current = Q;
223
+ }, [Q]), v(() => {
224
+ if (!(!R && !B || !_ || !V)) return m.subscribe((e, n) => {
225
+ let r = e.activeConversationId;
226
+ if (!r || r !== n.activeConversationId) return;
227
+ let i = e.processingByConversationId[r], a = n.processingByConversationId[r];
228
+ if (i?.status !== "complete" || !se(a?.status)) return;
229
+ let o = i.messageId ?? a?.messageId ?? null;
230
+ if (!o) return;
231
+ let s = e.conversations.find((e) => e.id === r)?.messages.find((e) => e.id === o);
232
+ if (!s || s.role !== "assistant" || t(s) !== B) return;
233
+ let { text: c } = ce(s.content);
234
+ c.trim() && Se.current(o, c, s.metadata?.lang);
235
+ });
236
+ }, [
237
+ R,
238
+ B,
239
+ V,
240
+ _
241
+ ]);
242
+ let Ce = y(() => l(O, K.tag), [O, K]), $ = y(() => a(O, K.tag, W[q]), [
243
+ O,
244
+ K,
245
+ W,
246
+ q
247
+ ]), we = y(() => ({
248
+ isSupported: V,
249
+ availability: Y,
250
+ isBlocked: Y === "no-voices",
251
+ disabledReason: Y === "no-voices" ? w("speech.noVoicesOnDevice", "This device has no speech voices installed. Add one in your device speech or accessibility settings.") : null,
252
+ blockedFor: ye,
253
+ voiceChoices: Ce,
254
+ spokenChunks: N ? {
255
+ chunks: N.chunks,
256
+ index: N.index
257
+ } : null,
258
+ activeVoiceName: $?.name ?? null,
259
+ preferredVoiceName: W[q] ?? null,
260
+ setVoice: (e) => G(q, e),
261
+ speakingMessageId: E.status === "speaking" ? E.messageId : null,
262
+ isSpeaking: (e) => p(E, e),
263
+ toggle: xe,
264
+ stop: X,
265
+ autoSpeakEnabled: R,
266
+ setAutoSpeak: z,
267
+ sessionSpeaking: B,
268
+ setSessionSpeaking: me,
269
+ previewVoice: ve
270
+ }), [
271
+ V,
272
+ Y,
273
+ ye,
274
+ Ce,
275
+ $,
276
+ W,
277
+ G,
278
+ q,
279
+ E,
280
+ xe,
281
+ X,
282
+ R,
283
+ z,
284
+ B,
285
+ ve,
286
+ w
287
+ ]);
288
+ return /* @__PURE__ */ ue(S.Provider, {
289
+ value: we,
290
+ children: h
291
+ });
292
+ };
293
+ //#endregion
294
+ export { w as AssistantSpeechProvider, C as useAssistantSpeech };
@@ -0,0 +1,30 @@
1
+ import { FC } from 'react';
2
+ export type AssistantToolStepStatus = "queued" | "running" | "completed" | "failed";
3
+ export interface AssistantToolStep {
4
+ /** Stable key within the message. */
5
+ id: string;
6
+ /** Humanised label shown in the step row. */
7
+ label: string;
8
+ /** Raw tool name (surfaced in the collapsed "Details"). */
9
+ tool: string;
10
+ status: AssistantToolStepStatus;
11
+ }
12
+ /**
13
+ * Split streamed assistant content into its tool-progress steps and the
14
+ * remaining prose. Called by AssistantMessageItem so the tool lines render as an
15
+ * AssistantToolActivity card while the rest renders as markdown.
16
+ */
17
+ export declare function parseToolActivity(content: string): {
18
+ steps: AssistantToolStep[];
19
+ text: string;
20
+ };
21
+ /**
22
+ * AssistantToolActivity — compact progress card for a turn's tool run (per the
23
+ * ai-assistant-v2 "tool-progress" mockup). Renders each step in one of four
24
+ * states (queued / running / completed / failed) with a hollow circle, spinner,
25
+ * green check or red x, plus a collapsed "Details" listing the raw tool names.
26
+ */
27
+ export declare const AssistantToolActivity: FC<{
28
+ steps: AssistantToolStep[];
29
+ }>;
30
+ //# sourceMappingURL=AssistantToolActivity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssistantToolActivity.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantToolActivity.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAIhC,MAAM,MAAM,uBAAuB,GAC/B,QAAQ,GACR,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,uBAAuB,CAAC;CACjC;AAsBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAClD,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd,CAsBA;AA2BD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC;IAAE,KAAK,EAAE,iBAAiB,EAAE,CAAA;CAAE,CAiEpE,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { useAssistantTr as e } from "../i18n.js";
2
+ import { Check as t, Circle as n, Loader2 as r, Wrench as i, X as a } from "lucide-react";
3
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
4
+ //#region src/shared/assistant/ui/AssistantToolActivity.tsx
5
+ var c = /^(Running|Completed|Failed):\s*(.+)$/, l = {
6
+ Running: "running",
7
+ Completed: "completed",
8
+ Failed: "failed"
9
+ };
10
+ function u(e) {
11
+ let t = e.replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
12
+ return t ? t.charAt(0).toUpperCase() + t.slice(1) : e;
13
+ }
14
+ function d(e) {
15
+ if (!e.includes(":")) return {
16
+ steps: [],
17
+ text: e
18
+ };
19
+ let t = [], n = [];
20
+ return e.split("\n").forEach((e, r) => {
21
+ let i = e.match(c);
22
+ if (i) {
23
+ let e = i[2].trim();
24
+ t.push({
25
+ id: `${r}-${e}`,
26
+ label: u(e),
27
+ tool: e,
28
+ status: l[i[1]] ?? "running"
29
+ });
30
+ } else n.push(e);
31
+ }), {
32
+ steps: t,
33
+ text: n.join("\n").trim()
34
+ };
35
+ }
36
+ function f({ status: e }) {
37
+ switch (e) {
38
+ case "completed": return /* @__PURE__ */ o(t, {
39
+ className: "size-3.5 text-status-success-text",
40
+ "aria-hidden": "true"
41
+ });
42
+ case "failed": return /* @__PURE__ */ o(a, {
43
+ className: "size-3.5 text-status-error-text",
44
+ "aria-hidden": "true"
45
+ });
46
+ case "running": return /* @__PURE__ */ o(r, {
47
+ className: "size-3.5 animate-spin text-action-primary-bg",
48
+ "aria-hidden": "true"
49
+ });
50
+ default: return /* @__PURE__ */ o(n, {
51
+ className: "size-3.5 text-text-muted",
52
+ "aria-hidden": "true"
53
+ });
54
+ }
55
+ }
56
+ var p = ({ steps: t }) => {
57
+ let n = e();
58
+ if (t.length === 0) return null;
59
+ let r = t.some((e) => e.status === "failed"), a = t.some((e) => e.status === "running" || e.status === "queued"), c = r ? n("tool.failed", "Some steps failed") : a ? n("tool.working", "Working on it…") : n("tool.done", "Done");
60
+ return /* @__PURE__ */ s("div", {
61
+ className: "my-1.5 rounded-xl border border-border-subtle bg-bg-sunken px-3 py-2.5",
62
+ "data-testid": "assistant-tool-activity",
63
+ children: [
64
+ /* @__PURE__ */ s("div", {
65
+ className: "flex items-center gap-1.5 text-[12px] font-medium text-text-secondary",
66
+ children: [/* @__PURE__ */ o(i, {
67
+ className: "size-3.5 text-text-muted",
68
+ "aria-hidden": "true"
69
+ }), /* @__PURE__ */ o("span", { children: c })]
70
+ }),
71
+ /* @__PURE__ */ o("ul", {
72
+ className: "mt-2 space-y-1.5",
73
+ children: t.map((e) => /* @__PURE__ */ s("li", {
74
+ className: "flex items-center gap-2 text-[13px] text-text-primary",
75
+ children: [/* @__PURE__ */ o("span", {
76
+ className: "flex size-3.5 flex-shrink-0 items-center justify-center",
77
+ children: /* @__PURE__ */ o(f, { status: e.status })
78
+ }), /* @__PURE__ */ o("span", {
79
+ className: e.status === "completed" ? "text-text-secondary" : "",
80
+ children: e.label
81
+ })]
82
+ }, e.id))
83
+ }),
84
+ /* @__PURE__ */ s("details", {
85
+ className: "group mt-2",
86
+ children: [/* @__PURE__ */ o("summary", {
87
+ className: "cursor-pointer list-none text-[11px] text-text-muted transition-colors hover:text-text-secondary",
88
+ children: n("tool.details", "Details")
89
+ }), /* @__PURE__ */ o("ul", {
90
+ className: "mt-1.5 space-y-0.5 border-t border-border-subtle pt-1.5",
91
+ children: t.map((e) => /* @__PURE__ */ s("li", {
92
+ className: "font-mono text-[11px] text-text-muted",
93
+ children: [
94
+ e.status,
95
+ /* @__PURE__ */ o("span", {
96
+ "aria-hidden": "true",
97
+ children: " · "
98
+ }),
99
+ e.tool
100
+ ]
101
+ }, `raw-${e.id}`))
102
+ })]
103
+ })
104
+ ]
105
+ });
106
+ };
107
+ //#endregion
108
+ export { p as AssistantToolActivity, d as parseToolActivity };
@@ -0,0 +1,88 @@
1
+ import { VoiceSessionSnapshot } from '../voice/voiceSession';
2
+ /**
3
+ * A problem worth TELLING the user about that does not warrant killing the
4
+ * session.
5
+ *
6
+ * These exist because the previous behaviour was the failure mode this whole
7
+ * feature keeps hitting: the overlay said "Listening…" while the recogniser was
8
+ * dead, and nothing anywhere said otherwise. `useVoiceInput` reports `network`
9
+ * and `unknown` and the session ignored both.
10
+ */
11
+ export type VoiceSessionNotice = "network" | "not-capturing" | "nothing-heard" | "recognition-failed";
12
+ interface Options {
13
+ /** Send a turn. The reply is observed via `latestReply`, not returned here. */
14
+ /**
15
+ * Send a turn.
16
+ *
17
+ * ★ The session id is handed over HERE rather than exposed for the parent to
18
+ * read, so the value that stamps the turn is the one live at the instant it
19
+ * was spoken. A parent reading an exposed id would sample it a render later,
20
+ * which is a race the very first turn of a session is most likely to lose.
21
+ */
22
+ onSubmit: (text: string, voiceSessionId: string) => void;
23
+ /**
24
+ * Id + text of the newest assistant reply. A CHANGE of id is what advances
25
+ * the session out of `thinking` — the text alone is not enough, because two
26
+ * consecutive replies can be identical.
27
+ */
28
+ latestReply: {
29
+ id: string;
30
+ text: string;
31
+ } | null;
32
+ /** Language the user is speaking, so the recogniser is pointed correctly. */
33
+ language: string;
34
+ }
35
+ /**
36
+ * Drives a live voice conversation (BOFF-7120).
37
+ *
38
+ * Deliberately thin: the loop itself is `voiceSessionReducer`, which is pure and
39
+ * tested, and playback is the assistant's EXISTING auto-speak path rather than a
40
+ * second speaking mechanism. Reusing auto-speak is what keeps the "never speak a
41
+ * reply in a language this device has no voice for" guarantee from BOFF-7106/7107
42
+ * true inside the session too — a bespoke playback path here would have quietly
43
+ * bypassed all of it.
44
+ *
45
+ * What this hook owns is only the parts a reducer cannot: the silence timer, the
46
+ * microphone's lifetime, and turning "auto-speak is currently on" back off again
47
+ * when the session ends.
48
+ */
49
+ export declare function useVoiceSession({ onSubmit, latestReply, language }: Options): {
50
+ session: Readonly<VoiceSessionSnapshot>;
51
+ /** A recoverable problem worth showing, or null when all is well. */
52
+ notice: VoiceSessionNotice | null;
53
+ /**
54
+ * The reply as it is being spoken, chunk by chunk — for the live caption.
55
+ *
56
+ * Relayed from the speech provider rather than read there directly by the
57
+ * UI, because the voice session is already this hook's contract with the
58
+ * component and a second speech consumer would be a second way for the two
59
+ * to disagree about what is playing.
60
+ */
61
+ spokenChunks: {
62
+ chunks: string[];
63
+ index: number;
64
+ } | null;
65
+ /** Whether the microphone is genuinely capturing right now. */
66
+ capturing: boolean;
67
+ /**
68
+ * True once started and not yet ended — what decides whether to render.
69
+ *
70
+ * ★ A failure keeps it true. `fail` sets phase `ended`, so this used to go
71
+ * false the instant the microphone was refused and the panel vanished
72
+ * without ever saying why. `end` clears the error, which is how the user
73
+ * dismisses it.
74
+ */
75
+ active: boolean;
76
+ /** The user has cut the assistant off and capture has not resumed yet. */
77
+ interrupting: boolean;
78
+ /** Quiet has fallen and the correction window is running. */
79
+ awaitingEndpoint: boolean;
80
+ /** Can a session be offered at all on this device? */
81
+ available: boolean;
82
+ start: () => void;
83
+ end: () => void;
84
+ toggleMute: () => void;
85
+ interrupt: () => void;
86
+ };
87
+ export {};
88
+ //# sourceMappingURL=useVoiceSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVoiceSession.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/useVoiceSession.ts"],"names":[],"mappings":"AAUA,OAAO,EAWL,KAAK,oBAAoB,EAE1B,MAAM,uBAAuB,CAAC;AAsC/B;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAC1B,SAAS,GACT,eAAe,GACf,eAAe,GACf,oBAAoB,CAAC;AAMzB,UAAU,OAAO;IACf,+EAA+E;IAC/E;;;;;;;OAOG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD;;;;OAIG;IACH,WAAW,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,OAAO;;IAwctE,qEAAqE;;IAErE;;;;;;;OAOG;;;;;IAEH,+DAA+D;;IAE/D;;;;;;;OAOG;;IAIH,0EAA0E;;IAE1E,6DAA6D;;IAE7D,sDAAsD;;;;;;EA4B3D"}
@@ -0,0 +1,162 @@
1
+ import { useVoiceInput as e } from "../voice/useVoiceInput.js";
2
+ import { INITIAL_SESSION as t, INTERRUPT_MAX_MS as n, SILENCE_SUBMIT_MS as r, isCapturing as i, isSubmittable as a, planReplyConsumption as o, spokenSoFar as s, voiceSessionReducer as c } from "../voice/voiceSession.js";
3
+ import { useAssistantSpeech as l } from "./AssistantSpeechProvider.js";
4
+ import { useCallback as u, useEffect as d, useMemo as f, useReducer as p, useRef as m, useState as h } from "react";
5
+ //#region src/shared/assistant/ui/useVoiceSession.ts
6
+ var g = 4e3, _ = 2500, v = 5e3, y = 12e3, b = "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=";
7
+ function x({ onSubmit: x, latestReply: S, language: C }) {
8
+ let w = m(null), [T, E] = p(u((e, t) => c(e, t, w.current), []), t), D = l(), { setSessionSpeaking: O, speakingMessageId: k, stop: A } = D, j = m(null), M = m(null), N = m(!1), [P, F] = h(null), [I, L] = h(!1), [R, z] = h(!1), B = m(T);
9
+ B.current = T;
10
+ let V = m(S);
11
+ V.current = S, d(() => {
12
+ if (T.phase === "speaking") {
13
+ w.current = S?.text ?? null;
14
+ return;
15
+ }
16
+ if (w.current === null) return;
17
+ let e = setTimeout(() => {
18
+ w.current = null;
19
+ }, _);
20
+ return () => clearTimeout(e);
21
+ }, [T.phase, S]);
22
+ let H = u((e, t) => {
23
+ E({
24
+ type: "speech",
25
+ transcript: e,
26
+ final: t
27
+ });
28
+ }, []), U = m(0), W = u(() => {
29
+ A(), U.current = Date.now(), L(!0), E({ type: "interrupt" });
30
+ }, [A]), G = e({
31
+ onFinalize: u((e) => H(e, !0), [H]),
32
+ language: C
33
+ });
34
+ d(() => {
35
+ G.interim && H(G.interim, !1);
36
+ }, [G.interim, H]), d(() => {
37
+ if (j.current && clearTimeout(j.current), z(!1), T.phase !== "listening" || T.muted || !a(s(T))) return;
38
+ let e = setTimeout(() => z(!0), 800);
39
+ return j.current = setTimeout(() => E({ type: "silence" }), r), () => {
40
+ clearTimeout(e), j.current && clearTimeout(j.current);
41
+ };
42
+ }, [T]), d(() => {
43
+ if (!I) return;
44
+ let e = Date.now() - U.current, t = G.isListening ? Math.max(0, 500 - e) : Math.max(0, n - e), r = setTimeout(() => L(!1), t);
45
+ return () => clearTimeout(r);
46
+ }, [I, G.isListening]);
47
+ let K = m("");
48
+ T.phase === "listening" && (K.current = s(T)), d(() => {
49
+ if (T.phase !== "thinking") return;
50
+ let e = K.current.trim();
51
+ e && x(e, q.current ?? `vs-orphan-${Date.now().toString(36)}`);
52
+ }, [T.phase]), d(() => {
53
+ let e = o(S?.id, T.phase, M.current);
54
+ e.claim && (M.current = S?.id ?? null, e.advance && S && E({
55
+ type: "reply",
56
+ text: S.text
57
+ }));
58
+ }, [S, T.phase]), d(() => {
59
+ let e = k !== null;
60
+ N.current && !e && E({ type: "speech-ended" }), N.current = e;
61
+ }, [k]), d(() => {
62
+ T.phase !== "listening" && F((e) => e === "nothing-heard" ? null : e);
63
+ }, [T.phase]), d(() => {
64
+ if (T.phase !== "speaking" || k !== null) return;
65
+ let e = setTimeout(() => {
66
+ N.current || E({ type: "speech-ended" });
67
+ }, g);
68
+ return () => clearTimeout(e);
69
+ }, [T.phase, k]), d(() => {
70
+ i(T) ? G.isListening || G.start() : G.isListening && G.stop();
71
+ }, [T, G]), d(() => {
72
+ G.error === "not-allowed" || G.error === "audio-capture" ? E({
73
+ type: "fail",
74
+ error: "mic-denied"
75
+ }) : G.error === "language-not-supported" ? E({
76
+ type: "fail",
77
+ error: "no-speech-support"
78
+ }) : G.error === "network" ? F("network") : G.error === "unknown" && F("recognition-failed");
79
+ }, [G.error]), d(() => {
80
+ if (!i(T) || G.isListening) return;
81
+ let e = setTimeout(() => F("not-capturing"), v);
82
+ return () => clearTimeout(e);
83
+ }, [T, G.isListening]), d(() => {
84
+ if (T.phase !== "listening" || T.muted || !G.isListening || s(T) !== "") return;
85
+ let e = setTimeout(() => F("nothing-heard"), y);
86
+ return () => clearTimeout(e);
87
+ }, [T, G.isListening]), d(() => {
88
+ s(T) !== "" && F(null);
89
+ }, [T]);
90
+ let q = m(null), J = u(() => {
91
+ if (L(!1), z(!1), !G.isSupported) {
92
+ E({
93
+ type: "fail",
94
+ error: "no-speech-support"
95
+ });
96
+ return;
97
+ }
98
+ if (D.isBlocked) {
99
+ E({
100
+ type: "fail",
101
+ error: "no-voice-support"
102
+ });
103
+ return;
104
+ }
105
+ try {
106
+ let e = typeof window < "u" ? window.speechSynthesis : null;
107
+ if (e && typeof window.SpeechSynthesisUtterance == "function") {
108
+ let t = new window.SpeechSynthesisUtterance("");
109
+ t.volume = 0, e.speak(t);
110
+ }
111
+ } catch {}
112
+ try {
113
+ if (typeof window < "u" && typeof window.Audio == "function") {
114
+ let e = new window.Audio(b);
115
+ e.volume = 0, e.play().catch(() => {});
116
+ }
117
+ } catch {}
118
+ O(!0), F(null), M.current = V.current?.id ?? null, q.current = `vs-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, E({ type: "start" });
119
+ }, [
120
+ G.isSupported,
121
+ D.isBlocked,
122
+ O
123
+ ]), Y = u(() => {
124
+ L(!1), z(!1), q.current = null, E({ type: "end" }), A(), O(!1);
125
+ }, [A, O]), X = u(() => {
126
+ E({
127
+ type: "mute",
128
+ muted: !B.current.muted
129
+ });
130
+ }, []);
131
+ return d(() => () => {
132
+ O(!1);
133
+ }, [O]), f(() => ({
134
+ session: T,
135
+ notice: P,
136
+ spokenChunks: D.spokenChunks,
137
+ capturing: G.isListening,
138
+ active: T.phase !== "idle" && (T.phase !== "ended" || T.error !== null),
139
+ interrupting: I,
140
+ awaitingEndpoint: R,
141
+ available: G.isSupported && !D.isBlocked,
142
+ start: J,
143
+ end: Y,
144
+ toggleMute: X,
145
+ interrupt: W
146
+ }), [
147
+ T,
148
+ P,
149
+ I,
150
+ R,
151
+ G.isListening,
152
+ G.isSupported,
153
+ D.isBlocked,
154
+ D.spokenChunks,
155
+ J,
156
+ Y,
157
+ X,
158
+ W
159
+ ]);
160
+ }
161
+ //#endregion
162
+ export { x as useVoiceSession };
@@ -35,4 +35,7 @@ import { BOILERPLATE_PAGE_SHARE as Qt, MAX_BOILERPLATE_LINE_CHARS as $t, MAX_EXT
35
35
  import { ATTACHMENT_ACCEPT as yn, AttachmentRejectedError as bn, INLINE_IMAGE_MAX_DIM as xn, INLINE_IMAGE_QUALITY as Sn, MAX_ATTACHMENTS as Cn, MAX_FILE_BYTES as wn, MAX_INLINE_CHARS_PER_FILE as Tn, MAX_INLINE_CHARS_TOTAL as En, MAX_INLINE_IMAGE_BASE64_CHARS as Dn, MAX_INLINE_IMAGE_PARTS as On, MAX_INLINE_TURN_BODY_BYTES as kn, MAX_TURN_BODY_BYTES as An, PROMPT_ENVELOPE_RESERVE_BYTES as jn, TURN_BODY_HEADROOM_BYTES as Mn, buildAttachmentPromptBlock as Nn, buildPromptParts as Pn, buildPromptWithAttachments as Fn, classifyAttachment as In, countInlineImageCandidates as Ln, countInlinedImageParts as Rn, detectDocumentFormat as zn, estimateTurnBodyBytes as Bn, formatBytes as Vn, getAttachmentSendBlocker as Hn, getExtension as Un, getSendableAttachments as Wn, hasAttachmentWorkInFlight as Gn, parseDataUrl as Kn, toMessageAttachment as qn, truncateForInline as Jn, validateFile as Yn } from "./shared/assistant/attachments.js";
36
36
  import { useAssistantStore as Xn } from "./shared/assistant/store.js";
37
37
  import { isTurnInFlight as Zn } from "./shared/assistant/turnState.js";
38
- export { p as ASSISTANT_API_AUTH_STRATEGY, At as ASSISTANT_LANGUAGES, yn as ATTACHMENT_ACCEPT, Rt as AssistantI18nProvider, U as AssistantRuntimeManager, bn as AttachmentRejectedError, Qt as BOILERPLATE_PAGE_SHARE, i as CHECK_AI_CREDITS, a as CREATE_SANDBOX, dt as DEFAULT_SPEAKABLE_LABELS, jt as DEFAULT_SPEECH_RATE, Re as ENDPOINT_GRACE_MS, o as EXTEND_SANDBOX_TTL, s as GET_ACTIVE_SANDBOXES, c as GET_SANDBOX, me as HOSTED_TTS_MAX_CHARS, ue as HOSTED_TTS_TIER, de as HOSTED_VOICES, Kt as IDLE_PLAYBACK, ze as INITIAL_SESSION, xn as INLINE_IMAGE_MAX_DIM, Sn as INLINE_IMAGE_QUALITY, Be as INTERRUPT_MAX_MS, Ve as INTERRUPT_MIN_MS, Cn as MAX_ATTACHMENTS, $t as MAX_BOILERPLATE_LINE_CHARS, en as MAX_EXTRACTED_CHARS, wn as MAX_FILE_BYTES, Tn as MAX_INLINE_CHARS_PER_FILE, En as MAX_INLINE_CHARS_TOTAL, Xt as MAX_INLINE_IMAGE_BASE64_BYTES, Dn as MAX_INLINE_IMAGE_BASE64_CHARS, On as MAX_INLINE_IMAGE_PARTS, kn as MAX_INLINE_TURN_BODY_BYTES, tn as MAX_PDF_PAGES_SCANNED, Zt as MAX_REQUEST_BODY_BYTES, ft as MAX_SPEAKABLE_CHARS, pt as MAX_SPOKEN_TABLE_ROWS, An as MAX_TURN_BODY_BYTES, nn as MIN_PAGE_TEXT_CHARS, rn as MIN_TEXT_PAGE_SHARE, Ct as POLL_FAST_MS, wt as POLL_FAST_UNTIL_MS, Tt as POLL_SLOW_MS, Et as POLL_STEADY_MS, Dt as POLL_STEADY_UNTIL_MS, xt as PREVIEW_LANGUAGES, jn as PROMPT_ENVELOPE_RESERVE_BYTES, l as PROXY_SANDBOX_REQUEST, u as REPORT_AI_TOKEN_USAGE, He as SILENCE_SUBMIT_MS, he as SPEAKING_RATE_MAX, ge as SPEAKING_RATE_MAX_STREAMING, _e as SPEAKING_RATE_MAX_SYNC, ve as SPEAKING_RATE_MIN, mt as SPEECH_CHUNK_CHARS, ht as SPEECH_CHUNK_CJK_CHARS, Xe as SPEECH_LANG, Ht as SPOKEN_LABEL_LANGUAGES, d as STOP_ASSISTANT_SANDBOX, Mn as TURN_BODY_HEADROOM_BYTES, Ze as VOICE_LIST_TIMEOUT_MS, f as WAIT_FOR_SANDBOX_RUNNING, zt as assistantTranslationKey, ye as base64ToBytes, le as browserSpeechEngine, C as buildAssistantSandboxEnv, Nn as buildAttachmentPromptBlock, ct as buildContinuedFromVoiceNote, W as buildProgress, Pn as buildPromptParts, w as buildPromptRequestBody, Fn as buildPromptWithAttachments, be as buildRequest, t as buildSandboxContextInput, lt as buildVoiceTranscriptMarkdown, ne as chatTurns, T as checkAssistantAiCredits, gt as chunkForSpeech, In as classifyAttachment, xe as classifyHttpFailure, an as columnLetterToIndex, De as configureSpeechEngine, _t as containsCjk, Ln as countInlineImageCandidates, Rn as countInlinedImageParts, E as createAssistantSandbox, D as createAssistantSession, Te as createHostedTtsEngine, Ee as createRoutingSpeechEngine, ee as createSandboxAssistantTransport, Oe as createSpeechEngines, Se as decodeAudioResponse, on as decodeXmlEntities, zn as detectDocumentFormat, Wt as detectScriptLocale, sn as docxXmlToText, O as downloadAssistantArtifact, Bn as estimateTurnBodyBytes, n as executeSandboxGraphQL, k as extendAssistantSandboxTTL, Mt as findAssistantLanguage, A as findExistingSandbox, It as findPendingWrite, cn as fitPdfPages, Vn as formatBytes, ln as formatPageRanges, j as getAssistantArtifacts, G as getAssistantMedia, M as getAssistantMessages, N as getAssistantSession, P as getAssistantStreamAccess, K as getAssistantText, Hn as getAttachmentSendBlocker, y as getCapability, Un as getExtension, m as getImageForMode, h as getImageTagForMode, q as getRawMessageCreatedAt, g as getResourcesForMode, _ as getSandboxImageRegistry, Wn as getSendableAttachments, ke as getSpeechEngine, J as getToolProgress, re as groupBySession, ie as groupConsecutiveBySession, Gn as hasAttachmentWorkInFlight, fe as hostedLocaleFor, pe as hostedSupportsLanguage, Ue as isCapturing, We as isEcho, un as isMostlyImagePages, r as isQuotaExhaustedError, Y as isReapedSandboxConnectionError, X as isRecoverable, qt as isSpeakingMessage, Qe as isSpeechBlocked, Ge as isSubmittable, Zn as isTurnInFlight, b as isUnknownFieldError, ae as isVoiceTurn, dn as joinSheets, $e as languageDisplayName, Z as mapSessionToHistory, x as markSupported, S as markUnsupported, Q as mergeMessagesById, Me as micIsInterrupt, F as modeRequiresConfirmWrites, et as normalizeLangTag, Ke as orbMode, fn as pageMarker, Lt as parseConfirmationRefusal, Kn as parseDataUrl, pn as parseSharedStrings, mn as parseWorkbookSheets, hn as pdfTextItemsToPage, qe as planReplyConsumption, Ot as pollDelayMs, tt as primaryLanguageSubtag, Ae as readSpeechEngineConfig, Pt as replyFollowsInput, I as reportAssistantAiTokenUsage, Jt as requiresCancel, je as resetSpeechEngine, Nt as resolveAssistantLanguage, Ft as resolveLanguagePair, v as resolveSandboxAssistantMode, Gt as resolveSpeechLocale, $ as sanitize, L as selectExistingAssistantSandbox, nt as selectVoiceForLocale, R as sendAssistantPromptAsync, z as sendHeartbeat, gn as sheetXmlToRows, Ce as shouldDisableEngine, Ne as showsAssistantCaption, Pe as showsUserCaption, vt as speakableEmail, rt as speechGate, Ut as speechLabelsFor, it as speechLangForLocale, Yt as speechPlaybackReducer, we as splitForVendor, yt as splitTableRow, Je as spokenSoFar, B as stopReapedAssistantSandbox, _n as summarisePageText, kt as toCaptionText, qn as toMessageAttachment, bt as toSpeakableText, Jn as truncateForInline, vn as truncatePagesForInline, Bt as useAssistantI18nNamespace, Xn as useAssistantStore, Vt as useAssistantTr, te as useSandboxAssistantTransport, e as useVoiceInput, Yn as validateFile, at as voiceAvailability, oe as voiceMarker, St as voicePreviewText, ot as voiceQualityRank, se as voiceSessionIdOf, Ye as voiceSessionReducer, Fe as voiceStageState, ut as voiceTranscriptFilename, ce as voiceTurnsOnly, st as voicesForLocale, V as waitForAssistantServiceReady, H as waitForSandboxRunning, Ie as waveformIsActive, Le as waveformIsSettled };
38
+ import { AssistantToolActivity as Qn, parseToolActivity as $n } from "./shared/assistant/ui/AssistantToolActivity.js";
39
+ import { AssistantSpeechProvider as er, useAssistantSpeech as tr } from "./shared/assistant/ui/AssistantSpeechProvider.js";
40
+ import { useVoiceSession as nr } from "./shared/assistant/ui/useVoiceSession.js";
41
+ export { p as ASSISTANT_API_AUTH_STRATEGY, At as ASSISTANT_LANGUAGES, yn as ATTACHMENT_ACCEPT, Rt as AssistantI18nProvider, U as AssistantRuntimeManager, er as AssistantSpeechProvider, Qn as AssistantToolActivity, bn as AttachmentRejectedError, Qt as BOILERPLATE_PAGE_SHARE, i as CHECK_AI_CREDITS, a as CREATE_SANDBOX, dt as DEFAULT_SPEAKABLE_LABELS, jt as DEFAULT_SPEECH_RATE, Re as ENDPOINT_GRACE_MS, o as EXTEND_SANDBOX_TTL, s as GET_ACTIVE_SANDBOXES, c as GET_SANDBOX, me as HOSTED_TTS_MAX_CHARS, ue as HOSTED_TTS_TIER, de as HOSTED_VOICES, Kt as IDLE_PLAYBACK, ze as INITIAL_SESSION, xn as INLINE_IMAGE_MAX_DIM, Sn as INLINE_IMAGE_QUALITY, Be as INTERRUPT_MAX_MS, Ve as INTERRUPT_MIN_MS, Cn as MAX_ATTACHMENTS, $t as MAX_BOILERPLATE_LINE_CHARS, en as MAX_EXTRACTED_CHARS, wn as MAX_FILE_BYTES, Tn as MAX_INLINE_CHARS_PER_FILE, En as MAX_INLINE_CHARS_TOTAL, Xt as MAX_INLINE_IMAGE_BASE64_BYTES, Dn as MAX_INLINE_IMAGE_BASE64_CHARS, On as MAX_INLINE_IMAGE_PARTS, kn as MAX_INLINE_TURN_BODY_BYTES, tn as MAX_PDF_PAGES_SCANNED, Zt as MAX_REQUEST_BODY_BYTES, ft as MAX_SPEAKABLE_CHARS, pt as MAX_SPOKEN_TABLE_ROWS, An as MAX_TURN_BODY_BYTES, nn as MIN_PAGE_TEXT_CHARS, rn as MIN_TEXT_PAGE_SHARE, Ct as POLL_FAST_MS, wt as POLL_FAST_UNTIL_MS, Tt as POLL_SLOW_MS, Et as POLL_STEADY_MS, Dt as POLL_STEADY_UNTIL_MS, xt as PREVIEW_LANGUAGES, jn as PROMPT_ENVELOPE_RESERVE_BYTES, l as PROXY_SANDBOX_REQUEST, u as REPORT_AI_TOKEN_USAGE, He as SILENCE_SUBMIT_MS, he as SPEAKING_RATE_MAX, ge as SPEAKING_RATE_MAX_STREAMING, _e as SPEAKING_RATE_MAX_SYNC, ve as SPEAKING_RATE_MIN, mt as SPEECH_CHUNK_CHARS, ht as SPEECH_CHUNK_CJK_CHARS, Xe as SPEECH_LANG, Ht as SPOKEN_LABEL_LANGUAGES, d as STOP_ASSISTANT_SANDBOX, Mn as TURN_BODY_HEADROOM_BYTES, Ze as VOICE_LIST_TIMEOUT_MS, f as WAIT_FOR_SANDBOX_RUNNING, zt as assistantTranslationKey, ye as base64ToBytes, le as browserSpeechEngine, C as buildAssistantSandboxEnv, Nn as buildAttachmentPromptBlock, ct as buildContinuedFromVoiceNote, W as buildProgress, Pn as buildPromptParts, w as buildPromptRequestBody, Fn as buildPromptWithAttachments, be as buildRequest, t as buildSandboxContextInput, lt as buildVoiceTranscriptMarkdown, ne as chatTurns, T as checkAssistantAiCredits, gt as chunkForSpeech, In as classifyAttachment, xe as classifyHttpFailure, an as columnLetterToIndex, De as configureSpeechEngine, _t as containsCjk, Ln as countInlineImageCandidates, Rn as countInlinedImageParts, E as createAssistantSandbox, D as createAssistantSession, Te as createHostedTtsEngine, Ee as createRoutingSpeechEngine, ee as createSandboxAssistantTransport, Oe as createSpeechEngines, Se as decodeAudioResponse, on as decodeXmlEntities, zn as detectDocumentFormat, Wt as detectScriptLocale, sn as docxXmlToText, O as downloadAssistantArtifact, Bn as estimateTurnBodyBytes, n as executeSandboxGraphQL, k as extendAssistantSandboxTTL, Mt as findAssistantLanguage, A as findExistingSandbox, It as findPendingWrite, cn as fitPdfPages, Vn as formatBytes, ln as formatPageRanges, j as getAssistantArtifacts, G as getAssistantMedia, M as getAssistantMessages, N as getAssistantSession, P as getAssistantStreamAccess, K as getAssistantText, Hn as getAttachmentSendBlocker, y as getCapability, Un as getExtension, m as getImageForMode, h as getImageTagForMode, q as getRawMessageCreatedAt, g as getResourcesForMode, _ as getSandboxImageRegistry, Wn as getSendableAttachments, ke as getSpeechEngine, J as getToolProgress, re as groupBySession, ie as groupConsecutiveBySession, Gn as hasAttachmentWorkInFlight, fe as hostedLocaleFor, pe as hostedSupportsLanguage, Ue as isCapturing, We as isEcho, un as isMostlyImagePages, r as isQuotaExhaustedError, Y as isReapedSandboxConnectionError, X as isRecoverable, qt as isSpeakingMessage, Qe as isSpeechBlocked, Ge as isSubmittable, Zn as isTurnInFlight, b as isUnknownFieldError, ae as isVoiceTurn, dn as joinSheets, $e as languageDisplayName, Z as mapSessionToHistory, x as markSupported, S as markUnsupported, Q as mergeMessagesById, Me as micIsInterrupt, F as modeRequiresConfirmWrites, et as normalizeLangTag, Ke as orbMode, fn as pageMarker, Lt as parseConfirmationRefusal, Kn as parseDataUrl, pn as parseSharedStrings, $n as parseToolActivity, mn as parseWorkbookSheets, hn as pdfTextItemsToPage, qe as planReplyConsumption, Ot as pollDelayMs, tt as primaryLanguageSubtag, Ae as readSpeechEngineConfig, Pt as replyFollowsInput, I as reportAssistantAiTokenUsage, Jt as requiresCancel, je as resetSpeechEngine, Nt as resolveAssistantLanguage, Ft as resolveLanguagePair, v as resolveSandboxAssistantMode, Gt as resolveSpeechLocale, $ as sanitize, L as selectExistingAssistantSandbox, nt as selectVoiceForLocale, R as sendAssistantPromptAsync, z as sendHeartbeat, gn as sheetXmlToRows, Ce as shouldDisableEngine, Ne as showsAssistantCaption, Pe as showsUserCaption, vt as speakableEmail, rt as speechGate, Ut as speechLabelsFor, it as speechLangForLocale, Yt as speechPlaybackReducer, we as splitForVendor, yt as splitTableRow, Je as spokenSoFar, B as stopReapedAssistantSandbox, _n as summarisePageText, kt as toCaptionText, qn as toMessageAttachment, bt as toSpeakableText, Jn as truncateForInline, vn as truncatePagesForInline, Bt as useAssistantI18nNamespace, tr as useAssistantSpeech, Xn as useAssistantStore, Vt as useAssistantTr, te as useSandboxAssistantTransport, e as useVoiceInput, nr as useVoiceSession, Yn as validateFile, at as voiceAvailability, oe as voiceMarker, St as voicePreviewText, ot as voiceQualityRank, se as voiceSessionIdOf, Ye as voiceSessionReducer, Fe as voiceStageState, ut as voiceTranscriptFilename, ce as voiceTurnsOnly, st as voicesForLocale, V as waitForAssistantServiceReady, H as waitForSandboxRunning, Ie as waveformIsActive, Le as waveformIsSettled };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.904.5",
3
+ "version": "2026.904.7",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "bin": {