@burdenoff/fe-libs 2026.1029.6 → 2026.1029.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.
Files changed (34) hide show
  1. package/dist/shared/assistant/index.d.ts +5 -0
  2. package/dist/shared/assistant/liveDeltas.d.ts +3 -0
  3. package/dist/shared/assistant/liveDeltas.js +17 -1
  4. package/dist/shared/assistant/liveOverlay.d.ts +4 -0
  5. package/dist/shared/assistant/liveOverlay.js +10 -1
  6. package/dist/shared/assistant/speech/appendSpeechQueue.d.ts +13 -0
  7. package/dist/shared/assistant/speech/appendSpeechQueue.js +75 -0
  8. package/dist/shared/assistant/speech/browserSpeechEngine.js +93 -46
  9. package/dist/shared/assistant/speech/hostedTtsSpeechEngine.js +173 -109
  10. package/dist/shared/assistant/speech/index.d.ts +1 -1
  11. package/dist/shared/assistant/speech/notifyCancellationSpeechEngine.js +53 -0
  12. package/dist/shared/assistant/speech/routingSpeechEngine.js +107 -23
  13. package/dist/shared/assistant/speech/speechFeed.d.ts +17 -2
  14. package/dist/shared/assistant/speech/speechFeed.js +35 -12
  15. package/dist/shared/assistant/speech/types.d.ts +20 -1
  16. package/dist/shared/assistant/turnTimeline.d.ts +3 -0
  17. package/dist/shared/assistant/turnTimeline.js +4 -1
  18. package/dist/shared/assistant/ui/AssistantChatArea.js +1410 -1393
  19. package/dist/shared/assistant/ui/AssistantSpeechProvider.js +351 -291
  20. package/dist/shared/assistant/ui/useVoiceSession.js +323 -288
  21. package/dist/shared/assistant/voice/livePhraseSpeech.d.ts +14 -0
  22. package/dist/shared/assistant/voice/livePhraseSpeech.js +72 -0
  23. package/dist/shared/assistant/voice/phraseGate.d.ts +11 -0
  24. package/dist/shared/assistant/voice/phraseGate.js +74 -0
  25. package/dist/shared/assistant/voice/phrasePlayback.d.ts +9 -0
  26. package/dist/shared/assistant/voice/phrasePlayback.js +78 -0
  27. package/dist/shared/assistant/voice/phraseSpeechConfig.d.ts +5 -0
  28. package/dist/shared/assistant/voice/phraseSpeechConfig.js +10 -0
  29. package/dist/shared/assistant/voice/speakableText.d.ts +1 -0
  30. package/dist/shared/assistant/voice/speakableText.js +1 -1
  31. package/dist/shared/assistant/voice/voiceSession.d.ts +5 -0
  32. package/dist/shared/assistant/voice/voiceSession.js +17 -1
  33. package/dist/shared-assistant.js +293 -288
  34. package/package.json +1 -1
@@ -365,5 +365,10 @@ export * from './avatar/live/LiveAvatarMinutes';
365
365
  export * from './avatar/live/createLiveAvatarSeam';
366
366
  export * from './avatar/live/avatarVoice';
367
367
  export * from './promptDelivery';
368
+ export * from './speech/appendSpeechQueue';
369
+ export * from './voice/phraseSpeechConfig';
370
+ export * from './voice/phraseGate';
371
+ export * from './voice/livePhraseSpeech';
372
+ export * from './voice/phrasePlayback';
368
373
  export * from './attachmentAccess';
369
374
  export * from './attachmentAccessCopy';
@@ -19,6 +19,8 @@ export interface LivePartState {
19
19
  readonly gapped: boolean;
20
20
  /** A new generation began while the part was being written. */
21
21
  readonly frozen: boolean;
22
+ /** S7: a tool began after this text part; it is no longer live narration. */
23
+ readonly toolAfter?: boolean;
22
24
  }
23
25
  export interface LiveMessageState {
24
26
  readonly created?: number;
@@ -27,6 +29,7 @@ export interface LiveMessageState {
27
29
  export interface LiveDeltaState {
28
30
  /** +1 for every re-mint. */
29
31
  readonly generation: number;
32
+ readonly toolIds?: ReadonlySet<string>;
30
33
  readonly parts: ReadonlyMap<string, LivePartState>;
31
34
  readonly messages: ReadonlyMap<string, LiveMessageState>;
32
35
  }
@@ -109,7 +109,23 @@ function p(e, t) {
109
109
  if (!n) return e;
110
110
  switch (t.event) {
111
111
  case "message.updated": return u(e, n);
112
- case "message.part.updated": return n.type === "text" ? d(e, n) : e;
112
+ case "message.part.updated": {
113
+ if (n.type === "text") return d(e, n);
114
+ if (n.type !== "tool") return e;
115
+ let t = a(n.id), r = a(n.messageID);
116
+ if (!t || !r || e.toolIds?.has(t)) return e;
117
+ if ((e.toolIds?.size ?? 0) >= 256) return c(e);
118
+ let i = new Map(e.parts);
119
+ for (let [e, t] of i) t.messageId === r && i.set(e, {
120
+ ...t,
121
+ toolAfter: !0
122
+ });
123
+ return {
124
+ ...e,
125
+ parts: i,
126
+ toolIds: new Set([...e.toolIds ?? [], t])
127
+ };
128
+ }
113
129
  case "message.part.delta": return n.partType === "text" || n.partType === void 0 ? f(e, n) : e;
114
130
  default: return e;
115
131
  }
@@ -34,6 +34,10 @@ export interface LiveRenderOptions {
34
34
  onFirstDelta?: () => void;
35
35
  /** Called once, when the overlay first shows text. */
36
36
  onFirstVisible?: () => void;
37
+ /** S7 reads validated live parts only AFTER this overlay write, never the poll text. */
38
+ /** Safety notifications precede throttling: a tool must cancel a hold timer now. */
39
+ onLiveState?: (live: LiveDeltaState, floor: number) => void;
40
+ onRendered?: (content: string, live: LiveDeltaState, floor: number) => void;
37
41
  /** Minimum gap between live renders. Leading edge, with a trailing call. */
38
42
  throttleMs?: number;
39
43
  now?: () => number;
@@ -31,9 +31,18 @@ function o(n) {
31
31
  }, y = () => {
32
32
  if (f = null, d || l === null) return;
33
33
  let e = _();
34
- e === "" || e === m || (p = i(), m = e, s.setLive(n.messageId, n.conversationId, e), g || (g = !0, n.onFirstVisible?.()));
34
+ if (e !== "") {
35
+ e !== m && (p = i(), m = e, s.setLive(n.messageId, n.conversationId, e));
36
+ try {
37
+ n.onRendered?.(e, n.stream.live, l);
38
+ } catch {}
39
+ g || (g = !0, n.onFirstVisible?.());
40
+ }
35
41
  }, b = n.stream.onLive(() => {
36
42
  if (d || l === null) return;
43
+ try {
44
+ n.onLiveState?.(n.stream.live, l);
45
+ } catch {}
37
46
  v();
38
47
  let e = p + c - i();
39
48
  if (e <= 0) {
@@ -0,0 +1,13 @@
1
+ import { SpeechErrorReason, SpeechQueue, SpeechSpeakHandlers } from './types';
2
+ export declare const SPEECH_QUEUE_CAPACITY = 32;
3
+ export declare const SPEECH_QUEUE_TEXT_LIMIT = 16384;
4
+ export interface AppendSpeechPort<T> {
5
+ prepare(text: string, signal: AbortSignal): Promise<T>;
6
+ play(value: T, index: number, began: () => void): Promise<void>;
7
+ drain(): Promise<void>;
8
+ retract(fromIndex: number): void;
9
+ cancel(): void;
10
+ failed(cause: unknown): SpeechErrorReason;
11
+ }
12
+ /** Bounded reply queue: one playing/preparing piece and at most one prefetched. */
13
+ export declare function createAppendSpeechQueue<T>(port: AppendSpeechPort<T>, handlers?: SpeechSpeakHandlers): SpeechQueue;
@@ -0,0 +1,75 @@
1
+ //#region src/shared/assistant/speech/appendSpeechQueue.ts
2
+ var e = 32, t = 16384;
3
+ function n(e, t = {}) {
4
+ let n = [], r = null, i = 0, a = !1, o = !1, s = !1, c = !1, l = !1, u = (t) => (t.ready || (t.ready = Promise.resolve().then(() => {
5
+ if (o || t.controller.signal.aborted) throw Error("speech cancelled");
6
+ return e.prepare(t.text, t.controller.signal);
7
+ }), t.ready.catch(() => {})), t.ready), d = () => {
8
+ r?.started && n[0] && u(n[0]);
9
+ }, f = (e) => {
10
+ e.withdrawn = !0, e.controller.abort();
11
+ }, p = () => {
12
+ o || (o = !0, r && f(r), n.forEach(f), n = [], e.cancel());
13
+ }, m = async () => {
14
+ if (!(s || o)) {
15
+ s = !0;
16
+ try {
17
+ for (; !o;) {
18
+ if (r = n.shift() ?? null, !r) {
19
+ if (await e.drain(), o) return;
20
+ if (n.length) continue;
21
+ a ? (o = !0, t.onEnd?.()) : l || (l = !0, t.onPause?.());
22
+ return;
23
+ }
24
+ l = !1;
25
+ let i = r, s;
26
+ try {
27
+ s = await u(i);
28
+ } catch (e) {
29
+ if (i.withdrawn) continue;
30
+ throw e;
31
+ }
32
+ if (o) return;
33
+ if (!i.withdrawn) {
34
+ if (n[0] && u(n[0]), await e.play(s, i.index, () => {
35
+ o || i.withdrawn || i.started || (i.started = !0, c || (c = !0, t.onStart?.()), t.onChunkStart?.(i.index), d());
36
+ }), o || (await e.drain(), o)) return;
37
+ r = null, i.withdrawn || t.onChunkEnd?.(i.index);
38
+ }
39
+ }
40
+ } catch (n) {
41
+ o || (p(), t.onError?.(e.failed(n)));
42
+ } finally {
43
+ s = !1;
44
+ }
45
+ }
46
+ };
47
+ return {
48
+ append(e) {
49
+ if (o || a) return !1;
50
+ let t = e.filter((e) => e.trim().length > 0), s = [...n, ...r ? [r] : []];
51
+ if (s.length + t.length > 32 || s.reduce((e, t) => e + t.text.length, 0) + t.join("").length > 16384) return !1;
52
+ for (let e of t) n.push({
53
+ index: i++,
54
+ text: e,
55
+ controller: new AbortController(),
56
+ started: !1,
57
+ withdrawn: !1
58
+ });
59
+ return d(), m(), !0;
60
+ },
61
+ end() {
62
+ o || (a = !0, m());
63
+ },
64
+ retract() {
65
+ if (o) return;
66
+ let i = n;
67
+ n = [];
68
+ let a = r && !r.started ? r.index : i[0]?.index;
69
+ a !== void 0 && e.retract(a), r && !r.started && i.push(r), i.forEach(f), i.length && t.onRetract?.(i.map((e) => e.index));
70
+ },
71
+ cancel: p
72
+ };
73
+ }
74
+ //#endregion
75
+ export { e as SPEECH_QUEUE_CAPACITY, t as SPEECH_QUEUE_TEXT_LIMIT, n as createAppendSpeechQueue };
@@ -1,6 +1,7 @@
1
- import { setDeviceVoice as e } from "../voice/handsfree/playbackReference.js";
1
+ import { createAppendSpeechQueue as e } from "./appendSpeechQueue.js";
2
+ import { setDeviceVoice as t } from "../voice/handsfree/playbackReference.js";
2
3
  //#region src/shared/assistant/speech/browserSpeechEngine.ts
3
- function t() {
4
+ function n() {
4
5
  if (typeof window > "u") return null;
5
6
  try {
6
7
  return window.speechSynthesis ?? null;
@@ -8,10 +9,10 @@ function t() {
8
9
  return null;
9
10
  }
10
11
  }
11
- function n() {
12
+ function r() {
12
13
  return typeof window < "u" && typeof window.SpeechSynthesisUtterance == "function";
13
14
  }
14
- function r(e) {
15
+ function i(e) {
15
16
  switch (e) {
16
17
  case "canceled":
17
18
  case "cancelled":
@@ -23,7 +24,7 @@ function r(e) {
23
24
  default: return "unknown";
24
25
  }
25
26
  }
26
- function i(e, t) {
27
+ function a(e, t) {
27
28
  if (!t) return null;
28
29
  let n;
29
30
  try {
@@ -33,85 +34,131 @@ function i(e, t) {
33
34
  }
34
35
  return n.find((e) => e === t) ?? n.find((e) => e.name === t.name && e.lang === t.lang) ?? null;
35
36
  }
36
- var a = 0, o = {
37
+ var o = 0, s = null, c = {
37
38
  id: "browser-speech-synthesis",
38
39
  isSupported() {
39
- return t() !== null && n();
40
+ return n() !== null && r();
40
41
  },
41
42
  listVoices() {
42
43
  try {
43
- return t()?.getVoices() ?? [];
44
+ return n()?.getVoices() ?? [];
44
45
  } catch {
45
46
  return [];
46
47
  }
47
48
  },
48
49
  subscribeVoices(e) {
49
- let n = t();
50
- if (!n?.addEventListener) return () => {};
50
+ let t = n();
51
+ if (!t?.addEventListener) return () => {};
51
52
  let r = () => e();
52
- return n.addEventListener("voiceschanged", r), () => n.removeEventListener("voiceschanged", r);
53
+ return t.addEventListener("voiceschanged", r), () => t.removeEventListener("voiceschanged", r);
53
54
  },
54
- speak(o, s, c = {}) {
55
- let l = t();
56
- if (!l || !n()) {
57
- c.onError?.("unsupported");
55
+ speak(e, c, l = {}) {
56
+ s?.cancel(), s = null;
57
+ let u = n();
58
+ if (!u || !r()) {
59
+ l.onError?.("unsupported");
58
60
  return;
59
61
  }
60
- let u = o.filter((e) => e.trim().length > 0);
61
- if (u.length === 0) {
62
- c.onEnd?.();
62
+ let d = e.filter((e) => e.trim().length > 0);
63
+ if (d.length === 0) {
64
+ l.onEnd?.();
63
65
  return;
64
66
  }
65
- a += 1;
66
- let d = a;
67
+ o += 1;
68
+ let f = o;
67
69
  try {
68
- l.cancel();
70
+ u.cancel();
69
71
  } catch {}
70
- let f = i(l, s.voice);
71
- e(!0), u.forEach((t, n) => {
72
- if (d !== a) return;
73
- let i;
72
+ let p = a(u, c.voice);
73
+ t(!0), d.forEach((e, n) => {
74
+ if (f !== o) return;
75
+ let r;
74
76
  try {
75
- i = new window.SpeechSynthesisUtterance(t), i.lang = s.lang, f && (i.voice = f), s.rate !== void 0 && (i.rate = s.rate);
77
+ r = new window.SpeechSynthesisUtterance(e), r.lang = c.lang, p && (r.voice = p), c.rate !== void 0 && (r.rate = c.rate);
76
78
  } catch {
77
- a += 1;
79
+ o += 1;
78
80
  try {
79
- l.cancel();
81
+ u.cancel();
80
82
  } catch {}
81
- c.onError?.("unknown");
83
+ l.onError?.("unknown");
82
84
  return;
83
85
  }
84
- i.onstart = () => {
85
- d === a && (n === 0 && c.onStart?.(), c.onChunkStart?.(n));
86
- }, i.onerror = (t) => {
87
- if (d === a) {
88
- e(!1), a += 1;
86
+ r.onstart = () => {
87
+ f === o && (n === 0 && l.onStart?.(), l.onChunkStart?.(n));
88
+ }, r.onerror = (e) => {
89
+ if (f === o) {
90
+ t(!1), o += 1;
89
91
  try {
90
- l.cancel();
92
+ u.cancel();
91
93
  } catch {}
92
- c.onError?.(r(t.error));
94
+ l.onError?.(i(e.error));
93
95
  }
94
- }, n === u.length - 1 && (i.onend = () => {
95
- d === a && (e(!1), c.onEnd?.());
96
+ }, n === d.length - 1 && (r.onend = () => {
97
+ f === o && (t(!1), l.onEnd?.());
96
98
  });
97
99
  try {
98
- l.speak(i);
100
+ u.speak(r);
99
101
  } catch {
100
- if (d !== a) return;
101
- a += 1, e(!1);
102
+ if (f !== o) return;
103
+ o += 1, t(!1);
102
104
  try {
103
- l.cancel();
105
+ u.cancel();
104
106
  } catch {}
105
- c.onError?.("unknown");
107
+ l.onError?.("unknown");
108
+ }
109
+ });
110
+ },
111
+ open(o, l = {}) {
112
+ c.cancel();
113
+ let u = n(), d = -1, f = null, p = () => {
114
+ f?.(), f = null, t(!1);
115
+ try {
116
+ u?.cancel();
117
+ } catch {}
118
+ }, m = e({
119
+ prepare: async (e) => e,
120
+ play: (e, n, s) => new Promise((c, l) => {
121
+ if (!u || !r()) {
122
+ l({ reason: "unsupported" });
123
+ return;
124
+ }
125
+ d = n;
126
+ let p = !1, m = (e) => {
127
+ p || (p = !0, t(!1), e ? l({ reason: e }) : c());
128
+ };
129
+ f = () => m();
130
+ try {
131
+ let n = new window.SpeechSynthesisUtterance(e);
132
+ n.lang = o.lang;
133
+ let r = a(u, o.voice);
134
+ r && (n.voice = r), o.rate !== void 0 && (n.rate = o.rate), n.onstart = () => {
135
+ p || s();
136
+ }, n.onend = () => m(), n.onerror = (e) => m(i(e.error)), t(!0), u.speak(n);
137
+ } catch {
138
+ m("unknown");
139
+ }
140
+ }),
141
+ drain: async () => {},
142
+ retract: (e) => {
143
+ d >= e && p();
144
+ },
145
+ cancel: p,
146
+ failed: (e) => e?.reason ?? "unknown"
147
+ }, {
148
+ ...l,
149
+ onEnd: () => {
150
+ s === m && (s = null), l.onEnd?.();
106
151
  }
107
152
  });
153
+ return s = m, m;
108
154
  },
109
155
  cancel() {
110
- a += 1, e(!1);
156
+ let e = s;
157
+ s = null, e?.cancel(), o += 1, t(!1);
111
158
  try {
112
- t()?.cancel();
159
+ n()?.cancel();
113
160
  } catch {}
114
161
  }
115
162
  };
116
163
  //#endregion
117
- export { o as browserSpeechEngine };
164
+ export { c as browserSpeechEngine };