@genesislcap/ai-assistant 15.33.1 → 15.34.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 (36) hide show
  1. package/dist/ai-assistant.api.json +1021 -6
  2. package/dist/ai-assistant.d.ts +209 -5
  3. package/dist/chat-driver.cjs +90 -0
  4. package/dist/chat-driver.cjs.map +3 -3
  5. package/dist/chat-driver.mjs +86 -0
  6. package/dist/chat-driver.mjs.map +3 -3
  7. package/dist/custom-elements.json +329 -3
  8. package/dist/dts/chat-driver-node.d.ts +2 -2
  9. package/dist/dts/chat-driver-node.d.ts.map +1 -1
  10. package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
  11. package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
  12. package/dist/dts/index.d.ts +2 -0
  13. package/dist/dts/index.d.ts.map +1 -1
  14. package/dist/dts/main/budget-controller.d.ts +38 -6
  15. package/dist/dts/main/budget-controller.d.ts.map +1 -1
  16. package/dist/dts/main/main.d.ts +22 -4
  17. package/dist/dts/main/main.d.ts.map +1 -1
  18. package/dist/dts/main/main.types.d.ts +25 -0
  19. package/dist/dts/main/main.types.d.ts.map +1 -1
  20. package/dist/dts/provider/tiered-provider-switcher.d.ts +123 -0
  21. package/dist/dts/provider/tiered-provider-switcher.d.ts.map +1 -0
  22. package/dist/dts/state/ai-assistant-slice.d.ts +34 -0
  23. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
  24. package/dist/dts/state/session-store.d.ts.map +1 -1
  25. package/dist/esm/chat-driver-node.js +11 -1
  26. package/dist/esm/components/settings-modal/settings-modal.styles.js +75 -0
  27. package/dist/esm/components/settings-modal/settings-modal.template.js +56 -4
  28. package/dist/esm/index.js +1 -0
  29. package/dist/esm/main/budget-controller.js +178 -6
  30. package/dist/esm/main/main.js +20 -0
  31. package/dist/esm/provider/tiered-provider-switcher.js +142 -0
  32. package/dist/esm/state/ai-assistant-slice.js +17 -3
  33. package/docs/styling.md +24 -0
  34. package/package.json +18 -17
  35. package/sandbox/sandbox.ts +52 -0
  36. package/scripts/smoke-chat-driver-node.mjs +86 -0
@@ -3,6 +3,9 @@ import type { AggregateUsage } from '@genesislcap/foundation-ai';
3
3
  import { AIProviderRegistry } from '@genesislcap/foundation-ai';
4
4
  import type { AIProviderRegistryStatusEntry } from '@genesislcap/foundation-ai';
5
5
  import { AIProviderType } from '@genesislcap/foundation-ai';
6
+ import { AITierId } from '@genesislcap/foundation-ai';
7
+ import { AITiers } from '@genesislcap/foundation-ai';
8
+ import { AITierVendor } from '@genesislcap/foundation-ai';
6
9
  import { BudgetExhaustedError } from '@genesislcap/foundation-ai';
7
10
  import type { CachePolicy } from '@genesislcap/foundation-ai';
8
11
  import type { ChatAttachment } from '@genesislcap/foundation-ai';
@@ -15,6 +18,7 @@ import type { ChatThinkingPolicy } from '@genesislcap/foundation-ai';
15
18
  import type { ChatToolChoice } from '@genesislcap/foundation-ai';
16
19
  import type { ChatToolDefinition } from '@genesislcap/foundation-ai';
17
20
  import type { ChatToolHandlers } from '@genesislcap/foundation-ai';
21
+ import { Container } from '@genesislcap/web-core';
18
22
  import { ElementStyles } from '@genesislcap/web-core';
19
23
  import { GenesisElement } from '@genesislcap/web-core';
20
24
  import type { InteractionRequestOptions } from '@genesislcap/foundation-ai';
@@ -3358,6 +3362,26 @@ export declare class FoundationAiAssistant extends GenesisElement {
3358
3362
  * }
3359
3363
  * ```
3360
3364
  *
3365
+ * A host that meters its spend by source can pass an optional `segments` breakdown, and
3366
+ * the vendor's bar is drawn stacked with a legend naming each one (NEW-15):
3367
+ *
3368
+ * ```ts
3369
+ * assistantEl.setVendorBudget('anthropic', {
3370
+ * budgetUsd: 50,
3371
+ * spentUsd: 12.5,
3372
+ * segments: [
3373
+ * { id: 'app', label: 'App', spentUsd: 9 },
3374
+ * { id: 'batch', label: 'Batch jobs', spentUsd: 3.5 },
3375
+ * ],
3376
+ * });
3377
+ * ```
3378
+ *
3379
+ * The labels are yours — the assistant has no vocabulary for what a host spends money on.
3380
+ * Segments may add up to less than `spentUsd` (the remainder draws unlabelled) but never
3381
+ * to more; a breakdown that does, or that is otherwise malformed, is dropped with a debug
3382
+ * log and the single bar renders. Omit `segments` entirely and nothing about the meter
3383
+ * changes.
3384
+ *
3361
3385
  * Re-run it on mount and after each turn. Scope and lifetime follow the rest
3362
3386
  * of the session slice (per `stateKey`, survives pop-in/out and "Clear" /
3363
3387
  * "New chat") — except that the figures are NOT persisted to the session
@@ -3366,10 +3390,7 @@ export declare class FoundationAiAssistant extends GenesisElement {
3366
3390
  *
3367
3391
  * @beta
3368
3392
  */
3369
- setVendorBudget(vendor: string, figures: {
3370
- budgetUsd: number;
3371
- spentUsd: number;
3372
- } | null): boolean;
3393
+ setVendorBudget(vendor: string, figures: VendorBudgetFigures | null): boolean;
3373
3394
  /**
3374
3395
  * When budget figures last arrived — from the host's
3375
3396
  * {@link FoundationAiAssistant.setVendorBudget} or the wall latch's own feed —
@@ -5232,6 +5253,22 @@ declare type ProviderRefusedDetail = NonNullable<Extract<ChatDriverResult, {
5232
5253
  reason: 'done';
5233
5254
  }>['providerRefused']>;
5234
5255
 
5256
+ /**
5257
+ * Build a {@link TieredAIProviderSwitcher} — which registers the tier-shaped provider
5258
+ * registry — and put it on the {@link (AIProviderSwitcher:variable)} token. Call once at app
5259
+ * bootstrap.
5260
+ *
5261
+ * @remarks
5262
+ * Everything here is synchronous and has to stay that way. `foundation-ai-assistant` resolves
5263
+ * the switcher and registry tokens as it connects, and an element that connects first caches
5264
+ * the no-op fallback — so an `await` above this call costs the whole page load its providers.
5265
+ * Load a stored vendor preference AFTER it, through
5266
+ * {@link TieredAIProviderSwitcher.adoptPersistedVendor}.
5267
+ *
5268
+ * @beta
5269
+ */
5270
+ export declare function registerTieredAIProviders(container: Container, options: Omit<TieredAIProviderSwitcherOptions, 'container'>): TieredAIProviderSwitcher;
5271
+
5235
5272
  /**
5236
5273
  * Name reserved for the cross-agent handoff tool — injected by OrchestratingDriver.
5237
5274
  *
@@ -5557,6 +5594,32 @@ export declare interface SettingsBudgetRow {
5557
5594
  * `Math.min(100, …)`).
5558
5595
  */
5559
5596
  barValue: number;
5597
+ /**
5598
+ * The stacked bar's pieces when the host broke this vendor's spend down, and `undefined`
5599
+ * when it did not — in which case the row renders the design system's single progress bar,
5600
+ * exactly as before (NEW-15).
5601
+ */
5602
+ segments?: SettingsBudgetSegment[];
5603
+ }
5604
+
5605
+ /**
5606
+ * One piece of a stacked budget bar, pre-computed — see {@link SettingsBudgetRow.segments}.
5607
+ *
5608
+ * @beta
5609
+ */
5610
+ export declare interface SettingsBudgetSegment {
5611
+ /** The host's segment id, or the platform's own id for the unattributed remainder. */
5612
+ id: string;
5613
+ /** The host's label. Empty for the remainder, which the legend does not list. */
5614
+ label: string;
5615
+ /** This piece's spend, e.g. `$12.34`. Empty for the remainder. */
5616
+ figures: string;
5617
+ /** Width as a percentage of the row's full track, already scaled to the clamped bar. */
5618
+ widthPercent: number;
5619
+ /** Alpha applied to the design system's accent, so the pieces read as one measure. */
5620
+ tone: number;
5621
+ /** Whether this piece appears in the legend — false for the unattributed remainder. */
5622
+ legend: boolean;
5560
5623
  }
5561
5624
 
5562
5625
  /**
@@ -5993,6 +6056,112 @@ export declare type TemperatureInput = number | ((ctx: SystemPromptContext) => n
5993
6056
  */
5994
6057
  export declare type ThinkingPolicyInput = ChatThinkingPolicy | ((ctx: SystemPromptContext) => ChatThinkingPolicy | undefined | Promise<ChatThinkingPolicy | undefined>);
5995
6058
 
6059
+ /**
6060
+ * Which cloud vendor backs the tier-shaped provider registry, plus the means to move it at
6061
+ * runtime (GENC-1610).
6062
+ *
6063
+ * @remarks
6064
+ * Upstreamed from Genesis Create, which has run this shape in production: a registry keyed by
6065
+ * tier (`low` / `high` / `reasoning`), a vendor switch that rebuilds it in place, and the other
6066
+ * vendor kept alive as a standby entry. Agents keep resolving tier names, so a switch never
6067
+ * touches agent config.
6068
+ *
6069
+ * Register it on the {@link (AIProviderSwitcher:interface)} DI token — via
6070
+ * {@link registerTieredAIProviders} — and the settings modal's vendor UI (or an app's own
6071
+ * control) drives it.
6072
+ *
6073
+ * @beta
6074
+ */
6075
+ export declare class TieredAIProviderSwitcher implements AIProviderSwitcher {
6076
+ readonly vendors: readonly AITierVendor[];
6077
+ private readonly options;
6078
+ private readonly defaultTier;
6079
+ private readonly registry;
6080
+ private readonly listeners;
6081
+ private _vendor;
6082
+ constructor(options: TieredAIProviderSwitcherOptions);
6083
+ get vendor(): AITierVendor;
6084
+ /**
6085
+ * A user-driven switch: move the registry, then tell the app so it can persist the choice.
6086
+ * An unknown vendor is ignored.
6087
+ */
6088
+ switchTo(vendor: string): void;
6089
+ /**
6090
+ * Adopt a vendor the app read back from its own store — the same registry move as
6091
+ * {@link TieredAIProviderSwitcher.switchTo}, without the `onSwitch` callback, because this
6092
+ * value came FROM the store and writing it back would be a pointless round-trip on load.
6093
+ *
6094
+ * Exists because registration has to stay synchronous: an element that connects before the
6095
+ * token is registered caches the platform's no-op fallback, so an app cannot await its
6096
+ * stored preference before constructing this. It boots on `initialVendor` and adopts the
6097
+ * stored one a moment later, through a registry that is mutable and held by reference.
6098
+ */
6099
+ adoptPersistedVendor(vendor: string): void;
6100
+ subscribe(listener: (vendor: string) => void): () => void;
6101
+ /** The registry move both entry points share, and nothing else. */
6102
+ private apply;
6103
+ private build;
6104
+ private isKnownVendor;
6105
+ }
6106
+
6107
+ /**
6108
+ * Options for {@link TieredAIProviderSwitcher}.
6109
+ *
6110
+ * @beta
6111
+ */
6112
+ export declare interface TieredAIProviderSwitcherOptions {
6113
+ /** DI container the provider registry is registered on. */
6114
+ container: Container;
6115
+ /**
6116
+ * Where each vendor's tiers post, as a function of the vendor — for example one that
6117
+ * returns `/gwf/ai-service/<vendor>/chat`.
6118
+ *
6119
+ * A function and never a plain string, because the switcher builds MORE than one vendor:
6120
+ * the active one, a standby for each other vendor, and every tier again after a switch. A
6121
+ * single string would send all of them to the one vendor's proxy, so picking the other vendor
6122
+ * in settings would post Anthropic-shaped requests to a Gemini route — failing on every turn
6123
+ * rather than at bootstrap. An app that only ever reaches one vendor narrows `vendors` and
6124
+ * still passes a function; it just ignores its argument.
6125
+ */
6126
+ serverEndpoint: (vendor: AITierVendor) => string;
6127
+ /**
6128
+ * The vendor to boot on. Required, and deliberately so: the platform has no opinion about
6129
+ * which cloud vendor an app should use, and a silent default would make one app's choice
6130
+ * look like a platform decision.
6131
+ */
6132
+ initialVendor: AITierVendor;
6133
+ /**
6134
+ * The vendors this app can switch between. Defaults to every vendor in the table.
6135
+ *
6136
+ * Narrow it when the app's proxy can only reach one: the others would otherwise report a
6137
+ * status, and the assistant's partial-wall UX reads statuses — so a walled vendor would
6138
+ * offer "switch to the other one" about a vendor that has no key behind it.
6139
+ */
6140
+ vendors?: readonly AITierVendor[];
6141
+ /**
6142
+ * Which tier the registry resolves for a caller that names no provider. Defaults to
6143
+ * `'high'`.
6144
+ *
6145
+ * High rather than the cheapest, because of what resolves through it: an orchestrated turn
6146
+ * classifies against the REGISTRY DEFAULT, so the classifier sees the session's accumulated
6147
+ * history on whatever model this names. On the smallest-window tier, a session grown to size
6148
+ * on a bigger model can hand the classifier a history that no longer fits — and the routing
6149
+ * turn fails at the moment it was supposed to hand off to a model that could have taken it.
6150
+ */
6151
+ defaultTier?: AITierId;
6152
+ /** The tier table to build from. Defaults to the platform's. */
6153
+ tiers?: AITiers;
6154
+ /**
6155
+ * Called when {@link TieredAIProviderSwitcher.switchTo} accepts a vendor — the hook for an
6156
+ * app that persists the user's choice.
6157
+ *
6158
+ * Fires even when the chosen vendor is the one already active. That is not a redundant
6159
+ * call: during boot the registry shows this app's initial vendor while a stored preference
6160
+ * may still be loading, so "confirm what I can see" and "leave it alone" are different acts.
6161
+ */
6162
+ onSwitch?: (vendor: AITierVendor) => void;
6163
+ }
6164
+
5996
6165
  /**
5997
6166
  * A chat message lifted into the debug-log timeline as a `kind: 'message'` entry.
5998
6167
  *
@@ -6305,11 +6474,46 @@ export declare const userIconSvg = "<svg viewBox=\"0 0 24 24\" fill=\"none\" xml
6305
6474
  *
6306
6475
  * @beta
6307
6476
  */
6308
- declare interface VendorBudgetFigures {
6477
+ export declare interface VendorBudgetFigures {
6309
6478
  /** The vendor's budget cap in USD. Finite and `>= 0`. */
6310
6479
  budgetUsd: number;
6311
6480
  /** Metered spend against that cap in USD. Finite and `>= 0`; may exceed the cap. */
6312
6481
  spentUsd: number;
6482
+ /**
6483
+ * Optional breakdown of {@link VendorBudgetFigures.spentUsd} by whatever the host meters
6484
+ * spend by — a surface, a team, an environment. Given, the vendor's bar is drawn stacked
6485
+ * with a legend; omitted, it is the single bar it has always been.
6486
+ *
6487
+ * Host-named, with no platform vocabulary: the assistant has no idea what a host's spend
6488
+ * sources are, and inventing labels for them is how a product word ends up in the platform.
6489
+ * Segments may cover less than the total (the remainder simply draws unlabelled); they may
6490
+ * not cover more, which would be a breakdown of a figure other than the one on screen.
6491
+ *
6492
+ * ORDER IS SIGNIFICANT: the bar and legend are drawn in array order, and the first segment
6493
+ * gets the strongest tone (full strength whenever the whole spend is attributed). Feed the
6494
+ * sources in a fixed, meaningful order — the one you want drawn first, first — and keep it
6495
+ * across re-feeds; a host that re-sorts its sources per response moves the strongest tone
6496
+ * between them with no change in the money.
6497
+ */
6498
+ segments?: VendorBudgetSegment[];
6499
+ }
6500
+
6501
+ /**
6502
+ * One slice of a vendor's spend — see {@link VendorBudgetFigures.segments}.
6503
+ *
6504
+ * @beta
6505
+ */
6506
+ export declare interface VendorBudgetSegment {
6507
+ /**
6508
+ * Identifies the source. Must be unique within a breakdown, and must not be
6509
+ * `__remainder__`, which the meter reserves for unattributed spend; a breakdown that breaks
6510
+ * either rule is dropped. It does not decide drawing order or tone — array order does.
6511
+ */
6512
+ id: string;
6513
+ /** What the host calls this slice of the spend; rendered verbatim in the legend. */
6514
+ label: string;
6515
+ /** This slice's share of the spend in USD. Finite and `>= 0`. */
6516
+ spentUsd: number;
6313
6517
  }
6314
6518
 
6315
6519
  /**
@@ -19,6 +19,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/chat-driver-node.ts
20
20
  var chat_driver_node_exports = {};
21
21
  __export(chat_driver_node_exports, {
22
+ AI_TIER_IDS: () => AI_TIER_IDS,
23
+ AI_TIER_VENDORS: () => AI_TIER_VENDORS,
22
24
  ANTHROPIC_CACHE_READ_MULTIPLIER: () => ANTHROPIC_CACHE_READ_MULTIPLIER,
23
25
  ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER: () => ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER,
24
26
  ANTHROPIC_CACHE_WRITE_5M_MULTIPLIER: () => ANTHROPIC_CACHE_WRITE_5M_MULTIPLIER,
@@ -26,6 +28,7 @@ __export(chat_driver_node_exports, {
26
28
  AnthropicProvider: () => AnthropicProvider,
27
29
  AnthropicTransport: () => AnthropicTransport,
28
30
  ChatDriver: () => ChatDriver,
31
+ DEFAULT_AI_TIERS: () => DEFAULT_AI_TIERS,
29
32
  DEFAULT_PROVIDER_REFUSED_MESSAGE: () => DEFAULT_PROVIDER_REFUSED_MESSAGE,
30
33
  EMPTY_RESPONSE_NUDGE: () => EMPTY_RESPONSE_NUDGE,
31
34
  GEMINI_CACHED_INPUT_MULTIPLIER: () => GEMINI_CACHED_INPUT_MULTIPLIER,
@@ -59,6 +62,7 @@ __export(chat_driver_node_exports, {
59
62
  getMetaEvents: () => getMetaEvents,
60
63
  isObservableAIProviderRegistry: () => isObservableAIProviderRegistry,
61
64
  messageUsage: () => messageUsage,
65
+ resolveAITiers: () => resolveAITiers,
62
66
  restoreMachine: () => restoreMachine,
63
67
  strictFallbackAgent: () => strictFallbackAgent,
64
68
  sumUsage: () => sumUsage,
@@ -1292,6 +1296,7 @@ function anthropicTokenCost(model, usage) {
1292
1296
  }
1293
1297
  var GEMINI_LONG_CONTEXT_THRESHOLD = 2e5;
1294
1298
  var GEMINI_CACHED_INPUT_MULTIPLIER = 0.1;
1299
+ var GEMINI_3_8_FLASH_INTRO_RATE_ENDS = Date.parse("2027-01-01T00:00:00Z");
1295
1300
  var GEMINI_PRICING = {
1296
1301
  "gemini-2.5-flash-lite": {
1297
1302
  kind: "flat",
@@ -3541,6 +3546,87 @@ var MutableAIProviderRegistry = class _MutableAIProviderRegistry {
3541
3546
  }
3542
3547
  };
3543
3548
 
3549
+ // ../../foundation-ai/dist/esm/tiers/ai-tiers.js
3550
+ var AI_TIER_IDS = ["low", "high", "reasoning"];
3551
+ var AI_TIER_VENDORS = ["anthropic", "gemini"];
3552
+ var AI_STALL_TIMEOUT_MS = 6e4;
3553
+ var AI_NO_RESPONSE_TIMEOUT_MS = 3e5;
3554
+ var DEFAULT_AI_TIERS = Object.freeze({
3555
+ anthropic: Object.freeze({
3556
+ low: Object.freeze({
3557
+ model: "claude-haiku-4-5-20251001",
3558
+ maxTokens: 16e3,
3559
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
3560
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS
3561
+ }),
3562
+ // The heavy tier: whole-file writes, and adaptive thinking out of the same budget
3563
+ // wherever a caller leaves thinking on — hence Sonnet 5's full 128K ceiling rather
3564
+ // than a figure that only fits one of the two.
3565
+ high: Object.freeze({
3566
+ model: "claude-sonnet-5",
3567
+ maxTokens: 128e3,
3568
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
3569
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS
3570
+ }),
3571
+ // One-shot judgement calls rather than tool loops, so a smaller ceiling is ample:
3572
+ // `max_tokens` is only a cap, and this tier never emits a whole file.
3573
+ //
3574
+ // Opus 4.8 rather than the 4.7 Create benched: the same price ($5 / $25) and the same 1M
3575
+ // window, but 4.8 supports native structured output where 4.7 takes the forced-tool
3576
+ // fallback — and structured judgement is what this tier is asked for most (review of #2540).
3577
+ reasoning: Object.freeze({
3578
+ model: "claude-opus-4-8",
3579
+ maxTokens: 32e3,
3580
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
3581
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS
3582
+ })
3583
+ }),
3584
+ gemini: Object.freeze({
3585
+ low: Object.freeze({
3586
+ model: "gemini-3.1-flash-lite",
3587
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
3588
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS
3589
+ }),
3590
+ // On an INTRODUCTORY rate that doubles to $1.50 / $7.50 on 2027-01-01, from which point it
3591
+ // is dearer on input than the gemini-3.5-flash it replaced. Chosen at the introductory rate
3592
+ // knowingly; `ai-tiers.test.ts` fails that day to force the re-bench.
3593
+ high: Object.freeze({
3594
+ model: "gemini-3.8-flash",
3595
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
3596
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS
3597
+ }),
3598
+ // A PREVIEW model: no stability guarantee, and it may be withdrawn at short notice (its
3599
+ // transport warns on every construction). Accepted knowingly as the platform default because
3600
+ // no stable Gemini Pro was on offer when the tiers were benched; revisit as soon as Google
3601
+ // promotes, deprecates or withdraws it, since every app on this default inherits the risk.
3602
+ reasoning: Object.freeze({
3603
+ model: "gemini-3.1-pro-preview",
3604
+ stallTimeoutMs: AI_STALL_TIMEOUT_MS,
3605
+ timeoutMs: AI_NO_RESPONSE_TIMEOUT_MS
3606
+ })
3607
+ })
3608
+ });
3609
+ function resolveAITiers(overrides) {
3610
+ if (!(overrides === null || overrides === void 0 ? void 0 : overrides.anthropic) && !(overrides === null || overrides === void 0 ? void 0 : overrides.gemini))
3611
+ return DEFAULT_AI_TIERS;
3612
+ return Object.freeze({
3613
+ anthropic: mergeVendor(DEFAULT_AI_TIERS.anthropic, overrides.anthropic),
3614
+ gemini: mergeVendor(DEFAULT_AI_TIERS.gemini, overrides.gemini)
3615
+ });
3616
+ }
3617
+ function mergeVendor(defaults, overrides) {
3618
+ var _a;
3619
+ const merged = {};
3620
+ for (const tier of AI_TIER_IDS) {
3621
+ const defined = Object.fromEntries(Object.entries((_a = overrides === null || overrides === void 0 ? void 0 : overrides[tier]) !== null && _a !== void 0 ? _a : {}).filter(([, value]) => value !== void 0));
3622
+ const entry = Object.assign(Object.assign({}, defaults[tier]), defined);
3623
+ Object.freeze(entry);
3624
+ merged[tier] = entry;
3625
+ }
3626
+ Object.freeze(merged);
3627
+ return merged;
3628
+ }
3629
+
3544
3630
  // src/channel/ai-activity-bus.ts
3545
3631
  var NOOP_ACTIVITY_BUS = { publish: () => {
3546
3632
  } };
@@ -8406,6 +8492,8 @@ function withFreshMetaSnapshot(stored, fresh) {
8406
8492
  }
8407
8493
  // Annotate the CommonJS export names for ESM import in node:
8408
8494
  0 && (module.exports = {
8495
+ AI_TIER_IDS,
8496
+ AI_TIER_VENDORS,
8409
8497
  ANTHROPIC_CACHE_READ_MULTIPLIER,
8410
8498
  ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER,
8411
8499
  ANTHROPIC_CACHE_WRITE_5M_MULTIPLIER,
@@ -8413,6 +8501,7 @@ function withFreshMetaSnapshot(stored, fresh) {
8413
8501
  AnthropicProvider,
8414
8502
  AnthropicTransport,
8415
8503
  ChatDriver,
8504
+ DEFAULT_AI_TIERS,
8416
8505
  DEFAULT_PROVIDER_REFUSED_MESSAGE,
8417
8506
  EMPTY_RESPONSE_NUDGE,
8418
8507
  GEMINI_CACHED_INPUT_MULTIPLIER,
@@ -8446,6 +8535,7 @@ function withFreshMetaSnapshot(stored, fresh) {
8446
8535
  getMetaEvents,
8447
8536
  isObservableAIProviderRegistry,
8448
8537
  messageUsage,
8538
+ resolveAITiers,
8449
8539
  restoreMachine,
8450
8540
  strictFallbackAgent,
8451
8541
  sumUsage,