@genesislcap/foundation-ai 15.6.2 → 15.7.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.
@@ -2465,6 +2465,346 @@
2465
2465
  }
2466
2466
  ]
2467
2467
  },
2468
+ {
2469
+ "kind": "Variable",
2470
+ "canonicalReference": "@genesislcap/foundation-ai!BUDGETED_VENDORS:var",
2471
+ "docComment": "/**\n * The vendors whose spend the ai-service proxy meters — i.e. the only vendors that can raise a {@link BudgetExhaustedError}, and therefore the only ones a 402's `otherVendorAvailable: false` is a statement about.\n *\n * Deliberately narrower than the keys of {@link VENDOR_LABELS}, and it must stay exactly the proxy's own list — `VENDORS` in ai-service's `utils/aiVendor.js`. A vendor listed here that the proxy does not meter gets walled by the `otherVendorAvailable: false` sweep on a verdict that says nothing about it, and if it is reachable, `blocked` then derives true and locks a composer with headroom left.\n *\n * Two vendors are excluded for that reason: - `chrome` runs on-device — no pot to exhaust, and free. - `openai` is **not budgeted by the proxy**. ai-service rejects it up front with `400 UNSUPPORTED_PROVIDER` (`SUPPORTED_PROVIDERS` in `aiVendor.js`), no `MODEL_PRICING` row names it, and `otherVendorAvailable` is computed over Anthropic and Gemini alone. So it can never raise a 402, and a 402 is never a statement about it.\n *\n * @beta\n */\n",
2472
+ "excerptTokens": [
2473
+ {
2474
+ "kind": "Content",
2475
+ "text": "BUDGETED_VENDORS: "
2476
+ },
2477
+ {
2478
+ "kind": "Content",
2479
+ "text": "readonly "
2480
+ },
2481
+ {
2482
+ "kind": "Reference",
2483
+ "text": "AIProviderType",
2484
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
2485
+ },
2486
+ {
2487
+ "kind": "Content",
2488
+ "text": "[]"
2489
+ }
2490
+ ],
2491
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
2492
+ "isReadonly": true,
2493
+ "releaseTag": "Beta",
2494
+ "name": "BUDGETED_VENDORS",
2495
+ "variableTypeTokenRange": {
2496
+ "startIndex": 1,
2497
+ "endIndex": 4
2498
+ }
2499
+ },
2500
+ {
2501
+ "kind": "Class",
2502
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError:class",
2503
+ "docComment": "/**\n * Thrown when the AI-spend budget for the caller (user, tenant, or project) is exhausted and the ai-service proxy refuses the request outright — HTTP `402` with `code: 'BUDGET_EXCEEDED'`, in either the legacy JSON or the NDJSON framed mode.\n *\n * This is a **terminal, non-transient** condition, in the same family as `ResponseTruncatedError`: no amount of retrying clears it, because nothing about the request is wrong. The budget has to be raised out-of-band before any further call can succeed, so both the transport retry ladder and the driver's transient-retry catch step aside for it and the failure surfaces immediately as the `'budget-exhausted'` `TurnFailureReason`.\n *\n * `budgetUsd`/`spentUsd` are populated from the rejection body when the proxy supplies them (the workstream-C contract does); they are optional because a plain-text or truncated 402 from an older proxy still has to classify.\n *\n * @beta\n */\n",
2504
+ "excerptTokens": [
2505
+ {
2506
+ "kind": "Content",
2507
+ "text": "export declare class BudgetExhaustedError extends "
2508
+ },
2509
+ {
2510
+ "kind": "Reference",
2511
+ "text": "Error",
2512
+ "canonicalReference": "!Error:interface"
2513
+ },
2514
+ {
2515
+ "kind": "Content",
2516
+ "text": " "
2517
+ }
2518
+ ],
2519
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
2520
+ "releaseTag": "Beta",
2521
+ "isAbstract": false,
2522
+ "name": "BudgetExhaustedError",
2523
+ "preserveMemberOrder": false,
2524
+ "members": [
2525
+ {
2526
+ "kind": "Constructor",
2527
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError:constructor(1)",
2528
+ "docComment": "/**\n * Constructs a new instance of the `BudgetExhaustedError` class\n */\n",
2529
+ "excerptTokens": [
2530
+ {
2531
+ "kind": "Content",
2532
+ "text": "constructor(\n vendorLabel: "
2533
+ },
2534
+ {
2535
+ "kind": "Content",
2536
+ "text": "string"
2537
+ },
2538
+ {
2539
+ "kind": "Content",
2540
+ "text": ", \n budgetUsd?: "
2541
+ },
2542
+ {
2543
+ "kind": "Content",
2544
+ "text": "number"
2545
+ },
2546
+ {
2547
+ "kind": "Content",
2548
+ "text": ", \n spentUsd?: "
2549
+ },
2550
+ {
2551
+ "kind": "Content",
2552
+ "text": "number"
2553
+ },
2554
+ {
2555
+ "kind": "Content",
2556
+ "text": ", \n detail?: "
2557
+ },
2558
+ {
2559
+ "kind": "Content",
2560
+ "text": "string"
2561
+ },
2562
+ {
2563
+ "kind": "Content",
2564
+ "text": ", \n extra?: "
2565
+ },
2566
+ {
2567
+ "kind": "Content",
2568
+ "text": "{\n otherVendorAvailable?: boolean;\n serverVendor?: string;\n }"
2569
+ },
2570
+ {
2571
+ "kind": "Content",
2572
+ "text": ");"
2573
+ }
2574
+ ],
2575
+ "releaseTag": "Beta",
2576
+ "isProtected": false,
2577
+ "overloadIndex": 1,
2578
+ "parameters": [
2579
+ {
2580
+ "parameterName": "vendorLabel",
2581
+ "parameterTypeTokenRange": {
2582
+ "startIndex": 1,
2583
+ "endIndex": 2
2584
+ },
2585
+ "isOptional": false
2586
+ },
2587
+ {
2588
+ "parameterName": "budgetUsd",
2589
+ "parameterTypeTokenRange": {
2590
+ "startIndex": 3,
2591
+ "endIndex": 4
2592
+ },
2593
+ "isOptional": true
2594
+ },
2595
+ {
2596
+ "parameterName": "spentUsd",
2597
+ "parameterTypeTokenRange": {
2598
+ "startIndex": 5,
2599
+ "endIndex": 6
2600
+ },
2601
+ "isOptional": true
2602
+ },
2603
+ {
2604
+ "parameterName": "detail",
2605
+ "parameterTypeTokenRange": {
2606
+ "startIndex": 7,
2607
+ "endIndex": 8
2608
+ },
2609
+ "isOptional": true
2610
+ },
2611
+ {
2612
+ "parameterName": "extra",
2613
+ "parameterTypeTokenRange": {
2614
+ "startIndex": 9,
2615
+ "endIndex": 10
2616
+ },
2617
+ "isOptional": true
2618
+ }
2619
+ ]
2620
+ },
2621
+ {
2622
+ "kind": "Property",
2623
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#budgetUsd:member",
2624
+ "docComment": "/**\n * The configured spend cap in USD, when the proxy reported one.\n */\n",
2625
+ "excerptTokens": [
2626
+ {
2627
+ "kind": "Content",
2628
+ "text": "readonly budgetUsd?: "
2629
+ },
2630
+ {
2631
+ "kind": "Content",
2632
+ "text": "number"
2633
+ },
2634
+ {
2635
+ "kind": "Content",
2636
+ "text": ";"
2637
+ }
2638
+ ],
2639
+ "isReadonly": true,
2640
+ "isOptional": true,
2641
+ "releaseTag": "Beta",
2642
+ "name": "budgetUsd",
2643
+ "propertyTypeTokenRange": {
2644
+ "startIndex": 1,
2645
+ "endIndex": 2
2646
+ },
2647
+ "isStatic": false,
2648
+ "isProtected": false,
2649
+ "isAbstract": false
2650
+ },
2651
+ {
2652
+ "kind": "Property",
2653
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#detail:member",
2654
+ "docComment": "/**\n * The proxy's human-readable rejection message, when present.\n */\n",
2655
+ "excerptTokens": [
2656
+ {
2657
+ "kind": "Content",
2658
+ "text": "readonly detail?: "
2659
+ },
2660
+ {
2661
+ "kind": "Content",
2662
+ "text": "string"
2663
+ },
2664
+ {
2665
+ "kind": "Content",
2666
+ "text": ";"
2667
+ }
2668
+ ],
2669
+ "isReadonly": true,
2670
+ "isOptional": true,
2671
+ "releaseTag": "Beta",
2672
+ "name": "detail",
2673
+ "propertyTypeTokenRange": {
2674
+ "startIndex": 1,
2675
+ "endIndex": 2
2676
+ },
2677
+ "isStatic": false,
2678
+ "isProtected": false,
2679
+ "isAbstract": false
2680
+ },
2681
+ {
2682
+ "kind": "Property",
2683
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#otherVendorAvailable:member",
2684
+ "docComment": "/**\n * The proxy's own verdict on whether **any other** vendor it meters still has headroom, from the 402's `otherVendorAvailable`.\n *\n * The one field on this error a client cannot re-derive. A client can only infer \"another vendor is free\" from registry membership — which knows nothing about that vendor's remaining spend — so a `false` here is the only thing standing between the user and \"Switch to Gemini to keep going\" for a Gemini pot that is already empty. `undefined` means the proxy did not say (an older build), and the client is back to inferring.\n */\n",
2685
+ "excerptTokens": [
2686
+ {
2687
+ "kind": "Content",
2688
+ "text": "readonly otherVendorAvailable?: "
2689
+ },
2690
+ {
2691
+ "kind": "Content",
2692
+ "text": "boolean"
2693
+ },
2694
+ {
2695
+ "kind": "Content",
2696
+ "text": ";"
2697
+ }
2698
+ ],
2699
+ "isReadonly": true,
2700
+ "isOptional": true,
2701
+ "releaseTag": "Beta",
2702
+ "name": "otherVendorAvailable",
2703
+ "propertyTypeTokenRange": {
2704
+ "startIndex": 1,
2705
+ "endIndex": 2
2706
+ },
2707
+ "isStatic": false,
2708
+ "isProtected": false,
2709
+ "isAbstract": false
2710
+ },
2711
+ {
2712
+ "kind": "Property",
2713
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#serverVendor:member",
2714
+ "docComment": "/**\n * The vendor the **proxy** says it refused, verbatim from the 402's `vendor`.\n *\n * A second opinion, not the first: {@link BudgetExhaustedError.vendorLabel} comes from the transport that was actually called and wins wherever it resolves. This one rescues attribution when the transport's label does not — a white-labelled or multiplexing gateway whose single transport fronts more than one upstream stamps one static label for all of them, and only the proxy knows which pot the request was actually booked against.\n */\n",
2715
+ "excerptTokens": [
2716
+ {
2717
+ "kind": "Content",
2718
+ "text": "readonly serverVendor?: "
2719
+ },
2720
+ {
2721
+ "kind": "Content",
2722
+ "text": "string"
2723
+ },
2724
+ {
2725
+ "kind": "Content",
2726
+ "text": ";"
2727
+ }
2728
+ ],
2729
+ "isReadonly": true,
2730
+ "isOptional": true,
2731
+ "releaseTag": "Beta",
2732
+ "name": "serverVendor",
2733
+ "propertyTypeTokenRange": {
2734
+ "startIndex": 1,
2735
+ "endIndex": 2
2736
+ },
2737
+ "isStatic": false,
2738
+ "isProtected": false,
2739
+ "isAbstract": false
2740
+ },
2741
+ {
2742
+ "kind": "Property",
2743
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#spentUsd:member",
2744
+ "docComment": "/**\n * Spend already booked against that cap in USD, when the proxy reported it.\n */\n",
2745
+ "excerptTokens": [
2746
+ {
2747
+ "kind": "Content",
2748
+ "text": "readonly spentUsd?: "
2749
+ },
2750
+ {
2751
+ "kind": "Content",
2752
+ "text": "number"
2753
+ },
2754
+ {
2755
+ "kind": "Content",
2756
+ "text": ";"
2757
+ }
2758
+ ],
2759
+ "isReadonly": true,
2760
+ "isOptional": true,
2761
+ "releaseTag": "Beta",
2762
+ "name": "spentUsd",
2763
+ "propertyTypeTokenRange": {
2764
+ "startIndex": 1,
2765
+ "endIndex": 2
2766
+ },
2767
+ "isStatic": false,
2768
+ "isProtected": false,
2769
+ "isAbstract": false
2770
+ },
2771
+ {
2772
+ "kind": "Property",
2773
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#vendorLabel:member",
2774
+ "docComment": "/**\n * Vendor label of the transport that was refused (e.g. `'Anthropic'`).\n */\n",
2775
+ "excerptTokens": [
2776
+ {
2777
+ "kind": "Content",
2778
+ "text": "readonly vendorLabel: "
2779
+ },
2780
+ {
2781
+ "kind": "Content",
2782
+ "text": "string"
2783
+ },
2784
+ {
2785
+ "kind": "Content",
2786
+ "text": ";"
2787
+ }
2788
+ ],
2789
+ "isReadonly": true,
2790
+ "isOptional": false,
2791
+ "releaseTag": "Beta",
2792
+ "name": "vendorLabel",
2793
+ "propertyTypeTokenRange": {
2794
+ "startIndex": 1,
2795
+ "endIndex": 2
2796
+ },
2797
+ "isStatic": false,
2798
+ "isProtected": false,
2799
+ "isAbstract": false
2800
+ }
2801
+ ],
2802
+ "extendsTokenRange": {
2803
+ "startIndex": 1,
2804
+ "endIndex": 2
2805
+ },
2806
+ "implementsTokenRanges": []
2807
+ },
2468
2808
  {
2469
2809
  "kind": "TypeAlias",
2470
2810
  "canonicalReference": "@genesislcap/foundation-ai!CachePolicy:type",
@@ -3327,7 +3667,16 @@
3327
3667
  },
3328
3668
  {
3329
3669
  "kind": "Content",
3330
- "text": ";\n} | {\n reason: 'agent-handoff';\n summary: string;\n remainingTask: string;\n}"
3670
+ "text": ";\n budget?: {\n budgetUsd?: number;\n spentUsd?: number;\n vendorLabel: string;\n vendor?: "
3671
+ },
3672
+ {
3673
+ "kind": "Reference",
3674
+ "text": "AIProviderType",
3675
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
3676
+ },
3677
+ {
3678
+ "kind": "Content",
3679
+ "text": ";\n otherVendorAvailable?: boolean;\n };\n} | {\n reason: 'agent-handoff';\n summary: string;\n remainingTask: string;\n}"
3331
3680
  },
3332
3681
  {
3333
3682
  "kind": "Content",
@@ -3339,7 +3688,7 @@
3339
3688
  "name": "ChatDriverResult",
3340
3689
  "typeTokenRange": {
3341
3690
  "startIndex": 1,
3342
- "endIndex": 4
3691
+ "endIndex": 6
3343
3692
  }
3344
3693
  },
3345
3694
  {
@@ -6664,6 +7013,33 @@
6664
7013
  ],
6665
7014
  "extendsTokenRanges": []
6666
7015
  },
7016
+ {
7017
+ "kind": "Variable",
7018
+ "canonicalReference": "@genesislcap/foundation-ai!DEFAULT_BUDGET_EXHAUSTED_MESSAGE:var",
7019
+ "docComment": "/**\n * The single copy shown to a user whose AI budget is gone — the transcript bubble the chat driver appends, and the default text of the assistant's blocked banner.\n *\n * Deliberately **vendor-neutral**: the same bundle ships to white-labelled deployments where \"contact Genesis\" is simply wrong. A host that wants branded wording overrides both surfaces — the banner via `FoundationAiAssistant.setBlocked(true, reason)`, the transcript bubble via `ChatDriverConfig.budgetExhaustedMessage` — so neither is stuck with this default while the other is customised.\n *\n * @beta\n */\n",
7020
+ "excerptTokens": [
7021
+ {
7022
+ "kind": "Content",
7023
+ "text": "DEFAULT_BUDGET_EXHAUSTED_MESSAGE = "
7024
+ },
7025
+ {
7026
+ "kind": "Content",
7027
+ "text": "\"You've reached your AI usage limit. Contact your administrator to raise it.\""
7028
+ }
7029
+ ],
7030
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
7031
+ "initializerTokenRange": {
7032
+ "startIndex": 1,
7033
+ "endIndex": 2
7034
+ },
7035
+ "isReadonly": true,
7036
+ "releaseTag": "Beta",
7037
+ "name": "DEFAULT_BUDGET_EXHAUSTED_MESSAGE",
7038
+ "variableTypeTokenRange": {
7039
+ "startIndex": 0,
7040
+ "endIndex": 0
7041
+ }
7042
+ },
6667
7043
  {
6668
7044
  "kind": "TypeAlias",
6669
7045
  "canonicalReference": "@genesislcap/foundation-ai!FieldLike:type",
@@ -7911,6 +8287,52 @@
7911
8287
  "parameters": [],
7912
8288
  "name": "isAIFeatureEnabled"
7913
8289
  },
8290
+ {
8291
+ "kind": "Function",
8292
+ "canonicalReference": "@genesislcap/foundation-ai!isBudgetedVendor:function(1)",
8293
+ "docComment": "/**\n * Whether this vendor's spend is metered by the proxy — see {@link BUDGETED_VENDORS}.\n *\n * A HOST-FACING helper with, deliberately, no internal caller: the library's own consumers iterate {@link BUDGETED_VENDORS} directly (structural, cannot drift), while a host writing its own budget pre-flight or status surface needs the membership test in predicate form. Kept exported for that use — genesis-create's pre-flight is the shape of consumer it exists for.\n *\n * @beta\n */\n",
8294
+ "excerptTokens": [
8295
+ {
8296
+ "kind": "Content",
8297
+ "text": "isBudgetedVendor: (vendor: "
8298
+ },
8299
+ {
8300
+ "kind": "Reference",
8301
+ "text": "AIProviderType",
8302
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
8303
+ },
8304
+ {
8305
+ "kind": "Content",
8306
+ "text": " | undefined"
8307
+ },
8308
+ {
8309
+ "kind": "Content",
8310
+ "text": ") => "
8311
+ },
8312
+ {
8313
+ "kind": "Content",
8314
+ "text": "boolean"
8315
+ }
8316
+ ],
8317
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
8318
+ "returnTypeTokenRange": {
8319
+ "startIndex": 4,
8320
+ "endIndex": 5
8321
+ },
8322
+ "releaseTag": "Beta",
8323
+ "overloadIndex": 1,
8324
+ "parameters": [
8325
+ {
8326
+ "parameterName": "vendor",
8327
+ "parameterTypeTokenRange": {
8328
+ "startIndex": 1,
8329
+ "endIndex": 3
8330
+ },
8331
+ "isOptional": false
8332
+ }
8333
+ ],
8334
+ "name": "isBudgetedVendor"
8335
+ },
7914
8336
  {
7915
8337
  "kind": "Function",
7916
8338
  "canonicalReference": "@genesislcap/foundation-ai!isChatToolCallUnknown:function(1)",
@@ -9754,7 +10176,7 @@
9754
10176
  {
9755
10177
  "kind": "TypeAlias",
9756
10178
  "canonicalReference": "@genesislcap/foundation-ai!SubAgentFailureReason:type",
9757
- "docComment": "/**\n * Why a sub-agent run ended without producing a structured result via `completeSubAgent`. Surfaced on the `{ ok: false }` branch of `requestSubAgent` so the parent tool handler can decide how to recover.\n *\n * - `max_iterations` — the sub-agent's tool loop ended without calling its completion tool (hit the iteration cap, or — defensively — a provider ignored forced tool use and returned a free-text turn). - `malformed_tool_call` — the provider repeatedly returned an unparseable tool call (e.g. Gemini `MALFORMED_FUNCTION_CALL`) after retries. - `empty_response` — the model repeatedly returned an empty response after retries. - `unknown_tool_limit` — the model repeatedly called tools that aren't available to it. - `timeout` — the sub-agent did not finish within {@link SubAgentRequestOptions.timeoutMs} (its run was aborted), or a transport request timed out inside the child's own run. - `response_truncated` — a turn stopped at the provider's output-token cap with an incomplete tool call; deterministic, so it is not retried. - `refusal` — a safety-classifier decline (e.g. Fable 5 `stop_reason: 'refusal'`), which returns empty content; deterministic, so it is not retried.\n *\n * @beta\n */\n",
10179
+ "docComment": "/**\n * Why a sub-agent run ended without producing a structured result via `completeSubAgent`. Surfaced on the `{ ok: false }` branch of `requestSubAgent` so the parent tool handler can decide how to recover.\n *\n * - `max_iterations` — the sub-agent's tool loop ended without calling its completion tool (hit the iteration cap, or — defensively — a provider ignored forced tool use and returned a free-text turn). - `malformed_tool_call` — the provider repeatedly returned an unparseable tool call (e.g. Gemini `MALFORMED_FUNCTION_CALL`) after retries. - `empty_response` — the model repeatedly returned an empty response after retries. - `unknown_tool_limit` — the model repeatedly called tools that aren't available to it. - `timeout` — the sub-agent did not finish within {@link SubAgentRequestOptions.timeoutMs} (its run was aborted), or a transport request timed out inside the child's own run. - `response_truncated` — a turn stopped at the provider's output-token cap with an incomplete tool call; deterministic, so it is not retried. - `refusal` — a safety-classifier decline (e.g. Fable 5 `stop_reason: 'refusal'`), which returns empty content; deterministic, so it is not retried. - `budget_exhausted` — the AI-spend budget is used up and the proxy refused the request (HTTP 402). **Terminal for the parent too, and enforced**: the parent driver ends its own turn as soon as this outcome comes back, instead of appending it as a tool result and calling the model again into the same wall. So a handler that receives it will not be called a second time this turn, and the parent turn reports `'budget-exhausted'` in its own right.\n *\n * Recovery guidance differs sharply by reason — see `docs/sub_agent.md`. Only `budget_exhausted` and `refusal` are terminal; retrying either just reproduces it. The rest are worth a retry or a question to the user.\n *\n * @beta\n */\n",
9758
10180
  "excerptTokens": [
9759
10181
  {
9760
10182
  "kind": "Content",
@@ -9762,7 +10184,7 @@
9762
10184
  },
9763
10185
  {
9764
10186
  "kind": "Content",
9765
- "text": "'max_iterations' | 'malformed_tool_call' | 'empty_response' | 'unknown_tool_limit' | 'timeout' | 'response_truncated' | 'refusal'"
10187
+ "text": "'max_iterations' | 'malformed_tool_call' | 'empty_response' | 'unknown_tool_limit' | 'timeout' | 'response_truncated' | 'refusal' | 'budget_exhausted'"
9766
10188
  },
9767
10189
  {
9768
10190
  "kind": "Content",
@@ -10003,7 +10425,7 @@
10003
10425
  {
10004
10426
  "kind": "TypeAlias",
10005
10427
  "canonicalReference": "@genesislcap/foundation-ai!TurnFailureReason:type",
10006
- "docComment": "/**\n * Why a driver turn ended in failure — the typed taxonomy the tool loop already records onto its debug-log timeline (`turn.error` / `turn.retry` details), surfaced here so callers of a turn can read the outcome structurally instead of scraping prose. Distinct from {@link SubAgentFailureReason} (underscored, its own `timeout` member) because the two enums serialise into different log surfaces and READMEs; a transport request timeout on the main turn is recorded as `exception`.\n *\n * - `exception` — an uncaught error escaped the tool loop (catch-all, includes a transport request timeout). - `malformed-function-call` — the provider returned an unparseable tool call. - `empty-response` — the model returned no content and no tool calls. - `unknown-tool-limit` — the model repeatedly called tools it couldn't dispatch, whether hallucinated or stale (real earlier, retired now). - `max-iterations` — the tool loop hit its iteration cap. - `response-truncated` — a turn stopped at the provider's output-token cap with an incomplete tool call; deterministic, so it bails without retry. - `refusal` — a safety-classifier decline (e.g. Fable 5 `stop_reason: 'refusal'`), which returns empty content; deterministic, so it is not retried.\n *\n * @beta\n */\n",
10428
+ "docComment": "/**\n * Why a driver turn ended in failure — the typed taxonomy the tool loop already records onto its debug-log timeline (`turn.error` / `turn.retry` details), surfaced here so callers of a turn can read the outcome structurally instead of scraping prose. Distinct from {@link SubAgentFailureReason} (underscored, its own `timeout` member) because the two enums serialise into different log surfaces and READMEs; a transport request timeout on the main turn is recorded as `exception`.\n *\n * - `exception` — an uncaught error escaped the tool loop (catch-all, includes a transport request timeout). - `malformed-function-call` — the provider returned an unparseable tool call. - `empty-response` — the model returned no content and no tool calls. - `unknown-tool-limit` — the model repeatedly called tools it couldn't dispatch, whether hallucinated or stale (real earlier, retired now). - `max-iterations` — the tool loop hit its iteration cap. - `response-truncated` — a turn stopped at the provider's output-token cap with an incomplete tool call; deterministic, so it bails without retry. - `refusal` — a safety-classifier decline (e.g. Fable 5 `stop_reason: 'refusal'`), which returns empty content; deterministic, so it is not retried. - `budget-exhausted` — the AI-spend budget is used up and the proxy refused the request (`BudgetExhaustedError`, HTTP 402). Terminal, not retried: the budget must be raised out-of-band before any call can succeed.\n *\n * @beta\n */\n",
10007
10429
  "excerptTokens": [
10008
10430
  {
10009
10431
  "kind": "Content",
@@ -10011,7 +10433,7 @@
10011
10433
  },
10012
10434
  {
10013
10435
  "kind": "Content",
10014
- "text": "'exception' | 'malformed-function-call' | 'empty-response' | 'unknown-tool-limit' | 'max-iterations' | 'response-truncated' | 'refusal'"
10436
+ "text": "'exception' | 'malformed-function-call' | 'empty-response' | 'unknown-tool-limit' | 'max-iterations' | 'response-truncated' | 'refusal' | 'budget-exhausted'"
10015
10437
  },
10016
10438
  {
10017
10439
  "kind": "Content",
@@ -10025,6 +10447,111 @@
10025
10447
  "startIndex": 1,
10026
10448
  "endIndex": 2
10027
10449
  }
10450
+ },
10451
+ {
10452
+ "kind": "Variable",
10453
+ "canonicalReference": "@genesislcap/foundation-ai!VENDOR_LABELS:var",
10454
+ "docComment": "/**\n * Display name for each concrete AI vendor: the **single source** every transport's `vendorLabel` is read from, and the only place a vendor's name is spelled for a user.\n *\n * This map is the hinge of the per-vendor budget model (GENC-1464). A wall is attributed to a vendor by reverse-looking-up the label the refused transport stamped on its {@link BudgetExhaustedError} — so a label that drifts from this map (a literal typo, a casing change, a new vendor added with a hardcoded string) silently degrades every wall from that transport to \"unattributable\" and re-locks the whole session instead of just that vendor. It fails safe, but the feature quietly stops working, which is why the transports read their label from here rather than restating it.\n *\n * The map has **two kinds of entry**, and the difference matters:\n *\n * - The {@link BUDGETED_VENDORS} — `anthropic` and `gemini` — reach the model through the ai-service proxy, are metered by it, and stamp their label on a {@link BudgetExhaustedError}. For these the label is both an attribution key and display copy. - `chrome` and `openai` are **display-only**. `ChromeProvider` talks to the on-device Prompt API and the proxy refuses `openai` outright, so neither can be refused for budget or stamp a label — but both report a `provider` from `getStatus()`, which puts them in the assistant's reachable set, which is what the blocked banner names when it tells a walled user where they can still go (\"Switch to Chrome to keep going.\"). Dropping either entry would print the raw type there.\n *\n * `'none'` is excluded deliberately: it is the \"no provider configured\" sentinel, not a vendor, and nothing can be refused by it — nor switched to.\n *\n * @beta\n */\n",
10455
+ "excerptTokens": [
10456
+ {
10457
+ "kind": "Content",
10458
+ "text": "VENDOR_LABELS: "
10459
+ },
10460
+ {
10461
+ "kind": "Reference",
10462
+ "text": "Readonly",
10463
+ "canonicalReference": "!Readonly:type"
10464
+ },
10465
+ {
10466
+ "kind": "Content",
10467
+ "text": "<"
10468
+ },
10469
+ {
10470
+ "kind": "Reference",
10471
+ "text": "Record",
10472
+ "canonicalReference": "!Record:type"
10473
+ },
10474
+ {
10475
+ "kind": "Content",
10476
+ "text": "<"
10477
+ },
10478
+ {
10479
+ "kind": "Reference",
10480
+ "text": "Exclude",
10481
+ "canonicalReference": "!Exclude:type"
10482
+ },
10483
+ {
10484
+ "kind": "Content",
10485
+ "text": "<"
10486
+ },
10487
+ {
10488
+ "kind": "Reference",
10489
+ "text": "AIProviderType",
10490
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
10491
+ },
10492
+ {
10493
+ "kind": "Content",
10494
+ "text": ", 'none'>, string>>"
10495
+ }
10496
+ ],
10497
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
10498
+ "isReadonly": true,
10499
+ "releaseTag": "Beta",
10500
+ "name": "VENDOR_LABELS",
10501
+ "variableTypeTokenRange": {
10502
+ "startIndex": 1,
10503
+ "endIndex": 9
10504
+ }
10505
+ },
10506
+ {
10507
+ "kind": "Function",
10508
+ "canonicalReference": "@genesislcap/foundation-ai!vendorTypeOfLabel:function(1)",
10509
+ "docComment": "/**\n * The {@link AIProviderType} behind a vendor label, or `undefined` for a label no vendor claims.\n *\n * Case-insensitive and whitespace-tolerant, because the label travels as free text on the wire (the driver contract carries `vendorLabel`, not the type) and an unrecognised label must degrade to \"unattributable\" rather than to a wrong attribution — blocking the wrong vendor is worse than blocking none.\n *\n * @beta\n */\n",
10510
+ "excerptTokens": [
10511
+ {
10512
+ "kind": "Content",
10513
+ "text": "export declare function vendorTypeOfLabel(label?: "
10514
+ },
10515
+ {
10516
+ "kind": "Content",
10517
+ "text": "string"
10518
+ },
10519
+ {
10520
+ "kind": "Content",
10521
+ "text": "): "
10522
+ },
10523
+ {
10524
+ "kind": "Reference",
10525
+ "text": "AIProviderType",
10526
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
10527
+ },
10528
+ {
10529
+ "kind": "Content",
10530
+ "text": " | undefined"
10531
+ },
10532
+ {
10533
+ "kind": "Content",
10534
+ "text": ";"
10535
+ }
10536
+ ],
10537
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
10538
+ "returnTypeTokenRange": {
10539
+ "startIndex": 3,
10540
+ "endIndex": 5
10541
+ },
10542
+ "releaseTag": "Beta",
10543
+ "overloadIndex": 1,
10544
+ "parameters": [
10545
+ {
10546
+ "parameterName": "label",
10547
+ "parameterTypeTokenRange": {
10548
+ "startIndex": 1,
10549
+ "endIndex": 2
10550
+ },
10551
+ "isOptional": true
10552
+ }
10553
+ ],
10554
+ "name": "vendorTypeOfLabel"
10028
10555
  }
10029
10556
  ]
10030
10557
  }