@burdenoff/fe-libs 2026.1029.6 → 2026.1029.8

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 (39) hide show
  1. package/dist/shared/assistant/appControls/grammar.js +21 -21
  2. package/dist/shared/assistant/appControls/registry.js +1 -1
  3. package/dist/shared/assistant/commandCompletions.js +87 -86
  4. package/dist/shared/assistant/index.d.ts +5 -0
  5. package/dist/shared/assistant/liveDeltas.d.ts +3 -0
  6. package/dist/shared/assistant/liveDeltas.js +17 -1
  7. package/dist/shared/assistant/liveOverlay.d.ts +4 -0
  8. package/dist/shared/assistant/liveOverlay.js +10 -1
  9. package/dist/shared/assistant/localUiCommands.js +106 -112
  10. package/dist/shared/assistant/localeCommands/languageNames.js +13 -13
  11. package/dist/shared/assistant/speech/appendSpeechQueue.d.ts +13 -0
  12. package/dist/shared/assistant/speech/appendSpeechQueue.js +75 -0
  13. package/dist/shared/assistant/speech/browserSpeechEngine.js +93 -46
  14. package/dist/shared/assistant/speech/hostedTtsSpeechEngine.js +173 -109
  15. package/dist/shared/assistant/speech/index.d.ts +1 -1
  16. package/dist/shared/assistant/speech/notifyCancellationSpeechEngine.js +53 -0
  17. package/dist/shared/assistant/speech/routingSpeechEngine.js +107 -23
  18. package/dist/shared/assistant/speech/speechFeed.d.ts +17 -2
  19. package/dist/shared/assistant/speech/speechFeed.js +35 -12
  20. package/dist/shared/assistant/speech/types.d.ts +20 -1
  21. package/dist/shared/assistant/turnTimeline.d.ts +3 -0
  22. package/dist/shared/assistant/turnTimeline.js +4 -1
  23. package/dist/shared/assistant/ui/AssistantChatArea.js +1410 -1393
  24. package/dist/shared/assistant/ui/AssistantSpeechProvider.js +351 -291
  25. package/dist/shared/assistant/ui/useVoiceSession.js +323 -288
  26. package/dist/shared/assistant/voice/livePhraseSpeech.d.ts +14 -0
  27. package/dist/shared/assistant/voice/livePhraseSpeech.js +72 -0
  28. package/dist/shared/assistant/voice/phraseGate.d.ts +11 -0
  29. package/dist/shared/assistant/voice/phraseGate.js +74 -0
  30. package/dist/shared/assistant/voice/phrasePlayback.d.ts +9 -0
  31. package/dist/shared/assistant/voice/phrasePlayback.js +78 -0
  32. package/dist/shared/assistant/voice/phraseSpeechConfig.d.ts +5 -0
  33. package/dist/shared/assistant/voice/phraseSpeechConfig.js +10 -0
  34. package/dist/shared/assistant/voice/speakableText.d.ts +1 -0
  35. package/dist/shared/assistant/voice/speakableText.js +1 -1
  36. package/dist/shared/assistant/voice/voiceSession.d.ts +5 -0
  37. package/dist/shared/assistant/voice/voiceSession.js +17 -1
  38. package/dist/shared-assistant.js +293 -288
  39. package/package.json +1 -1
@@ -10,6 +10,59 @@ function e(e) {
10
10
  listVoices: () => e.listVoices(),
11
11
  subscribeVoices: (t) => e.subscribeVoices(t),
12
12
  cancel: n,
13
+ ...e.open ? { open(r, i = {}) {
14
+ n();
15
+ let a = { handlers: i };
16
+ t = a;
17
+ let o = {
18
+ onStart: () => {
19
+ t === a && i.onStart?.();
20
+ },
21
+ onChunkStart: (e) => {
22
+ t === a && i.onChunkStart?.(e);
23
+ },
24
+ onChunkEnd: (e) => {
25
+ t === a && i.onChunkEnd?.(e);
26
+ },
27
+ onRetract: (e) => {
28
+ t === a && i.onRetract?.(e);
29
+ },
30
+ onPause: () => {
31
+ t === a && i.onPause?.();
32
+ },
33
+ onDeviceFallback: () => {
34
+ t === a && i.onDeviceFallback?.();
35
+ },
36
+ onEnd: () => {
37
+ t === a && (t = null, i.onEnd?.());
38
+ },
39
+ onError: (e) => {
40
+ t === a && (t = null, i.onError?.(e));
41
+ }
42
+ }, s;
43
+ try {
44
+ s = e.open(r, o);
45
+ } catch {
46
+ return o.onError?.("unknown"), {
47
+ append: () => !1,
48
+ end() {},
49
+ retract() {},
50
+ cancel() {}
51
+ };
52
+ }
53
+ return {
54
+ append: (e) => t === a && s.append(e),
55
+ end: () => {
56
+ t === a && s.end();
57
+ },
58
+ retract: () => {
59
+ t === a && s.retract();
60
+ },
61
+ cancel: () => {
62
+ t === a && n();
63
+ }
64
+ };
65
+ } } : {},
13
66
  speak(r, i, a = {}) {
14
67
  n();
15
68
  let o = { handlers: a };
@@ -4,6 +4,7 @@ import { selectVoiceForLocale as n } from "../voice/speechVoices.js";
4
4
  //#region src/shared/assistant/speech/routingSpeechEngine.ts
5
5
  function r(e) {
6
6
  switch (e) {
7
+ case "rate-limited":
7
8
  case "cancelled":
8
9
  case "output-not-cleared":
9
10
  case "output-left": return !1;
@@ -15,11 +16,11 @@ function r(e) {
15
16
  }
16
17
  function i({ device: i, hosted: a }) {
17
18
  if (!a) return i;
18
- let o = new Set(a.listVoices().map((e) => e.name)), s = (e) => e.source === "hosted" || o.has(e.name), c = (e, t, r, a = !1) => {
19
+ let o = null, s = new Set(a.listVoices().map((e) => e.name)), c = (e) => e.source === "hosted" || s.has(e.name), l = (e, t, r, a = !1) => {
19
20
  let o = () => {
20
21
  a && r?.onDeviceFallback?.();
21
22
  };
22
- if (!t.voice || !s(t.voice)) {
23
+ if (!t.voice || !c(t.voice)) {
23
24
  o(), i.speak(e, t, r);
24
25
  return;
25
26
  }
@@ -27,16 +28,16 @@ function i({ device: i, hosted: a }) {
27
28
  r?.onError?.("unsupported");
28
29
  return;
29
30
  }
30
- let c = n(i.listVoices(), t.lang);
31
- if (!c) {
31
+ let s = n(i.listVoices(), t.lang);
32
+ if (!s) {
32
33
  r?.onError?.("unsupported");
33
34
  return;
34
35
  }
35
36
  o(), i.speak(e, {
36
37
  ...t,
37
- voice: c
38
+ voice: s
38
39
  }, r);
39
- }, l = (t) => a.isSupported() ? t.voice ? o.has(t.voice.name) ? a : i : e(t.lang) ? a : i : i;
40
+ }, u = (t) => a.isSupported() ? t.voice ? s.has(t.voice.name) ? a : i : e(t.lang) ? a : i : i;
40
41
  return {
41
42
  id: `routing(${a.id}+${i.id})`,
42
43
  isSupported() {
@@ -51,45 +52,128 @@ function i({ device: i, hosted: a }) {
51
52
  t(), n();
52
53
  };
53
54
  },
54
- speak(e, n, o) {
55
- if (i.cancel(), a.cancel(), l(n) !== a) {
56
- c(e, n, o);
55
+ speak(e, n, s) {
56
+ if (o?.cancel(), o = null, i.cancel(), a.cancel(), u(n) !== a) {
57
+ l(e, n, s);
57
58
  return;
58
59
  }
59
- let s = !1, u = -1;
60
+ let c = !1, d = -1;
60
61
  a.speak(e, n, {
61
- ...o,
62
+ ...s,
62
63
  onStart: () => {
63
- s = !0, o?.onStart?.();
64
+ c = !0, s?.onStart?.();
64
65
  },
65
66
  onChunkStart: (e) => {
66
- u = e, o?.onChunkStart?.(e);
67
+ d = e, s?.onChunkStart?.(e);
67
68
  },
68
69
  onError: (i) => {
69
70
  if (!r(i) || n.fallback === !1) {
70
- o?.onError?.(i);
71
+ s?.onError?.(i);
71
72
  return;
72
73
  }
73
- if (!s) {
74
- c(e, n, o, !0);
74
+ if (!c) {
75
+ l(e, n, s, !0);
75
76
  return;
76
77
  }
77
- let a = u >= 0 ? t(e).slice(u + 1) : [];
78
+ let a = d >= 0 ? t(e).slice(d + 1) : [];
78
79
  if (a.length === 0) {
79
- o?.onError?.(i);
80
+ s?.onError?.(i);
80
81
  return;
81
82
  }
82
- let l = u + 1;
83
- c(a, n, {
84
- ...o,
83
+ let o = d + 1;
84
+ l(a, n, {
85
+ ...s,
85
86
  onStart: void 0,
86
- onChunkStart: (e) => o?.onChunkStart?.(l + e)
87
+ onChunkStart: (e) => s?.onChunkStart?.(o + e)
87
88
  }, !0);
88
89
  }
89
90
  });
90
91
  },
92
+ ...i.open && a.open ? { open(e, t = {}) {
93
+ o?.cancel(), i.cancel(), a.cancel();
94
+ let s = u(e), l = !1, d = !1, f = !1, p = 0, m = [], h = [], g, _ = () => {
95
+ if (!e.voice || !c(e.voice)) return e;
96
+ if (e.fallback === !1) return null;
97
+ let t = n(i.listVoices(), e.lang);
98
+ return t ? {
99
+ ...e,
100
+ voice: t
101
+ } : null;
102
+ }, v = (e) => {
103
+ d || (d = !0, h = [], t.onError?.(e));
104
+ }, y = {
105
+ ...t,
106
+ onStart: () => {
107
+ d || (l = !0, h = [], t.onStart?.());
108
+ },
109
+ onChunkStart: (e) => {
110
+ if (d) return;
111
+ l = !0, h = [];
112
+ let n = m[e];
113
+ n !== void 0 && t.onChunkStart?.(n);
114
+ },
115
+ onChunkEnd: (e) => {
116
+ let n = m[e];
117
+ !d && n !== void 0 && t.onChunkEnd?.(n);
118
+ },
119
+ onRetract: (e) => {
120
+ let n = e.flatMap((e) => m[e] === void 0 ? [] : [m[e]]), r = new Set(n);
121
+ h = h.filter((e) => !r.has(e.index)), t.onRetract?.(n);
122
+ },
123
+ onPause: () => {
124
+ d || t.onPause?.();
125
+ },
126
+ onEnd: () => {
127
+ d || (d = !0, h = [], t.onEnd?.());
128
+ },
129
+ onError: (n) => {
130
+ if (d) return;
131
+ if (s === i || l || !r(n) || e.fallback === !1) {
132
+ v(n);
133
+ return;
134
+ }
135
+ let a = _();
136
+ if (!a || !i.open) {
137
+ v("unsupported");
138
+ return;
139
+ }
140
+ if (g.cancel(), s = i, m = h.map((e) => e.index), g = i.open(a, y), t.onDeviceFallback?.(), !g.append(h.map((e) => e.text))) {
141
+ v("unknown");
142
+ return;
143
+ }
144
+ f && g.end();
145
+ }
146
+ }, b = s === i ? _() : e;
147
+ if (!b || !s.open) return v("unsupported"), {
148
+ append: () => !1,
149
+ end: () => {},
150
+ retract: () => {},
151
+ cancel: () => {}
152
+ };
153
+ g = s.open(b, y);
154
+ let x = {
155
+ append(e) {
156
+ if (d || f) return !1;
157
+ let t = e.filter((e) => e.trim()), n = t.map((e, t) => ({
158
+ index: p + t,
159
+ text: e
160
+ })), r = m.length;
161
+ return m.push(...n.map((e) => e.index)), l || h.push(...n), g.append(t) ? (p += n.length, !0) : (m.splice(r), h = h.filter((e) => e.index < p), !1);
162
+ },
163
+ end() {
164
+ d || (f = !0, g.end());
165
+ },
166
+ retract() {
167
+ d || g.retract();
168
+ },
169
+ cancel() {
170
+ d || (d = !0, h = [], g.cancel());
171
+ }
172
+ };
173
+ return o = x, x;
174
+ } } : {},
91
175
  cancel() {
92
- i.cancel(), a.cancel();
176
+ o?.cancel(), o = null, i.cancel(), a.cancel();
93
177
  }
94
178
  };
95
179
  }
@@ -34,13 +34,25 @@ export interface SpeechFeedItem {
34
34
  text: string;
35
35
  /** The turn's reply language, stamped on the message as `metadata.lang`. */
36
36
  lang?: string | undefined;
37
- /** Always true in C1: only a finished answer is published. */
38
- final: true;
37
+ /** False only for opted-in S7 text already written to the live overlay. */
38
+ final: boolean;
39
39
  }
40
40
  export type SpeechFeedEvent = {
41
41
  type: 'publish';
42
42
  messageId: string;
43
43
  item: SpeechFeedItem;
44
+ } | {
45
+ type: 'append';
46
+ messageId: string;
47
+ item: SpeechFeedItem;
48
+ phrases: readonly string[];
49
+ } | {
50
+ type: 'retract';
51
+ messageId: string;
52
+ } | {
53
+ type: 'audio';
54
+ messageId: string;
55
+ state: 'queued' | 'started' | 'paused' | 'ended';
44
56
  } | {
45
57
  type: 'abort';
46
58
  messageId: string;
@@ -48,6 +60,9 @@ export type SpeechFeedEvent = {
48
60
  type Listener = (event: SpeechFeedEvent) => void;
49
61
  export declare const speechFeed: {
50
62
  publish(messageId: string, item: SpeechFeedItem): void;
63
+ append(messageId: string, item: SpeechFeedItem, phrases: readonly string[]): void;
64
+ retract(messageId: string): void;
65
+ audio(messageId: string, state: "queued" | "started" | "paused" | "ended"): void;
51
66
  /** Forget a published message and tell the subscriber to stop speaking it. */
52
67
  abort(messageId: string): void;
53
68
  subscribe(listener: Listener): () => void;
@@ -5,17 +5,40 @@ function r(e) {
5
5
  n(e);
6
6
  } catch {}
7
7
  }
8
- var i = {
9
- publish(t, i) {
10
- for (n.delete(t), n.set(t, i); n.size > e;) {
11
- let e = n.keys().next();
12
- if (e.done) break;
13
- n.delete(e.value);
14
- }
15
- r({
8
+ function i(t, r) {
9
+ for (n.delete(t), n.set(t, r); n.size > e;) {
10
+ let e = n.keys().next();
11
+ if (e.done) break;
12
+ n.delete(e.value);
13
+ }
14
+ }
15
+ var a = {
16
+ publish(e, t) {
17
+ i(e, t), r({
16
18
  type: "publish",
17
- messageId: t,
18
- item: i
19
+ messageId: e,
20
+ item: t
21
+ });
22
+ },
23
+ append(e, t, n) {
24
+ i(e, t), r({
25
+ type: "append",
26
+ messageId: e,
27
+ item: t,
28
+ phrases: n
29
+ });
30
+ },
31
+ retract(e) {
32
+ r({
33
+ type: "retract",
34
+ messageId: e
35
+ });
36
+ },
37
+ audio(e, t) {
38
+ r({
39
+ type: "audio",
40
+ messageId: e,
41
+ state: t
19
42
  });
20
43
  },
21
44
  abort(e) {
@@ -33,8 +56,8 @@ var i = {
33
56
  return n.get(e);
34
57
  }
35
58
  };
36
- function a() {
59
+ function o() {
37
60
  t.clear(), n.clear();
38
61
  }
39
62
  //#endregion
40
- export { a as resetSpeechFeedForTest, i as speechFeed };
63
+ export { o as resetSpeechFeedForTest, a as speechFeed };
@@ -157,8 +157,14 @@ export interface SpeechOutputRefusal {
157
157
  * played on the speakers. Only the live avatar's engine decorator reports it,
158
158
  * and nothing re-speaks it.
159
159
  */
160
- export type SpeechErrorReason = 'cancelled' | 'not-allowed' | 'unsupported' | 'unknown' | 'output-not-cleared' | 'output-left';
160
+ export type SpeechErrorReason = 'rate-limited' | 'cancelled' | 'not-allowed' | 'unsupported' | 'unknown' | 'output-not-cleared' | 'output-left';
161
161
  export interface SpeechSpeakHandlers {
162
+ /** An open queue drained for now, but its producer has not ended it. */
163
+ onPause?: () => void;
164
+ /** Unplayed indices withdrawn by a tool start or a changed source message. */
165
+ onRetract?: (indices: readonly number[]) => void;
166
+ /** A piece finished; a bounded producer may append more. */
167
+ onChunkEnd?: (index: number) => void;
162
168
  /**
163
169
  * Audio has actually begun playing — not merely been requested.
164
170
  *
@@ -204,6 +210,8 @@ export interface SpeechSpeakHandlers {
204
210
  onDeviceFallback?: () => void;
205
211
  }
206
212
  export interface SpeechEngine {
213
+ /** Optional: engines/decorators without it retain completed-answer speech. */
214
+ open?(options: SpeechSpeakOptions, handlers?: SpeechSpeakHandlers): SpeechQueue;
207
215
  /** Stable id, for logging and for telling engines apart in a future swap. */
208
216
  readonly id: string;
209
217
  /** False when the platform has no usable speech output at all. */
@@ -226,3 +234,14 @@ export interface SpeechEngine {
226
234
  */
227
235
  wouldSpeakOnAvatar?(options: SpeechSpeakOptions): boolean;
228
236
  }
237
+ /** One reply. Appends never pre-empt the piece currently playing. */
238
+ export interface SpeechQueue {
239
+ /** False means full or terminal; a producer must retain its unaccepted text. */
240
+ append(chunks: readonly string[]): boolean;
241
+ /** No more appends; onEnd follows the last piece and confirmed drain. */
242
+ end(): void;
243
+ /** Drop queued/unplayed work while the sounding piece finishes. */
244
+ retract(): void;
245
+ /** Hard stop, including pending synthesis. No end event. */
246
+ cancel(): void;
247
+ }
@@ -131,6 +131,9 @@ export interface TurnDimensions {
131
131
  }
132
132
  /** What one attempt found out. */
133
133
  export interface TurnAttemptFacts {
134
+ /** S7 local diagnostics only: no text and no new metric labels. */
135
+ spokeNarration?: boolean;
136
+ speechDivergence?: boolean;
134
137
  runtime?: TurnRuntime;
135
138
  /** The model called at least one tool on the way to its answer. */
136
139
  usedTools?: boolean;
@@ -84,6 +84,7 @@ function m(e, t) {
84
84
  let n = i.get(e);
85
85
  if (!n) return;
86
86
  let r = !1;
87
+ for (let e of ["spokeNarration", "speechDivergence"]) t[e] === !0 && n[e] !== !0 && (n[e] = !0, r = !0);
87
88
  t.runtime !== void 0 && n.runtime === void 0 && (n.runtime = t.runtime, r = !0), t.usedTools !== void 0 && n.usedTools === void 0 && (n.usedTools = t.usedTools, r = !0), t.deadSandboxProbe !== void 0 && n.deadSandboxProbe === void 0 && (n.deadSandboxProbe = t.deadSandboxProbe, r = !0), t.cacheProofFailed !== void 0 && n.cacheProofFailed === void 0 && (n.cacheProofFailed = t.cacheProofFailed, r = !0), t.prewarm !== void 0 && n.prewarm === void 0 && (n.prewarm = t.prewarm, r = !0), t.transport !== void 0 && n.transport === void 0 && (n.transport = t.transport, r = !0), t.live !== void 0 && n.live === void 0 && (n.live = t.live, r = !0), t.session !== void 0 && n.session === void 0 && (n.session = t.session, r = !0), t.outcome !== void 0 && n.outcome === void 0 && (n.outcome = t.outcome, r = !0), r && d(n);
88
89
  }
89
90
  function h(e, n = c()) {
@@ -97,7 +98,9 @@ function h(e, n = c()) {
97
98
  let t = r.at[e];
98
99
  t !== void 0 && (a.at[e] = t, delete r.at[e]);
99
100
  }
100
- r.runtime !== void 0 && (a.runtime = r.runtime), r.usedTools !== void 0 && (a.usedTools = r.usedTools), r.deadSandboxProbe !== void 0 && (a.deadSandboxProbe = r.deadSandboxProbe), r.cacheProofFailed !== void 0 && (a.cacheProofFailed = r.cacheProofFailed), r.prewarm !== void 0 && (a.prewarm = r.prewarm), r.transport !== void 0 && (a.transport = r.transport), r.live !== void 0 && (a.live = r.live), r.session !== void 0 && (a.session = r.session), delete r.transport, delete r.live, delete r.session, delete r.runtime, delete r.usedTools, delete r.deadSandboxProbe, delete r.cacheProofFailed, delete r.prewarm, r.attempts = [...r.attempts ?? [], a], r.retried = !0, d(r);
101
+ r.runtime !== void 0 && (a.runtime = r.runtime), r.usedTools !== void 0 && (a.usedTools = r.usedTools), r.deadSandboxProbe !== void 0 && (a.deadSandboxProbe = r.deadSandboxProbe), r.cacheProofFailed !== void 0 && (a.cacheProofFailed = r.cacheProofFailed), r.prewarm !== void 0 && (a.prewarm = r.prewarm), r.transport !== void 0 && (a.transport = r.transport), r.live !== void 0 && (a.live = r.live), r.session !== void 0 && (a.session = r.session);
102
+ for (let e of ["spokeNarration", "speechDivergence"]) r[e] !== void 0 && (a[e] = r[e]), delete r[e];
103
+ delete r.transport, delete r.live, delete r.session, delete r.runtime, delete r.usedTools, delete r.deadSandboxProbe, delete r.cacheProofFailed, delete r.prewarm, r.attempts = [...r.attempts ?? [], a], r.retried = !0, d(r);
101
104
  }
102
105
  function g(e) {
103
106
  let t = i.get(e);