@genesislcap/foundation-ai 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.
@@ -64,6 +64,26 @@ export declare interface AggregateUsage {
64
64
  /** Feature flag name for AI functionality (beta). Enable via ?feature.ai in URL or GENX_ENABLE_AI env var. */
65
65
  export declare const AI_FEATURE_FLAG = "ai";
66
66
 
67
+ /**
68
+ * Every tier id, in ascending capability order.
69
+ *
70
+ * This array is the source of truth and {@link AITierId} is derived from it, so the two cannot
71
+ * drift: a tier exists exactly when it is listed here. That matters because the provider
72
+ * builders and the table's tests all loop over this array — a tier missing from it would get
73
+ * no provider and no test, with nothing to say so.
74
+ *
75
+ * @beta
76
+ */
77
+ export declare const AI_TIER_IDS: readonly ["low", "high", "reasoning"];
78
+
79
+ /**
80
+ * Every vendor in the table — the two the platform's proxy meters. The source of truth for
81
+ * {@link AITierVendor}, for the same reason as {@link AI_TIER_IDS}.
82
+ *
83
+ * @beta
84
+ */
85
+ export declare const AI_TIER_VENDORS: readonly ["anthropic", "gemini"];
86
+
67
87
  /**
68
88
  * Union type for provider-specific configs.
69
89
  * Extend when adding new providers.
@@ -229,6 +249,47 @@ export declare interface AIStatus {
229
249
  contextLimit?: number;
230
250
  }
231
251
 
252
+ /**
253
+ * The tier a caller asks for, rather than a model id.
254
+ *
255
+ * Tier-shaped names are what let an app swap vendor (or re-bench a tier onto a
256
+ * newer model) without touching a single agent config — agents resolve `'high'`,
257
+ * not `claude-sonnet-5`. Derived from {@link AI_TIER_IDS}.
258
+ *
259
+ * @beta
260
+ */
261
+ export declare type AITierId = (typeof AI_TIER_IDS)[number];
262
+
263
+ /**
264
+ * Per-tier overrides, shallow-merged field by field over the defaults.
265
+ *
266
+ * @beta
267
+ */
268
+ export declare interface AITierOverrides {
269
+ anthropic?: Partial<Record<AITierId, Partial<AnthropicTierConfig>>>;
270
+ gemini?: Partial<Record<AITierId, Partial<GeminiTierConfig>>>;
271
+ }
272
+
273
+ /**
274
+ * The whole table: every tier, for every vendor.
275
+ *
276
+ * Total records, not partials — a tier a vendor cannot serve is not a tier, it is a
277
+ * gap that only shows up when an agent resolves it at runtime.
278
+ *
279
+ * @beta
280
+ */
281
+ export declare interface AITiers {
282
+ anthropic: Record<AITierId, AnthropicTierConfig>;
283
+ gemini: Record<AITierId, GeminiTierConfig>;
284
+ }
285
+
286
+ /**
287
+ * The cloud vendors the tier table covers. Derived from {@link AI_TIER_VENDORS}.
288
+ *
289
+ * @beta
290
+ */
291
+ export declare type AITierVendor = (typeof AI_TIER_VENDORS)[number];
292
+
232
293
  /**
233
294
  * Transport interface for AI providers.
234
295
  * Each provider (OpenAI, Chrome, Anthropic) implements this to handle API-specific details.
@@ -325,6 +386,27 @@ declare interface AnthropicProviderConfig {
325
386
  */
326
387
  export declare function anthropicRatesFor(model: AnthropicModelId): TokenRates;
327
388
 
389
+ /**
390
+ * What one tier is worth on Anthropic: the model, its output ceiling, and the two clocks.
391
+ *
392
+ * @beta
393
+ */
394
+ export declare interface AnthropicTierConfig {
395
+ model: AnthropicModelId;
396
+ /**
397
+ * `max_tokens` for every request on this tier. Part of the tier, not an optional
398
+ * extra: Anthropic requires the field and the transport defaults it to 4096, which
399
+ * truncates a large single-call file write mid-stream (`stop_reason: max_tokens`)
400
+ * and leaves the tool input unparseable. Values sit inside each model's documented
401
+ * output ceiling.
402
+ */
403
+ maxTokens: number;
404
+ /** Inactivity window on a stream that has started. */
405
+ stallTimeoutMs: number;
406
+ /** Ceiling on the wait for the FIRST byte — a different clock from the stall one. */
407
+ timeoutMs: number;
408
+ }
409
+
328
410
  /**
329
411
  * Cost one Anthropic request.
330
412
  *
@@ -687,6 +769,48 @@ export declare class BudgetExhaustedError extends Error {
687
769
  });
688
770
  }
689
771
 
772
+ /**
773
+ * Builds one {@link AIProvider} per tier for a vendor, ready to register under the tier
774
+ * names agents resolve (`low` / `high` / `reasoning`).
775
+ *
776
+ * @remarks
777
+ * Each provider carries its tier's model, output ceiling (Anthropic) and both clocks, so no
778
+ * tier can silently fall back to the transport defaults — which is how a large tool write
779
+ * ends up truncated at 4096 tokens.
780
+ *
781
+ * @example
782
+ * ```ts
783
+ * registerAIProviders(
784
+ * container,
785
+ * buildTierProviders({ vendor: 'gemini', serverEndpoint: (v) => `/gwf/ai-service/${v}/chat` }),
786
+ * { default: 'high' },
787
+ * );
788
+ * ```
789
+ *
790
+ * @beta
791
+ */
792
+ export declare function buildTierProviders(options: BuildTierProvidersOptions): Record<AITierId, AIProvider>;
793
+
794
+ /**
795
+ * Options for {@link buildTierProviders}.
796
+ *
797
+ * @beta
798
+ */
799
+ export declare interface BuildTierProvidersOptions {
800
+ /** Which vendor's models to build. */
801
+ vendor: AITierVendor;
802
+ /**
803
+ * The proxy endpoint every tier posts to — a string, or a function of the vendor for a
804
+ * caller that builds more than one vendor from one rule (see the example below).
805
+ *
806
+ * Always a server endpoint, never an API key: these providers are meant for an app whose
807
+ * keys stay on its server. An empty endpoint throws here rather than at the first turn.
808
+ */
809
+ serverEndpoint: string | ((vendor: AITierVendor) => string);
810
+ /** The table to build from. Defaults to {@link DEFAULT_AI_TIERS}. */
811
+ tiers?: AITiers;
812
+ }
813
+
690
814
  /**
691
815
  * Prompt-cache policy for a chat request, resolved per turn. **Provider-neutral intent,
692
816
  * provider-specific effect** — read this carefully, the two providers differ sharply:
@@ -2419,6 +2543,30 @@ export declare interface CriteriaInterpretContext {
2419
2543
  fields?: FieldLike[];
2420
2544
  }
2421
2545
 
2546
+ /**
2547
+ * The platform's default tier table.
2548
+ *
2549
+ * @remarks
2550
+ * Frozen, because it is shared by every app in the process: {@link resolveAITiers} returns
2551
+ * a fresh object for callers that override, and hands this back untouched for callers that
2552
+ * do not.
2553
+ *
2554
+ * The models are the ones Genesis Create has been running these tiers on in production — except
2555
+ * Anthropic's `reasoning`, moved to `claude-opus-4-8` (see its entry) — and they are a
2556
+ * **benchmark result, not a preference**: re-bench before moving one. Two of the Gemini
2557
+ * defaults carry a caveat recorded beside them, a preview model and an introductory rate. Two dates
2558
+ * are already on the calendar for that: `gemini-3.8-flash` sits on an introductory rate that
2559
+ * doubles on 2027-01-01 (at which point it is dearer on input than the model it replaced),
2560
+ * and `token-cost.test.ts` fails the build that day; `ai-tiers.test.ts` fails alongside it
2561
+ * saying the High tier is the thing to re-bench.
2562
+ *
2563
+ * Every model here must be in `SUPPORTED_*_MODEL_IDS` with a context limit and a price —
2564
+ * pinned by tests, because an unpriced model silently bills at nothing.
2565
+ *
2566
+ * @beta
2567
+ */
2568
+ export declare const DEFAULT_AI_TIERS: AITiers;
2569
+
2422
2570
  /**
2423
2571
  * The single copy shown to a user whose AI budget is gone — the transcript bubble
2424
2572
  * the chat driver appends, and the default text of the assistant's blocked banner.
@@ -2573,6 +2721,22 @@ declare interface GeminiProviderConfig {
2573
2721
  */
2574
2722
  export declare function geminiRatesFor(model: GeminiModelId, promptTokens: number): TokenRates;
2575
2723
 
2724
+ /**
2725
+ * What one tier is worth on Gemini.
2726
+ *
2727
+ * No `maxTokens`: `GeminiAIConfig` has no such field and the provider's own (much
2728
+ * larger) default applies, so Gemini writes never hit the Anthropic wall above.
2729
+ *
2730
+ * @beta
2731
+ */
2732
+ export declare interface GeminiTierConfig {
2733
+ model: GeminiModelId;
2734
+ /** Inactivity window on a stream that has started. */
2735
+ stallTimeoutMs: number;
2736
+ /** Ceiling on the wait for the FIRST byte — a different clock from the stall one. */
2737
+ timeoutMs: number;
2738
+ }
2739
+
2576
2740
  /**
2577
2741
  * Cost one Gemini request.
2578
2742
  *
@@ -3234,6 +3398,33 @@ export declare interface ResolveAIConfigOptions {
3234
3398
  preferChrome?: boolean;
3235
3399
  }
3236
3400
 
3401
+ /**
3402
+ * The table an app should actually use: {@link DEFAULT_AI_TIERS} with `overrides` applied
3403
+ * per field, so an app that wants one model moved says only that and keeps the platform's
3404
+ * clocks and ceilings.
3405
+ *
3406
+ * @remarks
3407
+ * An override field set to `undefined` is skipped, exactly as if it had been left out. The
3408
+ * common caller passes an optional config value straight through
3409
+ * (`{ high: { model: appConfig.model } }`), and copying `undefined` over the default would
3410
+ * hand the transport no model at all — it then quietly falls back to its own default, with
3411
+ * nothing logged.
3412
+ *
3413
+ * The result is frozen whether or not anything was overridden, so whether mutating it throws
3414
+ * does not depend on what the caller passed. The default itself is returned untouched when
3415
+ * there is nothing to override, and is never mutated, which matters because it is shared
3416
+ * across every app in the process.
3417
+ *
3418
+ * @example
3419
+ * ```ts
3420
+ * // Keep everything, but run the reasoning tier on Fable.
3421
+ * const tiers = resolveAITiers({ anthropic: { reasoning: { model: 'claude-fable-5' } } });
3422
+ * ```
3423
+ *
3424
+ * @beta
3425
+ */
3426
+ export declare function resolveAITiers(overrides?: AITierOverrides): AITiers;
3427
+
3237
3428
  /**
3238
3429
  * Thrown when a response was cut off at the output-token limit (Anthropic
3239
3430
  * `stop_reason: 'max_tokens'`, Gemini `finishReason: 'MAX_TOKENS'`) in a way
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-ai",
3
3
  "description": "Genesis Foundation AI - Provider-agnostic AI configuration and shared utilities",
4
- "version": "15.33.1",
4
+ "version": "15.34.1",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -52,17 +52,17 @@
52
52
  }
53
53
  },
54
54
  "devDependencies": {
55
- "@genesislcap/foundation-testing": "15.33.1",
56
- "@genesislcap/genx": "15.33.1",
57
- "@genesislcap/rollup-builder": "15.33.1",
58
- "@genesislcap/ts-builder": "15.33.1",
59
- "@genesislcap/uvu-playwright-builder": "15.33.1",
60
- "@genesislcap/vite-builder": "15.33.1",
61
- "@genesislcap/webpack-builder": "15.33.1"
55
+ "@genesislcap/foundation-testing": "15.34.1",
56
+ "@genesislcap/genx": "15.34.1",
57
+ "@genesislcap/rollup-builder": "15.34.1",
58
+ "@genesislcap/ts-builder": "15.34.1",
59
+ "@genesislcap/uvu-playwright-builder": "15.34.1",
60
+ "@genesislcap/vite-builder": "15.34.1",
61
+ "@genesislcap/webpack-builder": "15.34.1"
62
62
  },
63
63
  "dependencies": {
64
- "@genesislcap/foundation-logger": "15.33.1",
65
- "@genesislcap/foundation-utils": "15.33.1",
64
+ "@genesislcap/foundation-logger": "15.34.1",
65
+ "@genesislcap/foundation-utils": "15.34.1",
66
66
  "@microsoft/fast-foundation": "2.50.0"
67
67
  },
68
68
  "repository": {