@burdenoff/fe-libs 2026.1014.4 → 2026.1015.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/shared/assistant/avatar/avatarPreferences.d.ts +61 -14
  2. package/dist/shared/assistant/avatar/avatarPreferences.js +54 -21
  3. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +33 -0
  4. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +278 -0
  5. package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +5 -0
  6. package/dist/shared/assistant/avatar/live/avatarSessionClient.js +25 -6
  7. package/dist/shared/assistant/avatar/live/avatarVoice.d.ts +43 -0
  8. package/dist/shared/assistant/avatar/live/avatarVoice.js +45 -0
  9. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +21 -1
  10. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +74 -24
  11. package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +47 -8
  12. package/dist/shared/assistant/avatar/live/liveAvatarController.js +244 -161
  13. package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +2 -2
  14. package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -4
  15. package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +52 -21
  16. package/dist/shared/assistant/avatar/useAvatarCopy.js +93 -48
  17. package/dist/shared/assistant/i18nManifest.d.ts +24 -2
  18. package/dist/shared/assistant/i18nManifest.js +24 -2
  19. package/dist/shared/assistant/index.d.ts +5 -0
  20. package/dist/shared/assistant/speech/hostedVoiceCatalogue.d.ts +19 -0
  21. package/dist/shared/assistant/speech/hostedVoiceCatalogue.js +25 -18
  22. package/dist/shared/assistant/ui/AIAssistantPanel.js +136 -103
  23. package/dist/shared/assistant/ui/AssistantAvatarTabs.d.ts +10 -0
  24. package/dist/shared/assistant/ui/AssistantAvatarTabs.js +48 -0
  25. package/dist/shared/assistant/ui/AssistantChatArea.js +1260 -1199
  26. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +165 -5
  27. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.js +141 -59
  28. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.d.ts +13 -0
  29. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +70 -0
  30. package/dist/shared/assistant/ui/composer/AssistantComposer.d.ts +18 -0
  31. package/dist/shared/assistant/ui/composer/AssistantComposer.js +121 -102
  32. package/dist/shared/assistant/ui/composer/AssistantHandsFreeInterruptToggle.d.ts +9 -0
  33. package/dist/shared/assistant/ui/composer/AssistantHandsFreeInterruptToggle.js +28 -14
  34. package/dist/shared/assistant/ui/panelOverlays.d.ts +24 -3
  35. package/dist/shared/assistant/ui/panelOverlays.js +11 -10
  36. package/dist/shared/assistant/ui/useVoiceSession.js +234 -233
  37. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.d.ts +6 -0
  38. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.js +161 -149
  39. package/dist/shared/assistant/ui/voice/VoiceMiniController.js +7 -7
  40. package/dist/shared/assistant/ui/voice/VoiceStage.js +7 -7
  41. package/dist/shared/assistant/ui/voice/voiceStartGate.d.ts +57 -0
  42. package/dist/shared/assistant/ui/voice/voiceStartGate.js +9 -0
  43. package/dist/shared/assistant/voice/handsfree/handsFreeSetting.d.ts +7 -1
  44. package/dist/shared/assistant/voice/handsfree/handsFreeSetting.js +2 -2
  45. package/dist/shared-assistant.js +259 -254
  46. package/package.json +1 -1
@@ -1,12 +1,19 @@
1
1
  //#region src/shared/assistant/avatar/live/liveAvatarStatus.ts
2
- function e(e, t) {
2
+ function e(e, t, n = Date.now()) {
3
3
  if (e.problem === "playback-blocked" && e.audioBlocked) return null;
4
- if (e.problem !== null) return t.problem(e.problem);
4
+ let r = e.voiceLive;
5
+ if (e.problem !== null) {
6
+ let i = e.retryAt, a = i !== null && i > n, o = t.problem(e.problem, {
7
+ voiceLive: r,
8
+ timed: a
9
+ });
10
+ return a ? `${o} ${t.availableAgain(t.retryTime(i, n))}` : o;
11
+ }
5
12
  if (e.phase === "checking") return t.chooserLoading;
6
13
  switch (e.notice) {
7
14
  case "interrupted": return t.stateInterrupted;
8
- case "voice-only": return t.stateVoiceOnly;
9
- case "time-limit": return t.stateTimeLimit;
15
+ case "voice-only": return r ? t.stateVoiceOnly : t.stateVoiceOnlyNoVoice;
16
+ case "time-limit": return r ? t.stateTimeLimit : t.stateTimeLimitNoVoice;
10
17
  default: return null;
11
18
  }
12
19
  }
@@ -1,23 +1,8 @@
1
- /**
2
- * Every user-visible word of the optional live avatar, in one place (BOFF-8822).
3
- *
4
- * ★★ ONE MODULE, LITERAL KEYS. `i18nManifest.test.ts` parses every module under
5
- * `shared/assistant` for `tr("<key>", "<English>")` calls and fails on a key the
6
- * manifest lacks, a default that differs, or a manifest entry no call requests.
7
- * Keeping every call here means the avatar UI imports words and cannot add a
8
- * string the manifest does not know — and the manifest entries and these calls
9
- * are one change.
10
- *
11
- * ★ The English names no product of ours (`noProductNames.test.ts`). The consent
12
- * is the one place that names the services the reply audio goes to: the
13
- * disclosure decided on 2026-10-08, which must stay true to the data flow —
14
- * only the assistant's reply audio is sent, as data to the avatar; the person's
15
- * microphone audio never is. It says nothing of credits (2026-10-09): avatar
16
- * time is billed to the company, and "credits" read as the person's own. Pinned
17
- * word for word by `avatarConsentCopy.test.ts`.
18
- */
1
+ import { VoiceStartBlocker } from '../ui/voice/voiceStartGate';
19
2
  /** What the person can be told went wrong; the adapter maps server codes onto these. */
20
- export type AvatarProblem = 'unavailable' | 'not-ready' | 'already-running' | 'capacity' | 'rate-limited' | 'paused' | 'start-timeout' | 'provider' | 'connection' | 'needs-hosted-voice' | 'unsupported-browser' | 'playback-blocked';
3
+ export type AvatarProblem = 'unavailable' | 'not-ready' | 'already-running' | 'capacity' | 'rate-limited' | 'paused'
4
+ /** The avatar time allowed for now (the daily credits) is used up; the server says when it comes back. */
5
+ | 'budget-exhausted' | 'start-timeout' | 'provider' | 'connection' | 'needs-hosted-voice' | 'unsupported-browser' | 'playback-blocked';
21
6
  export interface AvatarCopy {
22
7
  toggle: string;
23
8
  toggleHint: string;
@@ -45,9 +30,55 @@ export interface AvatarCopy {
45
30
  chooserOption: (name: string) => string;
46
31
  chooserSelected: (name: string) => string;
47
32
  voiceModeLabel: string;
48
- voiceModePaired: string;
33
+ /** The avatar voice the host configured for this avatar (`avatarVoices`), naming that speaker; offered only when there is one. */
34
+ voiceModeDefaultNamed: (name: string) => string;
49
35
  voiceModeMine: string;
36
+ /** "Keep my current voice", naming the voice the assistant resolves to now. */
37
+ voiceModeMineNamed: (name: string) => string;
50
38
  voiceModeHint: string;
51
- problem: (problem: AvatarProblem) => string;
39
+ /** The Avatar tab (BOFF-9341): the tablist's accessible name, and its two tabs. */
40
+ tabsLabel: string;
41
+ tabChat: string;
42
+ tabAvatar: string;
43
+ /** While the avatar is on, the chooser is read-only: switching avatars is Turn off, pick, Turn on (a new session). */
44
+ chooserLocked: string;
45
+ /** Leaving the Avatar tab turns its avatar off. */
46
+ tabLeaveHint: string;
47
+ /** After a "not now" refusal that said when: `{{time}}` is `retryTime(...)`. */
48
+ availableAgain: (time: string) => string;
49
+ /** The time a refused start may be tried again, in the interface's locale: the date is added when it is not today. */
50
+ retryTime: (at: number, now: number) => string;
51
+ /** The time-limit notice when no voice conversation is live (nothing continues "with voice only"). */
52
+ stateTimeLimitNoVoice: string;
53
+ /** The voice-only notice when no voice conversation is live. */
54
+ stateVoiceOnlyNoVoice: string;
55
+ /** The Avatar tab's Turn on while its start is under way: aria-disabled, so a second click starts nothing (critic #2). */
56
+ turnOnStarting: string;
57
+ /** The Avatar tab's way into the voice conversation: the composer's own words (`voiceSession.start`). */
58
+ startVoice: string;
59
+ /** Plays the chosen avatar voice on the speakers: the voice settings' own words (`voice.preview`). */
60
+ hearVoice: string;
61
+ /** Why the Avatar tab cannot start a voice conversation now (critic #6): the composer's own check says no. */
62
+ voiceStartBlocked: (blocker: VoiceStartBlocker) => string;
63
+ /** Said with a lasting reason: the avatar can be turned on, but it is asked by voice only. */
64
+ askNeedsVoice: string;
65
+ /** Why hands-free interrupt is off while the avatar is on (the pilot rule), and that it stays off after: the session's notice, and its control's reason. */
66
+ handsFreeOff: string;
67
+ /**
68
+ * What went wrong. `voiceLive` false: no voice conversation is live, so nothing is said to "continue" (BOFF-9341). `timed`: the next
69
+ * sentence says when it can start again, so the wording does not also say "in a moment".
70
+ */
71
+ problem: (problem: AvatarProblem, options?: AvatarProblemOptions) => string;
72
+ }
73
+ export interface AvatarProblemOptions {
74
+ /** A voice conversation is live (the default: the voice stage is shown only during one). */
75
+ readonly voiceLive?: boolean;
76
+ /** A "You can start it again at …" sentence follows. */
77
+ readonly timed?: boolean;
52
78
  }
79
+ /**
80
+ * `at` as the reader's locale writes a time, with the date when `at` is not on `now`'s day (a daily limit comes back tomorrow).
81
+ * ★ An unknown or malformed locale tag makes `Intl.DateTimeFormat` throw a RangeError: English stands in rather than losing the line.
82
+ */
83
+ export declare function formatAvatarRetryTime(at: number, now: number, locale: string | undefined): string;
53
84
  export declare function useAvatarCopy(): AvatarCopy;
@@ -1,54 +1,99 @@
1
- import { useAssistantTr as e } from "../i18n.js";
2
- import { useMemo as t } from "react";
1
+ import { useI18n as e } from "../../providers/shell/I18nProvider.js";
2
+ import { useAssistantTr as t } from "../i18n.js";
3
+ import { useMemo as n } from "react";
3
4
  //#region src/shared/assistant/avatar/useAvatarCopy.ts
4
- function n() {
5
- let n = e();
6
- return t(() => ({
7
- toggle: n("avatar.toggle", "Live avatar"),
8
- toggleHint: n("avatar.toggleHint", "Show the assistant as a speaking avatar during voice conversations."),
9
- stop: n("avatar.stop", "Turn off the avatar"),
10
- consent: n("avatar.consent", "Turning on Avatar shares the assistant's reply audio with LiveKit and Protoface to animate the avatar. This feature does not send your microphone audio to Protoface."),
11
- consentConfirm: n("avatar.consentConfirm", "Turn on the avatar"),
12
- consentCancel: n("avatar.consentCancel", "Not now"),
13
- stateOff: n("avatar.stateOff", "Avatar off"),
14
- stateOn: n("avatar.stateOn", "Avatar on"),
15
- stateStarting: n("avatar.stateStarting", "Starting the avatar…"),
16
- stateReady: n("avatar.stateReady", "Avatar ready"),
17
- stateEnding: n("avatar.stateEnding", "Stopping the avatar…"),
18
- stateInterrupted: n("avatar.stateInterrupted", "The avatar was interrupted. You can keep talking."),
19
- stateVoiceOnly: n("avatar.stateVoiceOnly", "The avatar is unavailable right now. Continuing with voice only."),
20
- stateTimeLimit: n("avatar.stateTimeLimit", "The avatar reached its time limit. Continuing with voice only."),
21
- stageLabel: n("avatar.stageLabel", "Live avatar"),
22
- stageWaiting: n("avatar.stageWaiting", "Waiting for the avatar…"),
23
- chooserTitle: n("avatar.chooserTitle", "Choose an avatar"),
24
- chooserLabel: n("avatar.chooserLabel", "Avatar"),
25
- chooserNone: n("avatar.chooserNone", "No avatar chosen"),
26
- chooserLoading: n("avatar.chooserLoading", "Loading avatars…"),
27
- chooserEmpty: n("avatar.chooserEmpty", "No avatars are available."),
28
- chooserRetry: n("avatar.chooserRetry", "Try again"),
29
- chooserOption: (e) => n("avatar.chooserOption", "Use {{name}}", { name: e }),
30
- chooserSelected: (e) => n("avatar.chooserSelected", "{{name}} (selected)", { name: e }),
31
- voiceModeLabel: n("avatar.voiceModeLabel", "Avatar voice"),
32
- voiceModePaired: n("avatar.voiceModePaired", "Use this avatar's recommended voice"),
33
- voiceModeMine: n("avatar.voiceModeMine", "Keep my current voice"),
34
- voiceModeHint: n("avatar.voiceModeHint", "The recommended voice is chosen for this avatar and the language of each reply."),
35
- problem: (e) => {
5
+ var r = {
6
+ hour: "numeric",
7
+ minute: "2-digit"
8
+ }, i = {
9
+ month: "short",
10
+ day: "numeric",
11
+ hour: "numeric",
12
+ minute: "2-digit"
13
+ };
14
+ function a(e, t, n) {
15
+ let a = new Date(e).toDateString() === new Date(t).toDateString() ? r : i;
16
+ try {
17
+ return new Intl.DateTimeFormat(n, a).format(e);
18
+ } catch {
19
+ return new Intl.DateTimeFormat("en", a).format(e);
20
+ }
21
+ }
22
+ function o() {
23
+ let r = t(), { locale: i } = e();
24
+ return n(() => ({
25
+ toggle: r("avatar.toggle", "Live avatar"),
26
+ toggleHint: r("avatar.toggleHint", "Show the assistant as a speaking avatar during voice conversations."),
27
+ stop: r("avatar.stop", "Turn off the avatar"),
28
+ consent: r("avatar.consent", "Turning on Avatar shares the assistant's reply audio with LiveKit and Protoface to animate the avatar. This feature does not send your microphone audio to Protoface."),
29
+ consentConfirm: r("avatar.consentConfirm", "Turn on the avatar"),
30
+ consentCancel: r("avatar.consentCancel", "Not now"),
31
+ stateOff: r("avatar.stateOff", "Avatar off"),
32
+ stateOn: r("avatar.stateOn", "Avatar on"),
33
+ stateStarting: r("avatar.stateStarting", "Starting the avatar…"),
34
+ stateReady: r("avatar.stateReady", "Avatar ready"),
35
+ stateEnding: r("avatar.stateEnding", "Stopping the avatar…"),
36
+ stateInterrupted: r("avatar.stateInterrupted", "The avatar was interrupted. You can keep talking."),
37
+ stateVoiceOnly: r("avatar.stateVoiceOnly", "The avatar is unavailable right now. Continuing with voice only."),
38
+ stateTimeLimit: r("avatar.stateTimeLimit", "The avatar reached its time limit. Continuing with voice only."),
39
+ stageLabel: r("avatar.stageLabel", "Live avatar"),
40
+ stageWaiting: r("avatar.stageWaiting", "Waiting for the avatar…"),
41
+ chooserTitle: r("avatar.chooserTitle", "Choose an avatar"),
42
+ chooserLabel: r("avatar.chooserLabel", "Avatar"),
43
+ chooserNone: r("avatar.chooserNone", "No avatar chosen"),
44
+ chooserLoading: r("avatar.chooserLoading", "Loading avatars…"),
45
+ chooserEmpty: r("avatar.chooserEmpty", "No avatars are available."),
46
+ chooserRetry: r("avatar.chooserRetry", "Try again"),
47
+ chooserOption: (e) => r("avatar.chooserOption", "Use {{name}}", { name: e }),
48
+ chooserSelected: (e) => r("avatar.chooserSelected", "{{name}} (selected)", { name: e }),
49
+ voiceModeLabel: r("avatar.voiceModeLabel", "Avatar voice"),
50
+ voiceModeDefaultNamed: (e) => r("avatar.voiceModeDefaultNamed", "Default for this avatar ({{name}})", { name: e }),
51
+ voiceModeMine: r("avatar.voiceModeMine", "Keep my current voice"),
52
+ voiceModeMineNamed: (e) => r("avatar.voiceModeMineNamed", "Keep my current voice ({{name}})", { name: e }),
53
+ voiceModeHint: r("avatar.voiceModeNextReplyHint", "Used from the avatar's next reply. The assistant's own voice doesn't change."),
54
+ tabsLabel: r("avatar.tabsLabel", "Assistant views"),
55
+ tabChat: r("avatar.tabChat", "Chat"),
56
+ tabAvatar: r("avatar.tabAvatar", "Avatar"),
57
+ chooserLocked: r("avatar.chooserLocked", "Turn off the avatar to change it."),
58
+ tabLeaveHint: r("avatar.tabLeaveHint", "Leaving the Avatar tab turns the avatar off."),
59
+ availableAgain: (e) => r("avatar.availableAgain", "You can start it again at {{time}}.", { time: e }),
60
+ retryTime: (e, t) => a(e, t, i),
61
+ stateTimeLimitNoVoice: r("avatar.stateTimeLimitNoVoice", "The avatar reached its time limit."),
62
+ stateVoiceOnlyNoVoice: r("avatar.stateVoiceOnlyNoVoice", "The avatar is unavailable right now."),
63
+ turnOnStarting: r("avatar.turnOnStarting", "Starting the avatar…"),
64
+ startVoice: r("voiceSession.start", "Start a voice conversation"),
65
+ hearVoice: r("voice.preview", "Hear this voice"),
66
+ voiceStartBlocked: (e) => {
67
+ switch (e) {
68
+ case "unavailable": return r("avatar.voiceStartUnavailable", "Voice conversations aren’t available here.");
69
+ case "mode": return r("avatar.voiceStartMode", "Voice conversations aren’t available in this mode.");
70
+ case "read-only": return r("avatar.voiceStartReadOnly", "This conversation can’t take new questions. Start a new conversation to ask by voice.");
71
+ case "busy": return r("avatar.voiceStartBusy", "You can start a voice conversation when the assistant is ready.");
72
+ case "committing": return r("avatar.voiceStartCommitting", "You can start a voice conversation once your attachments are saved.");
73
+ case "dictating": return r("avatar.voiceStartDictating", "Stop dictating to start a voice conversation.");
74
+ }
75
+ },
76
+ askNeedsVoice: r("avatar.askNeedsVoice", "You can still turn the avatar on, but you ask it questions in a voice conversation."),
77
+ handsFreeOff: r("avatar.handsFreeOff", "Hands-free interrupt is off, so the avatar's voice can't interrupt itself. It stays off after you turn the avatar off; turn it back on in More options. You can still tap to interrupt."),
78
+ problem: (e, t = {}) => {
79
+ let n = t.voiceLive !== !1, i = t.timed === !0;
36
80
  switch (e) {
37
- case "unavailable": return n("avatar.problemUnavailable", "The avatar isn’t available here.");
38
- case "not-ready": return n("avatar.problemNotReady", "This avatar isn’t ready yet. Try another one.");
39
- case "already-running": return n("avatar.problemAlreadyRunning", "An avatar is already running for you.");
40
- case "capacity": return n("avatar.problemCapacity", "Avatars are busy right now. Try again in a moment.");
41
- case "rate-limited": return n("avatar.problemRateLimited", "You started avatars too quickly. Wait a minute and try again.");
42
- case "paused": return n("avatar.problemPaused", "Avatars are paused for now.");
43
- case "start-timeout": return n("avatar.problemStartTimeout", "The avatar took too long to start. Try again in a moment.");
44
- case "provider": return n("avatar.problemProvider", "The avatar had a problem. Voice continues without it.");
45
- case "connection": return n("avatar.problemConnection", "The connection to the avatar was lost.");
46
- case "needs-hosted-voice": return n("avatar.problemNeedsHostedVoice", "The avatar needs one of the assistant’s own voices. Choose one in the voice settings.");
47
- case "unsupported-browser": return n("avatar.problemUnsupportedBrowser", "This browser can’t show the avatar.");
48
- case "playback-blocked": return n("avatar.problemPlaybackBlocked", "The browser blocked the avatar’s sound. Tap the avatar to turn it on.");
81
+ case "unavailable": return r("avatar.problemUnavailable", "The avatar isn’t available here.");
82
+ case "not-ready": return r("avatar.problemNotReady", "This avatar isn’t ready yet. Try another one.");
83
+ case "already-running": return r("avatar.problemAlreadyRunning", "An avatar is already running for you.");
84
+ case "capacity": return i ? r("avatar.problemCapacityTimed", "Avatars are busy right now.") : r("avatar.problemCapacity", "Avatars are busy right now. Try again in a moment.");
85
+ case "rate-limited": return i ? r("avatar.problemRateLimitedTimed", "You started avatars too quickly.") : r("avatar.problemRateLimited", "You started avatars too quickly. Wait a minute and try again.");
86
+ case "paused": return r("avatar.problemPaused", "Avatars are paused for now.");
87
+ case "budget-exhausted": return r("avatar.problemBudgetExhausted", "Avatar time is used up for now.");
88
+ case "start-timeout": return r("avatar.problemStartTimeout", "The avatar took too long to start. Try again in a moment.");
89
+ case "provider": return n ? r("avatar.problemProvider", "The avatar had a problem. Voice continues without it.") : r("avatar.problemProviderNoVoice", "The avatar had a problem.");
90
+ case "connection": return r("avatar.problemConnection", "The connection to the avatar was lost.");
91
+ case "needs-hosted-voice": return r("avatar.problemNeedsHostedVoice", "The avatar needs one of the assistant’s own voices. Choose one in the voice settings.");
92
+ case "unsupported-browser": return r("avatar.problemUnsupportedBrowser", "This browser can’t show the avatar.");
93
+ case "playback-blocked": return r("avatar.problemPlaybackBlocked", "The browser blocked the avatar’s sound. Tap the avatar to turn it on.");
49
94
  }
50
95
  }
51
- }), [n]);
96
+ }), [r, i]);
52
97
  }
53
98
  //#endregion
54
- export { n as useAvatarCopy };
99
+ export { a as formatAvatarRetryTime, o as useAvatarCopy };
@@ -216,9 +216,12 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
216
216
  readonly "attachments.unsupportedImage": "This device can't read that image format, so it can't be resized to send. Save it as JPEG and try again.";
217
217
  readonly "attachments.uploading": "Uploading…";
218
218
  readonly "attachments.voiceDropped": "{{count}} attachment(s) couldn't be saved and weren't sent.";
219
+ readonly "avatar.askNeedsVoice": "You can still turn the avatar on, but you ask it questions in a voice conversation.";
220
+ readonly "avatar.availableAgain": "You can start it again at {{time}}.";
219
221
  readonly "avatar.chooserEmpty": "No avatars are available.";
220
222
  readonly "avatar.chooserLabel": "Avatar";
221
223
  readonly "avatar.chooserLoading": "Loading avatars…";
224
+ readonly "avatar.chooserLocked": "Turn off the avatar to change it.";
222
225
  readonly "avatar.chooserNone": "No avatar chosen";
223
226
  readonly "avatar.chooserOption": "Use {{name}}";
224
227
  readonly "avatar.chooserRetry": "Try again";
@@ -227,15 +230,20 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
227
230
  readonly "avatar.consent": "Turning on Avatar shares the assistant's reply audio with LiveKit and Protoface to animate the avatar. This feature does not send your microphone audio to Protoface.";
228
231
  readonly "avatar.consentCancel": "Not now";
229
232
  readonly "avatar.consentConfirm": "Turn on the avatar";
233
+ readonly "avatar.handsFreeOff": "Hands-free interrupt is off, so the avatar's voice can't interrupt itself. It stays off after you turn the avatar off; turn it back on in More options. You can still tap to interrupt.";
230
234
  readonly "avatar.problemAlreadyRunning": "An avatar is already running for you.";
235
+ readonly "avatar.problemBudgetExhausted": "Avatar time is used up for now.";
231
236
  readonly "avatar.problemCapacity": "Avatars are busy right now. Try again in a moment.";
237
+ readonly "avatar.problemCapacityTimed": "Avatars are busy right now.";
232
238
  readonly "avatar.problemConnection": "The connection to the avatar was lost.";
233
239
  readonly "avatar.problemNeedsHostedVoice": "The avatar needs one of the assistant’s own voices. Choose one in the voice settings.";
234
240
  readonly "avatar.problemNotReady": "This avatar isn’t ready yet. Try another one.";
235
241
  readonly "avatar.problemPaused": "Avatars are paused for now.";
236
242
  readonly "avatar.problemPlaybackBlocked": "The browser blocked the avatar’s sound. Tap the avatar to turn it on.";
237
243
  readonly "avatar.problemProvider": "The avatar had a problem. Voice continues without it.";
244
+ readonly "avatar.problemProviderNoVoice": "The avatar had a problem.";
238
245
  readonly "avatar.problemRateLimited": "You started avatars too quickly. Wait a minute and try again.";
246
+ readonly "avatar.problemRateLimitedTimed": "You started avatars too quickly.";
239
247
  readonly "avatar.problemStartTimeout": "The avatar took too long to start. Try again in a moment.";
240
248
  readonly "avatar.problemUnavailable": "The avatar isn’t available here.";
241
249
  readonly "avatar.problemUnsupportedBrowser": "This browser can’t show the avatar.";
@@ -248,14 +256,28 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
248
256
  readonly "avatar.stateReady": "Avatar ready";
249
257
  readonly "avatar.stateStarting": "Starting the avatar…";
250
258
  readonly "avatar.stateTimeLimit": "The avatar reached its time limit. Continuing with voice only.";
259
+ readonly "avatar.stateTimeLimitNoVoice": "The avatar reached its time limit.";
251
260
  readonly "avatar.stateVoiceOnly": "The avatar is unavailable right now. Continuing with voice only.";
261
+ readonly "avatar.stateVoiceOnlyNoVoice": "The avatar is unavailable right now.";
252
262
  readonly "avatar.stop": "Turn off the avatar";
263
+ readonly "avatar.tabAvatar": "Avatar";
264
+ readonly "avatar.tabChat": "Chat";
265
+ readonly "avatar.tabLeaveHint": "Leaving the Avatar tab turns the avatar off.";
266
+ readonly "avatar.tabsLabel": "Assistant views";
253
267
  readonly "avatar.toggle": "Live avatar";
254
268
  readonly "avatar.toggleHint": "Show the assistant as a speaking avatar during voice conversations.";
255
- readonly "avatar.voiceModeHint": "The recommended voice is chosen for this avatar and the language of each reply.";
269
+ readonly "avatar.turnOnStarting": "Starting the avatar…";
270
+ readonly "avatar.voiceModeDefaultNamed": "Default for this avatar ({{name}})";
256
271
  readonly "avatar.voiceModeLabel": "Avatar voice";
257
272
  readonly "avatar.voiceModeMine": "Keep my current voice";
258
- readonly "avatar.voiceModePaired": "Use this avatar's recommended voice";
273
+ readonly "avatar.voiceModeMineNamed": "Keep my current voice ({{name}})";
274
+ readonly "avatar.voiceModeNextReplyHint": "Used from the avatar's next reply. The assistant's own voice doesn't change.";
275
+ readonly "avatar.voiceStartBusy": "You can start a voice conversation when the assistant is ready.";
276
+ readonly "avatar.voiceStartCommitting": "You can start a voice conversation once your attachments are saved.";
277
+ readonly "avatar.voiceStartDictating": "Stop dictating to start a voice conversation.";
278
+ readonly "avatar.voiceStartMode": "Voice conversations aren’t available in this mode.";
279
+ readonly "avatar.voiceStartReadOnly": "This conversation can’t take new questions. Start a new conversation to ask by voice.";
280
+ readonly "avatar.voiceStartUnavailable": "Voice conversations aren’t available here.";
259
281
  readonly "chatArea.placeholderActions": "What would you like me to do?";
260
282
  readonly "chatArea.placeholderGeneral": "Ask me anything…";
261
283
  readonly "chatInput.ariaLabel": "Message input";
@@ -110,9 +110,12 @@ var m = {
110
110
  "attachments.unsupportedImage": "This device can't read that image format, so it can't be resized to send. Save it as JPEG and try again.",
111
111
  "attachments.uploading": "Uploading…",
112
112
  "attachments.voiceDropped": "{{count}} attachment(s) couldn't be saved and weren't sent.",
113
+ "avatar.askNeedsVoice": "You can still turn the avatar on, but you ask it questions in a voice conversation.",
114
+ "avatar.availableAgain": "You can start it again at {{time}}.",
113
115
  "avatar.chooserEmpty": "No avatars are available.",
114
116
  "avatar.chooserLabel": "Avatar",
115
117
  "avatar.chooserLoading": "Loading avatars…",
118
+ "avatar.chooserLocked": "Turn off the avatar to change it.",
116
119
  "avatar.chooserNone": "No avatar chosen",
117
120
  "avatar.chooserOption": "Use {{name}}",
118
121
  "avatar.chooserRetry": "Try again",
@@ -121,15 +124,20 @@ var m = {
121
124
  "avatar.consent": "Turning on Avatar shares the assistant's reply audio with LiveKit and Protoface to animate the avatar. This feature does not send your microphone audio to Protoface.",
122
125
  "avatar.consentCancel": "Not now",
123
126
  "avatar.consentConfirm": "Turn on the avatar",
127
+ "avatar.handsFreeOff": "Hands-free interrupt is off, so the avatar's voice can't interrupt itself. It stays off after you turn the avatar off; turn it back on in More options. You can still tap to interrupt.",
124
128
  "avatar.problemAlreadyRunning": "An avatar is already running for you.",
129
+ "avatar.problemBudgetExhausted": "Avatar time is used up for now.",
125
130
  "avatar.problemCapacity": "Avatars are busy right now. Try again in a moment.",
131
+ "avatar.problemCapacityTimed": "Avatars are busy right now.",
126
132
  "avatar.problemConnection": "The connection to the avatar was lost.",
127
133
  "avatar.problemNeedsHostedVoice": "The avatar needs one of the assistant’s own voices. Choose one in the voice settings.",
128
134
  "avatar.problemNotReady": "This avatar isn’t ready yet. Try another one.",
129
135
  "avatar.problemPaused": "Avatars are paused for now.",
130
136
  "avatar.problemPlaybackBlocked": "The browser blocked the avatar’s sound. Tap the avatar to turn it on.",
131
137
  "avatar.problemProvider": "The avatar had a problem. Voice continues without it.",
138
+ "avatar.problemProviderNoVoice": "The avatar had a problem.",
132
139
  "avatar.problemRateLimited": "You started avatars too quickly. Wait a minute and try again.",
140
+ "avatar.problemRateLimitedTimed": "You started avatars too quickly.",
133
141
  "avatar.problemStartTimeout": "The avatar took too long to start. Try again in a moment.",
134
142
  "avatar.problemUnavailable": "The avatar isn’t available here.",
135
143
  "avatar.problemUnsupportedBrowser": "This browser can’t show the avatar.",
@@ -142,14 +150,28 @@ var m = {
142
150
  "avatar.stateReady": "Avatar ready",
143
151
  "avatar.stateStarting": "Starting the avatar…",
144
152
  "avatar.stateTimeLimit": "The avatar reached its time limit. Continuing with voice only.",
153
+ "avatar.stateTimeLimitNoVoice": "The avatar reached its time limit.",
145
154
  "avatar.stateVoiceOnly": "The avatar is unavailable right now. Continuing with voice only.",
155
+ "avatar.stateVoiceOnlyNoVoice": "The avatar is unavailable right now.",
146
156
  "avatar.stop": "Turn off the avatar",
157
+ "avatar.tabAvatar": "Avatar",
158
+ "avatar.tabChat": "Chat",
159
+ "avatar.tabLeaveHint": "Leaving the Avatar tab turns the avatar off.",
160
+ "avatar.tabsLabel": "Assistant views",
147
161
  "avatar.toggle": "Live avatar",
148
162
  "avatar.toggleHint": "Show the assistant as a speaking avatar during voice conversations.",
149
- "avatar.voiceModeHint": "The recommended voice is chosen for this avatar and the language of each reply.",
163
+ "avatar.turnOnStarting": "Starting the avatar…",
164
+ "avatar.voiceModeDefaultNamed": "Default for this avatar ({{name}})",
150
165
  "avatar.voiceModeLabel": "Avatar voice",
151
166
  "avatar.voiceModeMine": "Keep my current voice",
152
- "avatar.voiceModePaired": "Use this avatar's recommended voice",
167
+ "avatar.voiceModeMineNamed": "Keep my current voice ({{name}})",
168
+ "avatar.voiceModeNextReplyHint": "Used from the avatar's next reply. The assistant's own voice doesn't change.",
169
+ "avatar.voiceStartBusy": "You can start a voice conversation when the assistant is ready.",
170
+ "avatar.voiceStartCommitting": "You can start a voice conversation once your attachments are saved.",
171
+ "avatar.voiceStartDictating": "Stop dictating to start a voice conversation.",
172
+ "avatar.voiceStartMode": "Voice conversations aren’t available in this mode.",
173
+ "avatar.voiceStartReadOnly": "This conversation can’t take new questions. Start a new conversation to ask by voice.",
174
+ "avatar.voiceStartUnavailable": "Voice conversations aren’t available here.",
153
175
  "chatArea.placeholderActions": "What would you like me to do?",
154
176
  "chatArea.placeholderGeneral": "Ask me anything…",
155
177
  "chatInput.ariaLabel": "Message input",
@@ -223,6 +223,9 @@ export * from './ui/AssistantTurnActivity';
223
223
  export * from './ui/voice/AssistantVoiceSession';
224
224
  export * from './ui/voice/HandsFreePrivacyNotice';
225
225
  export * from './ui/assistantLiveAvatarSeam';
226
+ export * from './ui/AssistantAvatarTabs';
227
+ export * from './ui/avatar/AssistantAvatarTab';
228
+ export * from './ui/voice/voiceStartGate';
226
229
  export * from './useAssistantApiAccess';
227
230
  export * from './useConversationTurns';
228
231
  export * from './transportErrors';
@@ -329,5 +332,7 @@ export * from './avatar/live/avatarSessionClient';
329
332
  export * from './avatar/live/liveAvatarController';
330
333
  export * from './avatar/live/liveKitAvatarRoom';
331
334
  export * from './avatar/live/LiveAvatarStage';
335
+ export * from './avatar/live/LiveAvatarTabView';
332
336
  export * from './avatar/live/liveAvatarStatus';
333
337
  export * from './avatar/live/createLiveAvatarSeam';
338
+ export * from './avatar/live/avatarVoice';
@@ -1,6 +1,25 @@
1
1
  import { SpeechVoiceLike } from '../voice/speechVoices';
2
2
  /** Vendor tier this catalogue describes; sent to the backend with each request. */
3
3
  export declare const HOSTED_TTS_TIER = "chirp3-hd";
4
+ /**
5
+ * Speaker names are shared across locales in this tier — the same speaker id is
6
+ * offered in every supported locale, so the catalogue is a cross product rather
7
+ * than a hand-written list per language.
8
+ *
9
+ * Exported for the live avatar's own voice (BOFF-9341): a person picks a SPEAKER
10
+ * for the avatar, once, and each reply speaks it in that reply's language
11
+ * (`hostedVoiceName`). A stored speaker is checked against this list.
12
+ */
13
+ export declare const HOSTED_SPEAKERS: readonly ["Achernar", "Algenib", "Charon", "Kore", "Puck", "Zephyr"];
14
+ export type HostedSpeaker = (typeof HOSTED_SPEAKERS)[number];
15
+ /** Whether a value (stored, or from a host's config) is one of this tier's speakers. */
16
+ export declare function isHostedSpeaker(value: unknown): value is HostedSpeaker;
17
+ /**
18
+ * The vendor's voice id for one speaker in one VENDOR locale (`hostedLocaleFor`
19
+ * gives it for an app tag), e.g. `ta-IN-Chirp3-HD-Kore`: the one spelling every
20
+ * entry below uses, and the name a voice is looked up by.
21
+ */
22
+ export declare function hostedVoiceName(vendorLocale: string, speaker: HostedSpeaker): string;
4
23
  /**
5
24
  * Every hosted voice, as `SpeechVoiceLike` so it is interchangeable with a
6
25
  * device voice everywhere downstream.
@@ -6,7 +6,11 @@ var e = "chirp3-hd", t = [
6
6
  "Kore",
7
7
  "Puck",
8
8
  "Zephyr"
9
- ], n = [
9
+ ];
10
+ function n(e) {
11
+ return typeof e == "string" && t.includes(e);
12
+ }
13
+ var r = [
10
14
  "en-US",
11
15
  "en-GB",
12
16
  "en-IN",
@@ -26,32 +30,35 @@ var e = "chirp3-hd", t = [
26
30
  "ja-JP",
27
31
  "fr-FR",
28
32
  "de-DE"
29
- ], r = { "cmn-CN": "zh-CN" };
30
- function i(e) {
31
- return r[e] ?? e;
33
+ ], i = { "cmn-CN": "zh-CN" };
34
+ function a(e) {
35
+ return i[e] ?? e;
36
+ }
37
+ function o(e, t) {
38
+ return `${e}-Chirp3-HD-${t}`;
32
39
  }
33
- var a = n.flatMap((e) => t.map((t) => ({
34
- name: `${e}-Chirp3-HD-${t}`,
35
- lang: i(e),
40
+ var s = r.flatMap((e) => t.map((t) => ({
41
+ name: o(e, t),
42
+ lang: a(e),
36
43
  localService: !1,
37
44
  source: "hosted"
38
- }))), o = /* @__PURE__ */ new Map();
39
- for (let e of n) for (let t of new Set([i(e), e])) {
45
+ }))), c = /* @__PURE__ */ new Map();
46
+ for (let e of r) for (let t of new Set([a(e), e])) {
40
47
  let n = t.toLowerCase().split("-")[0];
41
- o.has(n) || o.set(n, e);
48
+ c.has(n) || c.set(n, e);
42
49
  }
43
- var s = /* @__PURE__ */ new Map();
44
- for (let e of n) s.set(e.toLowerCase(), e), s.set(i(e).toLowerCase(), e);
45
- function c(e) {
50
+ var l = /* @__PURE__ */ new Map();
51
+ for (let e of r) l.set(e.toLowerCase(), e), l.set(a(e).toLowerCase(), e);
52
+ function u(e) {
46
53
  let t = e.trim().replace("_", "-");
47
54
  if (!t) return null;
48
- let n = s.get(t.toLowerCase());
55
+ let n = l.get(t.toLowerCase());
49
56
  if (n) return n;
50
57
  let r = t.toLowerCase().split("-")[0];
51
- return o.get(r) ?? null;
58
+ return c.get(r) ?? null;
52
59
  }
53
- function l(e) {
54
- return c(e) !== null;
60
+ function d(e) {
61
+ return u(e) !== null;
55
62
  }
56
63
  //#endregion
57
- export { e as HOSTED_TTS_TIER, a as HOSTED_VOICES, c as hostedLocaleFor, l as hostedSupportsLanguage };
64
+ export { t as HOSTED_SPEAKERS, e as HOSTED_TTS_TIER, s as HOSTED_VOICES, u as hostedLocaleFor, d as hostedSupportsLanguage, o as hostedVoiceName, n as isHostedSpeaker };