@genesislcap/ai-assistant 15.23.2 → 15.24.0-FUI-2603.10
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.
- package/dist/ai-assistant.api.json +421 -52
- package/dist/ai-assistant.d.ts +239 -694
- package/dist/chat-driver.cjs +138 -120
- package/dist/chat-driver.cjs.map +4 -4
- package/dist/chat-driver.mjs +138 -120
- package/dist/chat-driver.mjs.map +4 -4
- package/dist/custom-elements.json +361 -602
- package/dist/dts/components/chat-driver/chat-driver.d.ts +22 -11
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/turn-failures.d.ts +96 -0
- package/dist/dts/components/chat-driver/turn-failures.d.ts.map +1 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +16 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/index.d.ts +1 -0
- package/dist/dts/components/settings-modal/index.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.d.ts +215 -0
- package/dist/dts/components/settings-modal/settings-modal.d.ts.map +1 -0
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
- package/dist/dts/main/attachment-controller.d.ts +66 -0
- package/dist/dts/main/attachment-controller.d.ts.map +1 -0
- package/dist/dts/main/budget-controller.d.ts +368 -0
- package/dist/dts/main/budget-controller.d.ts.map +1 -0
- package/dist/dts/main/composer-menu-controller.d.ts +113 -0
- package/dist/dts/main/composer-menu-controller.d.ts.map +1 -0
- package/dist/dts/main/composer-resize-controller.d.ts +80 -0
- package/dist/dts/main/composer-resize-controller.d.ts.map +1 -0
- package/dist/dts/main/context-gate-controller.d.ts +197 -0
- package/dist/dts/main/context-gate-controller.d.ts.map +1 -0
- package/dist/dts/main/cost-ledger-controller.d.ts +228 -0
- package/dist/dts/main/cost-ledger-controller.d.ts.map +1 -0
- package/dist/dts/main/main.d.ts +201 -722
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.types.d.ts +25 -0
- package/dist/dts/main/main.types.d.ts.map +1 -1
- package/dist/dts/main/meta-snapshot.d.ts +50 -0
- package/dist/dts/main/meta-snapshot.d.ts.map +1 -0
- package/dist/dts/state/persistence/session-persister.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +92 -142
- package/dist/esm/components/chat-driver/turn-failures.js +84 -0
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +18 -0
- package/dist/esm/components/settings-modal/index.js +1 -0
- package/dist/esm/components/settings-modal/settings-modal.js +436 -0
- package/dist/esm/components/settings-modal/settings-modal.template.js +15 -4
- package/dist/esm/main/attachment-controller.js +254 -0
- package/dist/esm/main/budget-controller.js +745 -0
- package/dist/esm/main/composer-menu-controller.js +234 -0
- package/dist/esm/main/composer-resize-controller.js +119 -0
- package/dist/esm/main/context-gate-controller.js +289 -0
- package/dist/esm/main/cost-ledger-controller.js +498 -0
- package/dist/esm/main/main.js +501 -2268
- package/dist/esm/main/meta-snapshot.js +108 -0
- package/dist/esm/state/persistence/session-persister.js +5 -4
- package/docs/migration-GENC-1580.md +209 -0
- package/package.json +16 -16
- package/docs/.gitattributes +0 -2
package/dist/ai-assistant.d.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { AgentPickerMode } from '@genesislcap/foundation-ai';
|
|
|
2
2
|
import type { AggregateUsage } from '@genesislcap/foundation-ai';
|
|
3
3
|
import { AIProviderRegistry } from '@genesislcap/foundation-ai';
|
|
4
4
|
import type { AIProviderRegistryStatusEntry } from '@genesislcap/foundation-ai';
|
|
5
|
-
import
|
|
5
|
+
import { AIProviderType } from '@genesislcap/foundation-ai';
|
|
6
6
|
import { BudgetExhaustedError } from '@genesislcap/foundation-ai';
|
|
7
7
|
import type { CachePolicy } from '@genesislcap/foundation-ai';
|
|
8
8
|
import type { ChatAttachment } from '@genesislcap/foundation-ai';
|
|
9
9
|
import type { ChatConfig } from '@genesislcap/foundation-ai';
|
|
10
|
-
import
|
|
10
|
+
import { ChatDriverResult } from '@genesislcap/foundation-ai';
|
|
11
11
|
import type { ChatFallback } from '@genesislcap/foundation-ai';
|
|
12
12
|
import type { ChatInputDuringExecutionMode } from '@genesislcap/foundation-ai';
|
|
13
13
|
import type { ChatMessage } from '@genesislcap/foundation-ai';
|
|
@@ -1719,6 +1719,26 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1719
1719
|
* the historical `{ reason: 'done' }`.
|
|
1720
1720
|
*/
|
|
1721
1721
|
private turnDone;
|
|
1722
|
+
/**
|
|
1723
|
+
* End the turn on a terminal failure: fail upward when this driver is a sub-agent, speak
|
|
1724
|
+
* to the user when it is not, and hand the reason to the caller either way.
|
|
1725
|
+
*
|
|
1726
|
+
* Only that fork lives here. Each exit keeps its own `recordTurnError` call, because the
|
|
1727
|
+
* telemetry payload is what legitimately differs between them — attempt counts, token
|
|
1728
|
+
* figures, tool names — and folding it in would mean one optional field per condition.
|
|
1729
|
+
*
|
|
1730
|
+
* `overrides.copy` serves the two walls whose prose is composed per occurrence:
|
|
1731
|
+
* `budgetExhaustedBubble` names the vendor and can offer another, `providerRefusedBubble`
|
|
1732
|
+
* prefers a host-supplied message. Every other exit reads its copy from the table.
|
|
1733
|
+
*
|
|
1734
|
+
* One exit stays out: the `BudgetExhaustedError` catch. Not because its shape differs —
|
|
1735
|
+
* its two telemetry payloads are field-for-field identical — but because its main-turn
|
|
1736
|
+
* branch calls `reportBudgetExhausted`, which is public and also serves
|
|
1737
|
+
* `OrchestratingDriver`'s classification phase, where no tool loop is running. That
|
|
1738
|
+
* method has to survive either way, so routing the catch through here would relocate the
|
|
1739
|
+
* duplication rather than remove it.
|
|
1740
|
+
*/
|
|
1741
|
+
private endTurnWithFailure;
|
|
1722
1742
|
/**
|
|
1723
1743
|
* Terminal budget outcome for a wall hit **outside** the tool loop — today,
|
|
1724
1744
|
* `OrchestratingDriver`'s classification phase, which calls the provider
|
|
@@ -1756,15 +1776,6 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1756
1776
|
* has gone unpaid — so there is nothing here to compose. A host override wins verbatim.
|
|
1757
1777
|
*/
|
|
1758
1778
|
private providerRefusedBubble;
|
|
1759
|
-
/**
|
|
1760
|
-
* Terminal provider-refusal outcome. Mirrors `reportBudgetExhausted` so the two walls behave
|
|
1761
|
-
* identically from the caller's side, while keeping their diagnostics distinct.
|
|
1762
|
-
*
|
|
1763
|
-
* `kind` reaches the debug log and the result but never the transcript: it is what lets an operator
|
|
1764
|
-
* tell "top up the account" from "rotate the key", and with cause-free user copy this is the only
|
|
1765
|
-
* place that distinction survives.
|
|
1766
|
-
*/
|
|
1767
|
-
private reportProviderRefused;
|
|
1768
1779
|
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
1769
1780
|
private static failureReasonOf;
|
|
1770
1781
|
/**
|
|
@@ -2209,7 +2220,7 @@ export declare interface ChatDriverConfig {
|
|
|
2209
2220
|
* Transcript copy appended when the AI-spend budget wall is hit (GENC-1464),
|
|
2210
2221
|
* used VERBATIM — set it only to own the copy entirely (white-labelling).
|
|
2211
2222
|
* When unset, the driver composes the bubble per wall: the neutral
|
|
2212
|
-
* `
|
|
2223
|
+
* `TURN_FAILURE_EXITS.budgetExhausted.copy`, upgraded with "switch to another AI
|
|
2213
2224
|
* provider" advice only when the 402's `otherVendorAvailable` positively says
|
|
2214
2225
|
* another metered vendor still has headroom — the same rule the blocked
|
|
2215
2226
|
* banner applies, so the two surfaces can no longer contradict each other.
|
|
@@ -2228,7 +2239,7 @@ export declare interface ChatDriverConfig {
|
|
|
2228
2239
|
budgetExhaustedMessage?: string;
|
|
2229
2240
|
/**
|
|
2230
2241
|
* Overrides the transcript sentence shown when the upstream PROVIDER refuses the account
|
|
2231
|
-
* (GENC-1506) — see `
|
|
2242
|
+
* (GENC-1506) — see `TURN_FAILURE_EXITS.providerRefused.copy`.
|
|
2232
2243
|
*
|
|
2233
2244
|
* Separate from `budgetExhaustedMessage` because the two conditions are separate: ours is a spend
|
|
2234
2245
|
* cap a host administrator can raise, this is the vendor declining to serve us at all. A host that
|
|
@@ -2761,6 +2772,35 @@ export declare function formatBlockedReason(budget?: Partial<Extract<ChatDriverR
|
|
|
2761
2772
|
* @beta
|
|
2762
2773
|
*/
|
|
2763
2774
|
export declare class FoundationAiAssistant extends GenesisElement {
|
|
2775
|
+
private readonly composerResize;
|
|
2776
|
+
private readonly composerMenus;
|
|
2777
|
+
private readonly attachmentCtrl;
|
|
2778
|
+
/**
|
|
2779
|
+
* Pure derivation — owns no state. Reaches the driver only through the two
|
|
2780
|
+
* callbacks below, so it stays clear of the stateful-agent surface (§11.2
|
|
2781
|
+
* constraint 2) and of redux entirely.
|
|
2782
|
+
*/
|
|
2783
|
+
private readonly contextGateCtrl;
|
|
2784
|
+
/**
|
|
2785
|
+
* Cost accounting and the persisted build-history ledger (GENC-1475). Reads the live
|
|
2786
|
+
* session through callbacks and owns the rows, the load/write latches and the debounce
|
|
2787
|
+
* timer — the element keeps only the names the settings modal binds.
|
|
2788
|
+
*/
|
|
2789
|
+
private readonly costLedger;
|
|
2790
|
+
/**
|
|
2791
|
+
* The AI-spend budget: vendor walls, the derived composer lock, the banner copy
|
|
2792
|
+
* and the settings meter (GENC-1464).
|
|
2793
|
+
*
|
|
2794
|
+
* Takes the session store itself rather than a callback per field — see
|
|
2795
|
+
* `BudgetDeps` for why this one differs from its siblings.
|
|
2796
|
+
*/
|
|
2797
|
+
private readonly budget;
|
|
2798
|
+
/**
|
|
2799
|
+
* The settings modal (`docs/code-health.md` §3). Reaches the two design-system
|
|
2800
|
+
* refs through callbacks, because the template writes them by name — see
|
|
2801
|
+
* `SettingsModalDeps`.
|
|
2802
|
+
*/
|
|
2803
|
+
private readonly settings;
|
|
2764
2804
|
providerRegistry: AIProviderRegistry;
|
|
2765
2805
|
appSettingsProvider: AssistantAppSettingsProvider;
|
|
2766
2806
|
designSystemPrefix: string;
|
|
@@ -3050,22 +3090,10 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3050
3090
|
/**
|
|
3051
3091
|
* The distinct vendors the registry can currently reach, from the provider
|
|
3052
3092
|
* statuses this element already loads on connect and refreshes on every
|
|
3053
|
-
* observable-registry change
|
|
3054
|
-
*
|
|
3055
|
-
*
|
|
3056
|
-
*
|
|
3057
|
-
* provider is resolved per turn AND per agent: `activeProviderInput` may be an
|
|
3058
|
-
* async function of the turn's context, an orchestrated turn picks its agent
|
|
3059
|
-
* with an LLM `classify()` call, and sub-agents resolve their own providers.
|
|
3060
|
-
* Any pre-turn "peek" would therefore be a guess that is wrong precisely on the
|
|
3061
|
-
* multi-agent hosts per-vendor budgets exist for.
|
|
3062
|
-
*
|
|
3063
|
-
* Asking instead which vendors are REACHABLE is answerable, synchronous, and
|
|
3064
|
-
* free — and it is enough: the composer must stay live while any reachable
|
|
3065
|
-
* vendor has headroom, and must lock when none does. It also makes vendor-switch
|
|
3066
|
-
* recovery a derivation rather than a mutation: a host that swaps its registry
|
|
3067
|
-
* to another vendor fires the observable, the statuses reload, the walled vendor
|
|
3068
|
-
* drops out of this set, and `blocked` goes false with every latch left intact.
|
|
3093
|
+
* observable-registry change — this element's answer to "which vendor would the
|
|
3094
|
+
* next turn use", a question that has no correct answer and deliberately gets
|
|
3095
|
+
* no API. See `BudgetController.reachableVendors` for why reachability is
|
|
3096
|
+
* the answerable question and what deriving from it buys.
|
|
3069
3097
|
*
|
|
3070
3098
|
* @beta
|
|
3071
3099
|
*/
|
|
@@ -3076,13 +3104,11 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3076
3104
|
*
|
|
3077
3105
|
* May include a vendor this registry cannot reach — either because the registry
|
|
3078
3106
|
* moved on after the wall was latched, or because a 402's
|
|
3079
|
-
* `otherVendorAvailable: false` walls every vendor the proxy meters
|
|
3080
|
-
*
|
|
3081
|
-
*
|
|
3082
|
-
*
|
|
3083
|
-
*
|
|
3084
|
-
* {@link FoundationAiAssistant.relevantBlockedVendors} — but a host rendering
|
|
3085
|
-
* this list itself should filter it against its own registry.
|
|
3107
|
+
* `otherVendorAvailable: false` walls every vendor the proxy meters. Neither
|
|
3108
|
+
* costs anything internally — {@link FoundationAiAssistant.blocked} asks only
|
|
3109
|
+
* about the reachable set, and the banner reads the reachability-filtered
|
|
3110
|
+
* `BudgetController.relevantBlockedVendors` — but a host rendering this
|
|
3111
|
+
* list itself should filter it against its own registry.
|
|
3086
3112
|
*
|
|
3087
3113
|
* Read-only on purpose: a settable array would let a host write a partial list
|
|
3088
3114
|
* and silently orphan the per-vendor banner copy.
|
|
@@ -3108,30 +3134,20 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3108
3134
|
* become true.
|
|
3109
3135
|
*
|
|
3110
3136
|
* `reason` is composed into the banner sentence for that vendor, **however many
|
|
3111
|
-
* vendors are walled** — see
|
|
3112
|
-
* It is per-vendor copy, not a whole-banner override;
|
|
3113
|
-
* is the override. Omitting it keeps
|
|
3114
|
-
* carried, so a driver latch landing
|
|
3115
|
-
*
|
|
3116
|
-
*
|
|
3117
|
-
*
|
|
3118
|
-
*
|
|
3119
|
-
*
|
|
3120
|
-
*
|
|
3121
|
-
* become derivable from it either. Use {@link FoundationAiAssistant.setBlocked}
|
|
3122
|
-
* for a vendor-agnostic block.
|
|
3137
|
+
* vendors are walled** — see `BudgetController.effectiveBlockedReason`.
|
|
3138
|
+
* It is per-vendor copy, not a whole-banner override;
|
|
3139
|
+
* {@link FoundationAiAssistant.blockedReason} is the override. Omitting it keeps
|
|
3140
|
+
* whatever explanation that vendor already carried, so a driver latch landing
|
|
3141
|
+
* after a host one cannot blank it.
|
|
3142
|
+
*
|
|
3143
|
+
* `'none'` is rejected with a warning rather than accepted — it is the "no
|
|
3144
|
+
* provider configured" sentinel, not a vendor, and a wall on it could never be
|
|
3145
|
+
* undone by derivation. Use {@link FoundationAiAssistant.setBlocked} for a
|
|
3146
|
+
* vendor-agnostic block.
|
|
3123
3147
|
*
|
|
3124
3148
|
* @beta
|
|
3125
3149
|
*/
|
|
3126
3150
|
setVendorBlocked(vendor: AIProviderType, blocked: boolean, reason?: string | null): void;
|
|
3127
|
-
/**
|
|
3128
|
-
* One warning per element for garbage fed to
|
|
3129
|
-
* {@link FoundationAiAssistant.setVendorBudget}. Once, not per call, because
|
|
3130
|
-
* the documented feed re-runs after every turn — a host bug would otherwise
|
|
3131
|
-
* repeat the same warning on every send for the life of the page.
|
|
3132
|
-
*/
|
|
3133
|
-
private vendorBudgetWarningIssued;
|
|
3134
|
-
private warnVendorBudgetOnce;
|
|
3135
3151
|
/**
|
|
3136
3152
|
* Feed (or clear) one vendor's AI-spend figures for the settings-modal budget
|
|
3137
3153
|
* meter — the numeric companion to
|
|
@@ -3194,8 +3210,9 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3194
3210
|
spentUsd: number;
|
|
3195
3211
|
} | null): boolean;
|
|
3196
3212
|
/**
|
|
3197
|
-
* When budget figures last arrived — from the host's
|
|
3198
|
-
* or the wall latch's own feed —
|
|
3213
|
+
* When budget figures last arrived — from the host's
|
|
3214
|
+
* {@link FoundationAiAssistant.setVendorBudget} or the wall latch's own feed —
|
|
3215
|
+
* as epoch ms; `null` until the first feed.
|
|
3199
3216
|
*
|
|
3200
3217
|
* ELEMENT state, not slice state, on purpose: the reducer is idempotent by
|
|
3201
3218
|
* value (an unchanged re-feed must not re-render the meter rows), but "when
|
|
@@ -3203,7 +3220,8 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3203
3220
|
* refresh that confirms the same dollars is still a refresh. Only the
|
|
3204
3221
|
* toolbar label observes this, so a stamp re-renders one span, not the rows.
|
|
3205
3222
|
*/
|
|
3206
|
-
budgetMeterLastFedAt: number | null;
|
|
3223
|
+
get budgetMeterLastFedAt(): number | null;
|
|
3224
|
+
set budgetMeterLastFedAt(value: number | null);
|
|
3207
3225
|
/** "Updated HH:MM:SS" for the meter toolbar, or `undefined` before any feed. */
|
|
3208
3226
|
get settingsBudgetUpdatedLabel(): string | undefined;
|
|
3209
3227
|
/**
|
|
@@ -3218,83 +3236,24 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3218
3236
|
* listener simply keeps the per-turn cadence.
|
|
3219
3237
|
*/
|
|
3220
3238
|
requestBudgetRefresh(): void;
|
|
3221
|
-
/** Whether this vendor's wall came from the sweep alone — see the slice's `sweptVendors`. */
|
|
3222
|
-
private isVendorSwept;
|
|
3223
|
-
/**
|
|
3224
|
-
* The walled vendors the user can still be routed to — i.e.
|
|
3225
|
-
* {@link FoundationAiAssistant.blockedVendors} narrowed to the reachable set,
|
|
3226
|
-
* which is the only set the banner may name.
|
|
3227
|
-
*
|
|
3228
|
-
* A wall the registry can no longer reach is not news: it cannot be hit, and
|
|
3229
|
-
* naming it puts a vendor in front of the user that is not theirs. The concrete
|
|
3230
|
-
* failure this exists to stop: a host ships Anthropic-only, Anthropic walls, the
|
|
3231
|
-
* host swaps its registry to Gemini, Gemini walls — and the banner reads "AI
|
|
3232
|
-
* usage limits are reached for Anthropic and Gemini" to a user who has never
|
|
3233
|
-
* had an Anthropic key. The spurious second name also flips the copy onto the
|
|
3234
|
-
* plural branch, so even the closing sentence is wrong.
|
|
3235
|
-
*
|
|
3236
|
-
* When nothing is reachable YET (the statuses are still loading — the exact
|
|
3237
|
-
* window the pre-flight and the cross-tab bus latch into), the fallback keeps
|
|
3238
|
-
* every wall for LOCKING (`blocked` still derives true) but names only the
|
|
3239
|
-
* vendors walled by a REFUSAL, dropping the ones the `otherVendorAvailable`
|
|
3240
|
-
* sweep added. A refusal is a fact about a vendor this user just used; a sweep
|
|
3241
|
-
* entry is the proxy's headroom verdict about a vendor the host may not even
|
|
3242
|
-
* ship — naming it here reproduced the exact failure above from the other
|
|
3243
|
-
* direction ("Gemini's AI usage limit is reached" to an Anthropic-only user),
|
|
3244
|
-
* and flipped the copy onto the plural branch with it. Self-corrects when the
|
|
3245
|
-
* statuses land: from then on reachability, not provenance, decides.
|
|
3246
|
-
*
|
|
3247
|
-
* @internal
|
|
3248
|
-
*/
|
|
3249
|
-
private get relevantBlockedVendors();
|
|
3250
3239
|
/**
|
|
3251
3240
|
* Whether the blocked banner has anything to say — a vendor-agnostic block, or
|
|
3252
|
-
* a partial wall that the NEXT send would actually hit.
|
|
3253
|
-
*
|
|
3254
|
-
*
|
|
3255
|
-
* default the next send resolves to) is among the walled ones. A user who has
|
|
3256
|
-
* switched to a vendor with headroom has already acted on the advice —
|
|
3257
|
-
* keeping the banner up past that point nagged about a vendor they just left
|
|
3258
|
-
* (GENC-1464 tester feedback), while switching BACK to the walled vendor
|
|
3259
|
-
* brings the banner straight back. Two conservative fallbacks keep it
|
|
3260
|
-
* fail-informative: while the statuses are still loading the active vendor is
|
|
3261
|
-
* unknowable, so any reachable-and-walled vendor still announces; and a
|
|
3262
|
-
* vendor-agnostic block announces always, exactly as before.
|
|
3263
|
-
*
|
|
3264
|
-
* Reads the reachability-filtered list for the same reason the copy does — a
|
|
3265
|
-
* host that has swapped its registry away from the walled vendor has nothing
|
|
3266
|
-
* left to announce, and would otherwise get a banner that falls through to the
|
|
3267
|
-
* generic default copy over a perfectly usable composer.
|
|
3241
|
+
* a partial wall that the NEXT send would actually hit. See
|
|
3242
|
+
* `BudgetController.bannerVisible` for why partial exhaustion is
|
|
3243
|
+
* announced only while the active vendor is among the walled ones.
|
|
3268
3244
|
*
|
|
3269
3245
|
* @internal
|
|
3270
3246
|
*/
|
|
3271
3247
|
get bannerVisible(): boolean;
|
|
3272
3248
|
/**
|
|
3273
|
-
* Whether a suggestions fetch would hit a wall
|
|
3274
|
-
*
|
|
3275
|
-
*
|
|
3276
|
-
*
|
|
3277
|
-
* (`ChatDriver.getSuggestions` calls `providerRegistry.default()`, and
|
|
3278
|
-
* `OrchestratingDriver.getSuggestions` just delegates to it). So the vendor a
|
|
3279
|
-
* suggestions call would use is knowable, and this is the one place in the
|
|
3280
|
-
* feature where that is true.
|
|
3281
|
-
*
|
|
3282
|
-
* Same rationale as the guard it replaces: a doomed suggestions call burns a
|
|
3283
|
-
* `suggestions.failed` meta event and parks a raw transport error in
|
|
3284
|
-
* `suggestionsState` for hosts to find.
|
|
3249
|
+
* Whether a suggestions fetch would hit a wall — the one place in this feature
|
|
3250
|
+
* where the vendor a call would use is exactly knowable, because both
|
|
3251
|
+
* suggestion paths go to the registry default. See
|
|
3252
|
+
* `BudgetController.suggestionsBlocked`.
|
|
3285
3253
|
*
|
|
3286
3254
|
* @internal
|
|
3287
3255
|
*/
|
|
3288
3256
|
get suggestionsBlocked(): boolean;
|
|
3289
|
-
/**
|
|
3290
|
-
* The vendor the NEXT send resolves to — the registry default's provider, as
|
|
3291
|
-
* reported through `providerStatuses`. `undefined` while the statuses are
|
|
3292
|
-
* still loading (or when the default reports the `'none'` sentinel), which
|
|
3293
|
-
* every consumer treats conservatively.
|
|
3294
|
-
*
|
|
3295
|
-
* @internal
|
|
3296
|
-
*/
|
|
3297
|
-
private get activeVendor();
|
|
3298
3257
|
/**
|
|
3299
3258
|
* Explanation shown in the blocked banner, or `null` for the element's default
|
|
3300
3259
|
* copy. Only meaningful while {@link FoundationAiAssistant.blocked} is true;
|
|
@@ -3311,96 +3270,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3311
3270
|
*/
|
|
3312
3271
|
get blockedReason(): string | null;
|
|
3313
3272
|
set blockedReason(value: string | null);
|
|
3314
|
-
/**
|
|
3315
|
-
* Latch the backend block off a turn outcome. The single decision point for
|
|
3316
|
-
* both latch sites (the `tool-loop-end` bus subscription and `send()`'s
|
|
3317
|
-
* return-value check), so the rule cannot diverge between them.
|
|
3318
|
-
*
|
|
3319
|
-
* Idempotent and one-way **per vendor**: it never unblocks, and it never
|
|
3320
|
-
* re-writes an existing wall. That guard is load-bearing, not just an
|
|
3321
|
-
* optimisation — the bus fires from the driver's `finally`, i.e. BEFORE
|
|
3322
|
-
* `sendMessage()` resolves, so a host subscribed to `tool-loop-end` (what the
|
|
3323
|
-
* migration guide's Option C recommends) sets its own detailed reason and the
|
|
3324
|
-
* return-value latch would otherwise land a moment later and blank it.
|
|
3325
|
-
*
|
|
3326
|
-
* The guard being per-vendor rather than global is the whole difference. A
|
|
3327
|
-
* single global "already blocked, do nothing" would swallow a second vendor's
|
|
3328
|
-
* wall — so a session walled on Anthropic could never record that Gemini went
|
|
3329
|
-
* too, and under a mixed registry an Anthropic-only wall would lock a composer
|
|
3330
|
-
* that Gemini could still serve.
|
|
3331
|
-
*
|
|
3332
|
-
* **Which field is authoritative**, in order:
|
|
3333
|
-
*
|
|
3334
|
-
* 1. `budget.vendorLabel` — stamped by the transport that was actually refused,
|
|
3335
|
-
* so it can never be stale. But it is a static per-transport string, so a
|
|
3336
|
-
* white-labelled or multiplexing gateway fronting several upstreams leaves
|
|
3337
|
-
* it unclaimed by any vendor.
|
|
3338
|
-
* 2. `budget.vendor` — which the driver resolved from the label where it could,
|
|
3339
|
-
* and otherwise from the proxy's own `vendor` field on the 402. It is
|
|
3340
|
-
* therefore NOT simply `vendorLabel` normalised, and the two can disagree;
|
|
3341
|
-
* that fallback is the only attribution on offer for the gateway case above.
|
|
3342
|
-
* 3. `vendorHint` — the bus detail's top-level vendor, i.e. the driver's
|
|
3343
|
-
* last-resolved provider. Last resort because it CAN be stale: an
|
|
3344
|
-
* orchestrated turn classifies against the registry default, a provider the
|
|
3345
|
-
* chat driver may never have resolved, so its last-resolved provider there is
|
|
3346
|
-
* the previous turn's vendor or nothing at all.
|
|
3347
|
-
*
|
|
3348
|
-
* A wall that names no recognised vendor falls back to the vendor-agnostic
|
|
3349
|
-
* block — fail-safe, and identical to the pre-per-vendor behaviour.
|
|
3350
|
-
*
|
|
3351
|
-
* **`otherVendorAvailable: false` is server-known truth and outranks every
|
|
3352
|
-
* inference made here.** The proxy meters the pots, so only it can say whether
|
|
3353
|
-
* anything else has headroom; this element can only observe which vendors the
|
|
3354
|
-
* registry can REACH, which says nothing about their remaining spend. So when
|
|
3355
|
-
* the proxy says no, every other *budgeted* vendor is walled in the same pass.
|
|
3356
|
-
* That is what makes `blocked` derive true — locking the composer on the first
|
|
3357
|
-
* response instead of after a second doomed turn — and what empties the "free"
|
|
3358
|
-
* set the banner would otherwise have advised switching to.
|
|
3359
|
-
*
|
|
3360
|
-
* The sweep runs over `BUDGETED_VENDORS`, deliberately **not** over
|
|
3361
|
-
* {@link FoundationAiAssistant.reachableVendors}. Reachability is loaded
|
|
3362
|
-
* asynchronously (`activateSession` kicks off `loadProviderStatuses()` and does
|
|
3363
|
-
* not await it), so a wall landing before the statuses resolve would have swept
|
|
3364
|
-
* an empty set — silently discarding the one fact on the 402 the client cannot
|
|
3365
|
-
* re-derive, with nothing to re-run it when the statuses arrived. Worse, it let
|
|
3366
|
-
* the verdict effectively un-latch: the single wall derived `blocked` only
|
|
3367
|
-
* while the reachable set was empty, so the moment the statuses landed the
|
|
3368
|
-
* composer came back to life against a budget the proxy had already said was
|
|
3369
|
-
* gone. Sweeping the metered vendor list instead makes the verdict independent
|
|
3370
|
-
* of load order and of registry membership, which is exactly what it is: a
|
|
3371
|
-
* statement about the proxy's pots, not about this user's registry.
|
|
3372
|
-
*
|
|
3373
|
-
* Walling a vendor the registry cannot reach costs nothing: `blocked` only asks
|
|
3374
|
-
* whether every REACHABLE vendor is walled, and the banner reads
|
|
3375
|
-
* {@link FoundationAiAssistant.relevantBlockedVendors}, which filters the walls
|
|
3376
|
-
* back down to the reachable set before naming any of them.
|
|
3377
|
-
*
|
|
3378
|
-
* One boundary remains on the sweep: it walls only vendors the proxy meters,
|
|
3379
|
-
* enforced structurally by iterating `BUDGETED_VENDORS` itself rather than
|
|
3380
|
-
* filtering a wider set through a predicate. Chrome runs on-device with no pot
|
|
3381
|
-
* to exhaust, so "no other vendor has budget" is not a statement about it, and
|
|
3382
|
-
* "Switch to Chrome to keep going." stays honest advice.
|
|
3383
|
-
*
|
|
3384
|
-
* The sweep runs last but sits OUTSIDE the per-vendor idempotence guard, and
|
|
3385
|
-
* both halves of that matter. Last, so the vendor that actually refused this
|
|
3386
|
-
* turn heads the wall order the multi-vendor banner reads. Outside the guard,
|
|
3387
|
-
* because the verdict can arrive on a LATER 402 for a vendor that is already
|
|
3388
|
-
* walled — turn 1 walls Anthropic while Gemini still has headroom, turn 2 walls
|
|
3389
|
-
* it again and reports that Gemini has since gone too — and an early return
|
|
3390
|
-
* would drop exactly the news the second turn was there to deliver.
|
|
3391
|
-
*
|
|
3392
|
-
* @param reason - the turn's failure reason; anything but `'budget-exhausted'` is ignored.
|
|
3393
|
-
* @param ref - session store to write through; defaults to the live one. `send()`
|
|
3394
|
-
* passes the ref it captured before its awaits, since a lifecycle event during the
|
|
3395
|
-
* turn may already have cleared `_sessionRef`.
|
|
3396
|
-
* @param budget - what the 402 reported: figures when the proxy sent any, the
|
|
3397
|
-
* refusing vendor, and its `otherVendorAvailable` verdict.
|
|
3398
|
-
* @param vendorHint - the `tool-loop-end` detail's top-level vendor, used only
|
|
3399
|
-
* when the budget payload names none.
|
|
3400
|
-
*
|
|
3401
|
-
* @internal
|
|
3402
|
-
*/
|
|
3403
|
-
private latchBlockedFrom;
|
|
3404
3273
|
/**
|
|
3405
3274
|
* Block or unblock the assistant, optionally with custom banner copy.
|
|
3406
3275
|
* Equivalent to the {@link FoundationAiAssistant.blocked} setter, plus the
|
|
@@ -3416,66 +3285,18 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3416
3285
|
*/
|
|
3417
3286
|
setBlocked(blocked: boolean, reason?: string | null): void;
|
|
3418
3287
|
/**
|
|
3419
|
-
* Copy shown in the blocked banner,
|
|
3420
|
-
*
|
|
3421
|
-
*
|
|
3422
|
-
*
|
|
3423
|
-
* otherwise their names) plus the action that is actually available;
|
|
3424
|
-
* 3. the default budget-exhaustion message.
|
|
3425
|
-
*
|
|
3426
|
-
* The action clause is computed here rather than stored, because whether
|
|
3427
|
-
* "switch vendor" or "contact your administrator" is honest depends on whether
|
|
3428
|
-
* another reachable vendor still has headroom — which changes when the registry
|
|
3429
|
-
* changes, long after the wall was latched. Telling a user to switch to a
|
|
3430
|
-
* vendor that is also exhausted is worse than saying nothing.
|
|
3431
|
-
*
|
|
3432
|
-
* The `free` set is a client-side inference — "reachable and not known to be
|
|
3433
|
-
* walled" — and it is only ever allowed to be one. Where the proxy has told us
|
|
3434
|
-
* otherwise (`otherVendorAvailable: false`) the latch has already walled those
|
|
3435
|
-
* vendors, so they are not in `free` here and no "switch to X" clause can be
|
|
3436
|
-
* composed from them. Both lists are filtered by reachability, so the copy
|
|
3437
|
-
* names only vendors this user's registry can actually route to.
|
|
3438
|
-
*
|
|
3439
|
-
* **Per-vendor copy is composed however many vendors are walled**, which is
|
|
3440
|
-
* what {@link FoundationAiAssistant.setVendorBlocked} and
|
|
3441
|
-
* `docs/migration-GENC-1464.md` both promise. It previously reached only the
|
|
3442
|
-
* single-vendor branch, so a host that had carefully set copy for each of its
|
|
3443
|
-
* vendors watched all of it vanish the moment a second one walled — replaced by
|
|
3444
|
-
* generic copy, at the exact moment the situation got worse.
|
|
3445
|
-
*
|
|
3446
|
-
* The compact list is the DEFAULT shape for several walls, now carrying each
|
|
3447
|
-
* vendor's figures: "AI usage limits are reached for Gemini ($62.34 of $50.00)
|
|
3448
|
-
* and Anthropic ($31.10 of $30.00)." A driver latch stores no copy of its own
|
|
3449
|
-
* (it stores the figures, which this composes from), so that shape is what the
|
|
3450
|
-
* real budget flow produces. As soon as any walled vendor carries HOST copy,
|
|
3451
|
-
* which cannot be folded into a list item, the branch lists one statement per
|
|
3452
|
-
* vendor instead — falling back to composed copy for the ones that have none, so
|
|
3453
|
-
* the sentence set stays complete rather than silently naming a subset.
|
|
3288
|
+
* Copy shown in the blocked banner — the host's override, else copy composed
|
|
3289
|
+
* from the walled vendors, else the default. See
|
|
3290
|
+
* `BudgetController.effectiveBlockedReason`, which explains why the
|
|
3291
|
+
* action clause is composed on read rather than stored at latch time.
|
|
3454
3292
|
*
|
|
3455
3293
|
* @internal
|
|
3456
3294
|
*/
|
|
3457
3295
|
get effectiveBlockedReason(): string;
|
|
3458
3296
|
/**
|
|
3459
3297
|
* Copy the driver writes into the TRANSCRIPT when a turn hits the budget wall,
|
|
3460
|
-
* or `undefined` to let the driver compose it per wall
|
|
3461
|
-
*
|
|
3462
|
-
* A set `blockedReason` still wins — a white-labelled host must not read its
|
|
3463
|
-
* own explanation in the banner and shipped copy directly below it, and the
|
|
3464
|
-
* vendor-agnostic latch's `formatBlockedReason(budget)` form stays advice-free
|
|
3465
|
-
* exactly as before.
|
|
3466
|
-
*
|
|
3467
|
-
* `undefined` (the usual case) is NOT the old `DEFAULT_BUDGET_EXHAUSTED_MESSAGE`
|
|
3468
|
-
* fallback: it hands composition to the driver, which builds the bubble from
|
|
3469
|
-
* EACH wall's own 402 — naming the exhausted vendor and advising a switch only
|
|
3470
|
-
* when that refusal's `otherVendorAvailable` positively vouched for headroom
|
|
3471
|
-
* elsewhere. That is immune to the staleness this getter used to guard against
|
|
3472
|
-
* by freezing the copy: construction-time advice could outlive its truth
|
|
3473
|
-
* (`getOrCreateDriver` re-runs on an agents swap and would have baked the
|
|
3474
|
-
* sentence into every later bubble), but per-wall advice is derived from the
|
|
3475
|
-
* refusal it annotates and dies with the turn. Forcing the default here was
|
|
3476
|
-
* also what made the driver's composed bubble unreachable in every real
|
|
3477
|
-
* mount — the banner advised switching while the bubble under it never did
|
|
3478
|
-
* (the GENC-1464 tester finding, root cause).
|
|
3298
|
+
* or `undefined` to let the driver compose it per wall — see
|
|
3299
|
+
* `BudgetController.transcriptMessage`.
|
|
3479
3300
|
*/
|
|
3480
3301
|
private get transcriptBudgetExhaustedMessage();
|
|
3481
3302
|
/**
|
|
@@ -3537,49 +3358,32 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3537
3358
|
*/
|
|
3538
3359
|
get sessionCostUsd(): number;
|
|
3539
3360
|
/**
|
|
3540
|
-
*
|
|
3541
|
-
*
|
|
3542
|
-
*
|
|
3543
|
-
* tiers that happen to resolve to the same model reports it once per tier, and the chips
|
|
3544
|
-
* rendered it once per tier too. Which tier a model came from is not what these chips are for.
|
|
3361
|
+
* The cost ledger's derived surface (GENC-1475). Every member below delegates to
|
|
3362
|
+
* `CostLedgerController` — the names are this element's public API and the
|
|
3363
|
+
* settings modal's bindings, so they stay put while the accounting moves.
|
|
3545
3364
|
*
|
|
3546
|
-
*
|
|
3547
|
-
*
|
|
3548
|
-
* label keeps them from collapsing into one another.
|
|
3365
|
+
* None carries `@volatile`: the decorators live on the controller's getters, and they
|
|
3366
|
+
* reach the binding observer from there — see `docs/code-health.md` §11.4.
|
|
3549
3367
|
*/
|
|
3550
3368
|
get uniqueProviderModels(): CostSessionModelEntry[];
|
|
3551
|
-
/**
|
|
3552
|
-
* Build history for display: most recently worked on first.
|
|
3553
|
-
*
|
|
3554
|
-
* The stored order is insertion order (see `upsertRecord`), which leaves a project returned to
|
|
3555
|
-
* sitting wherever it was first seen — so ordering is applied here, on read, where it also
|
|
3556
|
-
* covers rows a host provider supplied.
|
|
3557
|
-
*/
|
|
3369
|
+
/** Build history for display: most recently worked on first. */
|
|
3558
3370
|
get sortedCostSessionHistory(): CostSessionRecord[];
|
|
3559
|
-
/**
|
|
3560
|
-
* Lifetime usage across every project in the history scope — cost and all four token
|
|
3561
|
-
* buckets. What the Usage tab's lifetime section renders.
|
|
3562
|
-
*
|
|
3563
|
-
* Rows ONLY — deliberately not rows + live session. Each row holds a project's lifetime
|
|
3564
|
-
* usage and the active project's row is kept current on every transcript change, so
|
|
3565
|
-
* adding the live total would count this project's spend twice. That double-count is what
|
|
3566
|
-
* made the figure grow on every refresh: a page-hide banked a row while the restored
|
|
3567
|
-
* session still carried the same spend.
|
|
3568
|
-
*/
|
|
3371
|
+
/** Lifetime usage across every project in the history scope — rows only, never plus live. */
|
|
3569
3372
|
get cumulativeUsage(): AggregateUsage;
|
|
3570
3373
|
/** Lifetime USD across every project in the history scope. */
|
|
3571
3374
|
get cumulativeCostUsd(): number;
|
|
3572
|
-
/**
|
|
3573
|
-
* Whether the live session figures amount to this project's whole history, which is true
|
|
3574
|
-
* exactly when the transcript is restored on load. Drives the Usage tab's live labels
|
|
3575
|
-
* ("Project cost" vs "Session cost") so they state the scope rather than imply one.
|
|
3576
|
-
*
|
|
3577
|
-
* Note this is NOT what makes the *rows* lifetime — those bank their pre-transcript spend
|
|
3578
|
-
* (see `CostSessionRecord.banked`) and are lifetime either way.
|
|
3579
|
-
*/
|
|
3375
|
+
/** Whether the live session figures amount to this project's whole history. */
|
|
3580
3376
|
get liveUsageIsProjectLifetime(): boolean;
|
|
3581
3377
|
/** Resolved cost tab and build-history settings from `chatConfig.costHistory`. */
|
|
3582
3378
|
get resolvedCostHistory(): ResolvedCostHistoryConfig;
|
|
3379
|
+
/**
|
|
3380
|
+
* Persisted finalized cost sessions shown on the Usage tab build history list.
|
|
3381
|
+
*
|
|
3382
|
+
* A get/set pair rather than a read-only projection: the banking tests seed rows to
|
|
3383
|
+
* model a page load that found some, and a host provider's rows arrive the same way.
|
|
3384
|
+
*/
|
|
3385
|
+
get costSessionHistory(): CostSessionRecord[];
|
|
3386
|
+
set costSessionHistory(value: CostSessionRecord[]);
|
|
3583
3387
|
/**
|
|
3584
3388
|
* Total tokens across every chat turn in this session — all four buckets added up.
|
|
3585
3389
|
* Read-only; the per-bucket split is on `sessionUsage`.
|
|
@@ -3588,56 +3392,20 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3588
3392
|
/** Label shown on the Usage tab for the in-flight session (app title or first user phrase). */
|
|
3589
3393
|
get liveCostSessionTitle(): string | undefined;
|
|
3590
3394
|
/**
|
|
3591
|
-
* Write this session's usage into persisted build history
|
|
3592
|
-
*
|
|
3593
|
-
*
|
|
3594
|
-
* **Idempotent** — safe to call per turn, on tab close, and on a title change alike.
|
|
3595
|
-
* The name is historical: it does NOT end the session or reset the running totals.
|
|
3596
|
-
* Resetting them is what used to double-count spend, since the row was banked while the
|
|
3597
|
-
* persisted transcript still carried the same usage, so a reload restored it and the
|
|
3598
|
-
* Usage tab counted both.
|
|
3395
|
+
* Write this session's usage into persisted build history as a single row keyed by the
|
|
3396
|
+
* session key, replacing any existing row for that key.
|
|
3599
3397
|
*
|
|
3600
|
-
*
|
|
3601
|
-
*
|
|
3602
|
-
*
|
|
3398
|
+
* **Idempotent** — safe to call per turn, on tab close, and on a title change alike. The
|
|
3399
|
+
* name is historical: it does NOT end the session or reset the running totals. See
|
|
3400
|
+
* `CostLedgerController.finalize` for the full contract and the no-op cases.
|
|
3603
3401
|
*/
|
|
3604
3402
|
finalizeCostSession(options?: {
|
|
3605
3403
|
title?: string;
|
|
3606
3404
|
}): void;
|
|
3607
3405
|
/**
|
|
3608
|
-
*
|
|
3609
|
-
*
|
|
3610
|
-
*
|
|
3611
|
-
*
|
|
3612
|
-
* The two cases turn on whether the transcript is authoritative for this project:
|
|
3613
|
-
*
|
|
3614
|
-
* - **Transcript restored** (`persistenceActive`): it already accounts for everything after
|
|
3615
|
-
* the previous row's own banked figure, so carry that figure forward unchanged. Re-banking
|
|
3616
|
-
* `usage` here would add the restored history to itself on every reload.
|
|
3617
|
-
* - **Transcript starts empty**: nothing on screen accounts for the previous total, so the
|
|
3618
|
-
* whole of it becomes banked. Without this the next turn would rewrite the row downwards
|
|
3619
|
-
* to just that turn, losing the project's earlier spend.
|
|
3620
|
-
*
|
|
3621
|
-
* The baseline is deliberately NOT element state. This element is remounted repeatedly inside
|
|
3622
|
-
* a single session (the host re-rendering its layout, popping out, docking) and a fresh
|
|
3623
|
-
* instance re-deriving would take the second branch against rows that already hold the live
|
|
3624
|
-
* transcript — banking the session as its own baseline. Keying on the session instead means a
|
|
3625
|
-
* remount inherits the baseline that transcript was paired with.
|
|
3626
|
-
*/
|
|
3627
|
-
private bankedUsageFor;
|
|
3628
|
-
/**
|
|
3629
|
-
* Re-baseline this session against an emptied transcript: what came before, plus what the
|
|
3630
|
-
* transcript being discarded proved. `discarded` must be read BEFORE the live figures are
|
|
3631
|
-
* reset, since that is the only record of the latter.
|
|
3632
|
-
*
|
|
3633
|
-
* Not the same as dropping the baseline. Dropping it re-derives through `persistenceActive`,
|
|
3634
|
-
* and with persistence ON that carries the row's *old* banked figure forward — discarding
|
|
3635
|
-
* every turn between the last baseline and the clear. The transcript is gone either way, so
|
|
3636
|
-
* the branch that reads it as authoritative no longer applies.
|
|
3637
|
-
*
|
|
3638
|
-
* Derived from the memoised baseline and the live figure rather than from the project's row,
|
|
3639
|
-
* because a row is only as current as the last `finalizeCostSession` — which skips its write
|
|
3640
|
-
* while the ledger is unloaded, and while the session has no usage yet.
|
|
3406
|
+
* Re-baseline this session against an emptied transcript — see
|
|
3407
|
+
* `CostLedgerController.rebaselineOnClearedTranscript`. `discarded` must be read
|
|
3408
|
+
* BEFORE the live figures are reset, since that is the only record of the latter.
|
|
3641
3409
|
*/
|
|
3642
3410
|
private rebaselineOnClearedTranscript;
|
|
3643
3411
|
showConfirmationDialog(confirmationMessage: string, modalParent: HTMLElement, onConfirm: () => void, onCancel?: () => void): void;
|
|
@@ -3645,48 +3413,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3645
3413
|
removeCostSessionFromHistory(id: string): void;
|
|
3646
3414
|
/** Clear all persisted build history for this assistant scope. */
|
|
3647
3415
|
clearCostSessionHistory(): void;
|
|
3648
|
-
private getCostHistoryScope;
|
|
3649
|
-
private reloadCostSessionHistory;
|
|
3650
|
-
/**
|
|
3651
|
-
* The persistence provider, but only when it implements BOTH cost-history methods.
|
|
3652
|
-
*
|
|
3653
|
-
* Both or neither: a provider that can save but not load would silently accumulate a
|
|
3654
|
-
* ledger nobody ever reads back, so a partial implementation is treated as absent and
|
|
3655
|
-
* falls back to `localStorage`.
|
|
3656
|
-
*/
|
|
3657
|
-
private costHistoryProvider;
|
|
3658
|
-
/**
|
|
3659
|
-
* Adopt `records` as the live list, and schedule a write to whichever backend owns cost
|
|
3660
|
-
* history — the provider when the host implements it, else `localStorage`.
|
|
3661
|
-
*
|
|
3662
|
-
* The list is adopted synchronously because that is what the Usage tab renders; only the
|
|
3663
|
-
* *write* is deferred. This runs on every usage change (about once per LLM call, plus once
|
|
3664
|
-
* per sub-agent turn) and each write serialises the whole ledger, so coalescing cuts a
|
|
3665
|
-
* per-turn `localStorage.setItem` or network round-trip down to one per quiet period. Safe
|
|
3666
|
-
* because the write is a full replace of idempotent state: the newest call wins and no
|
|
3667
|
-
* intermediate state is meaningful. Flushed on `pagehide` and disconnect so nothing is lost.
|
|
3668
|
-
*/
|
|
3669
|
-
private persistCostHistory;
|
|
3670
|
-
/**
|
|
3671
|
-
* Write the pending ledger now. Safe to call when nothing is pending.
|
|
3672
|
-
*
|
|
3673
|
-
* Provider writes are fire-and-forget and best-effort: the ledger is accounting, not
|
|
3674
|
-
* conversation content, so a failed write is logged and never surfaced into the chat. On
|
|
3675
|
-
* `pagehide` the `localStorage` path completes synchronously; a provider write is subject to
|
|
3676
|
-
* the same unload race as every other network save the assistant makes.
|
|
3677
|
-
*/
|
|
3678
|
-
private flushCostHistory;
|
|
3679
|
-
/**
|
|
3680
|
-
* Finalize the previous session when the host-provided title changes — a rename should land on
|
|
3681
|
-
* the row under its old name rather than silently relabelling accrued spend.
|
|
3682
|
-
*
|
|
3683
|
-
* Only when the session key still matches the one that title accrued under. Hosts change
|
|
3684
|
-
* `costHistory.title` on project SWITCH too, and if the key has already flipped, this would
|
|
3685
|
-
* upsert the outgoing project's live usage onto the incoming project's row as
|
|
3686
|
-
* `banked + old live`, inflating it. Under the old append model a mis-keyed write left a stray
|
|
3687
|
-
* row; with the upsert it overwrites a real one.
|
|
3688
|
-
*/
|
|
3689
|
-
private syncActiveCostSessionTitle;
|
|
3690
3416
|
/** Active model id resolved from the AIProvider, if exposed. */
|
|
3691
3417
|
get activeModel(): string | undefined;
|
|
3692
3418
|
set activeModel(value: string | undefined);
|
|
@@ -3699,14 +3425,20 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3699
3425
|
private _suggestionsGeneration;
|
|
3700
3426
|
get inputValue(): string;
|
|
3701
3427
|
set inputValue(value: string);
|
|
3702
|
-
|
|
3703
|
-
|
|
3428
|
+
/** Attachments pending on the next message. */
|
|
3429
|
+
get attachments(): ChatAttachment[];
|
|
3430
|
+
set attachments(value: ChatAttachment[]);
|
|
3431
|
+
/** Rejection messages for files the last pick refused. */
|
|
3432
|
+
get attachmentErrors(): string[];
|
|
3433
|
+
set attachmentErrors(value: string[]);
|
|
3704
3434
|
/** Whether the loading spinner is currently visible. Controlled by the loading delay timer. */
|
|
3705
3435
|
showLoadingIndicator: boolean;
|
|
3706
3436
|
/** Bump to re-render app settings toggles when the host provider notifies a change. */
|
|
3707
|
-
appSettingsRevision: number;
|
|
3437
|
+
get appSettingsRevision(): number;
|
|
3438
|
+
set appSettingsRevision(value: number);
|
|
3708
3439
|
/** Whether the settings modal is open. */
|
|
3709
|
-
settingsOpen: boolean;
|
|
3440
|
+
get settingsOpen(): boolean;
|
|
3441
|
+
set settingsOpen(value: boolean);
|
|
3710
3442
|
/**
|
|
3711
3443
|
* True while `downloadDebugLog` is building the log (GENC-1351 §5.8). Drives the
|
|
3712
3444
|
* download button's disabled + spinner state — the lifetime-stitch path is async
|
|
@@ -3716,7 +3448,8 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3716
3448
|
*/
|
|
3717
3449
|
downloadingDebugLog: boolean;
|
|
3718
3450
|
/** Active tab inside the settings modal. */
|
|
3719
|
-
settingsModalTab: SettingsModalTab;
|
|
3451
|
+
get settingsModalTab(): SettingsModalTab;
|
|
3452
|
+
set settingsModalTab(value: SettingsModalTab);
|
|
3720
3453
|
/** Bound to the design-system modal via the template ref. */
|
|
3721
3454
|
settingsModal?: Modal;
|
|
3722
3455
|
/**
|
|
@@ -3795,16 +3528,33 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3795
3528
|
persistenceModal?: Modal;
|
|
3796
3529
|
/** Bound to the settings modal tab bar — used to resync selection after reopen. */
|
|
3797
3530
|
settingsTabsEl?: SettingsModalTabsElement;
|
|
3798
|
-
/**
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3531
|
+
/**
|
|
3532
|
+
* Light-DOM nodes assigned to settings modal slots. The template's `slotted()`
|
|
3533
|
+
* bindings write these by property NAME, so each needs a real setter here even
|
|
3534
|
+
* though the controller owns the arrays.
|
|
3535
|
+
*/
|
|
3536
|
+
get settingsModelSlotted(): HTMLElement[];
|
|
3537
|
+
set settingsModelSlotted(value: HTMLElement[]);
|
|
3538
|
+
get settingsAppSlotted(): HTMLElement[];
|
|
3539
|
+
set settingsAppSlotted(value: HTMLElement[]);
|
|
3540
|
+
get settingsCostCurrentSlotted(): HTMLElement[];
|
|
3541
|
+
set settingsCostCurrentSlotted(value: HTMLElement[]);
|
|
3542
|
+
get settingsCostHistorySlotted(): HTMLElement[];
|
|
3543
|
+
set settingsCostHistorySlotted(value: HTMLElement[]);
|
|
3805
3544
|
/** Whether the splash overlay is currently showing (no messages and showSplash is enabled). Reflected as a boolean attribute on the host. */
|
|
3806
3545
|
showingSplash: boolean;
|
|
3807
3546
|
private driver?;
|
|
3547
|
+
/**
|
|
3548
|
+
* This element's driver as an orchestrator, or `undefined` when the session is
|
|
3549
|
+
* running a bare `ChatDriver`.
|
|
3550
|
+
*
|
|
3551
|
+
* The five call sites that used to test the type themselves now read this
|
|
3552
|
+
* (§11.2 constraint 3 of `docs/code-health.md`): the agent pin, the flow-owner
|
|
3553
|
+
* lock, the wire-up in `wireDriver`, and the two `driverKind` diagnostics. The
|
|
3554
|
+
* type test itself lives in `asOrchestrator`, shared with the session
|
|
3555
|
+
* persister.
|
|
3556
|
+
*/
|
|
3557
|
+
private get orchestrator();
|
|
3808
3558
|
private driverCleanup?;
|
|
3809
3559
|
private loadingTimer;
|
|
3810
3560
|
private unsubBus?;
|
|
@@ -3812,29 +3562,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3812
3562
|
private _unsubBudgetLatch?;
|
|
3813
3563
|
/** Unsubscribe handle for the provider-registry change listener (observable registries only). */
|
|
3814
3564
|
private unsubProviderRegistry?;
|
|
3815
|
-
/** Unsubscribe handle for {@link (AssistantAppSettingsProvider:interface).subscribe}. */
|
|
3816
|
-
private unsubAppSettingsProvider?;
|
|
3817
|
-
/** Title tracked for the in-flight cost session (used when `header-title` changes). */
|
|
3818
|
-
private _activeCostSessionTitle;
|
|
3819
|
-
/**
|
|
3820
|
-
* Session key `_activeCostSessionTitle` was captured under, so a title change can tell a rename
|
|
3821
|
-
* (same key) from a project switch (key already flipped) — see `syncActiveCostSessionTitle`.
|
|
3822
|
-
*/
|
|
3823
|
-
private _activeCostSessionKey?;
|
|
3824
|
-
/**
|
|
3825
|
-
* Whether the row list reflects storage yet. Gates cost writes so a project's banked
|
|
3826
|
-
* spend is never derived from a ledger that simply hasn't arrived.
|
|
3827
|
-
*/
|
|
3828
|
-
private _costHistoryLoaded;
|
|
3829
|
-
/**
|
|
3830
|
-
* Set when a provider read failed. Latches writes off for this page-load: `saveCostHistory`
|
|
3831
|
-
* replaces the whole stored list, so writing anything derived from the empty list we fell back
|
|
3832
|
-
* to would destroy rows we never managed to read.
|
|
3833
|
-
*/
|
|
3834
|
-
private _ledgerUnreadable;
|
|
3835
|
-
/** Pending (adopted but unwritten) ledger changes — see `persistCostHistory`. */
|
|
3836
|
-
private _ledgerDirty;
|
|
3837
|
-
private _ledgerFlushTimer?;
|
|
3838
3565
|
private _handlePageHide;
|
|
3839
3566
|
private haloStartPublished;
|
|
3840
3567
|
/** Fingerprint of the agents array used to build the current driver. Used by agentsChanged to skip spurious rebuilds. */
|
|
@@ -3844,20 +3571,15 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
3844
3571
|
/** Bound to the rapid-text-area chat input via the template ref. */
|
|
3845
3572
|
chatInputEl?: HTMLElement;
|
|
3846
3573
|
/**
|
|
3847
|
-
*
|
|
3848
|
-
*
|
|
3849
|
-
*
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
* as the lower bound so the box can't be dragged smaller than it started. */
|
|
3857
|
-
private _composerNaturalHeight;
|
|
3858
|
-
/** Upper bound for the current drag — capped to the headroom above so growing
|
|
3859
|
-
* the composer never pushes the input row's buttons off the bottom. */
|
|
3860
|
-
private _resizeMaxHeight;
|
|
3574
|
+
* A get/set pair over one of this element's own properties, for a controller
|
|
3575
|
+
* that drives state it does not own. Written as a helper rather than two inline
|
|
3576
|
+
* closures per call: a pair of same-typed closures is easy to transpose by
|
|
3577
|
+
* accident, and a single object cannot be.
|
|
3578
|
+
*/
|
|
3579
|
+
private accessor;
|
|
3580
|
+
/** Explicit composer height once dragged; `null` keeps the default auto-grow. */
|
|
3581
|
+
get composerHeightPx(): number | null;
|
|
3582
|
+
set composerHeightPx(value: number | null);
|
|
3861
3583
|
/**
|
|
3862
3584
|
* True when the user's last interaction (pointer or keyboard focus) was inside
|
|
3863
3585
|
* the assistant. Defaults to true so the assistant claims focus on first mount.
|
|
@@ -4048,18 +3770,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4048
3770
|
* window is known.
|
|
4049
3771
|
*/
|
|
4050
3772
|
private syncContextGuard;
|
|
4051
|
-
/**
|
|
4052
|
-
* Guard policy pushed to the driver, or `undefined` when the host has switched
|
|
4053
|
-
* the gate off — so `enabled: false` really does mean no local blocking of any
|
|
4054
|
-
* kind, UI or mid-loop.
|
|
4055
|
-
*
|
|
4056
|
-
* A MARGIN, not a threshold: the driver resolves the window of the provider
|
|
4057
|
-
* each call actually goes to. The fallback window rides along because the
|
|
4058
|
-
* driver cannot learn it any other way — without it, a host that knows its
|
|
4059
|
-
* window while its provider does not report one had `fallbackContextLimit`
|
|
4060
|
-
* protecting the composer and nothing else.
|
|
4061
|
-
*/
|
|
4062
|
-
private get contextGuardPolicy();
|
|
4063
3773
|
private loadProviderStatuses;
|
|
4064
3774
|
chatConfigChanged(): void;
|
|
4065
3775
|
/**
|
|
@@ -4141,46 +3851,10 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4141
3851
|
* `contains()` check cannot tell "clicked the menu" from "clicked the
|
|
4142
3852
|
* transcript". The path crosses the boundary and carries both.
|
|
4143
3853
|
*/
|
|
4144
|
-
private _dismissPointerHandler?;
|
|
4145
|
-
private _dismissKeyHandler?;
|
|
4146
|
-
private _dismissResizeHandler?;
|
|
4147
|
-
private closeOpenComposerMenus;
|
|
4148
|
-
/**
|
|
4149
|
-
* Places an open composer menu directly above the control that opened it.
|
|
4150
|
-
*
|
|
4151
|
-
* CSS alone cannot: the menu is a child of `.composer` — which is what lets its
|
|
4152
|
-
* width be capped against the composer rather than against a button — while its
|
|
4153
|
-
* anchor is a button several levels down the bar, and no selector can read a
|
|
4154
|
-
* sibling's offset. So the left edge is measured on open and written as a
|
|
4155
|
-
* custom property.
|
|
4156
|
-
*
|
|
4157
|
-
* Clamped to the composer, so a menu opened from a control near the right edge
|
|
4158
|
-
* of a narrow panel slides back inside instead of overhanging. The animation
|
|
4159
|
-
* origin is written separately and keeps pointing at the button's centre even
|
|
4160
|
-
* after such a shift — the menu still grows out of the thing that opened it,
|
|
4161
|
-
* which is the whole point of anchoring it there.
|
|
4162
|
-
*/
|
|
4163
|
-
private positionComposerMenu;
|
|
4164
|
-
/** Re-place whichever menu is open. Cheap enough to run from a resize. */
|
|
4165
|
-
private repositionOpenComposerMenu;
|
|
4166
|
-
private syncComposerMenuDismissal;
|
|
4167
|
-
private teardownComposerMenuDismissal;
|
|
4168
|
-
/**
|
|
4169
|
-
* Vertical space an open menu must leave free above the button, covering BOTH
|
|
4170
|
-
* the gap the menu sits off the button by (8px, declared in design units in
|
|
4171
|
-
* main.styles.ts) and the breathing room wanted above it. The two are lumped
|
|
4172
|
-
* together because only the second half is a free choice: the first is fixed by
|
|
4173
|
-
* the stylesheet and simply has to be subtracted from the headroom, and a
|
|
4174
|
-
* constant named for only one of them invites someone to "fix" a gap that is
|
|
4175
|
-
* doing exactly what it should.
|
|
4176
|
-
*/
|
|
4177
|
-
private static readonly MENU_HEADROOM_RESERVE;
|
|
4178
|
-
private readonly _agentPickerToggle;
|
|
4179
3854
|
toggleAgentPicker(): void;
|
|
4180
3855
|
/** Two-step confirm for the destructive Clear action; reset whenever the menu toggles. */
|
|
4181
3856
|
confirmingClear: boolean;
|
|
4182
3857
|
confirmingCompact: boolean;
|
|
4183
|
-
private readonly _sessionMenuToggle;
|
|
4184
3858
|
/**
|
|
4185
3859
|
* Whether the lifecycle-menu button shows. It occupies the stop-button slot,
|
|
4186
3860
|
* so it only appears when idle (mutually exclusive with Stop). Gated on
|
|
@@ -4218,20 +3892,18 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4218
3892
|
/** Whether persistence is currently active (on + a provider) for this session. */
|
|
4219
3893
|
get persistenceActive(): boolean;
|
|
4220
3894
|
/**
|
|
4221
|
-
*
|
|
4222
|
-
*
|
|
3895
|
+
* The context gate's derived surface (GENC-1567). Every member below delegates to
|
|
3896
|
+
* `ContextGateController` — the names are this element's public API and the
|
|
3897
|
+
* template's bindings, so they stay put while the derivation moves.
|
|
4223
3898
|
*
|
|
4224
3899
|
* The per-agent `contextReserve` IS folded in, via the driver (GENC-1582). It
|
|
4225
3900
|
* used to be described here as a separate, later check made when an agent took
|
|
4226
3901
|
* ownership of a flow — but there is no such check, and by flow entry the send
|
|
4227
|
-
* has already been allowed. Resolving it
|
|
4228
|
-
*
|
|
3902
|
+
* has already been allowed. Resolving it before the send is what makes it a gate
|
|
3903
|
+
* rather than a label.
|
|
4229
3904
|
*
|
|
4230
|
-
*
|
|
4231
|
-
*
|
|
4232
|
-
* `estimateSystemOverhead` only looks at messages BEFORE the first usage-bearing
|
|
4233
|
-
* one, and the two lists agree completely that early — the drift between mirror
|
|
4234
|
-
* and driver history is only ever at the tail.
|
|
3905
|
+
* None carries `@volatile`: the decorators live on the controller's getters, and
|
|
3906
|
+
* they reach the binding observer from there — see `docs/code-health.md` §11.4.
|
|
4235
3907
|
*/
|
|
4236
3908
|
get contextBudget(): ResolvedContextBudget;
|
|
4237
3909
|
/**
|
|
@@ -4252,150 +3924,31 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4252
3924
|
* `contextReserve` is a plain number by design — see `ContextReserveInput` for
|
|
4253
3925
|
* why a resolver form was dropped rather than accepted and ignored.
|
|
4254
3926
|
*
|
|
3927
|
+
* Stays on the element rather than moving into `ContextGateController` because it
|
|
3928
|
+
* reaches the driver, which no extracted controller may do (`docs/code-health.md`
|
|
3929
|
+
* §11.2 constraint 2). The controller receives the resolved value as a callback.
|
|
3930
|
+
*
|
|
4255
3931
|
* @internal
|
|
4256
3932
|
*/
|
|
4257
3933
|
private get contextReserve();
|
|
4258
|
-
/**
|
|
4259
|
-
* What compacting right now would reclaim, or `null` when it would not be
|
|
4260
|
-
* worth it (GENC-1567). Sized against {@link FoundationAiAssistant.contextBudget},
|
|
4261
|
-
* so the plan aims at this model's window rather than a fixed default.
|
|
4262
|
-
*
|
|
4263
|
-
* Reads `messages` to stay reactive as the conversation grows: the driver's
|
|
4264
|
-
* history is not a tracked observable, so every consumer of this keys its
|
|
4265
|
-
* re-evaluation off the Redux mirror.
|
|
4266
|
-
*/
|
|
4267
3934
|
get compactionPlan(): CompactionProjection | null;
|
|
4268
|
-
/**
|
|
4269
|
-
* Tooltip and hint for the Compact row — why it is or is not available.
|
|
4270
|
-
*
|
|
4271
|
-
* Replaces a fixed "Not enough conversation yet to be worth compacting", which
|
|
4272
|
-
* since GENC-1567 could be flatly wrong: a long conversation whose tail is one
|
|
4273
|
-
* indivisible tool result has plenty of conversation and still cannot be
|
|
4274
|
-
* compacted. Reading the plan means the hint states the actual reason.
|
|
4275
|
-
*
|
|
4276
|
-
* @internal
|
|
4277
|
-
*/
|
|
4278
|
-
get compactHint(): string;
|
|
4279
|
-
/**
|
|
4280
|
-
* How close this conversation is to the model's context window (GENC-1567).
|
|
4281
|
-
*
|
|
4282
|
-
* **Derived, never latched** — the opposite of {@link FoundationAiAssistant.blocked}.
|
|
4283
|
-
* A budget wall stays set because nothing the user does fixes it; this clears
|
|
4284
|
-
* itself the moment the conversation shrinks, which is precisely what the user
|
|
4285
|
-
* is being asked to do. The two must not be conflated: `blocked` is also
|
|
4286
|
-
* deliberately preserved across `resetSession`, and a context gate obviously
|
|
4287
|
-
* must not survive starting a new chat.
|
|
4288
|
-
*
|
|
4289
|
-
* **A flow in progress is never blocked, only warned.** Machine state lives
|
|
4290
|
-
* outside the transcript, so a stateful flow is not desynced by compaction —
|
|
4291
|
-
* but the summary can still drop detail the flow's own prompt leans on, and
|
|
4292
|
-
* blocking mid-journey would strand the user between a flow they cannot finish
|
|
4293
|
-
* and a compaction that is a poor idea right there. Letting the flow complete
|
|
4294
|
-
* and gating on the way out is the lesser harm; the headroom reserve exists so
|
|
4295
|
-
* this is rare rather than routine.
|
|
4296
|
-
*/
|
|
4297
3935
|
/**
|
|
4298
3936
|
* WHY the composer is blocked, when it is (GENC-1582). `undefined` unless
|
|
4299
|
-
* {@link FoundationAiAssistant.contextGate} is `'blocked'`.
|
|
4300
|
-
*
|
|
4301
|
-
*
|
|
4302
|
-
* refused either way — and every consumer switches on the three levels. What
|
|
4303
|
-
* differs is what the user can do about it, which is worth splitting because the
|
|
4304
|
-
* two answers have no remedy in common.
|
|
4305
|
-
*
|
|
4306
|
-
* - `'transcript'` — the conversation has grown into the reserve. Compacting or
|
|
4307
|
-
* clearing helps, and the copy says so.
|
|
4308
|
-
* - `'model'` — the active agent's reserve meets or exceeds the whole window, so
|
|
4309
|
-
* `blockAt` is 0 and EVERY message is refused, including the first of an empty
|
|
4310
|
-
* chat. Neither compacting nor clearing can help: the constraint is the model,
|
|
4311
|
-
* not the transcript. Before this existed the user was told to clear the chat,
|
|
4312
|
-
* did, and hit the identical block with no way to understand why.
|
|
4313
|
-
*
|
|
4314
|
-
* Keyed off `blockAt === 0`, deliberately NOT off whether a compaction plan
|
|
4315
|
-
* exists. A null plan has two unrelated causes — nothing left worth summarizing,
|
|
4316
|
-
* versus a window that cannot host the agent at all — and conflating them is what
|
|
4317
|
-
* produced the misleading copy.
|
|
4318
|
-
*
|
|
4319
|
-
* Note the block itself is correct in both cases and must stay: an agent that
|
|
4320
|
-
* measurably cannot complete a turn in this window would otherwise start one and
|
|
4321
|
-
* die mid-loop on a provider context error, with partial writes and no clean
|
|
4322
|
-
* exit. Refusing up front is the better failure.
|
|
3937
|
+
* {@link FoundationAiAssistant.contextGate} is `'blocked'`. See
|
|
3938
|
+
* `ContextGateController.blockCause` for the `'transcript'` / `'model'` split
|
|
3939
|
+
* and why it is a cause rather than a fourth gate level.
|
|
4323
3940
|
*
|
|
4324
3941
|
* @internal
|
|
4325
3942
|
*/
|
|
4326
3943
|
get contextBlockCause(): 'transcript' | 'model' | undefined;
|
|
4327
3944
|
get contextGate(): 'ok' | 'warn' | 'blocked';
|
|
4328
|
-
/**
|
|
4329
|
-
* What the composer notice says about the context gate.
|
|
4330
|
-
*
|
|
4331
|
-
* The copy is a function of whether compaction can actually clear the gate,
|
|
4332
|
-
* which is why it reads {@link FoundationAiAssistant.compactionPlan} rather
|
|
4333
|
-
* than assuming. Telling someone to compact when the projection says it would
|
|
4334
|
-
* not free enough — a tail that is one indivisible tool result, say — sends
|
|
4335
|
-
* them into a summarizer call that leaves them exactly where they were.
|
|
4336
|
-
*
|
|
4337
|
-
* @internal
|
|
4338
|
-
*/
|
|
4339
3945
|
get contextGateReason(): string;
|
|
4340
|
-
/**
|
|
4341
|
-
* Short label the session-menu pill wears while the gate is active — the
|
|
4342
|
-
* primary visible signal, in place of a banner.
|
|
4343
|
-
*
|
|
4344
|
-
* Borrowed from the agent pin, which is the established way this composer says
|
|
4345
|
-
* "a mode is in force": a pill that grows a label rather than a strip of prose
|
|
4346
|
-
* above the input. It states the CONDITION only; the remedy lives in the
|
|
4347
|
-
* placeholder once the composer is actually blocked, which is the moment the
|
|
4348
|
-
* user needs to be told what to do rather than merely warned.
|
|
4349
|
-
*
|
|
4350
|
-
* @internal
|
|
4351
|
-
*/
|
|
4352
3946
|
get contextGateLabel(): string;
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
* context gate. One region for both, so the live region stays single and
|
|
4356
|
-
* registered (see the banner template's accessibility note).
|
|
4357
|
-
*
|
|
4358
|
-
* @internal
|
|
4359
|
-
*/
|
|
3947
|
+
get compactHint(): string;
|
|
3948
|
+
get compactable(): boolean;
|
|
4360
3949
|
get composerNoticeVisible(): boolean;
|
|
4361
|
-
/**
|
|
4362
|
-
* Text for that region. A budget wall outranks the context gate: it is the
|
|
4363
|
-
* condition the user cannot clear, so it must not be hidden behind advice
|
|
4364
|
-
* about compacting that would not restore sending anyway.
|
|
4365
|
-
*
|
|
4366
|
-
* For the context gate the region is present and announced but not drawn — see
|
|
4367
|
-
* {@link FoundationAiAssistant.composerNoticeSrOnly}.
|
|
4368
|
-
*
|
|
4369
|
-
* @internal
|
|
4370
|
-
*/
|
|
4371
3950
|
get composerNoticeText(): string;
|
|
4372
|
-
/**
|
|
4373
|
-
* Whether the notice region should be announced but not drawn.
|
|
4374
|
-
*
|
|
4375
|
-
* The context gate shows itself visually through the pill's label, the
|
|
4376
|
-
* composer outline and the placeholder — a strip of prose on top of those
|
|
4377
|
-
* would be a fourth statement of the same thing. But dropping the region
|
|
4378
|
-
* outright would be an accessibility regression, and a documented one: a
|
|
4379
|
-
* `role="status"` region announces MUTATIONS to a region already being
|
|
4380
|
-
* observed, and it is the only announcement available here, because the
|
|
4381
|
-
* composer flips to `disabled` at the same moment and a disabled control is out
|
|
4382
|
-
* of the tab order — so neither the placeholder nor an `aria-label` on it is
|
|
4383
|
-
* ever voiced.
|
|
4384
|
-
*
|
|
4385
|
-
* So the region stays mounted, keeps its text and keeps announcing; only the
|
|
4386
|
-
* visual treatment is withheld. A budget wall still draws its banner, because
|
|
4387
|
-
* that condition has no other visible expression.
|
|
4388
|
-
*
|
|
4389
|
-
* @internal
|
|
4390
|
-
*/
|
|
4391
3951
|
get composerNoticeSrOnly(): boolean;
|
|
4392
|
-
/**
|
|
4393
|
-
* Whether the menu's "Compact" action would do anything (GENC-1351 §5.7,
|
|
4394
|
-
* GENC-1567). Delegates to the driver's plan so the gate uses the exact same
|
|
4395
|
-
* history `compact()` acts on, judged against the same budget — one source of
|
|
4396
|
-
* truth for both whether a cut is legal and whether it reclaims enough.
|
|
4397
|
-
*/
|
|
4398
|
-
get compactable(): boolean;
|
|
4399
3952
|
toggleSessionMenu(): void;
|
|
4400
3953
|
/**
|
|
4401
3954
|
* Set persistence on/off for this session (the menu switch). Reassigns the
|
|
@@ -4480,33 +4033,40 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4480
4033
|
}): boolean;
|
|
4481
4034
|
/** Whether the picker toggle button should appear. Mirrors the picker's own visibility rule. */
|
|
4482
4035
|
get agentPickerEnabled(): boolean;
|
|
4483
|
-
/**
|
|
4036
|
+
/**
|
|
4037
|
+
* The settings modal's derived surface. Every member below delegates to
|
|
4038
|
+
* `SettingsModalController` — the names are this element's public API and the
|
|
4039
|
+
* 943-line template's bindings, so they stay put while the logic moves into the
|
|
4040
|
+
* `settings-modal.ts` that folder never had (`docs/code-health.md` §3).
|
|
4041
|
+
*
|
|
4042
|
+
* None carries `@volatile`: the decorators live on the controller's getters, and
|
|
4043
|
+
* they reach the binding observer from there — see `docs/code-health.md` §11.4.
|
|
4044
|
+
*/
|
|
4484
4045
|
get settingsModelSectionVisible(): boolean;
|
|
4485
4046
|
/** Built-in context window indicator (generic platform concern). */
|
|
4486
4047
|
get settingsContextUsageVisible(): boolean;
|
|
4048
|
+
/**
|
|
4049
|
+
* The context indicator's figures, pre-formatted, or `undefined` when there is
|
|
4050
|
+
* nothing measurable to show — see {@link SettingsContextUsage}.
|
|
4051
|
+
*
|
|
4052
|
+
* @internal
|
|
4053
|
+
*/
|
|
4054
|
+
get settingsContextUsage(): SettingsContextUsage | undefined;
|
|
4487
4055
|
/**
|
|
4488
4056
|
* Built-in per-vendor budget meter (GENC-1464) — the money sibling of
|
|
4489
4057
|
* {@link FoundationAiAssistant.settingsContextUsageVisible}, with the same
|
|
4490
|
-
* shape of gate: not configured off, and there is data to show.
|
|
4491
|
-
*
|
|
4492
|
-
*
|
|
4493
|
-
* context indicator's absent case.
|
|
4494
|
-
*
|
|
4495
|
-
* Answered from the RAW slice state rather than from
|
|
4496
|
-
* {@link FoundationAiAssistant.settingsBudgetRows}, whose `.length` would
|
|
4497
|
-
* format every row (a `formatUsd` pair and a `budgetPercentOf` each) only to
|
|
4498
|
-
* ask whether any exist. This is read several times per render pass — the
|
|
4499
|
-
* getter, the section gate, the template's `when` — so it stays as cheap as
|
|
4500
|
-
* the context sibling's `!= null`.
|
|
4058
|
+
* shape of gate: not configured off, and there is data to show. See
|
|
4059
|
+
* `BudgetController.settingsUsageVisible` for why it reads the raw slice
|
|
4060
|
+
* rather than counting the rows.
|
|
4501
4061
|
*
|
|
4502
4062
|
* @internal
|
|
4503
4063
|
*/
|
|
4504
4064
|
get settingsBudgetUsageVisible(): boolean;
|
|
4505
4065
|
/**
|
|
4506
4066
|
* The meter's rows — one per vendor that currently has figures, in
|
|
4507
|
-
* `BUDGETED_VENDORS` order
|
|
4508
|
-
*
|
|
4509
|
-
*
|
|
4067
|
+
* `BUDGETED_VENDORS` order so the rendering is stable however the host ordered
|
|
4068
|
+
* its feed calls. Pre-formatted; see {@link SettingsBudgetRow} for why the text
|
|
4069
|
+
* is unclamped while the bar is.
|
|
4510
4070
|
*
|
|
4511
4071
|
* @internal
|
|
4512
4072
|
*/
|
|
@@ -4523,38 +4083,25 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4523
4083
|
get appSettingsTogglesHeading(): AssistantAppSettingsHeading | undefined;
|
|
4524
4084
|
getAppSettingValue(id: string): boolean;
|
|
4525
4085
|
setAppSettingValue(id: string, value: boolean): void;
|
|
4526
|
-
private wireAppSettingsProvider;
|
|
4527
|
-
private unwireAppSettingsProvider;
|
|
4528
4086
|
get settingsCostCurrentSectionVisible(): boolean;
|
|
4529
4087
|
/**
|
|
4530
4088
|
* Whether the build-history section has anything in it — the built-in row list, or a host's own
|
|
4531
|
-
* markup in the `settings-cost-history` slot.
|
|
4532
|
-
*
|
|
4533
|
-
*
|
|
4534
|
-
* it gates the row writes, the reload, and the lifetime sum — and it defaults to `false`
|
|
4535
|
-
* (`cost?.enabled === true`), while the Usage tab itself shows for `showCost`/`showTokens`, which
|
|
4536
|
-
* default true. So a host can legitimately render its own build history into the slot and never
|
|
4537
|
-
* opt into our `localStorage` ledger. Short-circuiting on `enabled` above the slot check hid that
|
|
4538
|
-
* host's markup outright, since the slot is the only thing in the container.
|
|
4089
|
+
* markup in the `settings-cost-history` slot. See
|
|
4090
|
+
* `SettingsModalController.costHistorySectionVisible` for why slotted content is
|
|
4091
|
+
* checked ahead of `enabled`.
|
|
4539
4092
|
*/
|
|
4540
4093
|
get settingsCostHistorySectionVisible(): boolean;
|
|
4541
4094
|
/**
|
|
4542
|
-
* Whether the "Build history" heading has anything under it
|
|
4543
|
-
*
|
|
4544
|
-
*
|
|
4545
|
-
* heading also covers the lifetime total, which renders before the first row exists (rows are
|
|
4546
|
-
* written as spend accrues, the lifetime section only needs history switched on). Gating the
|
|
4547
|
-
* heading on the rows alone left that total sitting under nothing.
|
|
4095
|
+
* Whether the "Build history" heading has anything under it — broader than
|
|
4096
|
+
* {@link FoundationAiAssistant.settingsCostHistorySectionVisible}, because the
|
|
4097
|
+
* heading also covers the lifetime total, which renders before the first row exists.
|
|
4548
4098
|
*/
|
|
4549
4099
|
get settingsCostHistoryHeadingVisible(): boolean;
|
|
4550
4100
|
/** Whether the built-in Usage tab summary block has anything to show. */
|
|
4551
4101
|
get settingsCostSummaryVisible(): boolean;
|
|
4552
4102
|
/**
|
|
4553
|
-
* Whether the lifetime-usage section has anything to say.
|
|
4554
|
-
*
|
|
4555
|
-
* Requires build history: with it off there are no rows, so `cumulativeUsage` falls back to
|
|
4556
|
-
* the live session and the section would restate the block directly below it under a
|
|
4557
|
-
* "lifetime" heading it hasn't earned.
|
|
4103
|
+
* Whether the lifetime-usage section has anything to say. Requires build history
|
|
4104
|
+
* AND a resolvable state key — see `SettingsModalController.costLifetimeVisible`.
|
|
4558
4105
|
*/
|
|
4559
4106
|
get settingsCostLifetimeVisible(): boolean;
|
|
4560
4107
|
/** Whether the Usage tab should appear in the settings modal. */
|
|
@@ -4569,15 +4116,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4569
4116
|
setSettingsModalTab(tab: SettingsModalTab): void;
|
|
4570
4117
|
/** Sync the active settings modal tab when the design-system tab bar changes. */
|
|
4571
4118
|
onSettingsModalTabsChange(event: Event): void;
|
|
4572
|
-
/** Ensure the settings tab is selected when the modal opens. */
|
|
4573
|
-
private resetSettingsModalTabsOnOpen;
|
|
4574
|
-
/** Return to the settings tab when the modal closes on Cost. */
|
|
4575
|
-
private resetSettingsModalTabsOnClose;
|
|
4576
|
-
/**
|
|
4577
|
-
* Snap the tab underline to the settings tab without animating from Cost.
|
|
4578
|
-
* Used after hide/show when aria state is correct but the indicator drifted.
|
|
4579
|
-
*/
|
|
4580
|
-
private snapSettingsModalTabIndicator;
|
|
4581
4119
|
/** Hint text for the currently pinned agent, if any. Used in the toggle button tooltip. */
|
|
4582
4120
|
get pinnedAgentHint(): string | undefined;
|
|
4583
4121
|
/**
|
|
@@ -4630,14 +4168,13 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4630
4168
|
*/
|
|
4631
4169
|
private buildDiagnosticEntries;
|
|
4632
4170
|
/**
|
|
4633
|
-
* The
|
|
4634
|
-
*
|
|
4635
|
-
* the forward-capture delta keys on.
|
|
4171
|
+
* The `meta-snapshot` entry for right now. The assembly is a pure transformation
|
|
4172
|
+
* in `buildMetaSnapshot`; this gathers what it reports.
|
|
4636
4173
|
*
|
|
4637
|
-
*
|
|
4638
|
-
*
|
|
4639
|
-
*
|
|
4640
|
-
* `
|
|
4174
|
+
* The live agent comes from the DRIVER, not `this.activeAgent` (the redux-stripped
|
|
4175
|
+
* projection, no functions) and not a lookup in `this.agents` (which may mint
|
|
4176
|
+
* fresh, never-activated configs) — only the driver holds the instance whose
|
|
4177
|
+
* `onActivate` ran, so only its `getDebugSnapshot` closure holds state.
|
|
4641
4178
|
*/
|
|
4642
4179
|
private buildMetaSnapshot;
|
|
4643
4180
|
downloadDebugLog(): Promise<void>;
|
|
@@ -4743,13 +4280,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4743
4280
|
handleFileSelect(e: Event): void;
|
|
4744
4281
|
removeAttachment(attachment: ChatAttachment): void;
|
|
4745
4282
|
removeAttachmentError(message: string): void;
|
|
4746
|
-
private isAcceptedFile;
|
|
4747
|
-
private readFileAsText;
|
|
4748
|
-
/** The base64 payload of a file, without any `data:` prefix — the raw form
|
|
4749
|
-
* `ChatImageAttachment.data` specifies (transports add their own framing). */
|
|
4750
|
-
private readFileAsBase64;
|
|
4751
|
-
private processFiles;
|
|
4752
|
-
private loadSelectedFiles;
|
|
4753
4283
|
handleSendClick(): void;
|
|
4754
4284
|
/**
|
|
4755
4285
|
* User clicked Stop. Cancels the current turn via the driver and flips the
|
|
@@ -4826,23 +4356,12 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
4826
4356
|
* mid-turn.
|
|
4827
4357
|
*/
|
|
4828
4358
|
get sendButtonVisible(): boolean;
|
|
4829
|
-
/** Whether the message input shows a drag handle to resize its height.
|
|
4830
|
-
* On by default; hosts opt out via `chatConfig.ui.resizableInput: false`. */
|
|
4359
|
+
/** Whether the message input shows a drag handle to resize its height. */
|
|
4831
4360
|
get isComposerResizable(): boolean;
|
|
4832
|
-
/** Inline style
|
|
4833
|
-
* `--composer-height` custom property (read by `.chat-input` + its control in
|
|
4834
|
-
* `main.styles.ts`). Empty until the user drags (or when resizing is disabled),
|
|
4835
|
-
* so the default applies. */
|
|
4361
|
+
/** Inline style carrying the dragged height into `--composer-height`. */
|
|
4836
4362
|
get composerHeightStyle(): string;
|
|
4837
|
-
/** Begin a composer resize drag. Captures the pointer so move/up keep firing
|
|
4838
|
-
* on the handle even when the cursor leaves its 7px band. */
|
|
4839
4363
|
onComposerResizeStart(e: PointerEvent): void;
|
|
4840
|
-
/** Resize while dragging. Dragging up (clientY decreases) grows the composer;
|
|
4841
|
-
* the result is clamped between the natural height (lower) and the available
|
|
4842
|
-
* headroom/max (upper). No-op unless a drag is active (the handle holds pointer
|
|
4843
|
-
* capture), so plain hover-moves are ignored. */
|
|
4844
4364
|
onComposerResizeMove(e: PointerEvent): void;
|
|
4845
|
-
/** End a resize drag and release the pointer capture. */
|
|
4846
4365
|
onComposerResizeEnd(e: PointerEvent): void;
|
|
4847
4366
|
}
|
|
4848
4367
|
|
|
@@ -5704,6 +5223,32 @@ export declare interface SettingsBudgetRow {
|
|
|
5704
5223
|
barValue: number;
|
|
5705
5224
|
}
|
|
5706
5225
|
|
|
5226
|
+
/**
|
|
5227
|
+
* The settings-modal context-window indicator, pre-formatted — the context
|
|
5228
|
+
* sibling of {@link SettingsBudgetRow}, and pre-formatted for the same reason:
|
|
5229
|
+
* the template stays a pure binding layer, and a test can pin the exact text.
|
|
5230
|
+
*
|
|
5231
|
+
* `undefined` when there is nothing measurable to show, which is what keeps the
|
|
5232
|
+
* template free of non-null assertions. It used to read
|
|
5233
|
+
* `x.contextTokens!.toLocaleString()`, and the assertion was not merely untidy:
|
|
5234
|
+
* those bindings sit inside a `when(...)` block and re-evaluate in the SAME
|
|
5235
|
+
* notify batch as the guard that unmounts them, so a session Clear with the
|
|
5236
|
+
* modal open on the Usage tab threw two `TypeError`s per render. A shape that
|
|
5237
|
+
* can be absent cannot have that bug.
|
|
5238
|
+
*
|
|
5239
|
+
* @beta
|
|
5240
|
+
*/
|
|
5241
|
+
export declare interface SettingsContextUsage {
|
|
5242
|
+
/** Prompt tokens on the last call, e.g. `12,480`. */
|
|
5243
|
+
used: string;
|
|
5244
|
+
/** The model's context window, e.g. `200,000`. */
|
|
5245
|
+
limit: string;
|
|
5246
|
+
/** Whole-number percent consumed. Unclamped, like the budget row's text. */
|
|
5247
|
+
percent: number;
|
|
5248
|
+
/** The bar's value, clamped to `0..100` — only the bar clamps. */
|
|
5249
|
+
barValue: number;
|
|
5250
|
+
}
|
|
5251
|
+
|
|
5707
5252
|
/**
|
|
5708
5253
|
* Tab identifiers for the modal tab bar.
|
|
5709
5254
|
*
|