@genesislcap/ai-assistant 15.23.3 → 15.24.0
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 +419 -50
- package/dist/ai-assistant.d.ts +217 -683
- package/dist/chat-driver.cjs.map +1 -1
- package/dist/chat-driver.mjs.map +1 -1
- package/dist/custom-elements.json +310 -609
- 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/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/dist/dts/main/main.d.ts
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
import type { AggregateUsage, AIProviderRegistryStatusEntry, AIProviderType, ChatAttachment, ChatConfig,
|
|
1
|
+
import type { AggregateUsage, AIProviderRegistryStatusEntry, AIProviderType, ChatAttachment, ChatConfig, ChatInputDuringExecutionMode, ChatMessage } from '@genesislcap/foundation-ai';
|
|
2
2
|
import { AIProviderRegistry } from '@genesislcap/foundation-ai';
|
|
3
3
|
import type { Modal } from '@genesislcap/rapid-design-system';
|
|
4
4
|
import { GenesisElement } from '@genesislcap/web-core';
|
|
5
|
-
import type
|
|
5
|
+
import { type SettingsModalTab, type SettingsModalTabsElement } from '../components/settings-modal';
|
|
6
6
|
import type { AgentConfig } from '../config/config';
|
|
7
7
|
import { AssistantAppSettingsProvider, type AssistantAppSettingsHeading, type AssistantAppSettingsToggle } from '../provider/assistant-app-settings';
|
|
8
|
-
import type { VendorBudgetFigures } from '../state/ai-assistant-slice';
|
|
9
8
|
import type { DebugLog } from '../state/persistence/diagnostics';
|
|
10
9
|
import type { SessionPersistenceConfig } from '../state/persistence/session-persistence-provider';
|
|
11
10
|
import type { InteractionContext } from '../types/interaction-context';
|
|
12
11
|
import { type CompactionProjection } from '../utils/context-tokens';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import type { AgentPickerMode, AiAssistantAnimation, AiAssistantState, PopoutMode, SettingsBudgetRow, SubmitMessageResult, SuggestionsState } from './main.types';
|
|
17
|
-
/** Design-system tabs element bound to the settings modal tab bar. */
|
|
18
|
-
type SettingsModalTabsElement = HTMLElement & {
|
|
19
|
-
activeid?: string;
|
|
20
|
-
};
|
|
12
|
+
import type { CostSessionModelEntry, CostSessionRecord } from '../utils/cost-session-history';
|
|
13
|
+
import type { ResolvedContextBudget } from '../utils/resolve-context-budget';
|
|
14
|
+
import type { ResolvedCostHistoryConfig } from '../utils/resolve-cost-history-config';
|
|
15
|
+
import type { AgentPickerMode, AiAssistantAnimation, AiAssistantState, PopoutMode, SettingsBudgetRow, SettingsContextUsage, SubmitMessageResult, SuggestionsState } from './main.types';
|
|
21
16
|
/** Context window sizes (in tokens) for known models. */
|
|
22
17
|
/** Three-star AI "sparkle" mark — the conventional AI signifier; default assistant avatar. */
|
|
23
18
|
export declare const sparkleIconSvg = "<svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M9.5 5 C9.5 11.4 11.1 13 17.5 13 C11.1 13 9.5 14.6 9.5 21 C9.5 14.6 7.9 13 1.5 13 C7.9 13 9.5 11.4 9.5 5 Z\" fill=\"currentColor\"/><path d=\"M17 2.5 C17 5.7 17.8 6.5 21 6.5 C17.8 6.5 17 7.3 17 10.5 C17 7.3 16.2 6.5 13 6.5 C16.2 6.5 17 5.7 17 2.5 Z\" fill=\"currentColor\"/><path d=\"M17.5 15 C17.5 17 18 17.5 20 17.5 C18 17.5 17.5 18 17.5 20 C17.5 18 17 17.5 15 17.5 C17 17.5 17.5 17 17.5 15 Z\" fill=\"currentColor\"/></svg>";
|
|
@@ -26,36 +21,13 @@ export declare const userIconSvg = "<svg viewBox=\"0 0 24 24\" fill=\"none\" xml
|
|
|
26
21
|
/** Genesis G-mark — opt-in assistant avatar (the default before the sparkle mark). */
|
|
27
22
|
export declare const genesisIconSvg = "<svg viewBox=\"-4 0 30 26\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M18.7702 23.0437C18.7702 22.4675 18.8526 21.8089 19.0995 21.3149C19.3465 20.7386 19.6758 20.2447 20.0874 19.833C20.4991 19.4214 20.993 19.0921 21.5693 18.8451C22.1456 18.5982 22.7219 18.5158 23.2981 18.5158H25.9326V11.6828H19.1819V14.3172C19.1819 15.5521 18.6879 16.7047 17.8647 17.5279C17.0414 18.3512 15.8888 18.8451 14.7363 18.9275H6.7507V6.99025H16.4651V0.157227H3.54C2.63442 0.157227 1.72884 0.568855 1.07023 1.22746C0.411628 1.88606 0 2.79165 0 3.77955V22.2205C0 23.1261 0.411628 24.0316 1.07023 24.7726C1.72884 25.4312 2.63442 25.8428 3.54 25.8428H18.7702V23.0437Z\" fill=\"currentColor\"/></svg>";
|
|
28
23
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* makes a figureless driver latch leave a host-supplied reason intact rather
|
|
35
|
-
* than blanking it back to the default at the exact moment the wall is hit.
|
|
36
|
-
*
|
|
37
|
-
* Exported for the unit test that pins the formatting; not part of the element
|
|
38
|
-
* API.
|
|
39
|
-
*
|
|
40
|
-
* @internal
|
|
24
|
+
* Budget formatting helpers, re-exported so they stay on this module's public
|
|
25
|
+
* surface (`src/index.ts` does `export * from './main/main'`). They live in
|
|
26
|
+
* `budget-controller.ts` with the code that composes them; moving them without
|
|
27
|
+
* this line would have quietly dropped two `@internal` exports from the package
|
|
28
|
+
* entry point, and broken the two test files that import them from here.
|
|
41
29
|
*/
|
|
42
|
-
export
|
|
43
|
-
reason: 'done';
|
|
44
|
-
}>['budget']>, vendor?: AIProviderType): string | undefined;
|
|
45
|
-
/**
|
|
46
|
-
* Whole-number percentage of a budget consumed, for the meter (GENC-1464).
|
|
47
|
-
*
|
|
48
|
-
* Deliberately unclamped — spend can genuinely overshoot the cap (the last
|
|
49
|
-
* allowed call may cross it), and the meter's TEXT shows the truth; only the
|
|
50
|
-
* bar clamps, at the render site. The one special case is a `$0` cap, which the
|
|
51
|
-
* platform treats as "no spend allowed" (not "unlimited"): it has no headroom
|
|
52
|
-
* by definition, so the meter reads full rather than dividing by zero.
|
|
53
|
-
*
|
|
54
|
-
* Exported for the unit test that pins the arithmetic; not element API.
|
|
55
|
-
*
|
|
56
|
-
* @internal
|
|
57
|
-
*/
|
|
58
|
-
export declare function budgetPercentOf(figures: VendorBudgetFigures): number;
|
|
30
|
+
export { budgetPercentOf, formatBlockedReason } from './budget-controller';
|
|
59
31
|
/**
|
|
60
32
|
* Foundation AI Assistant component.
|
|
61
33
|
*
|
|
@@ -83,6 +55,35 @@ export declare function budgetPercentOf(figures: VendorBudgetFigures): number;
|
|
|
83
55
|
* @beta
|
|
84
56
|
*/
|
|
85
57
|
export declare class FoundationAiAssistant extends GenesisElement {
|
|
58
|
+
private readonly composerResize;
|
|
59
|
+
private readonly composerMenus;
|
|
60
|
+
private readonly attachmentCtrl;
|
|
61
|
+
/**
|
|
62
|
+
* Pure derivation — owns no state. Reaches the driver only through the two
|
|
63
|
+
* callbacks below, so it stays clear of the stateful-agent surface (§11.2
|
|
64
|
+
* constraint 2) and of redux entirely.
|
|
65
|
+
*/
|
|
66
|
+
private readonly contextGateCtrl;
|
|
67
|
+
/**
|
|
68
|
+
* Cost accounting and the persisted build-history ledger (GENC-1475). Reads the live
|
|
69
|
+
* session through callbacks and owns the rows, the load/write latches and the debounce
|
|
70
|
+
* timer — the element keeps only the names the settings modal binds.
|
|
71
|
+
*/
|
|
72
|
+
private readonly costLedger;
|
|
73
|
+
/**
|
|
74
|
+
* The AI-spend budget: vendor walls, the derived composer lock, the banner copy
|
|
75
|
+
* and the settings meter (GENC-1464).
|
|
76
|
+
*
|
|
77
|
+
* Takes the session store itself rather than a callback per field — see
|
|
78
|
+
* `BudgetDeps` for why this one differs from its siblings.
|
|
79
|
+
*/
|
|
80
|
+
private readonly budget;
|
|
81
|
+
/**
|
|
82
|
+
* The settings modal (`docs/code-health.md` §3). Reaches the two design-system
|
|
83
|
+
* refs through callbacks, because the template writes them by name — see
|
|
84
|
+
* `SettingsModalDeps`.
|
|
85
|
+
*/
|
|
86
|
+
private readonly settings;
|
|
86
87
|
providerRegistry: AIProviderRegistry;
|
|
87
88
|
appSettingsProvider: AssistantAppSettingsProvider;
|
|
88
89
|
designSystemPrefix: string;
|
|
@@ -372,22 +373,10 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
372
373
|
/**
|
|
373
374
|
* The distinct vendors the registry can currently reach, from the provider
|
|
374
375
|
* statuses this element already loads on connect and refreshes on every
|
|
375
|
-
* observable-registry change
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
* provider is resolved per turn AND per agent: `activeProviderInput` may be an
|
|
380
|
-
* async function of the turn's context, an orchestrated turn picks its agent
|
|
381
|
-
* with an LLM `classify()` call, and sub-agents resolve their own providers.
|
|
382
|
-
* Any pre-turn "peek" would therefore be a guess that is wrong precisely on the
|
|
383
|
-
* multi-agent hosts per-vendor budgets exist for.
|
|
384
|
-
*
|
|
385
|
-
* Asking instead which vendors are REACHABLE is answerable, synchronous, and
|
|
386
|
-
* free — and it is enough: the composer must stay live while any reachable
|
|
387
|
-
* vendor has headroom, and must lock when none does. It also makes vendor-switch
|
|
388
|
-
* recovery a derivation rather than a mutation: a host that swaps its registry
|
|
389
|
-
* to another vendor fires the observable, the statuses reload, the walled vendor
|
|
390
|
-
* drops out of this set, and `blocked` goes false with every latch left intact.
|
|
376
|
+
* observable-registry change — this element's answer to "which vendor would the
|
|
377
|
+
* next turn use", a question that has no correct answer and deliberately gets
|
|
378
|
+
* no API. See `BudgetController.reachableVendors` for why reachability is
|
|
379
|
+
* the answerable question and what deriving from it buys.
|
|
391
380
|
*
|
|
392
381
|
* @beta
|
|
393
382
|
*/
|
|
@@ -398,13 +387,11 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
398
387
|
*
|
|
399
388
|
* May include a vendor this registry cannot reach — either because the registry
|
|
400
389
|
* moved on after the wall was latched, or because a 402's
|
|
401
|
-
* `otherVendorAvailable: false` walls every vendor the proxy meters
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
* {@link FoundationAiAssistant.relevantBlockedVendors} — but a host rendering
|
|
407
|
-
* this list itself should filter it against its own registry.
|
|
390
|
+
* `otherVendorAvailable: false` walls every vendor the proxy meters. Neither
|
|
391
|
+
* costs anything internally — {@link FoundationAiAssistant.blocked} asks only
|
|
392
|
+
* about the reachable set, and the banner reads the reachability-filtered
|
|
393
|
+
* `BudgetController.relevantBlockedVendors` — but a host rendering this
|
|
394
|
+
* list itself should filter it against its own registry.
|
|
408
395
|
*
|
|
409
396
|
* Read-only on purpose: a settable array would let a host write a partial list
|
|
410
397
|
* and silently orphan the per-vendor banner copy.
|
|
@@ -430,30 +417,20 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
430
417
|
* become true.
|
|
431
418
|
*
|
|
432
419
|
* `reason` is composed into the banner sentence for that vendor, **however many
|
|
433
|
-
* vendors are walled** — see
|
|
434
|
-
* It is per-vendor copy, not a whole-banner override;
|
|
435
|
-
* is the override. Omitting it keeps
|
|
436
|
-
* carried, so a driver latch landing
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
* become derivable from it either. Use {@link FoundationAiAssistant.setBlocked}
|
|
444
|
-
* for a vendor-agnostic block.
|
|
420
|
+
* vendors are walled** — see `BudgetController.effectiveBlockedReason`.
|
|
421
|
+
* It is per-vendor copy, not a whole-banner override;
|
|
422
|
+
* {@link FoundationAiAssistant.blockedReason} is the override. Omitting it keeps
|
|
423
|
+
* whatever explanation that vendor already carried, so a driver latch landing
|
|
424
|
+
* after a host one cannot blank it.
|
|
425
|
+
*
|
|
426
|
+
* `'none'` is rejected with a warning rather than accepted — it is the "no
|
|
427
|
+
* provider configured" sentinel, not a vendor, and a wall on it could never be
|
|
428
|
+
* undone by derivation. Use {@link FoundationAiAssistant.setBlocked} for a
|
|
429
|
+
* vendor-agnostic block.
|
|
445
430
|
*
|
|
446
431
|
* @beta
|
|
447
432
|
*/
|
|
448
433
|
setVendorBlocked(vendor: AIProviderType, blocked: boolean, reason?: string | null): void;
|
|
449
|
-
/**
|
|
450
|
-
* One warning per element for garbage fed to
|
|
451
|
-
* {@link FoundationAiAssistant.setVendorBudget}. Once, not per call, because
|
|
452
|
-
* the documented feed re-runs after every turn — a host bug would otherwise
|
|
453
|
-
* repeat the same warning on every send for the life of the page.
|
|
454
|
-
*/
|
|
455
|
-
private vendorBudgetWarningIssued;
|
|
456
|
-
private warnVendorBudgetOnce;
|
|
457
434
|
/**
|
|
458
435
|
* Feed (or clear) one vendor's AI-spend figures for the settings-modal budget
|
|
459
436
|
* meter — the numeric companion to
|
|
@@ -516,8 +493,9 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
516
493
|
spentUsd: number;
|
|
517
494
|
} | null): boolean;
|
|
518
495
|
/**
|
|
519
|
-
* When budget figures last arrived — from the host's
|
|
520
|
-
* or the wall latch's own feed —
|
|
496
|
+
* When budget figures last arrived — from the host's
|
|
497
|
+
* {@link FoundationAiAssistant.setVendorBudget} or the wall latch's own feed —
|
|
498
|
+
* as epoch ms; `null` until the first feed.
|
|
521
499
|
*
|
|
522
500
|
* ELEMENT state, not slice state, on purpose: the reducer is idempotent by
|
|
523
501
|
* value (an unchanged re-feed must not re-render the meter rows), but "when
|
|
@@ -525,7 +503,8 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
525
503
|
* refresh that confirms the same dollars is still a refresh. Only the
|
|
526
504
|
* toolbar label observes this, so a stamp re-renders one span, not the rows.
|
|
527
505
|
*/
|
|
528
|
-
budgetMeterLastFedAt: number | null;
|
|
506
|
+
get budgetMeterLastFedAt(): number | null;
|
|
507
|
+
set budgetMeterLastFedAt(value: number | null);
|
|
529
508
|
/** "Updated HH:MM:SS" for the meter toolbar, or `undefined` before any feed. */
|
|
530
509
|
get settingsBudgetUpdatedLabel(): string | undefined;
|
|
531
510
|
/**
|
|
@@ -540,83 +519,24 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
540
519
|
* listener simply keeps the per-turn cadence.
|
|
541
520
|
*/
|
|
542
521
|
requestBudgetRefresh(): void;
|
|
543
|
-
/** Whether this vendor's wall came from the sweep alone — see the slice's `sweptVendors`. */
|
|
544
|
-
private isVendorSwept;
|
|
545
|
-
/**
|
|
546
|
-
* The walled vendors the user can still be routed to — i.e.
|
|
547
|
-
* {@link FoundationAiAssistant.blockedVendors} narrowed to the reachable set,
|
|
548
|
-
* which is the only set the banner may name.
|
|
549
|
-
*
|
|
550
|
-
* A wall the registry can no longer reach is not news: it cannot be hit, and
|
|
551
|
-
* naming it puts a vendor in front of the user that is not theirs. The concrete
|
|
552
|
-
* failure this exists to stop: a host ships Anthropic-only, Anthropic walls, the
|
|
553
|
-
* host swaps its registry to Gemini, Gemini walls — and the banner reads "AI
|
|
554
|
-
* usage limits are reached for Anthropic and Gemini" to a user who has never
|
|
555
|
-
* had an Anthropic key. The spurious second name also flips the copy onto the
|
|
556
|
-
* plural branch, so even the closing sentence is wrong.
|
|
557
|
-
*
|
|
558
|
-
* When nothing is reachable YET (the statuses are still loading — the exact
|
|
559
|
-
* window the pre-flight and the cross-tab bus latch into), the fallback keeps
|
|
560
|
-
* every wall for LOCKING (`blocked` still derives true) but names only the
|
|
561
|
-
* vendors walled by a REFUSAL, dropping the ones the `otherVendorAvailable`
|
|
562
|
-
* sweep added. A refusal is a fact about a vendor this user just used; a sweep
|
|
563
|
-
* entry is the proxy's headroom verdict about a vendor the host may not even
|
|
564
|
-
* ship — naming it here reproduced the exact failure above from the other
|
|
565
|
-
* direction ("Gemini's AI usage limit is reached" to an Anthropic-only user),
|
|
566
|
-
* and flipped the copy onto the plural branch with it. Self-corrects when the
|
|
567
|
-
* statuses land: from then on reachability, not provenance, decides.
|
|
568
|
-
*
|
|
569
|
-
* @internal
|
|
570
|
-
*/
|
|
571
|
-
private get relevantBlockedVendors();
|
|
572
522
|
/**
|
|
573
523
|
* Whether the blocked banner has anything to say — a vendor-agnostic block, or
|
|
574
|
-
* a partial wall that the NEXT send would actually hit.
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
* default the next send resolves to) is among the walled ones. A user who has
|
|
578
|
-
* switched to a vendor with headroom has already acted on the advice —
|
|
579
|
-
* keeping the banner up past that point nagged about a vendor they just left
|
|
580
|
-
* (GENC-1464 tester feedback), while switching BACK to the walled vendor
|
|
581
|
-
* brings the banner straight back. Two conservative fallbacks keep it
|
|
582
|
-
* fail-informative: while the statuses are still loading the active vendor is
|
|
583
|
-
* unknowable, so any reachable-and-walled vendor still announces; and a
|
|
584
|
-
* vendor-agnostic block announces always, exactly as before.
|
|
585
|
-
*
|
|
586
|
-
* Reads the reachability-filtered list for the same reason the copy does — a
|
|
587
|
-
* host that has swapped its registry away from the walled vendor has nothing
|
|
588
|
-
* left to announce, and would otherwise get a banner that falls through to the
|
|
589
|
-
* generic default copy over a perfectly usable composer.
|
|
524
|
+
* a partial wall that the NEXT send would actually hit. See
|
|
525
|
+
* `BudgetController.bannerVisible` for why partial exhaustion is
|
|
526
|
+
* announced only while the active vendor is among the walled ones.
|
|
590
527
|
*
|
|
591
528
|
* @internal
|
|
592
529
|
*/
|
|
593
530
|
get bannerVisible(): boolean;
|
|
594
531
|
/**
|
|
595
|
-
* Whether a suggestions fetch would hit a wall
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
* (`ChatDriver.getSuggestions` calls `providerRegistry.default()`, and
|
|
600
|
-
* `OrchestratingDriver.getSuggestions` just delegates to it). So the vendor a
|
|
601
|
-
* suggestions call would use is knowable, and this is the one place in the
|
|
602
|
-
* feature where that is true.
|
|
603
|
-
*
|
|
604
|
-
* Same rationale as the guard it replaces: a doomed suggestions call burns a
|
|
605
|
-
* `suggestions.failed` meta event and parks a raw transport error in
|
|
606
|
-
* `suggestionsState` for hosts to find.
|
|
532
|
+
* Whether a suggestions fetch would hit a wall — the one place in this feature
|
|
533
|
+
* where the vendor a call would use is exactly knowable, because both
|
|
534
|
+
* suggestion paths go to the registry default. See
|
|
535
|
+
* `BudgetController.suggestionsBlocked`.
|
|
607
536
|
*
|
|
608
537
|
* @internal
|
|
609
538
|
*/
|
|
610
539
|
get suggestionsBlocked(): boolean;
|
|
611
|
-
/**
|
|
612
|
-
* The vendor the NEXT send resolves to — the registry default's provider, as
|
|
613
|
-
* reported through `providerStatuses`. `undefined` while the statuses are
|
|
614
|
-
* still loading (or when the default reports the `'none'` sentinel), which
|
|
615
|
-
* every consumer treats conservatively.
|
|
616
|
-
*
|
|
617
|
-
* @internal
|
|
618
|
-
*/
|
|
619
|
-
private get activeVendor();
|
|
620
540
|
/**
|
|
621
541
|
* Explanation shown in the blocked banner, or `null` for the element's default
|
|
622
542
|
* copy. Only meaningful while {@link FoundationAiAssistant.blocked} is true;
|
|
@@ -633,96 +553,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
633
553
|
*/
|
|
634
554
|
get blockedReason(): string | null;
|
|
635
555
|
set blockedReason(value: string | null);
|
|
636
|
-
/**
|
|
637
|
-
* Latch the backend block off a turn outcome. The single decision point for
|
|
638
|
-
* both latch sites (the `tool-loop-end` bus subscription and `send()`'s
|
|
639
|
-
* return-value check), so the rule cannot diverge between them.
|
|
640
|
-
*
|
|
641
|
-
* Idempotent and one-way **per vendor**: it never unblocks, and it never
|
|
642
|
-
* re-writes an existing wall. That guard is load-bearing, not just an
|
|
643
|
-
* optimisation — the bus fires from the driver's `finally`, i.e. BEFORE
|
|
644
|
-
* `sendMessage()` resolves, so a host subscribed to `tool-loop-end` (what the
|
|
645
|
-
* migration guide's Option C recommends) sets its own detailed reason and the
|
|
646
|
-
* return-value latch would otherwise land a moment later and blank it.
|
|
647
|
-
*
|
|
648
|
-
* The guard being per-vendor rather than global is the whole difference. A
|
|
649
|
-
* single global "already blocked, do nothing" would swallow a second vendor's
|
|
650
|
-
* wall — so a session walled on Anthropic could never record that Gemini went
|
|
651
|
-
* too, and under a mixed registry an Anthropic-only wall would lock a composer
|
|
652
|
-
* that Gemini could still serve.
|
|
653
|
-
*
|
|
654
|
-
* **Which field is authoritative**, in order:
|
|
655
|
-
*
|
|
656
|
-
* 1. `budget.vendorLabel` — stamped by the transport that was actually refused,
|
|
657
|
-
* so it can never be stale. But it is a static per-transport string, so a
|
|
658
|
-
* white-labelled or multiplexing gateway fronting several upstreams leaves
|
|
659
|
-
* it unclaimed by any vendor.
|
|
660
|
-
* 2. `budget.vendor` — which the driver resolved from the label where it could,
|
|
661
|
-
* and otherwise from the proxy's own `vendor` field on the 402. It is
|
|
662
|
-
* therefore NOT simply `vendorLabel` normalised, and the two can disagree;
|
|
663
|
-
* that fallback is the only attribution on offer for the gateway case above.
|
|
664
|
-
* 3. `vendorHint` — the bus detail's top-level vendor, i.e. the driver's
|
|
665
|
-
* last-resolved provider. Last resort because it CAN be stale: an
|
|
666
|
-
* orchestrated turn classifies against the registry default, a provider the
|
|
667
|
-
* chat driver may never have resolved, so its last-resolved provider there is
|
|
668
|
-
* the previous turn's vendor or nothing at all.
|
|
669
|
-
*
|
|
670
|
-
* A wall that names no recognised vendor falls back to the vendor-agnostic
|
|
671
|
-
* block — fail-safe, and identical to the pre-per-vendor behaviour.
|
|
672
|
-
*
|
|
673
|
-
* **`otherVendorAvailable: false` is server-known truth and outranks every
|
|
674
|
-
* inference made here.** The proxy meters the pots, so only it can say whether
|
|
675
|
-
* anything else has headroom; this element can only observe which vendors the
|
|
676
|
-
* registry can REACH, which says nothing about their remaining spend. So when
|
|
677
|
-
* the proxy says no, every other *budgeted* vendor is walled in the same pass.
|
|
678
|
-
* That is what makes `blocked` derive true — locking the composer on the first
|
|
679
|
-
* response instead of after a second doomed turn — and what empties the "free"
|
|
680
|
-
* set the banner would otherwise have advised switching to.
|
|
681
|
-
*
|
|
682
|
-
* The sweep runs over `BUDGETED_VENDORS`, deliberately **not** over
|
|
683
|
-
* {@link FoundationAiAssistant.reachableVendors}. Reachability is loaded
|
|
684
|
-
* asynchronously (`activateSession` kicks off `loadProviderStatuses()` and does
|
|
685
|
-
* not await it), so a wall landing before the statuses resolve would have swept
|
|
686
|
-
* an empty set — silently discarding the one fact on the 402 the client cannot
|
|
687
|
-
* re-derive, with nothing to re-run it when the statuses arrived. Worse, it let
|
|
688
|
-
* the verdict effectively un-latch: the single wall derived `blocked` only
|
|
689
|
-
* while the reachable set was empty, so the moment the statuses landed the
|
|
690
|
-
* composer came back to life against a budget the proxy had already said was
|
|
691
|
-
* gone. Sweeping the metered vendor list instead makes the verdict independent
|
|
692
|
-
* of load order and of registry membership, which is exactly what it is: a
|
|
693
|
-
* statement about the proxy's pots, not about this user's registry.
|
|
694
|
-
*
|
|
695
|
-
* Walling a vendor the registry cannot reach costs nothing: `blocked` only asks
|
|
696
|
-
* whether every REACHABLE vendor is walled, and the banner reads
|
|
697
|
-
* {@link FoundationAiAssistant.relevantBlockedVendors}, which filters the walls
|
|
698
|
-
* back down to the reachable set before naming any of them.
|
|
699
|
-
*
|
|
700
|
-
* One boundary remains on the sweep: it walls only vendors the proxy meters,
|
|
701
|
-
* enforced structurally by iterating `BUDGETED_VENDORS` itself rather than
|
|
702
|
-
* filtering a wider set through a predicate. Chrome runs on-device with no pot
|
|
703
|
-
* to exhaust, so "no other vendor has budget" is not a statement about it, and
|
|
704
|
-
* "Switch to Chrome to keep going." stays honest advice.
|
|
705
|
-
*
|
|
706
|
-
* The sweep runs last but sits OUTSIDE the per-vendor idempotence guard, and
|
|
707
|
-
* both halves of that matter. Last, so the vendor that actually refused this
|
|
708
|
-
* turn heads the wall order the multi-vendor banner reads. Outside the guard,
|
|
709
|
-
* because the verdict can arrive on a LATER 402 for a vendor that is already
|
|
710
|
-
* walled — turn 1 walls Anthropic while Gemini still has headroom, turn 2 walls
|
|
711
|
-
* it again and reports that Gemini has since gone too — and an early return
|
|
712
|
-
* would drop exactly the news the second turn was there to deliver.
|
|
713
|
-
*
|
|
714
|
-
* @param reason - the turn's failure reason; anything but `'budget-exhausted'` is ignored.
|
|
715
|
-
* @param ref - session store to write through; defaults to the live one. `send()`
|
|
716
|
-
* passes the ref it captured before its awaits, since a lifecycle event during the
|
|
717
|
-
* turn may already have cleared `_sessionRef`.
|
|
718
|
-
* @param budget - what the 402 reported: figures when the proxy sent any, the
|
|
719
|
-
* refusing vendor, and its `otherVendorAvailable` verdict.
|
|
720
|
-
* @param vendorHint - the `tool-loop-end` detail's top-level vendor, used only
|
|
721
|
-
* when the budget payload names none.
|
|
722
|
-
*
|
|
723
|
-
* @internal
|
|
724
|
-
*/
|
|
725
|
-
private latchBlockedFrom;
|
|
726
556
|
/**
|
|
727
557
|
* Block or unblock the assistant, optionally with custom banner copy.
|
|
728
558
|
* Equivalent to the {@link FoundationAiAssistant.blocked} setter, plus the
|
|
@@ -738,66 +568,18 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
738
568
|
*/
|
|
739
569
|
setBlocked(blocked: boolean, reason?: string | null): void;
|
|
740
570
|
/**
|
|
741
|
-
* Copy shown in the blocked banner,
|
|
742
|
-
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
745
|
-
* otherwise their names) plus the action that is actually available;
|
|
746
|
-
* 3. the default budget-exhaustion message.
|
|
747
|
-
*
|
|
748
|
-
* The action clause is computed here rather than stored, because whether
|
|
749
|
-
* "switch vendor" or "contact your administrator" is honest depends on whether
|
|
750
|
-
* another reachable vendor still has headroom — which changes when the registry
|
|
751
|
-
* changes, long after the wall was latched. Telling a user to switch to a
|
|
752
|
-
* vendor that is also exhausted is worse than saying nothing.
|
|
753
|
-
*
|
|
754
|
-
* The `free` set is a client-side inference — "reachable and not known to be
|
|
755
|
-
* walled" — and it is only ever allowed to be one. Where the proxy has told us
|
|
756
|
-
* otherwise (`otherVendorAvailable: false`) the latch has already walled those
|
|
757
|
-
* vendors, so they are not in `free` here and no "switch to X" clause can be
|
|
758
|
-
* composed from them. Both lists are filtered by reachability, so the copy
|
|
759
|
-
* names only vendors this user's registry can actually route to.
|
|
760
|
-
*
|
|
761
|
-
* **Per-vendor copy is composed however many vendors are walled**, which is
|
|
762
|
-
* what {@link FoundationAiAssistant.setVendorBlocked} and
|
|
763
|
-
* `docs/migration-GENC-1464.md` both promise. It previously reached only the
|
|
764
|
-
* single-vendor branch, so a host that had carefully set copy for each of its
|
|
765
|
-
* vendors watched all of it vanish the moment a second one walled — replaced by
|
|
766
|
-
* generic copy, at the exact moment the situation got worse.
|
|
767
|
-
*
|
|
768
|
-
* The compact list is the DEFAULT shape for several walls, now carrying each
|
|
769
|
-
* vendor's figures: "AI usage limits are reached for Gemini ($62.34 of $50.00)
|
|
770
|
-
* and Anthropic ($31.10 of $30.00)." A driver latch stores no copy of its own
|
|
771
|
-
* (it stores the figures, which this composes from), so that shape is what the
|
|
772
|
-
* real budget flow produces. As soon as any walled vendor carries HOST copy,
|
|
773
|
-
* which cannot be folded into a list item, the branch lists one statement per
|
|
774
|
-
* vendor instead — falling back to composed copy for the ones that have none, so
|
|
775
|
-
* the sentence set stays complete rather than silently naming a subset.
|
|
571
|
+
* Copy shown in the blocked banner — the host's override, else copy composed
|
|
572
|
+
* from the walled vendors, else the default. See
|
|
573
|
+
* `BudgetController.effectiveBlockedReason`, which explains why the
|
|
574
|
+
* action clause is composed on read rather than stored at latch time.
|
|
776
575
|
*
|
|
777
576
|
* @internal
|
|
778
577
|
*/
|
|
779
578
|
get effectiveBlockedReason(): string;
|
|
780
579
|
/**
|
|
781
580
|
* Copy the driver writes into the TRANSCRIPT when a turn hits the budget wall,
|
|
782
|
-
* or `undefined` to let the driver compose it per wall
|
|
783
|
-
*
|
|
784
|
-
* A set `blockedReason` still wins — a white-labelled host must not read its
|
|
785
|
-
* own explanation in the banner and shipped copy directly below it, and the
|
|
786
|
-
* vendor-agnostic latch's `formatBlockedReason(budget)` form stays advice-free
|
|
787
|
-
* exactly as before.
|
|
788
|
-
*
|
|
789
|
-
* `undefined` (the usual case) is NOT the old `DEFAULT_BUDGET_EXHAUSTED_MESSAGE`
|
|
790
|
-
* fallback: it hands composition to the driver, which builds the bubble from
|
|
791
|
-
* EACH wall's own 402 — naming the exhausted vendor and advising a switch only
|
|
792
|
-
* when that refusal's `otherVendorAvailable` positively vouched for headroom
|
|
793
|
-
* elsewhere. That is immune to the staleness this getter used to guard against
|
|
794
|
-
* by freezing the copy: construction-time advice could outlive its truth
|
|
795
|
-
* (`getOrCreateDriver` re-runs on an agents swap and would have baked the
|
|
796
|
-
* sentence into every later bubble), but per-wall advice is derived from the
|
|
797
|
-
* refusal it annotates and dies with the turn. Forcing the default here was
|
|
798
|
-
* also what made the driver's composed bubble unreachable in every real
|
|
799
|
-
* mount — the banner advised switching while the bubble under it never did
|
|
800
|
-
* (the GENC-1464 tester finding, root cause).
|
|
581
|
+
* or `undefined` to let the driver compose it per wall — see
|
|
582
|
+
* `BudgetController.transcriptMessage`.
|
|
801
583
|
*/
|
|
802
584
|
private get transcriptBudgetExhaustedMessage();
|
|
803
585
|
/**
|
|
@@ -859,49 +641,32 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
859
641
|
*/
|
|
860
642
|
get sessionCostUsd(): number;
|
|
861
643
|
/**
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
*
|
|
865
|
-
* tiers that happen to resolve to the same model reports it once per tier, and the chips
|
|
866
|
-
* rendered it once per tier too. Which tier a model came from is not what these chips are for.
|
|
644
|
+
* The cost ledger's derived surface (GENC-1475). Every member below delegates to
|
|
645
|
+
* `CostLedgerController` — the names are this element's public API and the
|
|
646
|
+
* settings modal's bindings, so they stay put while the accounting moves.
|
|
867
647
|
*
|
|
868
|
-
*
|
|
869
|
-
*
|
|
870
|
-
* label keeps them from collapsing into one another.
|
|
648
|
+
* None carries `@volatile`: the decorators live on the controller's getters, and they
|
|
649
|
+
* reach the binding observer from there — see `docs/code-health.md` §11.4.
|
|
871
650
|
*/
|
|
872
651
|
get uniqueProviderModels(): CostSessionModelEntry[];
|
|
873
|
-
/**
|
|
874
|
-
* Build history for display: most recently worked on first.
|
|
875
|
-
*
|
|
876
|
-
* The stored order is insertion order (see `upsertRecord`), which leaves a project returned to
|
|
877
|
-
* sitting wherever it was first seen — so ordering is applied here, on read, where it also
|
|
878
|
-
* covers rows a host provider supplied.
|
|
879
|
-
*/
|
|
652
|
+
/** Build history for display: most recently worked on first. */
|
|
880
653
|
get sortedCostSessionHistory(): CostSessionRecord[];
|
|
881
|
-
/**
|
|
882
|
-
* Lifetime usage across every project in the history scope — cost and all four token
|
|
883
|
-
* buckets. What the Usage tab's lifetime section renders.
|
|
884
|
-
*
|
|
885
|
-
* Rows ONLY — deliberately not rows + live session. Each row holds a project's lifetime
|
|
886
|
-
* usage and the active project's row is kept current on every transcript change, so
|
|
887
|
-
* adding the live total would count this project's spend twice. That double-count is what
|
|
888
|
-
* made the figure grow on every refresh: a page-hide banked a row while the restored
|
|
889
|
-
* session still carried the same spend.
|
|
890
|
-
*/
|
|
654
|
+
/** Lifetime usage across every project in the history scope — rows only, never plus live. */
|
|
891
655
|
get cumulativeUsage(): AggregateUsage;
|
|
892
656
|
/** Lifetime USD across every project in the history scope. */
|
|
893
657
|
get cumulativeCostUsd(): number;
|
|
894
|
-
/**
|
|
895
|
-
* Whether the live session figures amount to this project's whole history, which is true
|
|
896
|
-
* exactly when the transcript is restored on load. Drives the Usage tab's live labels
|
|
897
|
-
* ("Project cost" vs "Session cost") so they state the scope rather than imply one.
|
|
898
|
-
*
|
|
899
|
-
* Note this is NOT what makes the *rows* lifetime — those bank their pre-transcript spend
|
|
900
|
-
* (see `CostSessionRecord.banked`) and are lifetime either way.
|
|
901
|
-
*/
|
|
658
|
+
/** Whether the live session figures amount to this project's whole history. */
|
|
902
659
|
get liveUsageIsProjectLifetime(): boolean;
|
|
903
660
|
/** Resolved cost tab and build-history settings from `chatConfig.costHistory`. */
|
|
904
661
|
get resolvedCostHistory(): ResolvedCostHistoryConfig;
|
|
662
|
+
/**
|
|
663
|
+
* Persisted finalized cost sessions shown on the Usage tab build history list.
|
|
664
|
+
*
|
|
665
|
+
* A get/set pair rather than a read-only projection: the banking tests seed rows to
|
|
666
|
+
* model a page load that found some, and a host provider's rows arrive the same way.
|
|
667
|
+
*/
|
|
668
|
+
get costSessionHistory(): CostSessionRecord[];
|
|
669
|
+
set costSessionHistory(value: CostSessionRecord[]);
|
|
905
670
|
/**
|
|
906
671
|
* Total tokens across every chat turn in this session — all four buckets added up.
|
|
907
672
|
* Read-only; the per-bucket split is on `sessionUsage`.
|
|
@@ -910,56 +675,20 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
910
675
|
/** Label shown on the Usage tab for the in-flight session (app title or first user phrase). */
|
|
911
676
|
get liveCostSessionTitle(): string | undefined;
|
|
912
677
|
/**
|
|
913
|
-
* Write this session's usage into persisted build history
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
* **Idempotent** — safe to call per turn, on tab close, and on a title change alike.
|
|
917
|
-
* The name is historical: it does NOT end the session or reset the running totals.
|
|
918
|
-
* Resetting them is what used to double-count spend, since the row was banked while the
|
|
919
|
-
* persisted transcript still carried the same usage, so a reload restored it and the
|
|
920
|
-
* Usage tab counted both.
|
|
678
|
+
* Write this session's usage into persisted build history as a single row keyed by the
|
|
679
|
+
* session key, replacing any existing row for that key.
|
|
921
680
|
*
|
|
922
|
-
*
|
|
923
|
-
*
|
|
924
|
-
*
|
|
681
|
+
* **Idempotent** — safe to call per turn, on tab close, and on a title change alike. The
|
|
682
|
+
* name is historical: it does NOT end the session or reset the running totals. See
|
|
683
|
+
* `CostLedgerController.finalize` for the full contract and the no-op cases.
|
|
925
684
|
*/
|
|
926
685
|
finalizeCostSession(options?: {
|
|
927
686
|
title?: string;
|
|
928
687
|
}): void;
|
|
929
688
|
/**
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
*
|
|
934
|
-
* The two cases turn on whether the transcript is authoritative for this project:
|
|
935
|
-
*
|
|
936
|
-
* - **Transcript restored** (`persistenceActive`): it already accounts for everything after
|
|
937
|
-
* the previous row's own banked figure, so carry that figure forward unchanged. Re-banking
|
|
938
|
-
* `usage` here would add the restored history to itself on every reload.
|
|
939
|
-
* - **Transcript starts empty**: nothing on screen accounts for the previous total, so the
|
|
940
|
-
* whole of it becomes banked. Without this the next turn would rewrite the row downwards
|
|
941
|
-
* to just that turn, losing the project's earlier spend.
|
|
942
|
-
*
|
|
943
|
-
* The baseline is deliberately NOT element state. This element is remounted repeatedly inside
|
|
944
|
-
* a single session (the host re-rendering its layout, popping out, docking) and a fresh
|
|
945
|
-
* instance re-deriving would take the second branch against rows that already hold the live
|
|
946
|
-
* transcript — banking the session as its own baseline. Keying on the session instead means a
|
|
947
|
-
* remount inherits the baseline that transcript was paired with.
|
|
948
|
-
*/
|
|
949
|
-
private bankedUsageFor;
|
|
950
|
-
/**
|
|
951
|
-
* Re-baseline this session against an emptied transcript: what came before, plus what the
|
|
952
|
-
* transcript being discarded proved. `discarded` must be read BEFORE the live figures are
|
|
953
|
-
* reset, since that is the only record of the latter.
|
|
954
|
-
*
|
|
955
|
-
* Not the same as dropping the baseline. Dropping it re-derives through `persistenceActive`,
|
|
956
|
-
* and with persistence ON that carries the row's *old* banked figure forward — discarding
|
|
957
|
-
* every turn between the last baseline and the clear. The transcript is gone either way, so
|
|
958
|
-
* the branch that reads it as authoritative no longer applies.
|
|
959
|
-
*
|
|
960
|
-
* Derived from the memoised baseline and the live figure rather than from the project's row,
|
|
961
|
-
* because a row is only as current as the last `finalizeCostSession` — which skips its write
|
|
962
|
-
* while the ledger is unloaded, and while the session has no usage yet.
|
|
689
|
+
* Re-baseline this session against an emptied transcript — see
|
|
690
|
+
* `CostLedgerController.rebaselineOnClearedTranscript`. `discarded` must be read
|
|
691
|
+
* BEFORE the live figures are reset, since that is the only record of the latter.
|
|
963
692
|
*/
|
|
964
693
|
private rebaselineOnClearedTranscript;
|
|
965
694
|
showConfirmationDialog(confirmationMessage: string, modalParent: HTMLElement, onConfirm: () => void, onCancel?: () => void): void;
|
|
@@ -967,48 +696,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
967
696
|
removeCostSessionFromHistory(id: string): void;
|
|
968
697
|
/** Clear all persisted build history for this assistant scope. */
|
|
969
698
|
clearCostSessionHistory(): void;
|
|
970
|
-
private getCostHistoryScope;
|
|
971
|
-
private reloadCostSessionHistory;
|
|
972
|
-
/**
|
|
973
|
-
* The persistence provider, but only when it implements BOTH cost-history methods.
|
|
974
|
-
*
|
|
975
|
-
* Both or neither: a provider that can save but not load would silently accumulate a
|
|
976
|
-
* ledger nobody ever reads back, so a partial implementation is treated as absent and
|
|
977
|
-
* falls back to `localStorage`.
|
|
978
|
-
*/
|
|
979
|
-
private costHistoryProvider;
|
|
980
|
-
/**
|
|
981
|
-
* Adopt `records` as the live list, and schedule a write to whichever backend owns cost
|
|
982
|
-
* history — the provider when the host implements it, else `localStorage`.
|
|
983
|
-
*
|
|
984
|
-
* The list is adopted synchronously because that is what the Usage tab renders; only the
|
|
985
|
-
* *write* is deferred. This runs on every usage change (about once per LLM call, plus once
|
|
986
|
-
* per sub-agent turn) and each write serialises the whole ledger, so coalescing cuts a
|
|
987
|
-
* per-turn `localStorage.setItem` or network round-trip down to one per quiet period. Safe
|
|
988
|
-
* because the write is a full replace of idempotent state: the newest call wins and no
|
|
989
|
-
* intermediate state is meaningful. Flushed on `pagehide` and disconnect so nothing is lost.
|
|
990
|
-
*/
|
|
991
|
-
private persistCostHistory;
|
|
992
|
-
/**
|
|
993
|
-
* Write the pending ledger now. Safe to call when nothing is pending.
|
|
994
|
-
*
|
|
995
|
-
* Provider writes are fire-and-forget and best-effort: the ledger is accounting, not
|
|
996
|
-
* conversation content, so a failed write is logged and never surfaced into the chat. On
|
|
997
|
-
* `pagehide` the `localStorage` path completes synchronously; a provider write is subject to
|
|
998
|
-
* the same unload race as every other network save the assistant makes.
|
|
999
|
-
*/
|
|
1000
|
-
private flushCostHistory;
|
|
1001
|
-
/**
|
|
1002
|
-
* Finalize the previous session when the host-provided title changes — a rename should land on
|
|
1003
|
-
* the row under its old name rather than silently relabelling accrued spend.
|
|
1004
|
-
*
|
|
1005
|
-
* Only when the session key still matches the one that title accrued under. Hosts change
|
|
1006
|
-
* `costHistory.title` on project SWITCH too, and if the key has already flipped, this would
|
|
1007
|
-
* upsert the outgoing project's live usage onto the incoming project's row as
|
|
1008
|
-
* `banked + old live`, inflating it. Under the old append model a mis-keyed write left a stray
|
|
1009
|
-
* row; with the upsert it overwrites a real one.
|
|
1010
|
-
*/
|
|
1011
|
-
private syncActiveCostSessionTitle;
|
|
1012
699
|
/** Active model id resolved from the AIProvider, if exposed. */
|
|
1013
700
|
get activeModel(): string | undefined;
|
|
1014
701
|
set activeModel(value: string | undefined);
|
|
@@ -1021,14 +708,20 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1021
708
|
private _suggestionsGeneration;
|
|
1022
709
|
get inputValue(): string;
|
|
1023
710
|
set inputValue(value: string);
|
|
1024
|
-
|
|
1025
|
-
|
|
711
|
+
/** Attachments pending on the next message. */
|
|
712
|
+
get attachments(): ChatAttachment[];
|
|
713
|
+
set attachments(value: ChatAttachment[]);
|
|
714
|
+
/** Rejection messages for files the last pick refused. */
|
|
715
|
+
get attachmentErrors(): string[];
|
|
716
|
+
set attachmentErrors(value: string[]);
|
|
1026
717
|
/** Whether the loading spinner is currently visible. Controlled by the loading delay timer. */
|
|
1027
718
|
showLoadingIndicator: boolean;
|
|
1028
719
|
/** Bump to re-render app settings toggles when the host provider notifies a change. */
|
|
1029
|
-
appSettingsRevision: number;
|
|
720
|
+
get appSettingsRevision(): number;
|
|
721
|
+
set appSettingsRevision(value: number);
|
|
1030
722
|
/** Whether the settings modal is open. */
|
|
1031
|
-
settingsOpen: boolean;
|
|
723
|
+
get settingsOpen(): boolean;
|
|
724
|
+
set settingsOpen(value: boolean);
|
|
1032
725
|
/**
|
|
1033
726
|
* True while `downloadDebugLog` is building the log (GENC-1351 §5.8). Drives the
|
|
1034
727
|
* download button's disabled + spinner state — the lifetime-stitch path is async
|
|
@@ -1038,7 +731,8 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1038
731
|
*/
|
|
1039
732
|
downloadingDebugLog: boolean;
|
|
1040
733
|
/** Active tab inside the settings modal. */
|
|
1041
|
-
settingsModalTab: SettingsModalTab;
|
|
734
|
+
get settingsModalTab(): SettingsModalTab;
|
|
735
|
+
set settingsModalTab(value: SettingsModalTab);
|
|
1042
736
|
/** Bound to the design-system modal via the template ref. */
|
|
1043
737
|
settingsModal?: Modal;
|
|
1044
738
|
/**
|
|
@@ -1117,16 +811,33 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1117
811
|
persistenceModal?: Modal;
|
|
1118
812
|
/** Bound to the settings modal tab bar — used to resync selection after reopen. */
|
|
1119
813
|
settingsTabsEl?: SettingsModalTabsElement;
|
|
1120
|
-
/**
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
814
|
+
/**
|
|
815
|
+
* Light-DOM nodes assigned to settings modal slots. The template's `slotted()`
|
|
816
|
+
* bindings write these by property NAME, so each needs a real setter here even
|
|
817
|
+
* though the controller owns the arrays.
|
|
818
|
+
*/
|
|
819
|
+
get settingsModelSlotted(): HTMLElement[];
|
|
820
|
+
set settingsModelSlotted(value: HTMLElement[]);
|
|
821
|
+
get settingsAppSlotted(): HTMLElement[];
|
|
822
|
+
set settingsAppSlotted(value: HTMLElement[]);
|
|
823
|
+
get settingsCostCurrentSlotted(): HTMLElement[];
|
|
824
|
+
set settingsCostCurrentSlotted(value: HTMLElement[]);
|
|
825
|
+
get settingsCostHistorySlotted(): HTMLElement[];
|
|
826
|
+
set settingsCostHistorySlotted(value: HTMLElement[]);
|
|
1127
827
|
/** Whether the splash overlay is currently showing (no messages and showSplash is enabled). Reflected as a boolean attribute on the host. */
|
|
1128
828
|
showingSplash: boolean;
|
|
1129
829
|
private driver?;
|
|
830
|
+
/**
|
|
831
|
+
* This element's driver as an orchestrator, or `undefined` when the session is
|
|
832
|
+
* running a bare `ChatDriver`.
|
|
833
|
+
*
|
|
834
|
+
* The five call sites that used to test the type themselves now read this
|
|
835
|
+
* (§11.2 constraint 3 of `docs/code-health.md`): the agent pin, the flow-owner
|
|
836
|
+
* lock, the wire-up in `wireDriver`, and the two `driverKind` diagnostics. The
|
|
837
|
+
* type test itself lives in `asOrchestrator`, shared with the session
|
|
838
|
+
* persister.
|
|
839
|
+
*/
|
|
840
|
+
private get orchestrator();
|
|
1130
841
|
private driverCleanup?;
|
|
1131
842
|
private loadingTimer;
|
|
1132
843
|
private unsubBus?;
|
|
@@ -1134,29 +845,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1134
845
|
private _unsubBudgetLatch?;
|
|
1135
846
|
/** Unsubscribe handle for the provider-registry change listener (observable registries only). */
|
|
1136
847
|
private unsubProviderRegistry?;
|
|
1137
|
-
/** Unsubscribe handle for {@link (AssistantAppSettingsProvider:interface).subscribe}. */
|
|
1138
|
-
private unsubAppSettingsProvider?;
|
|
1139
|
-
/** Title tracked for the in-flight cost session (used when `header-title` changes). */
|
|
1140
|
-
private _activeCostSessionTitle;
|
|
1141
|
-
/**
|
|
1142
|
-
* Session key `_activeCostSessionTitle` was captured under, so a title change can tell a rename
|
|
1143
|
-
* (same key) from a project switch (key already flipped) — see `syncActiveCostSessionTitle`.
|
|
1144
|
-
*/
|
|
1145
|
-
private _activeCostSessionKey?;
|
|
1146
|
-
/**
|
|
1147
|
-
* Whether the row list reflects storage yet. Gates cost writes so a project's banked
|
|
1148
|
-
* spend is never derived from a ledger that simply hasn't arrived.
|
|
1149
|
-
*/
|
|
1150
|
-
private _costHistoryLoaded;
|
|
1151
|
-
/**
|
|
1152
|
-
* Set when a provider read failed. Latches writes off for this page-load: `saveCostHistory`
|
|
1153
|
-
* replaces the whole stored list, so writing anything derived from the empty list we fell back
|
|
1154
|
-
* to would destroy rows we never managed to read.
|
|
1155
|
-
*/
|
|
1156
|
-
private _ledgerUnreadable;
|
|
1157
|
-
/** Pending (adopted but unwritten) ledger changes — see `persistCostHistory`. */
|
|
1158
|
-
private _ledgerDirty;
|
|
1159
|
-
private _ledgerFlushTimer?;
|
|
1160
848
|
private _handlePageHide;
|
|
1161
849
|
private haloStartPublished;
|
|
1162
850
|
/** Fingerprint of the agents array used to build the current driver. Used by agentsChanged to skip spurious rebuilds. */
|
|
@@ -1166,20 +854,15 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1166
854
|
/** Bound to the rapid-text-area chat input via the template ref. */
|
|
1167
855
|
chatInputEl?: HTMLElement;
|
|
1168
856
|
/**
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
* as the lower bound so the box can't be dragged smaller than it started. */
|
|
1179
|
-
private _composerNaturalHeight;
|
|
1180
|
-
/** Upper bound for the current drag — capped to the headroom above so growing
|
|
1181
|
-
* the composer never pushes the input row's buttons off the bottom. */
|
|
1182
|
-
private _resizeMaxHeight;
|
|
857
|
+
* A get/set pair over one of this element's own properties, for a controller
|
|
858
|
+
* that drives state it does not own. Written as a helper rather than two inline
|
|
859
|
+
* closures per call: a pair of same-typed closures is easy to transpose by
|
|
860
|
+
* accident, and a single object cannot be.
|
|
861
|
+
*/
|
|
862
|
+
private accessor;
|
|
863
|
+
/** Explicit composer height once dragged; `null` keeps the default auto-grow. */
|
|
864
|
+
get composerHeightPx(): number | null;
|
|
865
|
+
set composerHeightPx(value: number | null);
|
|
1183
866
|
/**
|
|
1184
867
|
* True when the user's last interaction (pointer or keyboard focus) was inside
|
|
1185
868
|
* the assistant. Defaults to true so the assistant claims focus on first mount.
|
|
@@ -1370,18 +1053,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1370
1053
|
* window is known.
|
|
1371
1054
|
*/
|
|
1372
1055
|
private syncContextGuard;
|
|
1373
|
-
/**
|
|
1374
|
-
* Guard policy pushed to the driver, or `undefined` when the host has switched
|
|
1375
|
-
* the gate off — so `enabled: false` really does mean no local blocking of any
|
|
1376
|
-
* kind, UI or mid-loop.
|
|
1377
|
-
*
|
|
1378
|
-
* A MARGIN, not a threshold: the driver resolves the window of the provider
|
|
1379
|
-
* each call actually goes to. The fallback window rides along because the
|
|
1380
|
-
* driver cannot learn it any other way — without it, a host that knows its
|
|
1381
|
-
* window while its provider does not report one had `fallbackContextLimit`
|
|
1382
|
-
* protecting the composer and nothing else.
|
|
1383
|
-
*/
|
|
1384
|
-
private get contextGuardPolicy();
|
|
1385
1056
|
private loadProviderStatuses;
|
|
1386
1057
|
chatConfigChanged(): void;
|
|
1387
1058
|
/**
|
|
@@ -1463,46 +1134,10 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1463
1134
|
* `contains()` check cannot tell "clicked the menu" from "clicked the
|
|
1464
1135
|
* transcript". The path crosses the boundary and carries both.
|
|
1465
1136
|
*/
|
|
1466
|
-
private _dismissPointerHandler?;
|
|
1467
|
-
private _dismissKeyHandler?;
|
|
1468
|
-
private _dismissResizeHandler?;
|
|
1469
|
-
private closeOpenComposerMenus;
|
|
1470
|
-
/**
|
|
1471
|
-
* Places an open composer menu directly above the control that opened it.
|
|
1472
|
-
*
|
|
1473
|
-
* CSS alone cannot: the menu is a child of `.composer` — which is what lets its
|
|
1474
|
-
* width be capped against the composer rather than against a button — while its
|
|
1475
|
-
* anchor is a button several levels down the bar, and no selector can read a
|
|
1476
|
-
* sibling's offset. So the left edge is measured on open and written as a
|
|
1477
|
-
* custom property.
|
|
1478
|
-
*
|
|
1479
|
-
* Clamped to the composer, so a menu opened from a control near the right edge
|
|
1480
|
-
* of a narrow panel slides back inside instead of overhanging. The animation
|
|
1481
|
-
* origin is written separately and keeps pointing at the button's centre even
|
|
1482
|
-
* after such a shift — the menu still grows out of the thing that opened it,
|
|
1483
|
-
* which is the whole point of anchoring it there.
|
|
1484
|
-
*/
|
|
1485
|
-
private positionComposerMenu;
|
|
1486
|
-
/** Re-place whichever menu is open. Cheap enough to run from a resize. */
|
|
1487
|
-
private repositionOpenComposerMenu;
|
|
1488
|
-
private syncComposerMenuDismissal;
|
|
1489
|
-
private teardownComposerMenuDismissal;
|
|
1490
|
-
/**
|
|
1491
|
-
* Vertical space an open menu must leave free above the button, covering BOTH
|
|
1492
|
-
* the gap the menu sits off the button by (8px, declared in design units in
|
|
1493
|
-
* main.styles.ts) and the breathing room wanted above it. The two are lumped
|
|
1494
|
-
* together because only the second half is a free choice: the first is fixed by
|
|
1495
|
-
* the stylesheet and simply has to be subtracted from the headroom, and a
|
|
1496
|
-
* constant named for only one of them invites someone to "fix" a gap that is
|
|
1497
|
-
* doing exactly what it should.
|
|
1498
|
-
*/
|
|
1499
|
-
private static readonly MENU_HEADROOM_RESERVE;
|
|
1500
|
-
private readonly _agentPickerToggle;
|
|
1501
1137
|
toggleAgentPicker(): void;
|
|
1502
1138
|
/** Two-step confirm for the destructive Clear action; reset whenever the menu toggles. */
|
|
1503
1139
|
confirmingClear: boolean;
|
|
1504
1140
|
confirmingCompact: boolean;
|
|
1505
|
-
private readonly _sessionMenuToggle;
|
|
1506
1141
|
/**
|
|
1507
1142
|
* Whether the lifecycle-menu button shows. It occupies the stop-button slot,
|
|
1508
1143
|
* so it only appears when idle (mutually exclusive with Stop). Gated on
|
|
@@ -1540,20 +1175,18 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1540
1175
|
/** Whether persistence is currently active (on + a provider) for this session. */
|
|
1541
1176
|
get persistenceActive(): boolean;
|
|
1542
1177
|
/**
|
|
1543
|
-
*
|
|
1544
|
-
*
|
|
1178
|
+
* The context gate's derived surface (GENC-1567). Every member below delegates to
|
|
1179
|
+
* `ContextGateController` — the names are this element's public API and the
|
|
1180
|
+
* template's bindings, so they stay put while the derivation moves.
|
|
1545
1181
|
*
|
|
1546
1182
|
* The per-agent `contextReserve` IS folded in, via the driver (GENC-1582). It
|
|
1547
1183
|
* used to be described here as a separate, later check made when an agent took
|
|
1548
1184
|
* ownership of a flow — but there is no such check, and by flow entry the send
|
|
1549
|
-
* has already been allowed. Resolving it
|
|
1550
|
-
*
|
|
1185
|
+
* has already been allowed. Resolving it before the send is what makes it a gate
|
|
1186
|
+
* rather than a label.
|
|
1551
1187
|
*
|
|
1552
|
-
*
|
|
1553
|
-
*
|
|
1554
|
-
* `estimateSystemOverhead` only looks at messages BEFORE the first usage-bearing
|
|
1555
|
-
* one, and the two lists agree completely that early — the drift between mirror
|
|
1556
|
-
* and driver history is only ever at the tail.
|
|
1188
|
+
* None carries `@volatile`: the decorators live on the controller's getters, and
|
|
1189
|
+
* they reach the binding observer from there — see `docs/code-health.md` §11.4.
|
|
1557
1190
|
*/
|
|
1558
1191
|
get contextBudget(): ResolvedContextBudget;
|
|
1559
1192
|
/**
|
|
@@ -1574,150 +1207,31 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1574
1207
|
* `contextReserve` is a plain number by design — see `ContextReserveInput` for
|
|
1575
1208
|
* why a resolver form was dropped rather than accepted and ignored.
|
|
1576
1209
|
*
|
|
1210
|
+
* Stays on the element rather than moving into `ContextGateController` because it
|
|
1211
|
+
* reaches the driver, which no extracted controller may do (`docs/code-health.md`
|
|
1212
|
+
* §11.2 constraint 2). The controller receives the resolved value as a callback.
|
|
1213
|
+
*
|
|
1577
1214
|
* @internal
|
|
1578
1215
|
*/
|
|
1579
1216
|
private get contextReserve();
|
|
1580
|
-
/**
|
|
1581
|
-
* What compacting right now would reclaim, or `null` when it would not be
|
|
1582
|
-
* worth it (GENC-1567). Sized against {@link FoundationAiAssistant.contextBudget},
|
|
1583
|
-
* so the plan aims at this model's window rather than a fixed default.
|
|
1584
|
-
*
|
|
1585
|
-
* Reads `messages` to stay reactive as the conversation grows: the driver's
|
|
1586
|
-
* history is not a tracked observable, so every consumer of this keys its
|
|
1587
|
-
* re-evaluation off the Redux mirror.
|
|
1588
|
-
*/
|
|
1589
1217
|
get compactionPlan(): CompactionProjection | null;
|
|
1590
|
-
/**
|
|
1591
|
-
* Tooltip and hint for the Compact row — why it is or is not available.
|
|
1592
|
-
*
|
|
1593
|
-
* Replaces a fixed "Not enough conversation yet to be worth compacting", which
|
|
1594
|
-
* since GENC-1567 could be flatly wrong: a long conversation whose tail is one
|
|
1595
|
-
* indivisible tool result has plenty of conversation and still cannot be
|
|
1596
|
-
* compacted. Reading the plan means the hint states the actual reason.
|
|
1597
|
-
*
|
|
1598
|
-
* @internal
|
|
1599
|
-
*/
|
|
1600
|
-
get compactHint(): string;
|
|
1601
|
-
/**
|
|
1602
|
-
* How close this conversation is to the model's context window (GENC-1567).
|
|
1603
|
-
*
|
|
1604
|
-
* **Derived, never latched** — the opposite of {@link FoundationAiAssistant.blocked}.
|
|
1605
|
-
* A budget wall stays set because nothing the user does fixes it; this clears
|
|
1606
|
-
* itself the moment the conversation shrinks, which is precisely what the user
|
|
1607
|
-
* is being asked to do. The two must not be conflated: `blocked` is also
|
|
1608
|
-
* deliberately preserved across `resetSession`, and a context gate obviously
|
|
1609
|
-
* must not survive starting a new chat.
|
|
1610
|
-
*
|
|
1611
|
-
* **A flow in progress is never blocked, only warned.** Machine state lives
|
|
1612
|
-
* outside the transcript, so a stateful flow is not desynced by compaction —
|
|
1613
|
-
* but the summary can still drop detail the flow's own prompt leans on, and
|
|
1614
|
-
* blocking mid-journey would strand the user between a flow they cannot finish
|
|
1615
|
-
* and a compaction that is a poor idea right there. Letting the flow complete
|
|
1616
|
-
* and gating on the way out is the lesser harm; the headroom reserve exists so
|
|
1617
|
-
* this is rare rather than routine.
|
|
1618
|
-
*/
|
|
1619
1218
|
/**
|
|
1620
1219
|
* WHY the composer is blocked, when it is (GENC-1582). `undefined` unless
|
|
1621
|
-
* {@link FoundationAiAssistant.contextGate} is `'blocked'`.
|
|
1622
|
-
*
|
|
1623
|
-
*
|
|
1624
|
-
* refused either way — and every consumer switches on the three levels. What
|
|
1625
|
-
* differs is what the user can do about it, which is worth splitting because the
|
|
1626
|
-
* two answers have no remedy in common.
|
|
1627
|
-
*
|
|
1628
|
-
* - `'transcript'` — the conversation has grown into the reserve. Compacting or
|
|
1629
|
-
* clearing helps, and the copy says so.
|
|
1630
|
-
* - `'model'` — the active agent's reserve meets or exceeds the whole window, so
|
|
1631
|
-
* `blockAt` is 0 and EVERY message is refused, including the first of an empty
|
|
1632
|
-
* chat. Neither compacting nor clearing can help: the constraint is the model,
|
|
1633
|
-
* not the transcript. Before this existed the user was told to clear the chat,
|
|
1634
|
-
* did, and hit the identical block with no way to understand why.
|
|
1635
|
-
*
|
|
1636
|
-
* Keyed off `blockAt === 0`, deliberately NOT off whether a compaction plan
|
|
1637
|
-
* exists. A null plan has two unrelated causes — nothing left worth summarizing,
|
|
1638
|
-
* versus a window that cannot host the agent at all — and conflating them is what
|
|
1639
|
-
* produced the misleading copy.
|
|
1640
|
-
*
|
|
1641
|
-
* Note the block itself is correct in both cases and must stay: an agent that
|
|
1642
|
-
* measurably cannot complete a turn in this window would otherwise start one and
|
|
1643
|
-
* die mid-loop on a provider context error, with partial writes and no clean
|
|
1644
|
-
* exit. Refusing up front is the better failure.
|
|
1220
|
+
* {@link FoundationAiAssistant.contextGate} is `'blocked'`. See
|
|
1221
|
+
* `ContextGateController.blockCause` for the `'transcript'` / `'model'` split
|
|
1222
|
+
* and why it is a cause rather than a fourth gate level.
|
|
1645
1223
|
*
|
|
1646
1224
|
* @internal
|
|
1647
1225
|
*/
|
|
1648
1226
|
get contextBlockCause(): 'transcript' | 'model' | undefined;
|
|
1649
1227
|
get contextGate(): 'ok' | 'warn' | 'blocked';
|
|
1650
|
-
/**
|
|
1651
|
-
* What the composer notice says about the context gate.
|
|
1652
|
-
*
|
|
1653
|
-
* The copy is a function of whether compaction can actually clear the gate,
|
|
1654
|
-
* which is why it reads {@link FoundationAiAssistant.compactionPlan} rather
|
|
1655
|
-
* than assuming. Telling someone to compact when the projection says it would
|
|
1656
|
-
* not free enough — a tail that is one indivisible tool result, say — sends
|
|
1657
|
-
* them into a summarizer call that leaves them exactly where they were.
|
|
1658
|
-
*
|
|
1659
|
-
* @internal
|
|
1660
|
-
*/
|
|
1661
1228
|
get contextGateReason(): string;
|
|
1662
|
-
/**
|
|
1663
|
-
* Short label the session-menu pill wears while the gate is active — the
|
|
1664
|
-
* primary visible signal, in place of a banner.
|
|
1665
|
-
*
|
|
1666
|
-
* Borrowed from the agent pin, which is the established way this composer says
|
|
1667
|
-
* "a mode is in force": a pill that grows a label rather than a strip of prose
|
|
1668
|
-
* above the input. It states the CONDITION only; the remedy lives in the
|
|
1669
|
-
* placeholder once the composer is actually blocked, which is the moment the
|
|
1670
|
-
* user needs to be told what to do rather than merely warned.
|
|
1671
|
-
*
|
|
1672
|
-
* @internal
|
|
1673
|
-
*/
|
|
1674
1229
|
get contextGateLabel(): string;
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
* context gate. One region for both, so the live region stays single and
|
|
1678
|
-
* registered (see the banner template's accessibility note).
|
|
1679
|
-
*
|
|
1680
|
-
* @internal
|
|
1681
|
-
*/
|
|
1230
|
+
get compactHint(): string;
|
|
1231
|
+
get compactable(): boolean;
|
|
1682
1232
|
get composerNoticeVisible(): boolean;
|
|
1683
|
-
/**
|
|
1684
|
-
* Text for that region. A budget wall outranks the context gate: it is the
|
|
1685
|
-
* condition the user cannot clear, so it must not be hidden behind advice
|
|
1686
|
-
* about compacting that would not restore sending anyway.
|
|
1687
|
-
*
|
|
1688
|
-
* For the context gate the region is present and announced but not drawn — see
|
|
1689
|
-
* {@link FoundationAiAssistant.composerNoticeSrOnly}.
|
|
1690
|
-
*
|
|
1691
|
-
* @internal
|
|
1692
|
-
*/
|
|
1693
1233
|
get composerNoticeText(): string;
|
|
1694
|
-
/**
|
|
1695
|
-
* Whether the notice region should be announced but not drawn.
|
|
1696
|
-
*
|
|
1697
|
-
* The context gate shows itself visually through the pill's label, the
|
|
1698
|
-
* composer outline and the placeholder — a strip of prose on top of those
|
|
1699
|
-
* would be a fourth statement of the same thing. But dropping the region
|
|
1700
|
-
* outright would be an accessibility regression, and a documented one: a
|
|
1701
|
-
* `role="status"` region announces MUTATIONS to a region already being
|
|
1702
|
-
* observed, and it is the only announcement available here, because the
|
|
1703
|
-
* composer flips to `disabled` at the same moment and a disabled control is out
|
|
1704
|
-
* of the tab order — so neither the placeholder nor an `aria-label` on it is
|
|
1705
|
-
* ever voiced.
|
|
1706
|
-
*
|
|
1707
|
-
* So the region stays mounted, keeps its text and keeps announcing; only the
|
|
1708
|
-
* visual treatment is withheld. A budget wall still draws its banner, because
|
|
1709
|
-
* that condition has no other visible expression.
|
|
1710
|
-
*
|
|
1711
|
-
* @internal
|
|
1712
|
-
*/
|
|
1713
1234
|
get composerNoticeSrOnly(): boolean;
|
|
1714
|
-
/**
|
|
1715
|
-
* Whether the menu's "Compact" action would do anything (GENC-1351 §5.7,
|
|
1716
|
-
* GENC-1567). Delegates to the driver's plan so the gate uses the exact same
|
|
1717
|
-
* history `compact()` acts on, judged against the same budget — one source of
|
|
1718
|
-
* truth for both whether a cut is legal and whether it reclaims enough.
|
|
1719
|
-
*/
|
|
1720
|
-
get compactable(): boolean;
|
|
1721
1235
|
toggleSessionMenu(): void;
|
|
1722
1236
|
/**
|
|
1723
1237
|
* Set persistence on/off for this session (the menu switch). Reassigns the
|
|
@@ -1802,33 +1316,40 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1802
1316
|
}): boolean;
|
|
1803
1317
|
/** Whether the picker toggle button should appear. Mirrors the picker's own visibility rule. */
|
|
1804
1318
|
get agentPickerEnabled(): boolean;
|
|
1805
|
-
/**
|
|
1319
|
+
/**
|
|
1320
|
+
* The settings modal's derived surface. Every member below delegates to
|
|
1321
|
+
* `SettingsModalController` — the names are this element's public API and the
|
|
1322
|
+
* 943-line template's bindings, so they stay put while the logic moves into the
|
|
1323
|
+
* `settings-modal.ts` that folder never had (`docs/code-health.md` §3).
|
|
1324
|
+
*
|
|
1325
|
+
* None carries `@volatile`: the decorators live on the controller's getters, and
|
|
1326
|
+
* they reach the binding observer from there — see `docs/code-health.md` §11.4.
|
|
1327
|
+
*/
|
|
1806
1328
|
get settingsModelSectionVisible(): boolean;
|
|
1807
1329
|
/** Built-in context window indicator (generic platform concern). */
|
|
1808
1330
|
get settingsContextUsageVisible(): boolean;
|
|
1331
|
+
/**
|
|
1332
|
+
* The context indicator's figures, pre-formatted, or `undefined` when there is
|
|
1333
|
+
* nothing measurable to show — see {@link SettingsContextUsage}.
|
|
1334
|
+
*
|
|
1335
|
+
* @internal
|
|
1336
|
+
*/
|
|
1337
|
+
get settingsContextUsage(): SettingsContextUsage | undefined;
|
|
1809
1338
|
/**
|
|
1810
1339
|
* Built-in per-vendor budget meter (GENC-1464) — the money sibling of
|
|
1811
1340
|
* {@link FoundationAiAssistant.settingsContextUsageVisible}, with the same
|
|
1812
|
-
* shape of gate: not configured off, and there is data to show.
|
|
1813
|
-
*
|
|
1814
|
-
*
|
|
1815
|
-
* context indicator's absent case.
|
|
1816
|
-
*
|
|
1817
|
-
* Answered from the RAW slice state rather than from
|
|
1818
|
-
* {@link FoundationAiAssistant.settingsBudgetRows}, whose `.length` would
|
|
1819
|
-
* format every row (a `formatUsd` pair and a `budgetPercentOf` each) only to
|
|
1820
|
-
* ask whether any exist. This is read several times per render pass — the
|
|
1821
|
-
* getter, the section gate, the template's `when` — so it stays as cheap as
|
|
1822
|
-
* the context sibling's `!= null`.
|
|
1341
|
+
* shape of gate: not configured off, and there is data to show. See
|
|
1342
|
+
* `BudgetController.settingsUsageVisible` for why it reads the raw slice
|
|
1343
|
+
* rather than counting the rows.
|
|
1823
1344
|
*
|
|
1824
1345
|
* @internal
|
|
1825
1346
|
*/
|
|
1826
1347
|
get settingsBudgetUsageVisible(): boolean;
|
|
1827
1348
|
/**
|
|
1828
1349
|
* The meter's rows — one per vendor that currently has figures, in
|
|
1829
|
-
* `BUDGETED_VENDORS` order
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1350
|
+
* `BUDGETED_VENDORS` order so the rendering is stable however the host ordered
|
|
1351
|
+
* its feed calls. Pre-formatted; see {@link SettingsBudgetRow} for why the text
|
|
1352
|
+
* is unclamped while the bar is.
|
|
1832
1353
|
*
|
|
1833
1354
|
* @internal
|
|
1834
1355
|
*/
|
|
@@ -1845,38 +1366,25 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1845
1366
|
get appSettingsTogglesHeading(): AssistantAppSettingsHeading | undefined;
|
|
1846
1367
|
getAppSettingValue(id: string): boolean;
|
|
1847
1368
|
setAppSettingValue(id: string, value: boolean): void;
|
|
1848
|
-
private wireAppSettingsProvider;
|
|
1849
|
-
private unwireAppSettingsProvider;
|
|
1850
1369
|
get settingsCostCurrentSectionVisible(): boolean;
|
|
1851
1370
|
/**
|
|
1852
1371
|
* Whether the build-history section has anything in it — the built-in row list, or a host's own
|
|
1853
|
-
* markup in the `settings-cost-history` slot.
|
|
1854
|
-
*
|
|
1855
|
-
*
|
|
1856
|
-
* it gates the row writes, the reload, and the lifetime sum — and it defaults to `false`
|
|
1857
|
-
* (`cost?.enabled === true`), while the Usage tab itself shows for `showCost`/`showTokens`, which
|
|
1858
|
-
* default true. So a host can legitimately render its own build history into the slot and never
|
|
1859
|
-
* opt into our `localStorage` ledger. Short-circuiting on `enabled` above the slot check hid that
|
|
1860
|
-
* host's markup outright, since the slot is the only thing in the container.
|
|
1372
|
+
* markup in the `settings-cost-history` slot. See
|
|
1373
|
+
* `SettingsModalController.costHistorySectionVisible` for why slotted content is
|
|
1374
|
+
* checked ahead of `enabled`.
|
|
1861
1375
|
*/
|
|
1862
1376
|
get settingsCostHistorySectionVisible(): boolean;
|
|
1863
1377
|
/**
|
|
1864
|
-
* Whether the "Build history" heading has anything under it
|
|
1865
|
-
*
|
|
1866
|
-
*
|
|
1867
|
-
* heading also covers the lifetime total, which renders before the first row exists (rows are
|
|
1868
|
-
* written as spend accrues, the lifetime section only needs history switched on). Gating the
|
|
1869
|
-
* heading on the rows alone left that total sitting under nothing.
|
|
1378
|
+
* Whether the "Build history" heading has anything under it — broader than
|
|
1379
|
+
* {@link FoundationAiAssistant.settingsCostHistorySectionVisible}, because the
|
|
1380
|
+
* heading also covers the lifetime total, which renders before the first row exists.
|
|
1870
1381
|
*/
|
|
1871
1382
|
get settingsCostHistoryHeadingVisible(): boolean;
|
|
1872
1383
|
/** Whether the built-in Usage tab summary block has anything to show. */
|
|
1873
1384
|
get settingsCostSummaryVisible(): boolean;
|
|
1874
1385
|
/**
|
|
1875
|
-
* Whether the lifetime-usage section has anything to say.
|
|
1876
|
-
*
|
|
1877
|
-
* Requires build history: with it off there are no rows, so `cumulativeUsage` falls back to
|
|
1878
|
-
* the live session and the section would restate the block directly below it under a
|
|
1879
|
-
* "lifetime" heading it hasn't earned.
|
|
1386
|
+
* Whether the lifetime-usage section has anything to say. Requires build history
|
|
1387
|
+
* AND a resolvable state key — see `SettingsModalController.costLifetimeVisible`.
|
|
1880
1388
|
*/
|
|
1881
1389
|
get settingsCostLifetimeVisible(): boolean;
|
|
1882
1390
|
/** Whether the Usage tab should appear in the settings modal. */
|
|
@@ -1891,15 +1399,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1891
1399
|
setSettingsModalTab(tab: SettingsModalTab): void;
|
|
1892
1400
|
/** Sync the active settings modal tab when the design-system tab bar changes. */
|
|
1893
1401
|
onSettingsModalTabsChange(event: Event): void;
|
|
1894
|
-
/** Ensure the settings tab is selected when the modal opens. */
|
|
1895
|
-
private resetSettingsModalTabsOnOpen;
|
|
1896
|
-
/** Return to the settings tab when the modal closes on Cost. */
|
|
1897
|
-
private resetSettingsModalTabsOnClose;
|
|
1898
|
-
/**
|
|
1899
|
-
* Snap the tab underline to the settings tab without animating from Cost.
|
|
1900
|
-
* Used after hide/show when aria state is correct but the indicator drifted.
|
|
1901
|
-
*/
|
|
1902
|
-
private snapSettingsModalTabIndicator;
|
|
1903
1402
|
/** Hint text for the currently pinned agent, if any. Used in the toggle button tooltip. */
|
|
1904
1403
|
get pinnedAgentHint(): string | undefined;
|
|
1905
1404
|
/**
|
|
@@ -1952,14 +1451,13 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
1952
1451
|
*/
|
|
1953
1452
|
private buildDiagnosticEntries;
|
|
1954
1453
|
/**
|
|
1955
|
-
* The
|
|
1956
|
-
*
|
|
1957
|
-
* the forward-capture delta keys on.
|
|
1454
|
+
* The `meta-snapshot` entry for right now. The assembly is a pure transformation
|
|
1455
|
+
* in `buildMetaSnapshot`; this gathers what it reports.
|
|
1958
1456
|
*
|
|
1959
|
-
*
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
1962
|
-
* `
|
|
1457
|
+
* The live agent comes from the DRIVER, not `this.activeAgent` (the redux-stripped
|
|
1458
|
+
* projection, no functions) and not a lookup in `this.agents` (which may mint
|
|
1459
|
+
* fresh, never-activated configs) — only the driver holds the instance whose
|
|
1460
|
+
* `onActivate` ran, so only its `getDebugSnapshot` closure holds state.
|
|
1963
1461
|
*/
|
|
1964
1462
|
private buildMetaSnapshot;
|
|
1965
1463
|
downloadDebugLog(): Promise<void>;
|
|
@@ -2065,13 +1563,6 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
2065
1563
|
handleFileSelect(e: Event): void;
|
|
2066
1564
|
removeAttachment(attachment: ChatAttachment): void;
|
|
2067
1565
|
removeAttachmentError(message: string): void;
|
|
2068
|
-
private isAcceptedFile;
|
|
2069
|
-
private readFileAsText;
|
|
2070
|
-
/** The base64 payload of a file, without any `data:` prefix — the raw form
|
|
2071
|
-
* `ChatImageAttachment.data` specifies (transports add their own framing). */
|
|
2072
|
-
private readFileAsBase64;
|
|
2073
|
-
private processFiles;
|
|
2074
|
-
private loadSelectedFiles;
|
|
2075
1566
|
handleSendClick(): void;
|
|
2076
1567
|
/**
|
|
2077
1568
|
* User clicked Stop. Cancels the current turn via the driver and flips the
|
|
@@ -2148,24 +1639,12 @@ export declare class FoundationAiAssistant extends GenesisElement {
|
|
|
2148
1639
|
* mid-turn.
|
|
2149
1640
|
*/
|
|
2150
1641
|
get sendButtonVisible(): boolean;
|
|
2151
|
-
/** Whether the message input shows a drag handle to resize its height.
|
|
2152
|
-
* On by default; hosts opt out via `chatConfig.ui.resizableInput: false`. */
|
|
1642
|
+
/** Whether the message input shows a drag handle to resize its height. */
|
|
2153
1643
|
get isComposerResizable(): boolean;
|
|
2154
|
-
/** Inline style
|
|
2155
|
-
* `--composer-height` custom property (read by `.chat-input` + its control in
|
|
2156
|
-
* `main.styles.ts`). Empty until the user drags (or when resizing is disabled),
|
|
2157
|
-
* so the default applies. */
|
|
1644
|
+
/** Inline style carrying the dragged height into `--composer-height`. */
|
|
2158
1645
|
get composerHeightStyle(): string;
|
|
2159
|
-
/** Begin a composer resize drag. Captures the pointer so move/up keep firing
|
|
2160
|
-
* on the handle even when the cursor leaves its 7px band. */
|
|
2161
1646
|
onComposerResizeStart(e: PointerEvent): void;
|
|
2162
|
-
/** Resize while dragging. Dragging up (clientY decreases) grows the composer;
|
|
2163
|
-
* the result is clamped between the natural height (lower) and the available
|
|
2164
|
-
* headroom/max (upper). No-op unless a drag is active (the handle holds pointer
|
|
2165
|
-
* capture), so plain hover-moves are ignored. */
|
|
2166
1647
|
onComposerResizeMove(e: PointerEvent): void;
|
|
2167
|
-
/** End a resize drag and release the pointer capture. */
|
|
2168
1648
|
onComposerResizeEnd(e: PointerEvent): void;
|
|
2169
1649
|
}
|
|
2170
|
-
export {};
|
|
2171
1650
|
//# sourceMappingURL=main.d.ts.map
|