@burdenoff/fe-libs 2026.1024.2 → 2026.1024.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/dist/chrome/FooterBar.d.ts +1 -1
  2. package/dist/chrome/NativePageTransition.d.ts +1 -1
  3. package/dist/chrome/ProductPickerSheet.js +42 -42
  4. package/dist/chrome/ProductSwitcher.d.ts +8 -0
  5. package/dist/chrome/ProductSwitcher.js +83 -72
  6. package/dist/chrome/index.d.ts +9 -9
  7. package/dist/chrome/swipe/swipeMath.d.ts +1 -1
  8. package/dist/shared/assistant/api.d.ts +3 -3
  9. package/dist/shared/assistant/appControls/grammar.d.ts +21 -21
  10. package/dist/shared/assistant/appControls/languageReply.d.ts +1 -1
  11. package/dist/shared/assistant/appControls/names.d.ts +3 -3
  12. package/dist/shared/assistant/appControls/pageAdapters.d.ts +5 -5
  13. package/dist/shared/assistant/appControls/plan.d.ts +38 -38
  14. package/dist/shared/assistant/appControls/types.d.ts +15 -15
  15. package/dist/shared/assistant/appControls/useAssistantControl.d.ts +1 -1
  16. package/dist/shared/assistant/attachmentSources.d.ts +2 -2
  17. package/dist/shared/assistant/attachments.d.ts +5 -5
  18. package/dist/shared/assistant/autoRouting.d.ts +6 -6
  19. package/dist/shared/assistant/avatar/live/LiveAvatarMinutes.d.ts +24 -0
  20. package/dist/shared/assistant/avatar/live/LiveAvatarMinutes.js +161 -0
  21. package/dist/shared/assistant/avatar/live/LiveAvatarStage.js +95 -73
  22. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +6 -0
  23. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +195 -171
  24. package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +77 -1
  25. package/dist/shared/assistant/avatar/live/avatarSessionClient.js +193 -72
  26. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +14 -1
  27. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +65 -40
  28. package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +64 -2
  29. package/dist/shared/assistant/avatar/live/liveAvatarController.js +403 -242
  30. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.d.ts +127 -0
  31. package/dist/shared/assistant/avatar/live/liveAvatarMinutesView.js +168 -0
  32. package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +3 -0
  33. package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -7
  34. package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +127 -2
  35. package/dist/shared/assistant/avatar/useAvatarCopy.js +116 -7
  36. package/dist/shared/assistant/conversation.d.ts +20 -20
  37. package/dist/shared/assistant/displayContent.d.ts +4 -4
  38. package/dist/shared/assistant/documentText.d.ts +3 -3
  39. package/dist/shared/assistant/exportConversation.d.ts +1 -1
  40. package/dist/shared/assistant/history/historyControls.d.ts +4 -4
  41. package/dist/shared/assistant/history/historyErrors.d.ts +3 -3
  42. package/dist/shared/assistant/history/historyList.d.ts +5 -5
  43. package/dist/shared/assistant/history/historyMerge.d.ts +4 -4
  44. package/dist/shared/assistant/history/historyOperations.d.ts +1 -1
  45. package/dist/shared/assistant/history/historySearch.d.ts +5 -5
  46. package/dist/shared/assistant/history/historyStatus.d.ts +1 -1
  47. package/dist/shared/assistant/history/historyTypes.d.ts +12 -12
  48. package/dist/shared/assistant/history/profileBoundStorage.d.ts +2 -2
  49. package/dist/shared/assistant/i18n.d.ts +1 -1
  50. package/dist/shared/assistant/i18nManifest.d.ts +566 -535
  51. package/dist/shared/assistant/i18nManifest.js +55 -24
  52. package/dist/shared/assistant/index.d.ts +2 -0
  53. package/dist/shared/assistant/liveDeltas.d.ts +1 -1
  54. package/dist/shared/assistant/liveOverlay.d.ts +1 -1
  55. package/dist/shared/assistant/localUiCommandCopy.d.ts +1 -1
  56. package/dist/shared/assistant/localUiCommands.d.ts +22 -22
  57. package/dist/shared/assistant/localeCommands/engine.d.ts +5 -5
  58. package/dist/shared/assistant/localeCommands/evaluate.d.ts +8 -8
  59. package/dist/shared/assistant/localeCommands/fold.d.ts +2 -2
  60. package/dist/shared/assistant/localeCommands/frames.d.ts +3 -3
  61. package/dist/shared/assistant/localeCommands/gate.d.ts +3 -3
  62. package/dist/shared/assistant/localeCommands/languageNames.d.ts +1 -1
  63. package/dist/shared/assistant/localeCommands/normalise.d.ts +1 -1
  64. package/dist/shared/assistant/localeCommands/types.d.ts +4 -4
  65. package/dist/shared/assistant/modeCapabilities.d.ts +1 -1
  66. package/dist/shared/assistant/modeCopy.d.ts +2 -2
  67. package/dist/shared/assistant/permissionError.d.ts +2 -2
  68. package/dist/shared/assistant/preview/surfaceState.d.ts +1 -1
  69. package/dist/shared/assistant/preview/types.d.ts +5 -5
  70. package/dist/shared/assistant/preview/writeActionResults.d.ts +1 -1
  71. package/dist/shared/assistant/prewarm.d.ts +5 -5
  72. package/dist/shared/assistant/quota.d.ts +2 -2
  73. package/dist/shared/assistant/sandboxReadiness.d.ts +2 -2
  74. package/dist/shared/assistant/speech/speechFeed.d.ts +3 -3
  75. package/dist/shared/assistant/turnOutcome.d.ts +2 -2
  76. package/dist/shared/assistant/turnProgress.d.ts +1 -1
  77. package/dist/shared/assistant/turnState.d.ts +1 -1
  78. package/dist/shared/assistant/turnStream.d.ts +3 -3
  79. package/dist/shared/assistant/turnTimeline.d.ts +13 -13
  80. package/dist/shared/assistant/turnTimingReport.d.ts +1 -1
  81. package/dist/shared/assistant/turnTimingReporter.d.ts +3 -3
  82. package/dist/shared/assistant/ui/AssistantAvatarTabs.d.ts +1 -1
  83. package/dist/shared/assistant/ui/AssistantChatArea.js +17 -17
  84. package/dist/shared/assistant/ui/AssistantHistoryNotices.d.ts +1 -1
  85. package/dist/shared/assistant/ui/AssistantMarkdownRenderer.d.ts +5 -2
  86. package/dist/shared/assistant/ui/AssistantToolActivity.d.ts +1 -1
  87. package/dist/shared/assistant/ui/AssistantUiOfferLink.d.ts +2 -2
  88. package/dist/shared/assistant/ui/AssistantWriteConfirmDialog.d.ts +3 -3
  89. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +10 -2
  90. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +78 -76
  91. package/dist/shared/assistant/ui/avatar/avatarAskGate.d.ts +2 -0
  92. package/dist/shared/assistant/ui/avatar/avatarAskGate.js +1 -0
  93. package/dist/shared/assistant/ui/composer/AssistantAttachmentButton.d.ts +1 -1
  94. package/dist/shared/assistant/ui/composer/AssistantConnectSourceItem.d.ts +1 -1
  95. package/dist/shared/assistant/ui/leaveConfirmFocus.d.ts +1 -1
  96. package/dist/shared/assistant/ui/leaveConfirmNavigate.d.ts +3 -3
  97. package/dist/shared/assistant/ui/panelOverlays.d.ts +5 -5
  98. package/dist/shared/assistant/ui/useVoiceSession.d.ts +5 -5
  99. package/dist/shared/assistant/ui/useWriteProposalConfirm.d.ts +4 -4
  100. package/dist/shared/assistant/ui/useWriteProposals.d.ts +2 -2
  101. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.d.ts +1 -1
  102. package/dist/shared/assistant/ui/voice/VoiceCaption.d.ts +1 -1
  103. package/dist/shared/assistant/ui/voice/VoiceWaveform.d.ts +1 -1
  104. package/dist/shared/assistant/ui/voice/voiceSessionEscape.d.ts +2 -2
  105. package/dist/shared/assistant/ui/voice/voiceStartGate.d.ts +6 -6
  106. package/dist/shared/assistant/ui/writeProposalPresentation.d.ts +1 -1
  107. package/dist/shared/assistant/uiActions/actOnResolve.d.ts +5 -5
  108. package/dist/shared/assistant/uiActions/assistantHref.d.ts +2 -2
  109. package/dist/shared/assistant/uiActions/backStack.d.ts +1 -1
  110. package/dist/shared/assistant/uiActions/deixis.d.ts +3 -3
  111. package/dist/shared/assistant/uiActions/entityRoutes.d.ts +1 -1
  112. package/dist/shared/assistant/uiActions/navigationOutcome.d.ts +8 -8
  113. package/dist/shared/assistant/uiActions/offerYesOnSurface.d.ts +1 -1
  114. package/dist/shared/assistant/uiActions/resolveTarget.d.ts +2 -2
  115. package/dist/shared/assistant/uiActions/uiOffers.d.ts +5 -5
  116. package/dist/shared/assistant/usageReporting.d.ts +2 -2
  117. package/dist/shared/assistant/useWorkspaceWriteAccess.d.ts +1 -1
  118. package/dist/shared/assistant/voice/affirmations.d.ts +1 -1
  119. package/dist/shared/assistant/voice/handsFree.d.ts +3 -3
  120. package/dist/shared/assistant/voice/handsfree/bargeIn.d.ts +5 -5
  121. package/dist/shared/assistant/voice/handsfree/echoGate.d.ts +6 -6
  122. package/dist/shared/assistant/voice/handsfree/handsFreeController.d.ts +7 -7
  123. package/dist/shared/assistant/voice/handsfree/handsFreeSetting.d.ts +2 -2
  124. package/dist/shared/assistant/voice/handsfree/micCapture.d.ts +1 -1
  125. package/dist/shared/assistant/voice/handsfree/playbackReference.d.ts +1 -1
  126. package/dist/shared/assistant/voice/handsfree/sttTransport.d.ts +3 -3
  127. package/dist/shared/assistant/voice/handsfree/useHandsFreeInterrupt.d.ts +4 -4
  128. package/dist/shared/assistant/voice/handsfree/vad.d.ts +1 -1
  129. package/dist/shared/assistant/voice/speechPlayback.d.ts +7 -7
  130. package/dist/shared/assistant/voice/useVoiceInput.d.ts +1 -1
  131. package/dist/shared/assistant/voice/voiceReopen.d.ts +8 -8
  132. package/dist/shared/assistant/writeConfirmSeam.d.ts +1 -1
  133. package/dist/shared/assistant/writeModeConsent.d.ts +7 -7
  134. package/dist/shared/assistant/writeProposalDiscovery.d.ts +3 -3
  135. package/dist/shared/assistant/writeProposalErrorCopy.d.ts +1 -1
  136. package/dist/shared/assistant/writeProposals.d.ts +23 -23
  137. package/dist/shared/assistant/writeReplyBinder.d.ts +11 -11
  138. package/dist/shared/assistant/writeReplySend.d.ts +7 -7
  139. package/dist/shared/components/LandingOrDashboardRedirect.d.ts +18 -3
  140. package/dist/shared/components/LandingOrDashboardRedirect.js +58 -28
  141. package/dist/shared/components/index.d.ts +2 -1
  142. package/dist/shared/events/index.d.ts +2 -1
  143. package/dist/shared/hooks/shell/globalSearchError.d.ts +2 -2
  144. package/dist/shared/navigation/productSwitchHandoff.d.ts +45 -0
  145. package/dist/shared/navigation/productSwitchHandoff.js +27 -0
  146. package/dist/shared/navigation/unsavedChanges.d.ts +1 -1
  147. package/dist/shared/utils/index.d.ts +3 -3
  148. package/dist/shared-assistant.js +227 -225
  149. package/dist/shared-components.js +7 -6
  150. package/dist/shared.js +71 -70
  151. package/package.json +3 -2
@@ -0,0 +1,127 @@
1
+ import { AvatarCopy } from '../useAvatarCopy';
2
+ import { LiveAvatarSnapshot } from './liveAvatarController';
3
+ /** At most this many credits left read as running low (DESIGN 3.10: 1 or 2). */
4
+ export declare const LIVE_AVATAR_MINUTES_LOW = 2;
5
+ /**
6
+ * The app's plans page (each app mounts billing at `/billing`), routed with the panel's navigator: the page the shared upgrade prompt
7
+ * opens by default (`UpgradePromptModal`'s `upgradePath`), so a plan with more avatar credits is bought as any plan is (plans page,
8
+ * checkout, activation), and the tab checks again when the person comes back.
9
+ */
10
+ export declare const LIVE_AVATAR_MINUTES_PLANS_PATH = "/billing/plans";
11
+ /** What the host's `minutesPurchase.access()` answers. */
12
+ export interface LiveAvatarMinutesPurchaseAccess {
13
+ /** This person may buy a plan for the workspace's organization (the host's billing check, `billing:create` on its billing account). */
14
+ readonly canPurchase: boolean;
15
+ }
16
+ /** ★ BOFF-9446: the host's way to tell whether this person may buy a plan. fe-libs cannot import billing, so the host passes it. */
17
+ export interface LiveAvatarMinutesPurchase {
18
+ /** Asked only where "See plans" could be offered (a plan with more credits exists), and once per workspace's credits answer. */
19
+ readonly access: () => Promise<LiveAvatarMinutesPurchaseAccess>;
20
+ }
21
+ /** What the tab knows of the host's answer. */
22
+ export type LiveAvatarMinutesAccessState =
23
+ /** No host option, or no navigator to route with: a person who has run out is told to ask their billing administrator. */
24
+ {
25
+ readonly status: 'absent';
26
+ }
27
+ /** Not answered yet: nothing is offered until it is. */
28
+ | {
29
+ readonly status: 'asking';
30
+ }
31
+ /** The question failed, or its answer could not be read: nothing is offered. */
32
+ | {
33
+ readonly status: 'failed';
34
+ } | {
35
+ readonly status: 'answered';
36
+ readonly access: LiveAvatarMinutesPurchaseAccess;
37
+ };
38
+ export type LiveAvatarMinutesState =
39
+ /** At least `LIVE_AVATAR_MINUTES_LOW + 1` credits left. */
40
+ 'available'
41
+ /** 1 or 2 credits left. */
42
+ | 'low'
43
+ /** The credits left are held by another person's running session in the workspace. */
44
+ | 'in-use'
45
+ /** M23: the credits left are held by this person's own session on another device, which keeps running. */
46
+ | 'in-use-self'
47
+ /** None left: the workspace's, this person's free credits (`minutesReason`), or the zero-session limit. */
48
+ | 'exhausted'
49
+ /** The workspace has no avatar credits at all (`NOT_INCLUDED`). */
50
+ | 'not-included'
51
+ /** The credits could not be checked; Turn on is the retry. */
52
+ | 'unavailable'
53
+ /** A credits refusal's time runs (the controller's Retry-After lock): the status line says what and when. */
54
+ | 'waiting'
55
+ /** The check after a plan purchase is running. */
56
+ | 'checking-purchase'
57
+ /** On: being started, running (its time left), or ending. */
58
+ | 'in-session'
59
+ /** Credits are in play, but there is nothing to say about them now. */
60
+ | 'quiet';
61
+ export type LiveAvatarMinutesAction =
62
+ /** Opens the app's plans page (`LIVE_AVATAR_MINUTES_PLANS_PATH`). */
63
+ 'see-plans'
64
+ /** Asks the credits again, for free (`refreshMinutes`). */
65
+ | 'check-again'
66
+ /** Runs the bounded check after a purchase again (`checkMinutesPurchase`). */
67
+ | 'check-purchase-again';
68
+ /** The action that opens billing: offered only with the host's `canPurchase`, and only while a plan with more credits exists. */
69
+ export declare const LIVE_AVATAR_MINUTES_BUY_ACTIONS: ReadonlySet<LiveAvatarMinutesAction>;
70
+ export interface LiveAvatarMinutesView {
71
+ readonly state: LiveAvatarMinutesState;
72
+ /** The sentence that says the state; null when the status line already says it (the server's last credits refusal), or there is none. */
73
+ readonly headline: string | null;
74
+ /** What follows it: the balance's details, or what a person who has run out can do about it. */
75
+ readonly details: readonly string[];
76
+ readonly action: LiveAvatarMinutesAction | null;
77
+ /** Turn on cannot start the avatar for its credits, and no refusal's time runs: Turn on and the stage's Confirm wait. */
78
+ readonly blocksStart: boolean;
79
+ /** The host's purchase access matters here: a state a plan lifts, a plan with more credits exists, nothing else wrong. */
80
+ readonly wantsAccess: boolean;
81
+ /** For the region's one polite live region, said when it first appears: never a ticking value. Null: nothing to announce. */
82
+ readonly announcement: string | null;
83
+ /** The voice stage's words (it offers no action): the state, and the warning when they run low. */
84
+ readonly stageLines: readonly string[];
85
+ /** W, the seconds of "How avatar credits are counted"; null when the answer gave none the client could read. */
86
+ readonly staleSeconds: number | null;
87
+ }
88
+ export interface LiveAvatarMinutesInput {
89
+ readonly snapshot: LiveAvatarSnapshot;
90
+ readonly access: LiveAvatarMinutesAccessState;
91
+ /**
92
+ * How the last check after a purchase ended, while no newer credits answer came (the tab follows it): `seen`, the credits rose;
93
+ * `not-seen`, they did not. Absent: no check ended since the last answer.
94
+ */
95
+ readonly purchaseOutcome?: 'seen' | 'not-seen' | null;
96
+ /**
97
+ * The answer a "Check again" waited for has come, and no newer answer since (the tab follows it): what it says is announced. That
98
+ * includes a refusal that stands: the status line shows it in the same words as before, so nothing else reads it again.
99
+ */
100
+ readonly rechecked?: boolean;
101
+ /** Wall-clock ms: a refusal's time, and the session's time left. */
102
+ readonly now: number;
103
+ }
104
+ /**
105
+ * ★★★ Whether the credits are in play at all: the server sent a block, a session has its own length (D'), a purchase is being checked,
106
+ * a session ended for its credits, or a credits refusal is shown. False with every answer before `enforce`: then nothing changes.
107
+ */
108
+ export declare function liveAvatarMinutesActive(snapshot: LiveAvatarSnapshot): boolean;
109
+ /**
110
+ * Whether Turn on (and the stage's Confirm) waits for the credits: off, no refusal's time running (that lock is the controller's, and
111
+ * says when), and a credits answer that says none are left for this person, or the server's last refusal for none left while there is a
112
+ * credits answer that a quiet question renews (the controller asks one after the refusal; "Check again" asks another). With no credits
113
+ * answer at all no quiet question is asked (the controller asks only once the server has sent credits), so a refusal holds nothing back:
114
+ * Turn on is the retry, and asks the server again. The same reading as `liveAvatarAskState`'s `no-minutes`, at the wall-clock time
115
+ * `now`; the controller's `confirm` reads the credits answer alone (and sends no start while it says in use either).
116
+ */
117
+ export declare function liveAvatarMinutesBlockStart(snapshot: LiveAvatarSnapshot, now: number): boolean;
118
+ /**
119
+ * Whether the credits say none are left for this person, with no clock: the newest of the server's last credits refusal (one a newer
120
+ * answer would have settled) and the credits answer decides, and only while there is a credits answer at all. `liveAvatarAskState`'s
121
+ * `no-minutes` and `liveAvatarMinutesBlockStart` both read it.
122
+ */
123
+ export declare function liveAvatarMinutesHoldStart(snapshot: LiveAvatarSnapshot): boolean;
124
+ /**
125
+ * ★★ BOFF-9446: the credits view for this snapshot, or null when the credits are not in play (`liveAvatarMinutesActive`): INERT.
126
+ */
127
+ export declare function liveAvatarMinutesView(input: LiveAvatarMinutesInput, copy: AvatarCopy): LiveAvatarMinutesView | null;
@@ -0,0 +1,168 @@
1
+ import { avatarMinutesRefusal as e } from "./avatarSessionClient.js";
2
+ //#region src/shared/assistant/avatar/live/liveAvatarMinutesView.ts
3
+ var t = 2, n = "/billing/plans", r = new Set(["see-plans"]), i = new Set([
4
+ "minutes-exhausted",
5
+ "minutes-in-use",
6
+ "minutes-in-use-self",
7
+ "minutes-unavailable"
8
+ ]), a = "minutes-exhausted", o = new Set([
9
+ "minutes-exhausted",
10
+ "minutes-in-use",
11
+ "minutes-in-use-self"
12
+ ]), s = (e) => e.phase === "starting" || e.phase === "connecting" || e.phase === "ready" || e.phase === "ending";
13
+ function c(e) {
14
+ return e.problem !== null && i.has(e.problem);
15
+ }
16
+ function l(e) {
17
+ return e.offered && (e.minutes !== null || e.sessionEndsAt !== null || e.purchase === "checking" || e.notice === "minutes-ended" || c(e));
18
+ }
19
+ function u(e, t) {
20
+ return s(e) || e.retryAt !== null && e.retryAt > t ? !1 : d(e);
21
+ }
22
+ function d(t) {
23
+ return t.minutes === null ? !1 : (t.problem !== null && o.has(t.problem) ? t.problem : e(t.minutes)?.problem ?? null) === a;
24
+ }
25
+ var f = {
26
+ details: [],
27
+ action: null,
28
+ wantsAccess: !1
29
+ };
30
+ function p(e, t, n, r) {
31
+ if (e?.balance?.morePlanCreditsAvailable !== !0) return f;
32
+ let i = r ? {
33
+ ...f,
34
+ wantsAccess: !0
35
+ } : {
36
+ details: [n.minutes.askAdmin],
37
+ action: null,
38
+ wantsAccess: !0
39
+ };
40
+ switch (t.status) {
41
+ case "absent": return {
42
+ ...i,
43
+ wantsAccess: !1
44
+ };
45
+ case "asking":
46
+ case "failed": return {
47
+ ...f,
48
+ wantsAccess: !0
49
+ };
50
+ case "answered": return t.access.canPurchase ? {
51
+ details: [],
52
+ action: "see-plans",
53
+ wantsAccess: !0
54
+ } : i;
55
+ }
56
+ }
57
+ function m(e, t, n) {
58
+ let r = [];
59
+ return n && r.push(t.minutes.low), r.push(t.minutes.shared), r.push(e.freeMinutesPerMonth > 0 ? t.minutes.freePerMonth(e.freeMinutesPerMonth) : t.minutes.noFree), e.planMinutesPerMonth > 0 && r.push(t.minutes.planPerMonth(e.planMinutesPerMonth)), r.push(t.minutes.resetsOn(e.resetsAt)), r;
60
+ }
61
+ function h(t, n) {
62
+ let { snapshot: r, access: a, now: o } = t;
63
+ if (!l(r)) return null;
64
+ let { minutes: c } = r, d = c?.balance ?? null, h = {
65
+ state: "quiet",
66
+ headline: null,
67
+ details: [],
68
+ action: null,
69
+ blocksStart: !1,
70
+ wantsAccess: !1,
71
+ announcement: null,
72
+ stageLines: [],
73
+ staleSeconds: c?.staleSeconds ?? null
74
+ };
75
+ if (s(r)) {
76
+ let e = r.phase === "ready" && !r.closing && r.sessionEndsAt !== null ? r.sessionEndsAt - o : 0;
77
+ if (e <= 0) return {
78
+ ...h,
79
+ state: "in-session"
80
+ };
81
+ let t = Math.ceil(e / 6e4), i = n.minutes.sessionLeft(t);
82
+ return {
83
+ ...h,
84
+ state: "in-session",
85
+ headline: i,
86
+ announcement: t <= 1 ? i : null
87
+ };
88
+ }
89
+ let g = u(r, o), { problem: _ } = r, v = _ !== null && i.has(_) ? _ : null, y = _ !== null && v === null;
90
+ if (r.purchase === "checking") {
91
+ let e = n.minutes.checkingPlan;
92
+ return {
93
+ ...h,
94
+ state: "checking-purchase",
95
+ headline: e,
96
+ blocksStart: g,
97
+ announcement: e,
98
+ stageLines: [e]
99
+ };
100
+ }
101
+ if (v !== null && r.retryAt !== null && r.retryAt > o) return {
102
+ ...h,
103
+ state: "waiting"
104
+ };
105
+ let b = e(c), x = v ?? b?.problem ?? (c?.state === "UNAVAILABLE" ? "minutes-unavailable" : null), S = v === null ? b?.minutesReason ?? null : r.minutesReason, C = x === null ? null : n.problem(x, {
106
+ minutesReason: S,
107
+ minutesResetsAt: d?.resetsAt ?? null
108
+ }), w = v === null ? C : null, T = (e, n) => t.rechecked === !0 && e.announcement === null && n !== null ? {
109
+ ...e,
110
+ announcement: n
111
+ } : e, E = (e) => t.purchaseOutcome === "seen" ? {
112
+ ...e,
113
+ announcement: e.headline
114
+ } : t.purchaseOutcome === "not-seen" ? {
115
+ ...e,
116
+ details: [n.minutes.assignPlan, ...e.details],
117
+ action: "check-purchase-again",
118
+ announcement: n.minutes.assignPlan
119
+ } : e, D = w === null ? [] : [w];
120
+ switch (x) {
121
+ case "minutes-exhausted": {
122
+ let e = S === "FREE_CEILING" && d !== null ? [...d.personFreeCeiling === null ? [] : [n.minutes.freePerPerson(d.personFreeCeiling)], n.minutes.resetsOn(d.resetsAt)] : [], t = S === "ZERO_SESSION_LIMIT" || y ? f : p(c, a, n, !1), r = c !== null && t.action === null && !t.wantsAccess && b === null && S !== "ZERO_SESSION_LIMIT";
123
+ return T(E({
124
+ ...h,
125
+ state: S === "NOT_INCLUDED" ? "not-included" : "exhausted",
126
+ headline: w,
127
+ details: [...e, ...t.details],
128
+ action: r ? "check-again" : t.action,
129
+ blocksStart: g,
130
+ wantsAccess: t.wantsAccess,
131
+ stageLines: D
132
+ }), C);
133
+ }
134
+ case "minutes-in-use":
135
+ case "minutes-in-use-self": return T({
136
+ ...h,
137
+ state: x === "minutes-in-use-self" ? "in-use-self" : "in-use",
138
+ headline: w,
139
+ blocksStart: g,
140
+ stageLines: D
141
+ }, C);
142
+ case "minutes-unavailable": return T({
143
+ ...h,
144
+ state: "unavailable",
145
+ headline: w,
146
+ blocksStart: g,
147
+ stageLines: D
148
+ }, C);
149
+ default: break;
150
+ }
151
+ if (c === null || d === null) return {
152
+ ...h,
153
+ blocksStart: g
154
+ };
155
+ let O = d.availableMinutes, k = O <= 2, A = n.minutes.left(O), j = k && !y ? p(c, a, n, !0) : f;
156
+ return T(E({
157
+ ...h,
158
+ state: k ? "low" : "available",
159
+ headline: A,
160
+ details: [...m(d, n, k), ...j.details],
161
+ action: j.action,
162
+ blocksStart: g,
163
+ wantsAccess: j.wantsAccess,
164
+ stageLines: k ? [A, n.minutes.low] : [A]
165
+ }), A);
166
+ }
167
+ //#endregion
168
+ export { r as LIVE_AVATAR_MINUTES_BUY_ACTIONS, t as LIVE_AVATAR_MINUTES_LOW, n as LIVE_AVATAR_MINUTES_PLANS_PATH, l as liveAvatarMinutesActive, u as liveAvatarMinutesBlockStart, d as liveAvatarMinutesHoldStart, h as liveAvatarMinutesView };
@@ -11,6 +11,9 @@ import { LiveAvatarSnapshot } from './liveAvatarController';
11
11
  * - `locked`: off after a "not now" refusal that said when (`retryAt`), until that time (`now`, wall-clock ms): Turn on waits for it
12
12
  * (critic m11). From that time on it is `off`, even while the snapshot still carries the time: the timer that clears it runs late
13
13
  * after a system sleep (BOFF-9333 UI-11);
14
+ * - `no-minutes` (★ BOFF-9446): off, and Turn on cannot start it for avatar credits: the credits answer says none are left for this
15
+ * person, or the server's last credits refusal said so while there is a credits answer (with none, Turn on is the retry:
16
+ * `liveAvatarMinutesBlockStart`);
14
17
  * - `off`: off, or being set up (checking, choosing).
15
18
  * A device voice (no hosted voice to animate) is not predicted here: the route says so per reply, and the tab's
16
19
  * status line already explains it (`needs-hosted-voice`).
@@ -1,21 +1,24 @@
1
+ import { liveAvatarMinutesHoldStart as e } from "./liveAvatarMinutesView.js";
1
2
  //#region src/shared/assistant/avatar/live/liveAvatarStatus.ts
2
- function e(e, t = Date.now()) {
3
- switch (e.phase) {
4
- case "ready": return e.closing ? "ending" : e.audioBlocked ? "sound-blocked" : "ready";
3
+ function t(t, n = Date.now()) {
4
+ switch (t.phase) {
5
+ case "ready": return t.closing ? "ending" : t.audioBlocked ? "sound-blocked" : "ready";
5
6
  case "starting":
6
7
  case "connecting": return "starting";
7
8
  case "ending": return "ending";
8
- default: return e.retryAt !== null && e.retryAt > t ? "locked" : "off";
9
+ default: return t.retryAt !== null && t.retryAt > n ? "locked" : e(t) ? "no-minutes" : "off";
9
10
  }
10
11
  }
11
- function t(e, t, n = Date.now()) {
12
+ function n(e, t, n = Date.now()) {
12
13
  if (e.problem === "playback-blocked" && e.audioBlocked) return null;
13
14
  let r = e.voiceLive;
14
15
  if (e.problem !== null) {
15
16
  let i = e.retryAt, a = i !== null && i > n, o = t.problem(e.problem, {
16
17
  voiceLive: r,
17
18
  timed: a,
18
- budgetScope: e.budgetScope
19
+ budgetScope: e.budgetScope,
20
+ minutesReason: e.minutesReason,
21
+ minutesResetsAt: e.minutes?.balance?.resetsAt ?? null
19
22
  });
20
23
  return a ? `${o} ${t.availableAgain(t.retryTime(i, n))}` : o;
21
24
  }
@@ -24,8 +27,9 @@ function t(e, t, n = Date.now()) {
24
27
  case "interrupted": return t.stateInterrupted;
25
28
  case "voice-only": return r ? t.stateVoiceOnly : t.stateVoiceOnlyNoVoice;
26
29
  case "time-limit": return r ? t.stateTimeLimit : t.stateTimeLimitNoVoice;
30
+ case "minutes-ended": return r ? t.minutes.ended : t.minutes.endedNoVoice;
27
31
  default: return null;
28
32
  }
29
33
  }
30
34
  //#endregion
31
- export { e as liveAvatarAskState, t as liveAvatarStatusText };
35
+ export { t as liveAvatarAskState, n as liveAvatarStatusText };
@@ -1,12 +1,23 @@
1
1
  import { AvatarAskBlocker } from '../ui/avatar/avatarAskGate';
2
2
  import { VoiceStartBlocker } from '../ui/voice/voiceStartGate';
3
- import { AvatarBudgetScope } from './live/avatarSessionClient';
3
+ import { AvatarBudgetScope, AvatarMinutesReason } from './live/avatarSessionClient';
4
4
  /** What the person can be told went wrong; the adapter maps server codes onto these. */
5
5
  export type AvatarProblem = 'unavailable' | 'not-ready' | 'already-running' | 'capacity' | 'rate-limited' | 'paused'
6
6
  /** The avatar time allowed for now (a credit cap) is used up; the server says when it comes back, and whose it is (`budgetScope`). */
7
7
  | 'budget-exhausted' | 'start-timeout' | 'provider'
8
8
  /** The avatar vendor's balance is at its floor: nobody's fault, it affects everyone, and no time is said for it. */
9
- | 'provider-out-of-credits' | 'connection' | 'needs-hosted-voice' | 'unsupported-browser' | 'playback-blocked';
9
+ | 'provider-out-of-credits' | 'connection' | 'needs-hosted-voice' | 'unsupported-browser' | 'playback-blocked'
10
+ /**
11
+ * ★ BOFF-9446: no avatar credits are left for this person (the server's reason, when it gave one, is on the snapshot): the workspace's
12
+ * are used up, their free ones for the month are, the workspace has none at all (`NOT_INCLUDED`), or the zero-session limit.
13
+ */
14
+ | 'minutes-exhausted'
15
+ /** ★ BOFF-9446: the workspace's credits left are held by another person's running session. Never a reason to buy. */
16
+ | 'minutes-in-use'
17
+ /** ★ BOFF-9446 (M23): the credits left are held by this person's own session on another device, which keeps running. Never a buy. */
18
+ | 'minutes-in-use-self'
19
+ /** ★ BOFF-9446: the credits could not be checked; a start is the retry. */
20
+ | 'minutes-unavailable';
10
21
  export interface AvatarCopy {
11
22
  toggle: string;
12
23
  toggleHint: string;
@@ -115,6 +126,8 @@ export interface AvatarCopy {
115
126
  * (`budget-exhausted` only; BOFF-9333).
116
127
  */
117
128
  problem: (problem: AvatarProblem, options?: AvatarProblemOptions) => string;
129
+ /** ★ BOFF-9446: the avatar minutes, in the Avatar tab and on the voice stage. */
130
+ minutes: AvatarMinutesCopy;
118
131
  }
119
132
  export interface AvatarProblemOptions {
120
133
  /** A voice conversation is live (the default: the voice stage is shown only during one). */
@@ -123,10 +136,122 @@ export interface AvatarProblemOptions {
123
136
  readonly timed?: boolean;
124
137
  /** With `budget-exhausted`, whose budget is used up. Absent or null: the server named none, and the general line is said. */
125
138
  readonly budgetScope?: AvatarBudgetScope | null;
139
+ /** ★ BOFF-9446: with `minutes-exhausted`, why none are left (the snapshot's `minutesReason`): each reason has its own words. */
140
+ readonly minutesReason?: AvatarMinutesReason | null;
141
+ /** ★ BOFF-9446: with `minutes-exhausted` and no reason, when the credits come back (UTC epoch ms); absent or null: no date is said. */
142
+ readonly minutesResetsAt?: number | null;
143
+ }
144
+ /**
145
+ * ★ BOFF-9446: the avatar credits' words (1 credit = 1 minute of avatar time; the credits are the workspace's). Count-neutral: every
146
+ * count follows a label. Every date is a UTC date (`formatAvatarMinutesDate`), and says so.
147
+ */
148
+ export interface AvatarMinutesCopy {
149
+ /** The balance: "Avatar credits left: 12". */
150
+ left: (count: number) => string;
151
+ /** The balance is the workspace's, shared by everyone in it. */
152
+ shared: string;
153
+ /** The workspace's free credits each month (F > 0). */
154
+ freePerMonth: (count: number) => string;
155
+ /** F = 0. */
156
+ noFree: string;
157
+ /** The credits the workspace's plans give each month (P > 0). */
158
+ planPerMonth: (count: number) => string;
159
+ /** The UTC date the free and plan credits reset on. */
160
+ resetsOn: (resetsAt: number) => string;
161
+ /** The free credits one person may use each month, across workspaces (the per-person ceiling), said once they are used. */
162
+ freePerPerson: (count: number) => string;
163
+ /** 1 or 2 credits left. */
164
+ low: string;
165
+ /** The running session's time left, in whole minutes, rounded up: "Minutes left in this session: about 1". */
166
+ sessionLeft: (count: number) => string;
167
+ /** The buy action, for a person who may buy, when a plan with more credits exists: opens the app's plans page. */
168
+ seePlans: string;
169
+ /** Said instead to a person who cannot buy, when a plan with more credits exists. */
170
+ askAdmin: string;
171
+ /** Asks the credits again, for free (a refusal the latest answer does not explain). */
172
+ checkAgain: string;
173
+ /** "Check again", while its answer is on its way. */
174
+ checking: string;
175
+ /** The bounded check after a plan purchase is running. */
176
+ checkingPlan: string;
177
+ /** That check ended without the credits rising: the new plan is not assigned to this workspace yet. */
178
+ assignPlan: string;
179
+ /** The session ended because the credits it had ran out (D' was its length); voice continues. */
180
+ ended: string;
181
+ /** The same, with no voice conversation live. */
182
+ endedNoVoice: string;
183
+ /** "How avatar credits are counted": the control that shows the text. */
184
+ howCountedTitle: string;
185
+ /** Its sentences (`AVATAR_MINUTES_HOW_COUNTED`), with W in seconds: the server's `staleSeconds`, or null when it sent none. */
186
+ howCounted: (staleSeconds: number | null) => readonly string[];
126
187
  }
188
+ /** One line of "How avatar credits are counted": its key and its English. */
189
+ export interface AvatarMinutesCountedLine {
190
+ readonly key: string;
191
+ readonly fallback: string;
192
+ }
193
+ /**
194
+ * ★★★ The operator approved this English copy on 2026-10-24 (BOFF-9446, DESIGN 3.10, "How avatar credits are counted").
195
+ * R4 requires legal review of the refund rule before any customer is charged. The approved words use credits
196
+ * (1 credit = 1 minute of avatar time, M12), word for word (`avatarMinutesCopy.test.ts` pins them), and the Avatar tab shows them
197
+ * only once the server sends credits (`enforce`).
198
+ *
199
+ * ★★ D-23: its translations are a native speaker's, or reviewed by one, before any customer reads them; never machine-translated. (The
200
+ * consent is the other text under that rule.)
201
+ *
202
+ * ONE table, so approved words change in one place: the copy reads it (`AvatarMinutesCopy.howCounted`), and so does the manifest
203
+ * (`ASSISTANT_I18N_COMPUTED_COPY`). `lost` carries W, the server's `staleSeconds` (`{{seconds}}`); `lostNoFigure` stands in only for an
204
+ * answer whose W the client could not read.
205
+ */
206
+ export declare const AVATAR_MINUTES_HOW_COUNTED: {
207
+ readonly title: {
208
+ readonly key: "avatar.minutes.howCounted.title";
209
+ readonly fallback: "How avatar credits are counted";
210
+ };
211
+ readonly unit: {
212
+ readonly key: "avatar.minutes.howCounted.unit";
213
+ readonly fallback: "1 credit = 1 minute of avatar time.";
214
+ };
215
+ readonly start: {
216
+ readonly key: "avatar.minutes.howCounted.start";
217
+ readonly fallback: "Counting starts once you and the avatar are both connected. Waiting for the avatar and starts that fail are free.";
218
+ };
219
+ readonly rounding: {
220
+ readonly key: "avatar.minutes.howCounted.rounding";
221
+ readonly fallback: "Each started minute uses one avatar credit. For example, a 2-minute-10-second session uses 3 credits.";
222
+ };
223
+ readonly drops: {
224
+ readonly key: "avatar.minutes.howCounted.drops";
225
+ readonly fallback: "Short connection drops that don't end the session count as part of it, like a phone call.";
226
+ };
227
+ readonly lost: {
228
+ readonly key: "avatar.minutes.howCounted.lost";
229
+ readonly fallback: "If the connection is lost, counting stops at the last moment we saw you both connected, minus a {{seconds}}-second safety margin.";
230
+ };
231
+ readonly lostNoFigure: {
232
+ readonly key: "avatar.minutes.howCounted.lostNoFigure";
233
+ readonly fallback: "If the connection is lost, counting stops at the last moment we saw you both connected, minus a safety margin.";
234
+ };
235
+ readonly ourEnd: {
236
+ readonly key: "avatar.minutes.howCounted.ourEnd";
237
+ readonly fallback: "If we stop the session because of a problem or maintenance on our side, or the avatar fails, the unfinished minute is free.";
238
+ };
239
+ readonly support: {
240
+ readonly key: "avatar.minutes.howCounted.support";
241
+ readonly fallback: "If something looks wrong, contact support: we credit the whole session back.";
242
+ };
243
+ };
127
244
  /**
128
245
  * `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).
129
246
  * ★ An unknown or malformed locale tag makes `Intl.DateTimeFormat` throw a RangeError: English stands in rather than losing the line.
130
247
  */
131
248
  export declare function formatAvatarRetryTime(at: number, now: number, locale: string | undefined): string;
249
+ /**
250
+ * ★ BOFF-9446: a reset instant (UTC epoch ms) as the UTC date it falls on, in the reader's locale. The credits' months are UTC months, so
251
+ * "They come back on 1 Nov (UTC)" reads the same in every time zone: local time is never used, here or anywhere a credits date is shown.
252
+ * ★ An unknown or malformed locale tag falls back to English, as `formatAvatarRetryTime`.
253
+ */
254
+ export declare function formatAvatarMinutesDate(at: number, locale: string | undefined): string;
255
+ /** ★ BOFF-9446: a whole count of credits, minutes or seconds, in the reader's locale's digits; English for a tag it cannot read. */
256
+ export declare function formatAvatarCount(count: number, locale: string | undefined): string;
132
257
  export declare function useAvatarCopy(): AvatarCopy;