@genesislcap/ai-assistant 15.7.0 → 15.7.2
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 +32 -2
- package/dist/ai-assistant.d.ts +42 -14
- package/dist/custom-elements.json +55 -42
- package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +42 -14
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/utils/banked-usage-baselines.d.ts +51 -0
- package/dist/dts/utils/banked-usage-baselines.d.ts.map +1 -0
- package/dist/esm/components/settings-modal/settings-modal.styles.js +63 -18
- package/dist/esm/components/settings-modal/settings-modal.template.js +36 -7
- package/dist/esm/main/cost-session-banking.test.js +238 -8
- package/dist/esm/main/main.js +127 -37
- package/dist/esm/main/main.styles.js +26 -1
- package/dist/esm/main/main.styles.test.js +13 -0
- package/dist/esm/utils/banked-usage-baselines.js +84 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/components/settings-modal/settings-modal.styles.ts +63 -18
- package/src/components/settings-modal/settings-modal.template.ts +38 -7
- package/src/main/cost-session-banking.test.ts +283 -11
- package/src/main/main.styles.test.ts +27 -0
- package/src/main/main.styles.ts +26 -1
- package/src/main/main.ts +128 -37
- package/src/utils/banked-usage-baselines.ts +90 -0
package/src/main/main.ts
CHANGED
|
@@ -123,6 +123,11 @@ import { ChatSuggestions } from '../suggestions/chat-suggestions';
|
|
|
123
123
|
import type { InteractionContext } from '../types/interaction-context';
|
|
124
124
|
import { AnimatedPanelToggle } from '../utils/animated-panel-toggle';
|
|
125
125
|
import { resolveExclusiveLoadingStyle } from '../utils/animation-exclusivity';
|
|
126
|
+
import {
|
|
127
|
+
deleteBankedBaseline,
|
|
128
|
+
getBankedBaseline,
|
|
129
|
+
setBankedBaseline,
|
|
130
|
+
} from '../utils/banked-usage-baselines';
|
|
126
131
|
import { collectSessionModels } from '../utils/collect-session-models';
|
|
127
132
|
import {
|
|
128
133
|
clearCostSessionHistory,
|
|
@@ -877,11 +882,6 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
877
882
|
*
|
|
878
883
|
* @beta
|
|
879
884
|
*/
|
|
880
|
-
/** Whether this vendor's wall came from the sweep alone — see the slice's `sweptVendors`. */
|
|
881
|
-
private isVendorSwept(vendor: AIProviderType): boolean {
|
|
882
|
-
return (this._sessionRef?.store.aiAssistant.sweptVendors ?? []).includes(vendor);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
885
|
setVendorBlocked(vendor: AIProviderType, blocked: boolean, reason?: string | null): void {
|
|
886
886
|
if (vendor === 'none') {
|
|
887
887
|
logger.warn(
|
|
@@ -893,6 +893,11 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
893
893
|
this._sessionRef?.actions.aiAssistant.setVendorBlocked({ vendor, blocked, reason });
|
|
894
894
|
}
|
|
895
895
|
|
|
896
|
+
/** Whether this vendor's wall came from the sweep alone — see the slice's `sweptVendors`. */
|
|
897
|
+
private isVendorSwept(vendor: AIProviderType): boolean {
|
|
898
|
+
return (this._sessionRef?.store.aiAssistant.sweptVendors ?? []).includes(vendor);
|
|
899
|
+
}
|
|
900
|
+
|
|
896
901
|
/**
|
|
897
902
|
* The walled vendors the user can still be routed to — i.e.
|
|
898
903
|
* {@link FoundationAiAssistant.blockedVendors} narrowed to the reachable set,
|
|
@@ -1535,9 +1540,9 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1535
1540
|
}
|
|
1536
1541
|
|
|
1537
1542
|
/**
|
|
1538
|
-
* Spend for `projectKey` that predates the current transcript, resolved once per
|
|
1539
|
-
*
|
|
1540
|
-
* rewritten by our own upserts.
|
|
1543
|
+
* Spend for `projectKey` that predates the current transcript, resolved once per SESSION and
|
|
1544
|
+
* shared by every element instance wired to it (see `banked-usage-baselines`), so it cannot
|
|
1545
|
+
* drift as the row it came from is rewritten by our own upserts.
|
|
1541
1546
|
*
|
|
1542
1547
|
* The two cases turn on whether the transcript is authoritative for this project:
|
|
1543
1548
|
*
|
|
@@ -1547,28 +1552,77 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1547
1552
|
* - **Transcript starts empty**: nothing on screen accounts for the previous total, so the
|
|
1548
1553
|
* whole of it becomes banked. Without this the next turn would rewrite the row downwards
|
|
1549
1554
|
* to just that turn, losing the project's earlier spend.
|
|
1555
|
+
*
|
|
1556
|
+
* The baseline is deliberately NOT element state. This element is remounted repeatedly inside
|
|
1557
|
+
* a single session (the host re-rendering its layout, popping out, docking) and a fresh
|
|
1558
|
+
* instance re-deriving would take the second branch against rows that already hold the live
|
|
1559
|
+
* transcript — banking the session as its own baseline. Keying on the session instead means a
|
|
1560
|
+
* remount inherits the baseline that transcript was paired with.
|
|
1550
1561
|
*/
|
|
1551
1562
|
private bankedUsageFor(projectKey: string): AggregateUsage {
|
|
1552
|
-
|
|
1563
|
+
const resolved = getBankedBaseline(projectKey);
|
|
1564
|
+
if (resolved) return resolved;
|
|
1553
1565
|
const existing = this.costSessionHistory.find((r) => r.projectKey === projectKey);
|
|
1554
1566
|
// `persistenceActive` is what decides whether the transcript speaks for the project's
|
|
1555
1567
|
// history: it is on only when a provider is wired AND the user is remembering the chat.
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
return
|
|
1568
|
+
const banked = resolveBankedUsage(existing, this.persistenceActive);
|
|
1569
|
+
setBankedBaseline(projectKey, banked);
|
|
1570
|
+
return banked;
|
|
1559
1571
|
}
|
|
1560
1572
|
|
|
1561
1573
|
/**
|
|
1562
|
-
*
|
|
1563
|
-
*
|
|
1574
|
+
* Re-baseline this session against an emptied transcript: what came before, plus what the
|
|
1575
|
+
* transcript being discarded proved. `discarded` must be read BEFORE the live figures are
|
|
1576
|
+
* reset, since that is the only record of the latter.
|
|
1564
1577
|
*
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1578
|
+
* Not the same as dropping the baseline. Dropping it re-derives through `persistenceActive`,
|
|
1579
|
+
* and with persistence ON that carries the row's *old* banked figure forward — discarding
|
|
1580
|
+
* every turn between the last baseline and the clear. The transcript is gone either way, so
|
|
1581
|
+
* the branch that reads it as authoritative no longer applies.
|
|
1582
|
+
*
|
|
1583
|
+
* Derived from the memoised baseline and the live figure rather than from the project's row,
|
|
1584
|
+
* because a row is only as current as the last `finalizeCostSession` — which skips its write
|
|
1585
|
+
* while the ledger is unloaded, and while the session has no usage yet.
|
|
1568
1586
|
*/
|
|
1569
|
-
private
|
|
1570
|
-
this
|
|
1571
|
-
this
|
|
1587
|
+
private rebaselineOnClearedTranscript(key: string, discarded: AggregateUsage): void {
|
|
1588
|
+
// Already resolved for this session? Then the new baseline is fully determined without
|
|
1589
|
+
// consulting the ledger at all — `bankedUsageFor` would short-circuit on this same value — so
|
|
1590
|
+
// answer before the guard below, which would otherwise discard a figure that is both correct
|
|
1591
|
+
// and independent of the rows. Losing it costs real spend: with a row at `usage: 120,
|
|
1592
|
+
// banked: 90` and 30 on screen, a clear during a reload would drop the memoised 90, and the
|
|
1593
|
+
// next turn would re-derive `row.banked` and rewrite the row BACKWARDS from 120 to 95.
|
|
1594
|
+
const resolved = getBankedBaseline(key);
|
|
1595
|
+
if (resolved) {
|
|
1596
|
+
setBankedBaseline(key, addUsage(resolved, discarded));
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// Nothing memoised, so a baseline can only come from the rows — and a stamped one is STICKY
|
|
1601
|
+
// (`bankedUsageFor` short-circuits on any stored entry, and a zero usage is a *resolved* value
|
|
1602
|
+
// there, not "unresolved"). So unless the rows are genuinely known, do not guess:
|
|
1603
|
+
//
|
|
1604
|
+
// - `enabled` false: `reloadCostSessionHistory` sets `_costHistoryLoaded` TRUE over a
|
|
1605
|
+
// deliberately empty list, meaning "nothing will be loaded" rather than "rows are known".
|
|
1606
|
+
// The flag alone therefore is not enough. Reachable because `enabled` is
|
|
1607
|
+
// `cost?.enabled === true` — false for an unbound `chatConfig`, and for any host resolving
|
|
1608
|
+
// it from an async setting, exactly as `chatConfigChanged` already expects.
|
|
1609
|
+
// - Provider read in flight: the list is emptied and the flag cleared synchronously, on
|
|
1610
|
+
// connect and again on every `chatConfig` rebind.
|
|
1611
|
+
// - Unreadable: the rows we hold are a fallback, not the ledger.
|
|
1612
|
+
//
|
|
1613
|
+
// In all three, pinning would bury this project's real total the moment it arrived — a later
|
|
1614
|
+
// load does NOT invalidate baselines (deliberately, see `reloadCostSessionHistory`), so the
|
|
1615
|
+
// next write would replace the row off the pinned figure and lose everything before it.
|
|
1616
|
+
//
|
|
1617
|
+
// Dropping instead leaves the next write to re-derive from rows that actually arrived. It
|
|
1618
|
+
// under-banks `discarded` in that window — a bounded loss, and what every clear did before
|
|
1619
|
+
// this method existed. Nothing is written while disabled, so there is no banked value to
|
|
1620
|
+
// preserve in that case either.
|
|
1621
|
+
if (!this.resolvedCostHistory.enabled || !this._costHistoryLoaded || this._ledgerUnreadable) {
|
|
1622
|
+
deleteBankedBaseline(key);
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
setBankedBaseline(key, addUsage(this.bankedUsageFor(key), discarded));
|
|
1572
1626
|
}
|
|
1573
1627
|
|
|
1574
1628
|
showConfirmationDialog(
|
|
@@ -1593,24 +1647,26 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1593
1647
|
|
|
1594
1648
|
/** Remove one entry from persisted build history. */
|
|
1595
1649
|
removeCostSessionFromHistory(id: string): void {
|
|
1596
|
-
// Deleting
|
|
1597
|
-
//
|
|
1598
|
-
//
|
|
1650
|
+
// Deleting a row discards its banked spend by intent, so drop the baseline too —
|
|
1651
|
+
// otherwise the next turn would rewrite the row we just removed, restoring the total the
|
|
1652
|
+
// user asked to be rid of.
|
|
1599
1653
|
//
|
|
1600
|
-
// Resolve the row first: `id` is a row id while
|
|
1601
|
-
// only for rows this element wrote (`finalizeCostSession` sets `id: projectKey`); a
|
|
1602
|
-
// a host provider can carry any id, and
|
|
1603
|
-
//
|
|
1654
|
+
// Resolve the row first: `id` is a row id while baselines are keyed by projectKey. They
|
|
1655
|
+
// match only for rows this element wrote (`finalizeCostSession` sets `id: projectKey`); a
|
|
1656
|
+
// row from a host provider can carry any id, and deleting by the wrong namespace would
|
|
1657
|
+
// silently leave the baseline in place.
|
|
1604
1658
|
const removed = this.costSessionHistory.find((record) => record.id === id);
|
|
1605
|
-
if (removed
|
|
1659
|
+
if (removed) deleteBankedBaseline(removed.projectKey);
|
|
1606
1660
|
this.persistCostHistory(this.costSessionHistory.filter((record) => record.id !== id));
|
|
1607
1661
|
}
|
|
1608
1662
|
|
|
1609
1663
|
/** Clear all persisted build history for this assistant scope. */
|
|
1610
1664
|
clearCostSessionHistory(): void {
|
|
1611
|
-
// Every row is gone, so nothing is banked any more — without this the
|
|
1612
|
-
//
|
|
1613
|
-
|
|
1665
|
+
// Every row is gone, so nothing is banked any more — without this the next turn would
|
|
1666
|
+
// resurrect the pre-clear total. Dropped per row rather than for the active session alone,
|
|
1667
|
+
// so a project whose row was cleared while another session was displaying it does not keep
|
|
1668
|
+
// a baseline pointing at spend that no longer exists anywhere.
|
|
1669
|
+
for (const record of this.costSessionHistory) deleteBankedBaseline(record.projectKey);
|
|
1614
1670
|
// Provider-backed: write an empty ledger through. The `localStorage` path instead
|
|
1615
1671
|
// removes its key outright rather than leaving an empty array behind.
|
|
1616
1672
|
if (this.costHistoryProvider()) {
|
|
@@ -1843,10 +1899,9 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1843
1899
|
* (same key) from a project switch (key already flipped) — see `syncActiveCostSessionTitle`.
|
|
1844
1900
|
*/
|
|
1845
1901
|
private _activeCostSessionKey?: string;
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
private _bankedFor: string | undefined;
|
|
1902
|
+
// NOTE: the pre-transcript ("banked") spend is deliberately NOT held here. It is keyed by
|
|
1903
|
+
// session in `banked-usage-baselines` so the many element instances a single session sees —
|
|
1904
|
+
// layout re-renders, popout, docking — share one baseline. See `bankedUsageFor`.
|
|
1850
1905
|
/**
|
|
1851
1906
|
* Whether the row list reflects storage yet. Gates cost writes so a project's banked
|
|
1852
1907
|
* spend is never derived from a ledger that simply hasn't arrived.
|
|
@@ -2663,6 +2718,10 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
2663
2718
|
// keeping the high-water `lastEventIndex` would then skip every event of the
|
|
2664
2719
|
// resumed session (GENC-1351 §5.12 / #9). Clear (same-session) still uses reset.
|
|
2665
2720
|
deleteDiagnosticsCursorsFor(oldKey);
|
|
2721
|
+
// The cost baseline is paired with the transcript in the store just disposed, so it goes
|
|
2722
|
+
// with it (GENC-1475). A later switch back re-restores from the provider — or starts empty
|
|
2723
|
+
// where it does not — and re-derives from the ledger rows accordingly.
|
|
2724
|
+
deleteBankedBaseline(oldKey);
|
|
2666
2725
|
clearMetaSession(oldKey);
|
|
2667
2726
|
// Activate the incoming session. Swapping the observable `_sessionRef` re-points
|
|
2668
2727
|
// every store-backed getter binding at the new store; `activateSession` rebuilds
|
|
@@ -3272,9 +3331,16 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
3272
3331
|
} catch (e) {
|
|
3273
3332
|
logger.error('Session clear: agent serialize() failed:', e);
|
|
3274
3333
|
}
|
|
3334
|
+
// Read BEFORE `resetSession()` zeroes it: this is what the transcript about to be discarded
|
|
3335
|
+
// proved, and nothing else records it. Banking it is what stops the next turn rewriting the
|
|
3336
|
+
// project's row down to itself and losing the spend up to the clear (GENC-1475).
|
|
3337
|
+
const discarded = this.sessionUsage;
|
|
3275
3338
|
if (this.driver) this.teardownDriver(key);
|
|
3276
3339
|
this._sessionRef?.actions.aiAssistant.resetSession();
|
|
3277
|
-
if (key)
|
|
3340
|
+
if (key) {
|
|
3341
|
+
this.rebaselineOnClearedTranscript(key, discarded);
|
|
3342
|
+
this.rebuildDriver(key);
|
|
3343
|
+
}
|
|
3278
3344
|
// Restore-core is owned by the shared per-session persister (GENC-1351 §5.10):
|
|
3279
3345
|
// it drops the snapshot + resets the carry-forward cache. Diagnostics is
|
|
3280
3346
|
// append-only and handled separately below.
|
|
@@ -3396,10 +3462,35 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
3396
3462
|
return this.settingsCostCurrentSlotted.length > 0;
|
|
3397
3463
|
}
|
|
3398
3464
|
|
|
3465
|
+
/**
|
|
3466
|
+
* Whether the build-history section has anything in it — the built-in row list, or a host's own
|
|
3467
|
+
* markup in the `settings-cost-history` slot.
|
|
3468
|
+
*
|
|
3469
|
+
* Slotted content is checked FIRST, ahead of `enabled`. That flag governs the *built-in* ledger:
|
|
3470
|
+
* it gates the row writes, the reload, and the lifetime sum — and it defaults to `false`
|
|
3471
|
+
* (`cost?.enabled === true`), while the Usage tab itself shows for `showCost`/`showTokens`, which
|
|
3472
|
+
* default true. So a host can legitimately render its own build history into the slot and never
|
|
3473
|
+
* opt into our `localStorage` ledger. Short-circuiting on `enabled` above the slot check hid that
|
|
3474
|
+
* host's markup outright, since the slot is the only thing in the container.
|
|
3475
|
+
*/
|
|
3399
3476
|
@volatile
|
|
3400
3477
|
get settingsCostHistorySectionVisible(): boolean {
|
|
3478
|
+
if (this.settingsCostHistorySlotted.length > 0) return true;
|
|
3401
3479
|
if (!this.resolvedCostHistory.enabled) return false;
|
|
3402
|
-
return this.costSessionHistory.length > 0
|
|
3480
|
+
return this.costSessionHistory.length > 0;
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
/**
|
|
3484
|
+
* Whether the "Build history" heading has anything under it.
|
|
3485
|
+
*
|
|
3486
|
+
* Broader than {@link FoundationAiAssistant.settingsCostHistorySectionVisible} because the
|
|
3487
|
+
* heading also covers the lifetime total, which renders before the first row exists (rows are
|
|
3488
|
+
* written as spend accrues, the lifetime section only needs history switched on). Gating the
|
|
3489
|
+
* heading on the rows alone left that total sitting under nothing.
|
|
3490
|
+
*/
|
|
3491
|
+
@volatile
|
|
3492
|
+
get settingsCostHistoryHeadingVisible(): boolean {
|
|
3493
|
+
return this.settingsCostHistorySectionVisible || this.settingsCostLifetimeVisible;
|
|
3403
3494
|
}
|
|
3404
3495
|
|
|
3405
3496
|
/** Whether the built-in Usage tab summary block has anything to show. */
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { AggregateUsage } from '@genesislcap/foundation-ai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-session banked baselines for the cost ledger (GENC-1475) — the spend a project had
|
|
5
|
+
* accrued *before* the transcript currently in memory. Module-level and keyed by the session
|
|
6
|
+
* `stateKey`, exactly like the diagnostics cursors and the meta-event buffer.
|
|
7
|
+
*
|
|
8
|
+
* The lifetime is the point of this module. A baseline is only meaningful paired with the
|
|
9
|
+
* transcript it complements: `row.usage = baseline + whatever that transcript proves`. The
|
|
10
|
+
* transcript lives in the per-key session store, so the baseline must live and die with that
|
|
11
|
+
* store — which means module state keyed the same way, NOT element state.
|
|
12
|
+
*
|
|
13
|
+
* Held on the element it silently double-counted. Element instances are created and destroyed
|
|
14
|
+
* many times inside one session (the assistant is remounted as the host re-renders its layout,
|
|
15
|
+
* and a popped-out bubble and a docked panel can be wired to the same session at once), and each
|
|
16
|
+
* new instance re-derived the baseline from the ledger rows. By then those rows contain the
|
|
17
|
+
* instance-before-last's own upserts, whose `usage` already includes the live transcript — so
|
|
18
|
+
* where the transcript is NOT restored on load (`resolveBankedUsage`'s "bank the whole prior
|
|
19
|
+
* total" branch, i.e. persistence off) the live session was banked as its own baseline. Lifetime
|
|
20
|
+
* then jumped by the whole session-to-date on every remount while the live figure kept climbing
|
|
21
|
+
* normally, which is the "the two totals went up by different amounts" report.
|
|
22
|
+
*
|
|
23
|
+
* Module state dies with the JS realm, so a real page reload starts clean — exactly right, since
|
|
24
|
+
* that is also when the transcript starts over.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
const baselinesByKey = new Map<string, AggregateUsage>();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The baseline resolved for `key`, or `undefined` when this session has not resolved one yet.
|
|
32
|
+
*
|
|
33
|
+
* `undefined` rather than an empty usage: "nothing banked" is a legitimate resolved value (a
|
|
34
|
+
* project's first ever session), and it must not be mistaken for "not resolved", or every write
|
|
35
|
+
* would re-derive and hit the double-count above.
|
|
36
|
+
*
|
|
37
|
+
* Returns a copy — see `setBankedBaseline` for why both ends of this map are cloned.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export function getBankedBaseline(key: string): AggregateUsage | undefined {
|
|
42
|
+
const usage = baselinesByKey.get(key);
|
|
43
|
+
return usage ? { ...usage } : undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Record the baseline for `key`, replacing any previous one.
|
|
48
|
+
*
|
|
49
|
+
* Stores a copy, and `getBankedBaseline` hands back a copy, so nothing outside this module can
|
|
50
|
+
* reach the object the map holds. Both ends have to clone, because a baseline is aliased at both:
|
|
51
|
+
* `resolveBankedUsage` returns a *reference into a ledger row* (`row.banked` or `row.usage`),
|
|
52
|
+
* which is what gets stored here, and the caller then assigns what it reads back onto the NEXT
|
|
53
|
+
* row's `banked` field. Uncloned, one object is reachable from page-global state, two rows, and —
|
|
54
|
+
* since the row list is public and gets handed to a host's `saveCostHistory` — from host code.
|
|
55
|
+
* Whoever mutated it would silently rewrite this project's baseline for the rest of the page.
|
|
56
|
+
*
|
|
57
|
+
* A shallow spread is a complete copy: `AggregateUsage` is five flat numbers, no nested state.
|
|
58
|
+
*
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
export function setBankedBaseline(key: string, usage: AggregateUsage): void {
|
|
62
|
+
baselinesByKey.set(key, { ...usage });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Drop the baseline for `key`, so the next write re-derives it from the ledger rows.
|
|
67
|
+
*
|
|
68
|
+
* For when the pairing is broken deliberately at the *row* end — the row this baseline came from
|
|
69
|
+
* has been deleted, or the whole ledger cleared — and for a session teardown, where the store
|
|
70
|
+
* (and with it the transcript) is disposed and a later return re-restores from scratch.
|
|
71
|
+
*
|
|
72
|
+
* NOT for a mid-session reload of the rows: those rows already include this session's own
|
|
73
|
+
* upserts, so re-deriving from them folds the live transcript into its own baseline.
|
|
74
|
+
*
|
|
75
|
+
* @internal
|
|
76
|
+
*/
|
|
77
|
+
export function deleteBankedBaseline(key: string): void {
|
|
78
|
+
baselinesByKey.delete(key);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Forget every baseline — the page-load boundary, which production gets for free from module
|
|
83
|
+
* state dying with the realm. Exists so tests can model separate page loads (and so one test's
|
|
84
|
+
* session key cannot leak a baseline into the next).
|
|
85
|
+
*
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
export function resetBankedBaselines(): void {
|
|
89
|
+
baselinesByKey.clear();
|
|
90
|
+
}
|