@genesislcap/ai-assistant 15.7.3-alpha-e7d4aa5.0 → 15.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/ai-assistant.api.json +191 -1
  2. package/dist/ai-assistant.d.ts +155 -1
  3. package/dist/custom-elements.json +114 -2
  4. package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
  5. package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts +2 -0
  6. package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts.map +1 -0
  7. package/dist/dts/components/settings-modal/settings-modal.template.d.ts +22 -0
  8. package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
  9. package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts +2 -0
  10. package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts.map +1 -0
  11. package/dist/dts/main/budget-meter.test.d.ts +2 -0
  12. package/dist/dts/main/budget-meter.test.d.ts.map +1 -0
  13. package/dist/dts/main/main.d.ts +113 -2
  14. package/dist/dts/main/main.d.ts.map +1 -1
  15. package/dist/dts/main/main.types.d.ts +25 -0
  16. package/dist/dts/main/main.types.d.ts.map +1 -1
  17. package/dist/dts/state/ai-assistant-slice.d.ts +47 -0
  18. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
  19. package/dist/dts/state/session-store.d.ts +4 -0
  20. package/dist/dts/state/session-store.d.ts.map +1 -1
  21. package/dist/dts/utils/format-usd.d.ts +24 -0
  22. package/dist/dts/utils/format-usd.d.ts.map +1 -0
  23. package/dist/esm/components/settings-modal/settings-modal.styles.js +33 -3
  24. package/dist/esm/components/settings-modal/settings-modal.styles.test.js +80 -0
  25. package/dist/esm/components/settings-modal/settings-modal.template.js +64 -14
  26. package/dist/esm/components/settings-modal/settings-modal.template.test.js +91 -0
  27. package/dist/esm/main/budget-meter.test.js +317 -0
  28. package/dist/esm/main/main.js +189 -3
  29. package/dist/esm/state/ai-assistant-slice.js +38 -0
  30. package/dist/esm/utils/format-usd.js +23 -0
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/docs/migration-GENC-1464.md +63 -0
  33. package/docs/styling.md +23 -0
  34. package/package.json +17 -17
  35. package/src/components/settings-modal/settings-modal.styles.test.ts +94 -0
  36. package/src/components/settings-modal/settings-modal.styles.ts +33 -3
  37. package/src/components/settings-modal/settings-modal.template.test.ts +124 -0
  38. package/src/components/settings-modal/settings-modal.template.ts +82 -13
  39. package/src/main/budget-meter.test.ts +427 -0
  40. package/src/main/main.ts +198 -3
  41. package/src/main/main.types.ts +26 -0
  42. package/src/state/ai-assistant-slice.ts +74 -0
  43. package/src/utils/format-usd.ts +26 -0
@@ -11515,7 +11515,7 @@
11515
11515
  {
11516
11516
  "kind": "Property",
11517
11517
  "canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#settingsModelSectionVisible:member",
11518
- "docComment": "/**\n * Whether the settings modal AI Model section has slotted app content or context usage.\n */\n",
11518
+ "docComment": "/**\n * Whether the settings modal AI Model section has slotted app content, context usage or budget figures.\n */\n",
11519
11519
  "excerptTokens": [
11520
11520
  {
11521
11521
  "kind": "Content",
@@ -11749,6 +11749,70 @@
11749
11749
  "isAbstract": false,
11750
11750
  "name": "setVendorBlocked"
11751
11751
  },
11752
+ {
11753
+ "kind": "Method",
11754
+ "canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#setVendorBudget:member(1)",
11755
+ "docComment": "/**\n * Feed (or clear) one vendor's AI-spend figures for the settings-modal budget meter — the numeric companion to {@link FoundationAiAssistant.setVendorBlocked}, and like it a host-driven write: the element meters nothing itself.\n *\n * The meter renders a row for exactly the vendors that currently HAVE figures. An **unlimited** vendor (the budget endpoint reports `unlimited: true` / `limitUsd: null`) is expressed by never feeding it — there is deliberately no \"unlimited\" figure shape, because a bar with no cap has nothing truthful to fill to. `spentUsd` MAY exceed `budgetUsd` (the last allowed call can overshoot the cap): the row's text shows the real figures and only the bar clamps.\n *\n * Returns `false` — and warns once, not per call — instead of throwing when `vendor` is not a budgeted vendor (`BUDGETED_VENDORS`, the proxy's own metered list) or either figure is not a finite number `>= 0`. Invalid input changes no state. Idempotent: re-feeding a vendor's current figures is a no-op, so hosts can re-feed unconditionally after every turn.\n *\n * `false` ALSO means \"not stored, because the element is not ready yet\": a feed that lands before the element has its session store (a host calling ahead of the first append, or during a pop-out remount) stores nothing and says so, rather than reporting a write that never happened. That case is transient and needs no host handling — the documented per-turn re-feed is what recovers it — so it is deliberately NOT warned about; the warn-once channel is for garbage input only.\n *\n * `figures` is nullish-checked at run time, so `undefined` behaves exactly like `null` and CLEARS that vendor's row. Untyped JS hosts feed this straight from `GET /api/budget`, where a per-vendor lookup that misses yields `undefined`, not `null` — and \"no figures\" coherently means \"no row\". The `| null` parameter type is unchanged for typed callers; the runtime is deliberately the more forgiving of the two.\n *\n * The intended feed is the platform budget endpoint:\n * ```ts\n * // `vendors` is an OBJECT keyed by vendor id, not an array.\n * const { vendors } = await (await fetch('/api/budget')).json();\n * for (const v of Object.values(vendors)) {\n * if (v.unlimited || v.limitUsd == null) {\n * assistantEl.setVendorBudget(v.vendor, null); // clear stale figures after a cap is lifted\n * continue;\n * }\n * assistantEl.setVendorBudget(v.vendor, { budgetUsd: v.limitUsd, spentUsd: v.spentUsd });\n * }\n * ```\n *\n * Re-run it on mount and after each turn. Scope and lifetime follow the rest of the session slice (per `stateKey`, survives pop-in/out and \"Clear\" / \"New chat\") — except that the figures are NOT persisted to the session snapshot: the server is authoritative and the host re-feeds at boot, so a persisted copy could only show stale dollars after a reload.\n *\n * @beta\n */\n",
11756
+ "excerptTokens": [
11757
+ {
11758
+ "kind": "Content",
11759
+ "text": "setVendorBudget(vendor: "
11760
+ },
11761
+ {
11762
+ "kind": "Content",
11763
+ "text": "string"
11764
+ },
11765
+ {
11766
+ "kind": "Content",
11767
+ "text": ", figures: "
11768
+ },
11769
+ {
11770
+ "kind": "Content",
11771
+ "text": "{\n budgetUsd: number;\n spentUsd: number;\n } | null"
11772
+ },
11773
+ {
11774
+ "kind": "Content",
11775
+ "text": "): "
11776
+ },
11777
+ {
11778
+ "kind": "Content",
11779
+ "text": "boolean"
11780
+ },
11781
+ {
11782
+ "kind": "Content",
11783
+ "text": ";"
11784
+ }
11785
+ ],
11786
+ "isStatic": false,
11787
+ "returnTypeTokenRange": {
11788
+ "startIndex": 5,
11789
+ "endIndex": 6
11790
+ },
11791
+ "releaseTag": "Beta",
11792
+ "isProtected": false,
11793
+ "overloadIndex": 1,
11794
+ "parameters": [
11795
+ {
11796
+ "parameterName": "vendor",
11797
+ "parameterTypeTokenRange": {
11798
+ "startIndex": 1,
11799
+ "endIndex": 2
11800
+ },
11801
+ "isOptional": false
11802
+ },
11803
+ {
11804
+ "parameterName": "figures",
11805
+ "parameterTypeTokenRange": {
11806
+ "startIndex": 3,
11807
+ "endIndex": 4
11808
+ },
11809
+ "isOptional": false
11810
+ }
11811
+ ],
11812
+ "isOptional": false,
11813
+ "isAbstract": false,
11814
+ "name": "setVendorBudget"
11815
+ },
11752
11816
  {
11753
11817
  "kind": "Property",
11754
11818
  "canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#showAgentSwitchIndicator:member",
@@ -16544,6 +16608,132 @@
16544
16608
  ],
16545
16609
  "extendsTokenRanges": []
16546
16610
  },
16611
+ {
16612
+ "kind": "Interface",
16613
+ "canonicalReference": "@genesislcap/ai-assistant!SettingsBudgetRow:interface",
16614
+ "docComment": "/**\n * One rendered row of the settings-modal budget meter (GENC-1464) — a vendor that currently has budget figures, pre-formatted so the template stays a pure binding layer (and so tests can pin the exact text a user reads).\n *\n * @beta\n */\n",
16615
+ "excerptTokens": [
16616
+ {
16617
+ "kind": "Content",
16618
+ "text": "export interface SettingsBudgetRow "
16619
+ }
16620
+ ],
16621
+ "fileUrlPath": "src/main/main.types.ts",
16622
+ "releaseTag": "Beta",
16623
+ "name": "SettingsBudgetRow",
16624
+ "preserveMemberOrder": false,
16625
+ "members": [
16626
+ {
16627
+ "kind": "PropertySignature",
16628
+ "canonicalReference": "@genesislcap/ai-assistant!SettingsBudgetRow#barValue:member",
16629
+ "docComment": "/**\n * The bar's value, clamped to `0..100` — a progress bar past its end renders nonsense, so only the bar clamps (mirroring the context-usage indicator's `Math.min(100, …)`).\n */\n",
16630
+ "excerptTokens": [
16631
+ {
16632
+ "kind": "Content",
16633
+ "text": "barValue: "
16634
+ },
16635
+ {
16636
+ "kind": "Content",
16637
+ "text": "number"
16638
+ },
16639
+ {
16640
+ "kind": "Content",
16641
+ "text": ";"
16642
+ }
16643
+ ],
16644
+ "isReadonly": false,
16645
+ "isOptional": false,
16646
+ "releaseTag": "Beta",
16647
+ "name": "barValue",
16648
+ "propertyTypeTokenRange": {
16649
+ "startIndex": 1,
16650
+ "endIndex": 2
16651
+ }
16652
+ },
16653
+ {
16654
+ "kind": "PropertySignature",
16655
+ "canonicalReference": "@genesislcap/ai-assistant!SettingsBudgetRow#figures:member",
16656
+ "docComment": "/**\n * The real figures — `$45.00 / $50.00 (90%)`. The percentage is NOT clamped: spend can genuinely overshoot the cap (the last allowed call may cross it), and the text tells the truth about it.\n */\n",
16657
+ "excerptTokens": [
16658
+ {
16659
+ "kind": "Content",
16660
+ "text": "figures: "
16661
+ },
16662
+ {
16663
+ "kind": "Content",
16664
+ "text": "string"
16665
+ },
16666
+ {
16667
+ "kind": "Content",
16668
+ "text": ";"
16669
+ }
16670
+ ],
16671
+ "isReadonly": false,
16672
+ "isOptional": false,
16673
+ "releaseTag": "Beta",
16674
+ "name": "figures",
16675
+ "propertyTypeTokenRange": {
16676
+ "startIndex": 1,
16677
+ "endIndex": 2
16678
+ }
16679
+ },
16680
+ {
16681
+ "kind": "PropertySignature",
16682
+ "canonicalReference": "@genesislcap/ai-assistant!SettingsBudgetRow#label:member",
16683
+ "docComment": "/**\n * Display name from `VENDOR_LABELS`, e.g. `Anthropic`.\n */\n",
16684
+ "excerptTokens": [
16685
+ {
16686
+ "kind": "Content",
16687
+ "text": "label: "
16688
+ },
16689
+ {
16690
+ "kind": "Content",
16691
+ "text": "string"
16692
+ },
16693
+ {
16694
+ "kind": "Content",
16695
+ "text": ";"
16696
+ }
16697
+ ],
16698
+ "isReadonly": false,
16699
+ "isOptional": false,
16700
+ "releaseTag": "Beta",
16701
+ "name": "label",
16702
+ "propertyTypeTokenRange": {
16703
+ "startIndex": 1,
16704
+ "endIndex": 2
16705
+ }
16706
+ },
16707
+ {
16708
+ "kind": "PropertySignature",
16709
+ "canonicalReference": "@genesislcap/ai-assistant!SettingsBudgetRow#vendor:member",
16710
+ "docComment": "/**\n * The vendor the row describes (an `AIProviderType` member).\n */\n",
16711
+ "excerptTokens": [
16712
+ {
16713
+ "kind": "Content",
16714
+ "text": "vendor: "
16715
+ },
16716
+ {
16717
+ "kind": "Content",
16718
+ "text": "string"
16719
+ },
16720
+ {
16721
+ "kind": "Content",
16722
+ "text": ";"
16723
+ }
16724
+ ],
16725
+ "isReadonly": false,
16726
+ "isOptional": false,
16727
+ "releaseTag": "Beta",
16728
+ "name": "vendor",
16729
+ "propertyTypeTokenRange": {
16730
+ "startIndex": 1,
16731
+ "endIndex": 2
16732
+ }
16733
+ }
16734
+ ],
16735
+ "extendsTokenRanges": []
16736
+ },
16547
16737
  {
16548
16738
  "kind": "Variable",
16549
16739
  "canonicalReference": "@genesislcap/ai-assistant!sparkleIconSvg:var",
@@ -1143,6 +1143,21 @@ declare type BudgetDetail = NonNullable<Extract<ChatDriverResult, {
1143
1143
  reason: 'done';
1144
1144
  }>['budget']>;
1145
1145
 
1146
+ /**
1147
+ * Whole-number percentage of a budget consumed, for the meter (GENC-1464).
1148
+ *
1149
+ * Deliberately unclamped — spend can genuinely overshoot the cap (the last
1150
+ * allowed call may cross it), and the meter's TEXT shows the truth; only the
1151
+ * bar clamps, at the render site. The one special case is a `$0` cap, which the
1152
+ * platform treats as "no spend allowed" (not "unlimited"): it has no headroom
1153
+ * by definition, so the meter reads full rather than dividing by zero.
1154
+ *
1155
+ * Exported for the unit test that pins the arithmetic; not element API.
1156
+ *
1157
+ * @internal
1158
+ */
1159
+ export declare function budgetPercentOf(figures: VendorBudgetFigures): number;
1160
+
1146
1161
  export { CachePolicy }
1147
1162
 
1148
1163
  /**
@@ -2555,6 +2570,75 @@ export declare class FoundationAiAssistant extends GenesisElement {
2555
2570
  * @beta
2556
2571
  */
2557
2572
  setVendorBlocked(vendor: AIProviderType, blocked: boolean, reason?: string | null): void;
2573
+ /**
2574
+ * One warning per element for garbage fed to
2575
+ * {@link FoundationAiAssistant.setVendorBudget}. Once, not per call, because
2576
+ * the documented feed re-runs after every turn — a host bug would otherwise
2577
+ * repeat the same warning on every send for the life of the page.
2578
+ */
2579
+ private vendorBudgetWarningIssued;
2580
+ private warnVendorBudgetOnce;
2581
+ /**
2582
+ * Feed (or clear) one vendor's AI-spend figures for the settings-modal budget
2583
+ * meter — the numeric companion to
2584
+ * {@link FoundationAiAssistant.setVendorBlocked}, and like it a host-driven
2585
+ * write: the element meters nothing itself.
2586
+ *
2587
+ * The meter renders a row for exactly the vendors that currently HAVE
2588
+ * figures. An **unlimited** vendor (the budget endpoint reports
2589
+ * `unlimited: true` / `limitUsd: null`) is expressed by never feeding it —
2590
+ * there is deliberately no "unlimited" figure shape, because a bar with no
2591
+ * cap has nothing truthful to fill to. `spentUsd` MAY exceed `budgetUsd`
2592
+ * (the last allowed call can overshoot the cap): the row's text shows the
2593
+ * real figures and only the bar clamps.
2594
+ *
2595
+ * Returns `false` — and warns once, not per call — instead of throwing when
2596
+ * `vendor` is not a budgeted vendor (`BUDGETED_VENDORS`, the proxy's own
2597
+ * metered list) or either figure is not a finite number `>= 0`. Invalid input
2598
+ * changes no state. Idempotent: re-feeding a vendor's current figures is a
2599
+ * no-op, so hosts can re-feed unconditionally after every turn.
2600
+ *
2601
+ * `false` ALSO means "not stored, because the element is not ready yet": a
2602
+ * feed that lands before the element has its session store (a host calling
2603
+ * ahead of the first append, or during a pop-out remount) stores nothing and
2604
+ * says so, rather than reporting a write that never happened. That case is
2605
+ * transient and needs no host handling — the documented per-turn re-feed is
2606
+ * what recovers it — so it is deliberately NOT warned about; the warn-once
2607
+ * channel is for garbage input only.
2608
+ *
2609
+ * `figures` is nullish-checked at run time, so `undefined` behaves exactly
2610
+ * like `null` and CLEARS that vendor's row. Untyped JS hosts feed this
2611
+ * straight from `GET /api/budget`, where a per-vendor lookup that misses
2612
+ * yields `undefined`, not `null` — and "no figures" coherently means "no
2613
+ * row". The `| null` parameter type is unchanged for typed callers; the
2614
+ * runtime is deliberately the more forgiving of the two.
2615
+ *
2616
+ * The intended feed is the platform budget endpoint:
2617
+ *
2618
+ * ```ts
2619
+ * // `vendors` is an OBJECT keyed by vendor id, not an array.
2620
+ * const { vendors } = await (await fetch('/api/budget')).json();
2621
+ * for (const v of Object.values(vendors)) {
2622
+ * if (v.unlimited || v.limitUsd == null) {
2623
+ * assistantEl.setVendorBudget(v.vendor, null); // clear stale figures after a cap is lifted
2624
+ * continue;
2625
+ * }
2626
+ * assistantEl.setVendorBudget(v.vendor, { budgetUsd: v.limitUsd, spentUsd: v.spentUsd });
2627
+ * }
2628
+ * ```
2629
+ *
2630
+ * Re-run it on mount and after each turn. Scope and lifetime follow the rest
2631
+ * of the session slice (per `stateKey`, survives pop-in/out and "Clear" /
2632
+ * "New chat") — except that the figures are NOT persisted to the session
2633
+ * snapshot: the server is authoritative and the host re-feeds at boot, so a
2634
+ * persisted copy could only show stale dollars after a reload.
2635
+ *
2636
+ * @beta
2637
+ */
2638
+ setVendorBudget(vendor: string, figures: {
2639
+ budgetUsd: number;
2640
+ spentUsd: number;
2641
+ } | null): boolean;
2558
2642
  /** Whether this vendor's wall came from the sweep alone — see the slice's `sweptVendors`. */
2559
2643
  private isVendorSwept;
2560
2644
  /**
@@ -3448,10 +3532,37 @@ export declare class FoundationAiAssistant extends GenesisElement {
3448
3532
  }): boolean;
3449
3533
  /** Whether the picker toggle button should appear. Mirrors the picker's own visibility rule. */
3450
3534
  get agentPickerEnabled(): boolean;
3451
- /** Whether the settings modal AI Model section has slotted app content or context usage. */
3535
+ /** Whether the settings modal AI Model section has slotted app content, context usage or budget figures. */
3452
3536
  get settingsModelSectionVisible(): boolean;
3453
3537
  /** Built-in context window indicator (generic platform concern). */
3454
3538
  get settingsContextUsageVisible(): boolean;
3539
+ /**
3540
+ * Built-in per-vendor budget meter (GENC-1464) — the money sibling of
3541
+ * {@link FoundationAiAssistant.settingsContextUsageVisible}, with the same
3542
+ * shape of gate: not configured off, and there is data to show. "Data" here
3543
+ * is "at least one vendor has figures", so a host that feeds nothing (or only
3544
+ * unlimited vendors) gets no meter and no empty container — exactly the
3545
+ * context indicator's absent case.
3546
+ *
3547
+ * Answered from the RAW slice state rather than from
3548
+ * {@link FoundationAiAssistant.settingsBudgetRows}, whose `.length` would
3549
+ * format every row (a `formatUsd` pair and a `budgetPercentOf` each) only to
3550
+ * ask whether any exist. This is read several times per render pass — the
3551
+ * getter, the section gate, the template's `when` — so it stays as cheap as
3552
+ * the context sibling's `!= null`.
3553
+ *
3554
+ * @internal
3555
+ */
3556
+ get settingsBudgetUsageVisible(): boolean;
3557
+ /**
3558
+ * The meter's rows — one per vendor that currently has figures, in
3559
+ * `BUDGETED_VENDORS` order (the proxy's own metered list) so the rendering is
3560
+ * stable however the host ordered its feed calls. Pre-formatted; see
3561
+ * {@link SettingsBudgetRow} for why the text is unclamped while the bar is.
3562
+ *
3563
+ * @internal
3564
+ */
3565
+ get settingsBudgetRows(): readonly SettingsBudgetRow[];
3455
3566
  /** Whether the settings modal UI Builder section should render. */
3456
3567
  get settingsAppSectionVisible(): boolean;
3457
3568
  /** App toggles currently visible per each entry's `visible()` predicate. */
@@ -4356,6 +4467,32 @@ export declare interface SessionPreferences {
4356
4467
  animations?: string[];
4357
4468
  }
4358
4469
 
4470
+ /**
4471
+ * One rendered row of the settings-modal budget meter (GENC-1464) — a vendor
4472
+ * that currently has budget figures, pre-formatted so the template stays a pure
4473
+ * binding layer (and so tests can pin the exact text a user reads).
4474
+ *
4475
+ * @beta
4476
+ */
4477
+ export declare interface SettingsBudgetRow {
4478
+ /** The vendor the row describes (an `AIProviderType` member). */
4479
+ vendor: string;
4480
+ /** Display name from `VENDOR_LABELS`, e.g. `Anthropic`. */
4481
+ label: string;
4482
+ /**
4483
+ * The real figures — `$45.00 / $50.00 (90%)`. The percentage is NOT clamped:
4484
+ * spend can genuinely overshoot the cap (the last allowed call may cross it),
4485
+ * and the text tells the truth about it.
4486
+ */
4487
+ figures: string;
4488
+ /**
4489
+ * The bar's value, clamped to `0..100` — a progress bar past its end renders
4490
+ * nonsense, so only the bar clamps (mirroring the context-usage indicator's
4491
+ * `Math.min(100, …)`).
4492
+ */
4493
+ barValue: number;
4494
+ }
4495
+
4359
4496
  /**
4360
4497
  * Tab identifiers for the modal tab bar.
4361
4498
  *
@@ -4924,6 +5061,23 @@ export declare type UnresolvedToolInput = (ctx: UnresolvedToolContext) => string
4924
5061
  /** Person silhouette — default user avatar. */
4925
5062
  export declare const userIconSvg = "<svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\" fill=\"currentColor\"/></svg>";
4926
5063
 
5064
+ /**
5065
+ * One vendor's AI-spend figures, as fed by the host from the budget endpoint
5066
+ * (GENC-1464 — the settings-modal budget meter).
5067
+ *
5068
+ * Dollars, not a percentage: the meter's text shows the real figures (spend CAN
5069
+ * exceed the cap — the last allowed call may overshoot it), and only the bar
5070
+ * clamps. Storing a pre-computed percentage would destroy the overshoot.
5071
+ *
5072
+ * @beta
5073
+ */
5074
+ declare interface VendorBudgetFigures {
5075
+ /** The vendor's budget cap in USD. Finite and `>= 0`. */
5076
+ budgetUsd: number;
5077
+ /** Metered spend against that cap in USD. Finite and `>= 0`; may exceed the cap. */
5078
+ spentUsd: number;
5079
+ }
5080
+
4927
5081
  /**
4928
5082
  * A {@link SessionPersistenceProvider} backed by a Web Storage area. Both
4929
5083
  * `window.sessionStorage` and `window.localStorage` implement the `Storage`