@genesislcap/foundation-ai 15.3.2 → 15.3.3-alpha-4899ddd.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.
@@ -2312,6 +2312,346 @@
2312
2312
  }
2313
2313
  ]
2314
2314
  },
2315
+ {
2316
+ "kind": "Variable",
2317
+ "canonicalReference": "@genesislcap/foundation-ai!BUDGETED_VENDORS:var",
2318
+ "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",
2319
+ "excerptTokens": [
2320
+ {
2321
+ "kind": "Content",
2322
+ "text": "BUDGETED_VENDORS: "
2323
+ },
2324
+ {
2325
+ "kind": "Content",
2326
+ "text": "readonly "
2327
+ },
2328
+ {
2329
+ "kind": "Reference",
2330
+ "text": "AIProviderType",
2331
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
2332
+ },
2333
+ {
2334
+ "kind": "Content",
2335
+ "text": "[]"
2336
+ }
2337
+ ],
2338
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
2339
+ "isReadonly": true,
2340
+ "releaseTag": "Beta",
2341
+ "name": "BUDGETED_VENDORS",
2342
+ "variableTypeTokenRange": {
2343
+ "startIndex": 1,
2344
+ "endIndex": 4
2345
+ }
2346
+ },
2347
+ {
2348
+ "kind": "Class",
2349
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError:class",
2350
+ "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",
2351
+ "excerptTokens": [
2352
+ {
2353
+ "kind": "Content",
2354
+ "text": "export declare class BudgetExhaustedError extends "
2355
+ },
2356
+ {
2357
+ "kind": "Reference",
2358
+ "text": "Error",
2359
+ "canonicalReference": "!Error:interface"
2360
+ },
2361
+ {
2362
+ "kind": "Content",
2363
+ "text": " "
2364
+ }
2365
+ ],
2366
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
2367
+ "releaseTag": "Beta",
2368
+ "isAbstract": false,
2369
+ "name": "BudgetExhaustedError",
2370
+ "preserveMemberOrder": false,
2371
+ "members": [
2372
+ {
2373
+ "kind": "Constructor",
2374
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError:constructor(1)",
2375
+ "docComment": "/**\n * Constructs a new instance of the `BudgetExhaustedError` class\n */\n",
2376
+ "excerptTokens": [
2377
+ {
2378
+ "kind": "Content",
2379
+ "text": "constructor(\n vendorLabel: "
2380
+ },
2381
+ {
2382
+ "kind": "Content",
2383
+ "text": "string"
2384
+ },
2385
+ {
2386
+ "kind": "Content",
2387
+ "text": ", \n budgetUsd?: "
2388
+ },
2389
+ {
2390
+ "kind": "Content",
2391
+ "text": "number"
2392
+ },
2393
+ {
2394
+ "kind": "Content",
2395
+ "text": ", \n spentUsd?: "
2396
+ },
2397
+ {
2398
+ "kind": "Content",
2399
+ "text": "number"
2400
+ },
2401
+ {
2402
+ "kind": "Content",
2403
+ "text": ", \n detail?: "
2404
+ },
2405
+ {
2406
+ "kind": "Content",
2407
+ "text": "string"
2408
+ },
2409
+ {
2410
+ "kind": "Content",
2411
+ "text": ", \n extra?: "
2412
+ },
2413
+ {
2414
+ "kind": "Content",
2415
+ "text": "{\n otherVendorAvailable?: boolean;\n serverVendor?: string;\n }"
2416
+ },
2417
+ {
2418
+ "kind": "Content",
2419
+ "text": ");"
2420
+ }
2421
+ ],
2422
+ "releaseTag": "Beta",
2423
+ "isProtected": false,
2424
+ "overloadIndex": 1,
2425
+ "parameters": [
2426
+ {
2427
+ "parameterName": "vendorLabel",
2428
+ "parameterTypeTokenRange": {
2429
+ "startIndex": 1,
2430
+ "endIndex": 2
2431
+ },
2432
+ "isOptional": false
2433
+ },
2434
+ {
2435
+ "parameterName": "budgetUsd",
2436
+ "parameterTypeTokenRange": {
2437
+ "startIndex": 3,
2438
+ "endIndex": 4
2439
+ },
2440
+ "isOptional": true
2441
+ },
2442
+ {
2443
+ "parameterName": "spentUsd",
2444
+ "parameterTypeTokenRange": {
2445
+ "startIndex": 5,
2446
+ "endIndex": 6
2447
+ },
2448
+ "isOptional": true
2449
+ },
2450
+ {
2451
+ "parameterName": "detail",
2452
+ "parameterTypeTokenRange": {
2453
+ "startIndex": 7,
2454
+ "endIndex": 8
2455
+ },
2456
+ "isOptional": true
2457
+ },
2458
+ {
2459
+ "parameterName": "extra",
2460
+ "parameterTypeTokenRange": {
2461
+ "startIndex": 9,
2462
+ "endIndex": 10
2463
+ },
2464
+ "isOptional": true
2465
+ }
2466
+ ]
2467
+ },
2468
+ {
2469
+ "kind": "Property",
2470
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#budgetUsd:member",
2471
+ "docComment": "/**\n * The configured spend cap in USD, when the proxy reported one.\n */\n",
2472
+ "excerptTokens": [
2473
+ {
2474
+ "kind": "Content",
2475
+ "text": "readonly budgetUsd?: "
2476
+ },
2477
+ {
2478
+ "kind": "Content",
2479
+ "text": "number"
2480
+ },
2481
+ {
2482
+ "kind": "Content",
2483
+ "text": ";"
2484
+ }
2485
+ ],
2486
+ "isReadonly": true,
2487
+ "isOptional": true,
2488
+ "releaseTag": "Beta",
2489
+ "name": "budgetUsd",
2490
+ "propertyTypeTokenRange": {
2491
+ "startIndex": 1,
2492
+ "endIndex": 2
2493
+ },
2494
+ "isStatic": false,
2495
+ "isProtected": false,
2496
+ "isAbstract": false
2497
+ },
2498
+ {
2499
+ "kind": "Property",
2500
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#detail:member",
2501
+ "docComment": "/**\n * The proxy's human-readable rejection message, when present.\n */\n",
2502
+ "excerptTokens": [
2503
+ {
2504
+ "kind": "Content",
2505
+ "text": "readonly detail?: "
2506
+ },
2507
+ {
2508
+ "kind": "Content",
2509
+ "text": "string"
2510
+ },
2511
+ {
2512
+ "kind": "Content",
2513
+ "text": ";"
2514
+ }
2515
+ ],
2516
+ "isReadonly": true,
2517
+ "isOptional": true,
2518
+ "releaseTag": "Beta",
2519
+ "name": "detail",
2520
+ "propertyTypeTokenRange": {
2521
+ "startIndex": 1,
2522
+ "endIndex": 2
2523
+ },
2524
+ "isStatic": false,
2525
+ "isProtected": false,
2526
+ "isAbstract": false
2527
+ },
2528
+ {
2529
+ "kind": "Property",
2530
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#otherVendorAvailable:member",
2531
+ "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",
2532
+ "excerptTokens": [
2533
+ {
2534
+ "kind": "Content",
2535
+ "text": "readonly otherVendorAvailable?: "
2536
+ },
2537
+ {
2538
+ "kind": "Content",
2539
+ "text": "boolean"
2540
+ },
2541
+ {
2542
+ "kind": "Content",
2543
+ "text": ";"
2544
+ }
2545
+ ],
2546
+ "isReadonly": true,
2547
+ "isOptional": true,
2548
+ "releaseTag": "Beta",
2549
+ "name": "otherVendorAvailable",
2550
+ "propertyTypeTokenRange": {
2551
+ "startIndex": 1,
2552
+ "endIndex": 2
2553
+ },
2554
+ "isStatic": false,
2555
+ "isProtected": false,
2556
+ "isAbstract": false
2557
+ },
2558
+ {
2559
+ "kind": "Property",
2560
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#serverVendor:member",
2561
+ "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",
2562
+ "excerptTokens": [
2563
+ {
2564
+ "kind": "Content",
2565
+ "text": "readonly serverVendor?: "
2566
+ },
2567
+ {
2568
+ "kind": "Content",
2569
+ "text": "string"
2570
+ },
2571
+ {
2572
+ "kind": "Content",
2573
+ "text": ";"
2574
+ }
2575
+ ],
2576
+ "isReadonly": true,
2577
+ "isOptional": true,
2578
+ "releaseTag": "Beta",
2579
+ "name": "serverVendor",
2580
+ "propertyTypeTokenRange": {
2581
+ "startIndex": 1,
2582
+ "endIndex": 2
2583
+ },
2584
+ "isStatic": false,
2585
+ "isProtected": false,
2586
+ "isAbstract": false
2587
+ },
2588
+ {
2589
+ "kind": "Property",
2590
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#spentUsd:member",
2591
+ "docComment": "/**\n * Spend already booked against that cap in USD, when the proxy reported it.\n */\n",
2592
+ "excerptTokens": [
2593
+ {
2594
+ "kind": "Content",
2595
+ "text": "readonly spentUsd?: "
2596
+ },
2597
+ {
2598
+ "kind": "Content",
2599
+ "text": "number"
2600
+ },
2601
+ {
2602
+ "kind": "Content",
2603
+ "text": ";"
2604
+ }
2605
+ ],
2606
+ "isReadonly": true,
2607
+ "isOptional": true,
2608
+ "releaseTag": "Beta",
2609
+ "name": "spentUsd",
2610
+ "propertyTypeTokenRange": {
2611
+ "startIndex": 1,
2612
+ "endIndex": 2
2613
+ },
2614
+ "isStatic": false,
2615
+ "isProtected": false,
2616
+ "isAbstract": false
2617
+ },
2618
+ {
2619
+ "kind": "Property",
2620
+ "canonicalReference": "@genesislcap/foundation-ai!BudgetExhaustedError#vendorLabel:member",
2621
+ "docComment": "/**\n * Vendor label of the transport that was refused (e.g. `'Anthropic'`).\n */\n",
2622
+ "excerptTokens": [
2623
+ {
2624
+ "kind": "Content",
2625
+ "text": "readonly vendorLabel: "
2626
+ },
2627
+ {
2628
+ "kind": "Content",
2629
+ "text": "string"
2630
+ },
2631
+ {
2632
+ "kind": "Content",
2633
+ "text": ";"
2634
+ }
2635
+ ],
2636
+ "isReadonly": true,
2637
+ "isOptional": false,
2638
+ "releaseTag": "Beta",
2639
+ "name": "vendorLabel",
2640
+ "propertyTypeTokenRange": {
2641
+ "startIndex": 1,
2642
+ "endIndex": 2
2643
+ },
2644
+ "isStatic": false,
2645
+ "isProtected": false,
2646
+ "isAbstract": false
2647
+ }
2648
+ ],
2649
+ "extendsTokenRange": {
2650
+ "startIndex": 1,
2651
+ "endIndex": 2
2652
+ },
2653
+ "implementsTokenRanges": []
2654
+ },
2315
2655
  {
2316
2656
  "kind": "TypeAlias",
2317
2657
  "canonicalReference": "@genesislcap/foundation-ai!CachePolicy:type",
@@ -3147,7 +3487,16 @@
3147
3487
  },
3148
3488
  {
3149
3489
  "kind": "Content",
3150
- "text": ";\n} | {\n reason: 'agent-handoff';\n summary: string;\n remainingTask: string;\n}"
3490
+ "text": ";\n budget?: {\n budgetUsd?: number;\n spentUsd?: number;\n vendorLabel: string;\n vendor?: "
3491
+ },
3492
+ {
3493
+ "kind": "Reference",
3494
+ "text": "AIProviderType",
3495
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
3496
+ },
3497
+ {
3498
+ "kind": "Content",
3499
+ "text": ";\n otherVendorAvailable?: boolean;\n };\n} | {\n reason: 'agent-handoff';\n summary: string;\n remainingTask: string;\n}"
3151
3500
  },
3152
3501
  {
3153
3502
  "kind": "Content",
@@ -3159,7 +3508,7 @@
3159
3508
  "name": "ChatDriverResult",
3160
3509
  "typeTokenRange": {
3161
3510
  "startIndex": 1,
3162
- "endIndex": 4
3511
+ "endIndex": 6
3163
3512
  }
3164
3513
  },
3165
3514
  {
@@ -6430,6 +6779,33 @@
6430
6779
  ],
6431
6780
  "extendsTokenRanges": []
6432
6781
  },
6782
+ {
6783
+ "kind": "Variable",
6784
+ "canonicalReference": "@genesislcap/foundation-ai!DEFAULT_BUDGET_EXHAUSTED_MESSAGE:var",
6785
+ "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",
6786
+ "excerptTokens": [
6787
+ {
6788
+ "kind": "Content",
6789
+ "text": "DEFAULT_BUDGET_EXHAUSTED_MESSAGE = "
6790
+ },
6791
+ {
6792
+ "kind": "Content",
6793
+ "text": "\"You've reached your AI usage limit. Contact your administrator to raise it.\""
6794
+ }
6795
+ ],
6796
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
6797
+ "initializerTokenRange": {
6798
+ "startIndex": 1,
6799
+ "endIndex": 2
6800
+ },
6801
+ "isReadonly": true,
6802
+ "releaseTag": "Beta",
6803
+ "name": "DEFAULT_BUDGET_EXHAUSTED_MESSAGE",
6804
+ "variableTypeTokenRange": {
6805
+ "startIndex": 0,
6806
+ "endIndex": 0
6807
+ }
6808
+ },
6433
6809
  {
6434
6810
  "kind": "TypeAlias",
6435
6811
  "canonicalReference": "@genesislcap/foundation-ai!FieldLike:type",
@@ -7677,6 +8053,52 @@
7677
8053
  "parameters": [],
7678
8054
  "name": "isAIFeatureEnabled"
7679
8055
  },
8056
+ {
8057
+ "kind": "Function",
8058
+ "canonicalReference": "@genesislcap/foundation-ai!isBudgetedVendor:function(1)",
8059
+ "docComment": "/**\n * Whether this vendor's spend is metered by the proxy — see {@link BUDGETED_VENDORS}.\n *\n * @beta\n */\n",
8060
+ "excerptTokens": [
8061
+ {
8062
+ "kind": "Content",
8063
+ "text": "isBudgetedVendor: (vendor: "
8064
+ },
8065
+ {
8066
+ "kind": "Reference",
8067
+ "text": "AIProviderType",
8068
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
8069
+ },
8070
+ {
8071
+ "kind": "Content",
8072
+ "text": " | undefined"
8073
+ },
8074
+ {
8075
+ "kind": "Content",
8076
+ "text": ") => "
8077
+ },
8078
+ {
8079
+ "kind": "Content",
8080
+ "text": "boolean"
8081
+ }
8082
+ ],
8083
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
8084
+ "returnTypeTokenRange": {
8085
+ "startIndex": 4,
8086
+ "endIndex": 5
8087
+ },
8088
+ "releaseTag": "Beta",
8089
+ "overloadIndex": 1,
8090
+ "parameters": [
8091
+ {
8092
+ "parameterName": "vendor",
8093
+ "parameterTypeTokenRange": {
8094
+ "startIndex": 1,
8095
+ "endIndex": 3
8096
+ },
8097
+ "isOptional": false
8098
+ }
8099
+ ],
8100
+ "name": "isBudgetedVendor"
8101
+ },
7680
8102
  {
7681
8103
  "kind": "Function",
7682
8104
  "canonicalReference": "@genesislcap/foundation-ai!isChatToolCallUnknown:function(1)",
@@ -9492,7 +9914,7 @@
9492
9914
  {
9493
9915
  "kind": "TypeAlias",
9494
9916
  "canonicalReference": "@genesislcap/foundation-ai!SubAgentFailureReason:type",
9495
- "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",
9917
+ "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",
9496
9918
  "excerptTokens": [
9497
9919
  {
9498
9920
  "kind": "Content",
@@ -9500,7 +9922,7 @@
9500
9922
  },
9501
9923
  {
9502
9924
  "kind": "Content",
9503
- "text": "'max_iterations' | 'malformed_tool_call' | 'empty_response' | 'unknown_tool_limit' | 'timeout' | 'response_truncated' | 'refusal'"
9925
+ "text": "'max_iterations' | 'malformed_tool_call' | 'empty_response' | 'unknown_tool_limit' | 'timeout' | 'response_truncated' | 'refusal' | 'budget_exhausted'"
9504
9926
  },
9505
9927
  {
9506
9928
  "kind": "Content",
@@ -9741,7 +10163,7 @@
9741
10163
  {
9742
10164
  "kind": "TypeAlias",
9743
10165
  "canonicalReference": "@genesislcap/foundation-ai!TurnFailureReason:type",
9744
- "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",
10166
+ "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",
9745
10167
  "excerptTokens": [
9746
10168
  {
9747
10169
  "kind": "Content",
@@ -9749,7 +10171,7 @@
9749
10171
  },
9750
10172
  {
9751
10173
  "kind": "Content",
9752
- "text": "'exception' | 'malformed-function-call' | 'empty-response' | 'unknown-tool-limit' | 'max-iterations' | 'response-truncated' | 'refusal'"
10174
+ "text": "'exception' | 'malformed-function-call' | 'empty-response' | 'unknown-tool-limit' | 'max-iterations' | 'response-truncated' | 'refusal' | 'budget-exhausted'"
9753
10175
  },
9754
10176
  {
9755
10177
  "kind": "Content",
@@ -9763,6 +10185,111 @@
9763
10185
  "startIndex": 1,
9764
10186
  "endIndex": 2
9765
10187
  }
10188
+ },
10189
+ {
10190
+ "kind": "Variable",
10191
+ "canonicalReference": "@genesislcap/foundation-ai!VENDOR_LABELS:var",
10192
+ "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",
10193
+ "excerptTokens": [
10194
+ {
10195
+ "kind": "Content",
10196
+ "text": "VENDOR_LABELS: "
10197
+ },
10198
+ {
10199
+ "kind": "Reference",
10200
+ "text": "Readonly",
10201
+ "canonicalReference": "!Readonly:type"
10202
+ },
10203
+ {
10204
+ "kind": "Content",
10205
+ "text": "<"
10206
+ },
10207
+ {
10208
+ "kind": "Reference",
10209
+ "text": "Record",
10210
+ "canonicalReference": "!Record:type"
10211
+ },
10212
+ {
10213
+ "kind": "Content",
10214
+ "text": "<"
10215
+ },
10216
+ {
10217
+ "kind": "Reference",
10218
+ "text": "Exclude",
10219
+ "canonicalReference": "!Exclude:type"
10220
+ },
10221
+ {
10222
+ "kind": "Content",
10223
+ "text": "<"
10224
+ },
10225
+ {
10226
+ "kind": "Reference",
10227
+ "text": "AIProviderType",
10228
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
10229
+ },
10230
+ {
10231
+ "kind": "Content",
10232
+ "text": ", 'none'>, string>>"
10233
+ }
10234
+ ],
10235
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
10236
+ "isReadonly": true,
10237
+ "releaseTag": "Beta",
10238
+ "name": "VENDOR_LABELS",
10239
+ "variableTypeTokenRange": {
10240
+ "startIndex": 1,
10241
+ "endIndex": 9
10242
+ }
10243
+ },
10244
+ {
10245
+ "kind": "Function",
10246
+ "canonicalReference": "@genesislcap/foundation-ai!vendorTypeOfLabel:function(1)",
10247
+ "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",
10248
+ "excerptTokens": [
10249
+ {
10250
+ "kind": "Content",
10251
+ "text": "export declare function vendorTypeOfLabel(label?: "
10252
+ },
10253
+ {
10254
+ "kind": "Content",
10255
+ "text": "string"
10256
+ },
10257
+ {
10258
+ "kind": "Content",
10259
+ "text": "): "
10260
+ },
10261
+ {
10262
+ "kind": "Reference",
10263
+ "text": "AIProviderType",
10264
+ "canonicalReference": "@genesislcap/foundation-ai!AIProviderType:type"
10265
+ },
10266
+ {
10267
+ "kind": "Content",
10268
+ "text": " | undefined"
10269
+ },
10270
+ {
10271
+ "kind": "Content",
10272
+ "text": ";"
10273
+ }
10274
+ ],
10275
+ "fileUrlPath": "src/transports/budget-exhausted-error.ts",
10276
+ "returnTypeTokenRange": {
10277
+ "startIndex": 3,
10278
+ "endIndex": 5
10279
+ },
10280
+ "releaseTag": "Beta",
10281
+ "overloadIndex": 1,
10282
+ "parameters": [
10283
+ {
10284
+ "parameterName": "label",
10285
+ "parameterTypeTokenRange": {
10286
+ "startIndex": 1,
10287
+ "endIndex": 2
10288
+ },
10289
+ "isOptional": true
10290
+ }
10291
+ ],
10292
+ "name": "vendorTypeOfLabel"
9766
10293
  }
9767
10294
  ]
9768
10295
  }