agl 40.4.0 → 40.5.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 (156) hide show
  1. package/changelog.md +33 -0
  2. package/dist_serve/bundle.js +2228 -2177
  3. package/dist_serve/bundle.js.map +2 -2
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/aglapi/aglapisurface.document.js +2 -2
  6. package/dist_ts/classes.accountsactions.d.ts +11 -1
  7. package/dist_ts/classes.accountsactions.js +59 -2
  8. package/dist_ts/classes.controller.js +5 -5
  9. package/dist_ts/classes.flexaccountaccess.js +3 -2
  10. package/dist_ts/classes.harnessreleases.d.ts +14 -4
  11. package/dist_ts/classes.harnessreleases.js +28 -4
  12. package/dist_ts/classes.inferencegateway.d.ts +28 -3
  13. package/dist_ts/classes.inferencegateway.js +177 -61
  14. package/dist_ts/classes.inferenceplacement.d.ts +5 -3
  15. package/dist_ts/classes.inferenceplacement.js +2 -2
  16. package/dist_ts/classes.inferencerouter.d.ts +349 -91
  17. package/dist_ts/classes.inferencerouter.js +804 -297
  18. package/dist_ts/classes.inferenceservice.d.ts +14 -2
  19. package/dist_ts/classes.inferenceservice.js +53 -14
  20. package/dist_ts/classes.inferencespend.d.ts +339 -0
  21. package/dist_ts/classes.inferencespend.js +815 -0
  22. package/dist_ts/classes.inferenceusagecache.d.ts +8 -0
  23. package/dist_ts/classes.inferenceusagecache.js +9 -5
  24. package/dist_ts/classes.inferenceusagetap.d.ts +50 -9
  25. package/dist_ts/classes.inferenceusagetap.js +185 -31
  26. package/dist_ts/classes.proxycontrol.d.ts +3 -2
  27. package/dist_ts/classes.proxycontrol.js +1 -2
  28. package/dist_ts/classes.proxydaemon.js +3 -2
  29. package/dist_ts/classes.proxystore.d.ts +6 -0
  30. package/dist_ts/classes.proxystore.js +11 -2
  31. package/dist_ts/fleet/classes.claudeworkers.d.ts +5 -4
  32. package/dist_ts/fleet/classes.claudeworkers.js +10 -9
  33. package/dist_ts/functions.claudecoderelease.d.ts +12 -6
  34. package/dist_ts/functions.claudecoderelease.js +13 -7
  35. package/dist_ts/functions.inferencecli.js +12 -8
  36. package/dist_ts/interfaces.auth.d.ts +1 -1
  37. package/dist_ts/interfaces.auth.js +3 -1
  38. package/dist_ts/plugins.d.ts +3 -1
  39. package/dist_ts/plugins.js +7 -2
  40. package/dist_ts_authswitch/authority-apikeys.d.ts +32 -0
  41. package/dist_ts_authswitch/authority-apikeys.js +65 -0
  42. package/dist_ts_authswitch/authority-contract.d.ts +172 -33
  43. package/dist_ts_authswitch/authority-contract.js +54 -1
  44. package/dist_ts_authswitch/authority-overview.js +8 -1
  45. package/dist_ts_authswitch/authority-placement.d.ts +7 -2
  46. package/dist_ts_authswitch/authority-placement.js +10 -3
  47. package/dist_ts_authswitch/authority-runtime-contract.d.ts +2 -2
  48. package/dist_ts_authswitch/authority-spend.d.ts +103 -0
  49. package/dist_ts_authswitch/authority-spend.js +110 -0
  50. package/dist_ts_authswitch/authority-subscription.d.ts +12 -1
  51. package/dist_ts_authswitch/authority-subscription.js +24 -1
  52. package/dist_ts_authswitch/authority-usagelimit.d.ts +7 -4
  53. package/dist_ts_authswitch/authority-usagelimit.js +89 -39
  54. package/dist_ts_authswitch/authority-usagewindows.d.ts +4 -1
  55. package/dist_ts_authswitch/authority-usagewindows.js +41 -16
  56. package/dist_ts_authswitch/classes.authoritybroker.d.ts +30 -19
  57. package/dist_ts_authswitch/classes.authoritybroker.js +152 -61
  58. package/dist_ts_authswitch/classes.authoritycli.d.ts +9 -4
  59. package/dist_ts_authswitch/classes.authoritycli.js +99 -30
  60. package/dist_ts_authswitch/classes.authorityclient.d.ts +24 -7
  61. package/dist_ts_authswitch/classes.authorityclient.js +45 -15
  62. package/dist_ts_authswitch/classes.authoritydaemon.js +39 -12
  63. package/dist_ts_authswitch/classes.authoritydatabase.d.ts +28 -5
  64. package/dist_ts_authswitch/classes.authoritydatabase.js +171 -9
  65. package/dist_ts_authswitch/classes.authorityimport.js +2 -2
  66. package/dist_ts_authswitch/classes.authoritymodels.d.ts +71 -3
  67. package/dist_ts_authswitch/classes.authoritymodels.js +249 -20
  68. package/dist_ts_authswitch/classes.authorityregistry.js +4 -2
  69. package/dist_ts_authswitch/classes.authorityusage.d.ts +69 -7
  70. package/dist_ts_authswitch/classes.authorityusage.js +118 -4
  71. package/dist_ts_authswitch/classes.claudenative.d.ts +23 -8
  72. package/dist_ts_authswitch/classes.claudenative.js +49 -21
  73. package/dist_ts_authswitch/classes.claudesignin.d.ts +14 -2
  74. package/dist_ts_authswitch/classes.claudesignin.js +72 -23
  75. package/dist_ts_authswitch/classes.codexmanaged.d.ts +8 -0
  76. package/dist_ts_authswitch/classes.codexmanaged.js +33 -18
  77. package/dist_ts_authswitch/classes.harnessprocesses.d.ts +8 -0
  78. package/dist_ts_authswitch/classes.harnessprocesses.js +41 -9
  79. package/dist_ts_authswitch/testsupport.d.ts +24 -5
  80. package/dist_ts_authswitch/testsupport.js +56 -12
  81. package/dist_ts_authswitch/ts_migration/0011_account_api_key_fields.d.ts +12 -0
  82. package/dist_ts_authswitch/ts_migration/0011_account_api_key_fields.js +21 -0
  83. package/dist_ts_authswitch/ts_migration/index.js +3 -1
  84. package/dist_ts_interfaces/functions.authorityscope.d.ts +18 -7
  85. package/dist_ts_interfaces/functions.authorityscope.js +35 -8
  86. package/dist_ts_interfaces/interfaces.d.ts +1 -1
  87. package/dist_ts_interfaces/interfaces.js +1 -1
  88. package/dist_ts_interfaces/plugins.d.ts +2 -1
  89. package/dist_ts_interfaces/plugins.js +7 -4
  90. package/dist_ts_interfaces/requests.accounts.d.ts +56 -0
  91. package/dist_ts_interfaces/requests.accounts.js +11 -1
  92. package/dist_ts_interfaces/requests.d.ts +2 -2
  93. package/dist_ts_interfaces/requests.js +1 -1
  94. package/docs/authswitch.md +75 -12
  95. package/package.json +5 -5
  96. package/readme.md +199 -22
  97. package/ts/00_commitinfo_data.ts +1 -1
  98. package/ts/aglapi/aglapisurface.document.ts +2 -2
  99. package/ts/classes.accountsactions.ts +75 -1
  100. package/ts/classes.controller.ts +4 -4
  101. package/ts/classes.flexaccountaccess.ts +2 -1
  102. package/ts/classes.harnessreleases.ts +25 -4
  103. package/ts/classes.inferencegateway.ts +184 -64
  104. package/ts/classes.inferenceplacement.ts +6 -4
  105. package/ts/classes.inferencerouter.ts +1026 -314
  106. package/ts/classes.inferenceservice.ts +56 -13
  107. package/ts/classes.inferencespend.ts +870 -0
  108. package/ts/classes.inferenceusagecache.ts +16 -4
  109. package/ts/classes.inferenceusagetap.ts +159 -34
  110. package/ts/classes.proxycontrol.ts +3 -3
  111. package/ts/classes.proxydaemon.ts +2 -1
  112. package/ts/classes.proxystore.ts +11 -1
  113. package/ts/fleet/classes.claudeworkers.ts +9 -8
  114. package/ts/functions.claudecoderelease.ts +13 -6
  115. package/ts/functions.inferencecli.ts +11 -7
  116. package/ts/interfaces.auth.ts +2 -0
  117. package/ts/plugins.ts +7 -0
  118. package/ts_authswitch/authority-apikeys.ts +85 -0
  119. package/ts_authswitch/authority-contract.ts +201 -34
  120. package/ts_authswitch/authority-overview.ts +7 -0
  121. package/ts_authswitch/authority-placement.ts +12 -4
  122. package/ts_authswitch/authority-runtime-contract.ts +2 -2
  123. package/ts_authswitch/authority-spend.ts +168 -0
  124. package/ts_authswitch/authority-subscription.ts +28 -1
  125. package/ts_authswitch/authority-usagelimit.ts +95 -40
  126. package/ts_authswitch/authority-usagewindows.ts +42 -17
  127. package/ts_authswitch/classes.authoritybroker.ts +161 -68
  128. package/ts_authswitch/classes.authoritycli.ts +112 -32
  129. package/ts_authswitch/classes.authorityclient.ts +66 -19
  130. package/ts_authswitch/classes.authoritydaemon.ts +43 -13
  131. package/ts_authswitch/classes.authoritydatabase.ts +178 -8
  132. package/ts_authswitch/classes.authorityimport.ts +1 -1
  133. package/ts_authswitch/classes.authoritymodels.ts +165 -19
  134. package/ts_authswitch/classes.authorityregistry.ts +3 -0
  135. package/ts_authswitch/classes.authorityusage.ts +171 -10
  136. package/ts_authswitch/classes.claudenative.ts +51 -22
  137. package/ts_authswitch/classes.claudesignin.ts +74 -24
  138. package/ts_authswitch/classes.codexmanaged.ts +30 -16
  139. package/ts_authswitch/classes.harnessprocesses.ts +39 -8
  140. package/ts_authswitch/native-contracts.md +82 -21
  141. package/ts_authswitch/readme.md +1 -1
  142. package/ts_authswitch/testsupport.ts +58 -11
  143. package/ts_authswitch/ts_migration/0011_account_api_key_fields.ts +20 -0
  144. package/ts_authswitch/ts_migration/index.ts +2 -0
  145. package/ts_interfaces/functions.authorityscope.ts +43 -8
  146. package/ts_interfaces/interfaces.ts +1 -1
  147. package/ts_interfaces/plugins.ts +6 -3
  148. package/ts_interfaces/requests.accounts.ts +48 -0
  149. package/ts_interfaces/requests.ts +4 -0
  150. package/ts_web/00_commitinfo_data.ts +1 -1
  151. package/ts_web/accounts.plugins.ts +7 -1
  152. package/ts_web/accounts.templates.ts +20 -11
  153. package/ts_web/classes.accountsreadmodel.ts +326 -71
  154. package/ts_web/elements.accounts.ts +219 -20
  155. package/ts_web/elements.codexconnections.ts +3 -1
  156. package/ts_web/plugins.ts +4 -2
@@ -1,6 +1,7 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import type { TInferenceCredential, TInferenceProvider } from './functions.proxycontract.js';
3
3
  import type { IGatewayAuthorityAccess } from './classes.flexaccountaccess.js';
4
+ import type { IInferenceSpendRecord } from './classes.inferencespend.js';
4
5
  export type TInferenceModel = plugins.openAiProvider.IOpenAiChatGptModel;
5
6
  /** A model a Claude account or an Anthropic API key may use, as the Anthropic API lists it. */
6
7
  export type TInferenceClaudeModel = plugins.anthropicProvider.IAnthropicOAuthModel;
@@ -15,30 +16,49 @@ export declare const parseHttpDate: (valueArg: string, nowArg: number) => number
15
16
  * counted from `nowArg` (a date already past is 0). `undefined` when the header is absent or neither form.
16
17
  */
17
18
  export declare const inferenceRetryAfterSeconds: (valueArg: string | null, nowArg: number) => number | undefined;
19
+ /**
20
+ * The credential tiers a pool is served from, best first: the one a policy names alone, or with none the provider's
21
+ * subscription sign-ins first and its API keys after them. A key takes a request only when no subscription can take it,
22
+ * for whatever reason, and a session placed on a key returns to a subscription at the next request one can take.
23
+ */
24
+ export declare const inferenceCredentialTiers: (credentialArg: TInferenceCredential | undefined) => readonly TInferenceCredential[];
18
25
  export interface IInferenceAccount {
19
26
  id: string;
20
27
  label: string;
21
28
  revision: string;
22
29
  providerId: TInferenceProvider;
23
- /** What the account authenticates with: a subscription sign-in, or an Anthropic API key. */
30
+ /** What the account authenticates with: a subscription sign-in, or an API key. */
24
31
  credential: TInferenceCredential;
25
32
  /** The account's usage threshold (`IAuthSwitchAccount.maxUsagePercent`); `null` when unset. */
26
33
  maxUsagePercent: number | null;
27
34
  reserveLiftUntil: string | null;
28
35
  /** When the account's subscription ends (`IAuthSwitchAccount.lapsesAt`); `null` while it renews. */
29
36
  lapsesAt: string | null;
37
+ /**
38
+ * An API key's monthly spend limit in micro-dollars (`IAuthSwitchAccount.spendLimitMicroUsd`); `null` without one,
39
+ * and for a sign-in. It decides only whether a key whose spend the proxy holds no reading of may take work: one with
40
+ * a limit may not (`spend_unread`). A spend reading carries the limit the key is judged against.
41
+ */
42
+ spendLimitMicroUsd: number | null;
43
+ /**
44
+ * The login the account's requests are served on (the account service's gateway login of it), as the pool was read;
45
+ * `null` when the account service names none. An API key's spend is reported on it.
46
+ */
47
+ loginId: string | null;
30
48
  }
31
49
  export interface IInferenceRoutingPolicy {
32
50
  /** The pool's provider; omitted means ChatGPT accounts (`openai`). */
33
51
  provider?: TInferenceProvider;
34
52
  /**
35
- * What the pool's accounts authenticate with; omitted means subscription sign-ins. `api_key` is an Anthropic pool's
36
- * alone: its Anthropic API keys, which Flex's Claude models run on, never a Claude account.
53
+ * What the pool's accounts authenticate with. Omitted, a Claude pool is served by its Claude accounts first and its
54
+ * Anthropic API keys when no Claude account can take a request (`inferenceCredentialTiers`), a ChatGPT pool by its
55
+ * ChatGPT accounts. `subscription` names the sign-ins alone; `api_key` is an Anthropic pool's alone: its Anthropic API
56
+ * keys, which Flex's Claude models run on, never a Claude account.
37
57
  */
38
58
  credential?: TInferenceCredential;
39
59
  /** Omitted means all eligible authority-owned accounts of the provider. */
40
60
  accountIds?: readonly string[];
41
- /** Exact current provider plan names; omission accepts every entitled plan. */
61
+ /** Exact current provider plan names; omission accepts every entitled plan. A key has no plan, so this leaves keys out. */
42
62
  plans?: readonly string[];
43
63
  }
44
64
  export interface IInferenceRouterOptions {
@@ -64,18 +84,48 @@ export interface IInferenceRouterOptions {
64
84
  */
65
85
  reportAccount?: (changeArg: IInferenceAccountChange) => void;
66
86
  /**
67
- * Tells the account service that Anthropic refused a request on the Claude account with a billing error (HTTP 402
68
- * `billing_error`) at `observedAtArg`; resolves once the service holds it, so the account's usage carries it from then
69
- * on (`IAuthSwitchUsageSnapshot.paymentProblem`). Unset, the router holds the account out of rotation itself.
87
+ * Tells the account service that the provider refused a request on the account for a payment problem at
88
+ * `observedAtArg` -- a Claude account's billing error (HTTP 402 `billing_error`), or an API key's refusal for
89
+ * `causeArg` (`TInferencePaymentCause`); resolves once the service holds it, so the account's usage carries it from
90
+ * then on (`IAuthSwitchUsageSnapshot.paymentProblem`). Unset, the router holds the account out of rotation itself.
70
91
  */
71
- reportPaymentProblem?: (accountIdArg: string, observedAtArg: string) => Promise<void>;
92
+ reportPaymentProblem?: (accountIdArg: string, observedAtArg: string, causeArg: TInferencePaymentCause) => Promise<void>;
72
93
  /**
73
94
  * Where a payment problem the account service could not be told of is reported: once per account, never per request.
74
95
  * One line on standard error, the proxy's journal, when unset.
75
96
  */
76
97
  reportPaymentFailure?: (failureArg: IInferencePaymentReportFailure) => void;
98
+ /**
99
+ * Where the spend of the answers relayed on API keys is kept until the accounts service acknowledged it
100
+ * (`InferenceSpendOutbox`). A key's usage arrives judged with that spend already (`InferenceUsageCache` `view`).
101
+ */
102
+ spend?: IInferenceSpendLedger;
77
103
  now?: () => number;
78
104
  }
105
+ /** What the router asks of the proxy's spend outbox (`InferenceSpendOutbox`). */
106
+ export interface IInferenceSpendLedger {
107
+ /**
108
+ * Counts a relayed answer's spend and keeps it until the accounts service acknowledged it, reported on the login the
109
+ * route was served on (`IInferenceClaudeRoute.loginId`); `null` when none could be named, which still counts.
110
+ */
111
+ record(accountIdArg: string, loginIdArg: string | null, recordArg: IInferenceSpendRecord): void;
112
+ /**
113
+ * Whether spend relayed on the key is recorded nowhere yet: the accounts service refused a report the outbox still
114
+ * holds, an answer is in neither the outbox's store, whose write failed, nor the ledger, or no login of the key could
115
+ * be named for an answer, so it cannot be reported.
116
+ */
117
+ held(accountIdArg: string): boolean;
118
+ /** Says that an answer relayed on the key ended before its provider stated its usage, so nothing of it is counted. */
119
+ uncounted(accountIdArg: string, modelArg: string): void;
120
+ }
121
+ /**
122
+ * What a payment problem's report adds for the account service (`IReq_AuthSwitchReportPaymentProblem`): the cause of an
123
+ * API key's spend cap or quota, nothing for a billing error, whose cause a missing one means -- so a Claude account's
124
+ * report is the one an accounts service of 40.3.0, which refuses a cause, takes.
125
+ */
126
+ export declare const inferencePaymentReportOptions: (causeArg: TInferencePaymentCause) => {
127
+ cause?: Exclude<TInferencePaymentCause, 'billing'>;
128
+ };
79
129
  /** A payment problem the account service could not be told of, as the proxy reports it: the account by its id's digest. */
80
130
  export interface IInferencePaymentReportFailure {
81
131
  /** The first 12 hex characters of the SHA-256 digest of the account's id. */
@@ -87,16 +137,41 @@ export interface IInferencePaymentReportFailure {
87
137
  * Why an account takes no request although no limit says so. `usage_unread`: the account service holds no usage reading
88
138
  * the proxy routes on. `catalog_failed`: the account's model list could not be read and none read before is held for its
89
139
  * revision. `needs_login`: its sign-in needs renewing. `access_failed`: the account service answered no access for it.
90
- * `model_missing`: its model list lacks the requested model. `payment_problem`: Anthropic refused a request on the Claude
91
- * account with a billing error (HTTP 402 `billing_error`); it takes none until a usage read or a re-check clears it.
140
+ * `model_missing`: its model list lacks the requested model. `payment_problem`: the provider refused a request on the
141
+ * account for a payment problem (`TInferencePaymentCause`); it takes none until a usage read or a re-check clears it, or
142
+ * a spend cap ends. `spend_unread`: an API key with a monthly spend limit whose spend the proxy holds no reading of --
143
+ * the account service could not read its ledger (`spend_unavailable`) or did not answer -- so it may be at its limit.
144
+ * `spend_unrecorded`: an API key with a monthly spend limit whose spend the proxy relayed is recorded nowhere yet --
145
+ * the account service refused its report, which the proxy holds to send again, an answer is held in neither the
146
+ * proxy's store, whose write failed, nor the account service's ledger, or no login of the key could be named for an
147
+ * answer to report it on (`InferenceSpendOutbox.held`). `model_unpriced`: an API key with a monthly spend limit, and a
148
+ * request whose model, service tier, speed, region or server tool the proxy's price table cannot price (`failure` names
149
+ * it), so its cost could not be counted against the limit.
150
+ */
151
+ export type TInferenceUnavailableReason = 'usage_unread' | 'catalog_failed' | 'needs_login' | 'access_failed' | 'model_missing' | 'payment_problem' | 'spend_unread' | 'spend_unrecorded' | 'model_unpriced';
152
+ /**
153
+ * Why a provider refused a request on an account for a payment problem, as the gateway reads the provider's answer.
154
+ * `billing`: Anthropic's billing error (HTTP 402 `billing_error`), on a Claude account's subscription or an Anthropic API
155
+ * key's organization. `spend_cap`: an Anthropic API key's organization reached its usage tier's monthly spend cap (HTTP
156
+ * 429 `rate_limit_error` with `error.details.error_code: "enforced_spend_limit_reached"`), which holds until 00:00 UTC on
157
+ * the first day of the next month. `quota`: OpenAI refused an API key for its quota (HTTP 429 `insufficient_quota`): no
158
+ * prepaid credit left, or an organization or project spend limit or the organization's usage limit reached.
92
159
  */
93
- export type TInferenceUnavailableReason = 'usage_unread' | 'catalog_failed' | 'needs_login' | 'access_failed' | 'model_missing' | 'payment_problem';
160
+ export type TInferencePaymentCause = plugins.authswitchAuthority.TAuthSwitchPaymentProblemCause;
94
161
  export interface IInferenceAccountUnavailable {
95
162
  reason: TInferenceUnavailableReason;
96
- /** `usage_unread`: when the account service reads the account's usage next, or `null` when it does not say. */
163
+ /**
164
+ * `usage_unread`: when the account service reads the account's usage next, or `null` when it does not say.
165
+ * `payment_problem` of a spend cap: when the cap ends and the account takes requests again.
166
+ */
97
167
  retryAt?: string | null;
98
- /** `catalog_failed`: the HTTP status the model list answered with (`HTTP 429`), or what failed instead. */
168
+ /**
169
+ * `catalog_failed`: the HTTP status the model list answered with (`HTTP 429`), or what failed instead.
170
+ * `model_unpriced`: what of the request the price table cannot price.
171
+ */
99
172
  failure?: string;
173
+ /** `payment_problem`: what the provider refused the account for. */
174
+ cause?: TInferencePaymentCause;
100
175
  }
101
176
  /** A change of why an account is refused, as the proxy reports it: the account by its id's digest, never its id. */
102
177
  export interface IInferenceAccountChange {
@@ -115,6 +190,17 @@ export interface IInferenceAccountChange {
115
190
  * 429 (`cooling`).
116
191
  */
117
192
  export type TInferenceAccountLimit = 'exhausted' | 'reserved' | 'cooling';
193
+ /** An API key a request found held by its monthly spend limit: the account, and its month as the account service read it. */
194
+ export interface IInferenceSpendLimited {
195
+ accountId: string;
196
+ label: string;
197
+ spend: plugins.authswitchSpend.IAuthSwitchSpendReading;
198
+ }
199
+ /**
200
+ * What a refusal says of the API keys it found held by their monthly spend limit: each key with its month, and when the
201
+ * first of them takes requests again. Empty without any.
202
+ */
203
+ export declare const inferenceSpendLimitText: (keysArg: readonly IInferenceSpendLimited[]) => string;
118
204
  export declare class InferenceRoutingError extends Error {
119
205
  readonly status: number;
120
206
  readonly code: string;
@@ -125,8 +211,13 @@ export declare class InferenceRoutingError extends Error {
125
211
  readonly accountLimit?: TInferenceAccountLimit | undefined;
126
212
  /** The account a refusal for a reason of its own (`reason`) names. */
127
213
  readonly accountId?: string | undefined;
128
- /** For `no_eligible_accounts` of a pinned account: why that account takes no request, though no limit says so. */
129
- readonly reason?: TInferenceUnavailableReason | undefined;
214
+ /**
215
+ * For `no_eligible_accounts` of a pinned account: why that account takes no request though no limit says so, in
216
+ * full -- a payment problem's cause, say.
217
+ */
218
+ readonly unavailable?: IInferenceAccountUnavailable | undefined;
219
+ /** For `accounts_exhausted`: the API keys found held by their monthly spend limit, which its message names. */
220
+ readonly spendLimited: readonly IInferenceSpendLimited[];
130
221
  constructor(status: number, code: string, messageArg: string, retryAfterSeconds?: number | undefined,
131
222
  /** For `accounts_exhausted`: the provider plans of the accounts found at their limits. */
132
223
  plans?: readonly string[],
@@ -134,17 +225,39 @@ export declare class InferenceRoutingError extends Error {
134
225
  accountLimit?: TInferenceAccountLimit | undefined,
135
226
  /** The account a refusal for a reason of its own (`reason`) names. */
136
227
  accountId?: string | undefined,
137
- /** For `no_eligible_accounts` of a pinned account: why that account takes no request, though no limit says so. */
138
- reason?: TInferenceUnavailableReason | undefined);
228
+ /**
229
+ * For `no_eligible_accounts` of a pinned account: why that account takes no request though no limit says so, in
230
+ * full -- a payment problem's cause, say.
231
+ */
232
+ unavailable?: IInferenceAccountUnavailable | undefined,
233
+ /** For `accounts_exhausted`: the API keys found held by their monthly spend limit, which its message names. */
234
+ spendLimited?: readonly IInferenceSpendLimited[]);
235
+ /** The reason of `unavailable`: a refusal names a reason of its own only with the refusal in full. */
236
+ get reason(): TInferenceUnavailableReason | undefined;
139
237
  }
238
+ /**
239
+ * A ChatGPT account or an OpenAI API key chosen for one request: a ChatGPT sign-in through the ChatGPT backend, a key
240
+ * through OpenAI's API (`OPENAI_API_BASE_URL`), each through `connection`.
241
+ */
140
242
  export interface IInferenceRoute {
141
243
  provider: 'openai';
244
+ /** What the chosen account authenticates with: a ChatGPT sign-in, or an OpenAI API key. */
245
+ credential: TInferenceCredential;
142
246
  accountId: string;
143
247
  plan: string;
144
248
  /** The account's placement score when it was chosen (`IAuthSwitchPlacement.score`). */
145
249
  score: number | null;
146
250
  model: TInferenceModel;
147
251
  connection: plugins.openAiProvider.IOpenAiModelConnection;
252
+ /** The session left its account on a later credential tier for one of a better tier (`returnToBetterTier`). */
253
+ returned: boolean;
254
+ /** The login the account was served on as the pool was read (`IInferenceAccount.loginId`): an answer's spend names it. */
255
+ loginId: string | null;
256
+ /**
257
+ * An OpenAI API key with a monthly spend limit: the request goes out as the price check read it, on the service tier it
258
+ * was checked for (`inferenceOpenAiCheckedBytes`).
259
+ */
260
+ spendLimit: boolean;
148
261
  release: () => void;
149
262
  }
150
263
  /**
@@ -160,6 +273,18 @@ export interface IInferenceClaudeRoute {
160
273
  score: number | null;
161
274
  model: TInferenceClaudeModel;
162
275
  connection: plugins.anthropicProvider.IAnthropicModelConnection;
276
+ /**
277
+ * The session's account is an Anthropic API key, and a Claude account of a better credential tier took the request
278
+ * instead (`returnToBetterTier`): the session returns to it.
279
+ */
280
+ returned: boolean;
281
+ /** The login the account was served on as the pool was read (`IInferenceAccount.loginId`): an answer's spend names it. */
282
+ loginId: string | null;
283
+ /**
284
+ * An Anthropic API key with a monthly spend limit: the request goes out as the price check read it, on the standard
285
+ * service tier only (`inferenceAnthropicCheckedBytes`), the one the price table prices.
286
+ */
287
+ spendLimit: boolean;
163
288
  release: () => void;
164
289
  }
165
290
  /** One account of the proxy as `agl proxy status` shows it, judged on the held reading without reading again. */
@@ -219,37 +344,99 @@ export interface IInferenceAccountStatus {
219
344
  export type TInferenceNativeModel = Record<string, unknown> & {
220
345
  slug: string;
221
346
  };
347
+ /** How a request is placed on a Claude pool (`InferenceRouter.acquireClaude`). */
348
+ export interface IInferenceClaudeAcquireOptions {
349
+ /** The session's account: the request is judged on it alone, unless `returnToBetterTier` finds a better one. */
350
+ accountId?: string;
351
+ /** The accounts this request left already; it never goes back to them. */
352
+ exclude?: ReadonlySet<string>;
353
+ /**
354
+ * The accounts of `exclude` the request left for a reason of their own, and that reason: each counts as its own
355
+ * judgement counts the reason, never as at its limit. An account of `exclude` it does not name counts as at its limit.
356
+ */
357
+ excludedBecause?: ReadonlyMap<string, IInferenceAccountUnavailable>;
358
+ /** A request that uses no quota (`count_tokens`): an account at its limit takes it as well. */
359
+ ignoreLimits?: boolean;
360
+ /**
361
+ * What of the request the proxy's price table cannot price (`inferenceAnthropicUnpricedRequest`): an API key with a
362
+ * monthly spend limit takes no such request (`model_unpriced`). A request that uses no quota ignores it.
363
+ */
364
+ unpriced?: string;
365
+ /**
366
+ * The session may move: when its account is of a later credential tier -- an Anthropic API key of a pool that names
367
+ * no credential -- a Claude account that can take the request takes it instead, and the route is `returned`.
368
+ */
369
+ returnToBetterTier?: boolean;
370
+ }
371
+ /** How a request is placed on a ChatGPT pool (`InferenceRouter.acquire`). */
372
+ export interface IInferenceAcquireOptions {
373
+ /** The session's account: the request is judged on it alone, unless `returnToBetterTier` finds a better one. */
374
+ accountId?: string;
375
+ /** The accounts this request left already; it never goes back to them. */
376
+ exclude?: ReadonlySet<string>;
377
+ /** The accounts of `exclude` the request left for a reason of their own, as for a Claude pool (`IInferenceClaudeAcquireOptions`). */
378
+ excludedBecause?: ReadonlyMap<string, IInferenceAccountUnavailable>;
379
+ /** The request's `reasoning.effort`, which a ChatGPT account's model must accept; a key's model is OpenAI's to judge. */
380
+ reasoningEffort?: string;
381
+ /** The request's `service_tier`, which a ChatGPT account's model must offer (`auto` and `default` always). */
382
+ serviceTier?: string;
383
+ /** The Codex release the client read its catalog as: the model it names is checked against the catalog it was offered. */
384
+ clientVersion?: string;
385
+ /** A Responses request, which an OpenAI API key may take; a translated Messages request never goes to a key. */
386
+ apiKeys?: boolean;
387
+ /**
388
+ * What of the request the proxy's price table cannot price (`inferenceOpenAiUnpricedRequest`): an OpenAI API key with a
389
+ * monthly spend limit takes no such request (`model_unpriced`).
390
+ */
391
+ unpriced?: string;
392
+ /**
393
+ * The session may move: when its account is an OpenAI API key of a pool that names no credential, a ChatGPT account
394
+ * that can take the request takes it instead, and the route is `returned`. The gateway asks it only of a request that
395
+ * carries no account state, which never leaves its account.
396
+ */
397
+ returnToBetterTier?: boolean;
398
+ }
222
399
  /** Model-preserving account selection. Refresh grants and native sign-ins remain the authority's. */
223
400
  export declare class InferenceRouter {
224
401
  private readonly options;
225
402
  /** ChatGPT catalogs by account and the Codex release they were read as. */
226
403
  private readonly catalogs;
227
404
  private readonly nativeCatalogs;
405
+ /** The model lists of the Claude accounts and the Anthropic API keys (`claudeCatalog`). */
228
406
  private readonly claudeCatalogs;
407
+ /** The model lists of the OpenAI API keys (`keyCatalog`). */
408
+ private readonly keyCatalogs;
229
409
  /** Why each Claude account was last reported refused, or routed on its last model list (`noteAccount`). */
230
410
  private readonly noted;
231
411
  private readonly active;
232
412
  private readonly cooldowns;
233
413
  /**
234
- * The Claude accounts a billing error (HTTP 402) took out of rotation, by when it was seen, until the account service
235
- * holds the problem; one it could not be told of stays here for the proxy's lifetime (`reportPaymentProblem`).
414
+ * The accounts a payment problem took out of rotation (`reportPaymentProblem`): a Claude account until the account
415
+ * service holds the problem -- one it could not be told of stays here for the proxy's lifetime -- and an API key until
416
+ * its spend cap ends, or for the proxy's lifetime.
236
417
  */
237
418
  private readonly paymentMarks;
238
419
  /** The accounts whose payment problem the account service could not be told of, each reported once. */
239
420
  private readonly paymentReportsFailed;
240
421
  /**
241
- * The Anthropic API keys among the accounts the last pool read listed: the account service reads no usage for them,
242
- * so nothing a request on one shows is handed to it.
422
+ * The API keys, Anthropic's and OpenAI's, among the accounts the last pool read listed: the account service reads no
423
+ * subscription usage for them, so no usage a request on one shows -- headers, tokens, a 429 -- is handed to it.
243
424
  */
244
425
  private apiKeyAccounts;
245
426
  private readonly now;
246
427
  constructor(options: IInferenceRouterOptions);
247
428
  private accounts;
248
429
  /**
249
- * The policy's accounts of its provider and credential, and every eligible account's id of that pool: an account
250
- * absent from both left the pool. An account whose lapse date passed is in neither -- it is never routed to again --
251
- * and is named in `lapsed`. A Claude account and an Anthropic API key never share a pool, so a session placed on one
252
- * kind never moves to the other.
430
+ * The credential tiers of the policy's pool, best first (`inferenceCredentialTiers`). A plan restriction leaves the
431
+ * API keys out, as a key has no plan, so a lease with `plans` never reaches a key.
432
+ */
433
+ private tiers;
434
+ /**
435
+ * The policy's accounts of its provider and credential tiers (`tiers`), and every eligible account's id of that pool:
436
+ * an account absent from both left the pool. An account whose lapse date passed is in neither -- it is never routed to
437
+ * again -- and is named in `lapsed`. A pool that names its credential serves that kind alone: Flex's API-key pool
438
+ * never takes a Claude account. One that names none spans both, and a session placed on a key returns to a Claude
439
+ * account once one can take its request.
253
440
  */
254
441
  private pool;
255
442
  /**
@@ -262,93 +449,145 @@ export declare class InferenceRouter {
262
449
  private sessionPool;
263
450
  /** An account's ChatGPT catalog as the Codex release `clientVersionArg` is offered it, or the package's default. */
264
451
  private catalog;
452
+ /** The models a Claude account or an Anthropic API key may use (`heldModels`). */
453
+ private claudeCatalog;
454
+ /** The models an OpenAI API key may use, as its `GET /v1/models` lists them (`heldModels`, `openAiKeyModel`). */
455
+ private keyCatalog;
265
456
  /**
266
- * The models a Claude account or an Anthropic API key may use: the list read within the last minute, else read now.
457
+ * The models an account may use, from its list in `listsArg`: the list read within the last minute, else read now.
267
458
  * A model-list read that fails keeps the list read before for the account's revision, which the account routes on
268
459
  * meanwhile; with none, the account is unavailable for the failure's reason. A failure of the account's access or
269
460
  * sign-in makes it unavailable whether a list is held or not. Either way the failure is noted and its account is not
270
- * read again, however many requests ask, for a minute -- or 5 s after a failed access (`holdClaudeFailure`).
461
+ * read again, however many requests ask, for a minute -- or 5 s after a failed access (`holdListFailure`).
271
462
  */
272
- private claudeCatalog;
463
+ private heldModels;
273
464
  /**
274
- * Records why a Claude account's model list or access failed, keeping the list held for its revision, and notes it:
465
+ * Records why an account's model list or access failed, keeping the list held for its revision, and notes it:
275
466
  * neither is read again for a minute after a failed model-list read or sign-in, or for 5 s after a failed access
276
467
  * (`access_failed`). The access is then tried again by reading the list again, even one read within the minute: a
277
468
  * single list read on recovery. Answers the held list when the account routes on it meanwhile -- only after a failed
278
- * model-list read (`claudeCatalogRefusal`).
469
+ * model-list read (`heldListRefusal`).
279
470
  */
280
- private holdClaudeFailure;
471
+ private holdListFailure;
281
472
  /**
282
- * Reads the models a Claude account or an Anthropic API key may use. A rejected access is reported once: a sign-in is
283
- * renewed, while a key is ended, so the account service refuses the second read. Every failure but an abort is a
284
- * `ClaudeCatalogError` naming its reason.
473
+ * Reads the models an account may use through `listArg`. A rejected access (`accessRejected`) is reported once: a
474
+ * sign-in is renewed, while a key is ended, so the account service refuses the second read. Every failure but an abort
475
+ * is a `ModelListError` naming its reason.
285
476
  */
286
- private readClaudeCatalog;
477
+ private readModelList;
287
478
  /**
288
479
  * Notes why a Claude account is refused, or routed on its last model list, now (`null`: neither), and reports a change
289
480
  * of it -- never the same reason again, however many requests meet it.
290
481
  */
291
482
  private noteAccount;
292
- /** The model list a Claude account's revision was last read with, without reading it: `undefined` when none is held. */
293
- private heldClaudeCatalog;
483
+ /** The model list an account's revision was last read with, without reading it: `undefined` when none is held. */
484
+ private heldList;
294
485
  /**
295
- * Why a Claude account's model list or access keeps it from routing, as a request would find it now, before its next
296
- * read is due: a failed access or sign-in, or a failed model-list read with no list held for its revision.
486
+ * Why an account's model list or access keeps it from routing, as a request would find it now, before its next read is
487
+ * due: a failed access or sign-in, or a failed model-list read with no list held for its revision. Only the accounts
488
+ * whose model list the router reads have one: Claude accounts and API keys.
297
489
  */
298
- private catalogRefusal;
490
+ private listRefusal;
299
491
  /**
300
- * The models every readable account of the Claude pool -- its Claude accounts, or its Anthropic API keys -- offers: a
301
- * session may move to any of them, so a model one account lacks is not offered. Limits are the smallest any of them
302
- * states; efforts are those all of them accept, and adaptive thinking is offered when all of them accept it. What one
303
- * account does not state, the pool does not state.
492
+ * The Claude pool's models, from its best credential tier with a readable account (`inferenceCredentialTiers`): the
493
+ * Claude accounts' while one of them can read its model list, else the Anthropic API keys'. Within the tier, the
494
+ * models every readable account offers: a session may move to any of them, so a model one account lacks is not
495
+ * offered. Limits are the smallest any of them states; efforts are those all of them accept, and adaptive thinking is
496
+ * offered when all of them accept it. What one account does not state, the pool does not state.
304
497
  */
305
498
  claudeModels(policyArg: IInferenceRoutingPolicy, signalArg: AbortSignal): Promise<TInferenceClaudeModel[]>;
499
+ /** The models every readable account of one credential tier of a Claude pool offers; `undefined` when none is readable. */
500
+ private tierClaudeModels;
306
501
  /**
307
- * Current model entitlements, merged conservatively across the account pool. A pool whose every account read needs a
308
- * fresh sign-in -- ChatGPT rejected its access even when renewed, or the account service could not renew it -- answers
502
+ * The ChatGPT pool's models, from its best credential tier with a readable account (`inferenceCredentialTiers`): the
503
+ * ChatGPT sign-ins' current entitlements while one of them can read its catalog, else the OpenAI API keys' models
504
+ * (`keyTierModels`). A pool whose every ChatGPT account read needs a fresh sign-in -- ChatGPT rejected its access even
505
+ * when renewed, or the account service could not renew it -- and no key to fall back to answers
309
506
  * `account_needs_login`, so its caller asks for a sign-in rather than for the proxy.
310
507
  */
311
508
  models(policyArg: IInferenceRoutingPolicy, signalArg: AbortSignal): Promise<TInferenceModel[]>;
312
509
  /**
313
- * The pool's Codex model catalog in the Codex backend's own entry shape, read for the client's version.
314
- * An entry shared by several accounts keeps only what every one of them offers.
510
+ * Current model entitlements, merged conservatively across the pool's ChatGPT sign-ins, and whether every account read
511
+ * needed a fresh sign-in.
512
+ */
513
+ private signInModels;
514
+ /**
515
+ * The models every readable OpenAI API key of the pool lists and flexharness describes (`openAiKeyModel`), newest first
516
+ * as `OPENAI_API_MODELS` orders them, the first the default: a session may move to any of the keys, so a model one key
517
+ * lacks is not offered. `undefined` when no key is readable.
518
+ */
519
+ private keyTierModels;
520
+ /**
521
+ * The pool's Codex model catalog in the Codex backend's own entry shape, read for the client's version through its
522
+ * ChatGPT sign-ins alone: an OpenAI API key never reaches the ChatGPT catalog connection, so a pool of keys alone has
523
+ * no Codex catalog here (`no_eligible_accounts`; Codex then uses its bundled one). An entry shared by several
524
+ * accounts keeps only what every one of them offers.
315
525
  */
316
526
  nativeModels(policyArg: IInferenceRoutingPolicy, clientVersionArg: string, signalArg: AbortSignal): Promise<TInferenceNativeModel[]>;
317
527
  private nativeCatalog;
318
528
  /**
319
- * A ChatGPT account for one request. `clientVersion` is the Codex release the client read its catalog as, so the
320
- * model it names is checked against the catalog it was offered.
321
- */
322
- acquire(modelIdArg: string, policyArg: IInferenceRoutingPolicy, signalArg: AbortSignal, optionsArg?: {
323
- accountId?: string;
324
- exclude?: ReadonlySet<string>;
325
- reasoningEffort?: string;
326
- serviceTier?: string;
327
- clientVersion?: string;
328
- }): Promise<IInferenceRoute>;
329
- /**
330
- * A Claude account for one of Claude Code's requests, or an Anthropic API key for one of Flex's, chosen as `acquire`
331
- * chooses: the pinned session account if it is still eligible, else the least busy account with the most headroom
332
- * whose current reading shows it available -- every counted window under its threshold, no provider retry ahead --
333
- * with no cooldown after a provider 429, and which lists the model. A key has no reading (`apiKeyLimits`): it is
334
- * available unless it cools down after a 429.
529
+ * A ChatGPT account or an OpenAI API key for one request, chosen as `acquireClaude` chooses: the pinned session account
530
+ * if it is still eligible, else the best credential tier's least busy account with the most headroom
531
+ * (`inferenceCredentialTiers`). A ChatGPT account is judged on its usage reading and the Codex catalog of the release
532
+ * the client read it as (`clientVersion`). An OpenAI API key takes only a Responses request (`apiKeys`), never a
533
+ * translated Messages request, and is judged as an Anthropic API key is (`judgeListed`): a payment problem -- OpenAI's
534
+ * quota refusal -- or its month's spend at its limit keeps it out, a key with a limit takes no request it cannot price
535
+ * (`unpriced`, `model_unpriced`), and its model list (`GET /v1/models`, held a minute) must list the model. The accounts
536
+ * of `exclude` count as `excludedBecause` says, as for a Claude pool. `returnToBetterTier`: a session pinned to a key
537
+ * goes back to a ChatGPT account as soon as one can take its request, and the route says so (`returned`).
538
+ */
539
+ acquire(modelIdArg: string, policyArg: IInferenceRoutingPolicy, signalArg: AbortSignal, optionsArg?: IInferenceAcquireOptions): Promise<IInferenceRoute>;
540
+ /** `acquire` among `accountsArg`, the pool's accounts or those of its better tiers. */
541
+ private routeOpenAi;
542
+ /**
543
+ * Counts an account the request left already (`exclude`) as its own judgement counts the reason the request left it
544
+ * for (`excludedBecause`): a payment problem joins the 402 list, a key whose spend could not be read or recorded is
545
+ * named in the 503, one that could not price the request joins the 400 list, one without the model leads to the 404,
546
+ * and every other reason to the 503 for want of an account. An account left for no reason of its own counts as at its
547
+ * limit (429). The switch is exhaustive: a new reason must be placed here.
548
+ */
549
+ private countLeft;
550
+ /**
551
+ * Judges an account whose model list the router reads -- a Claude account, an Anthropic or an OpenAI API key -- for one
552
+ * request, as placement takes it, recording on `stateArg` what keeps it out. A payment problem refuses it before anything
553
+ * else; without limits to route on (`limitsArg`) it is unavailable (`usage_unread`, a key's `spend_unread`); a key with
554
+ * a spend limit whose spend is recorded nowhere yet is refused (`spend_unrecorded`). Limits and cool-down are judged
555
+ * before any model list is read: an account that cannot take the request now is never read for it, its held list says
556
+ * whether it would serve the model, and without one it counts as at its limit, so a session on it moves rather than
557
+ * waiting for a list. A key's spend reading says nothing of the provider's rate limits, so only its own end ends a key's
558
+ * cool-down. A key with a spend limit then takes no request whose cost the price table cannot count (`unpriced`,
559
+ * Decision Q4), and the model list (`read`) must list the model. Answers the candidate's model and limits, or the refusal
560
+ * of an account that takes no request for a reason of its own -- a pinned refusal names it -- or `undefined`.
561
+ */
562
+ private judgeListed;
563
+ /**
564
+ * A Claude account or an Anthropic API key for one of Claude Code's requests, or an Anthropic API key for one of
565
+ * Flex's, chosen as `acquire` chooses: the pinned session account if it is still eligible, else the least busy account
566
+ * with the most headroom whose current reading shows it available -- every counted window under its threshold, no
567
+ * provider retry ahead -- with no cooldown after a provider 429, and which lists the model. A key is judged on its
568
+ * month's spend (`keyLimits`): at its spend limit it takes no request until the month ends, as an account at its limit,
569
+ * and a session on it moves; with a limit and no readable spend it is refused (`spend_unread`). A pool that names no
570
+ * credential places on its Claude accounts first and on its keys only when no Claude account can take the request, for
571
+ * whatever reason (`inferenceCredentialTiers`).
335
572
  * `ignoreLimits` serves a request that uses no quota (`count_tokens`) on an account at its limit as well. A pinned
336
573
  * account that takes no request for a reason of its own -- no usage reading, no model list, no access, or no such
337
574
  * model -- is refused naming the account and the reason (`InferenceRoutingError.reason`). An unpinned request whose
338
- * chosen account's access fails goes to the next candidate.
575
+ * chosen account's access fails goes to the next candidate. `returnToBetterTier`: a session pinned to a key goes back
576
+ * to a Claude account as soon as one can take its request, and the route says so (`returned`).
339
577
  *
340
- * A Claude account held with a payment problem -- Anthropic answered a request on it with a billing error (HTTP 402),
341
- * as its usage reading says or this proxy saw (`reportPaymentProblem`) -- takes no request, `count_tokens` included,
342
- * and is refused for the reason `payment_problem`. So is an account of `exclude` that `paymentExcluded` names: the
343
- * request left it for a payment problem. Neither counts as at its limit: when such accounts are all that is left the
344
- * refusal is a 402 `payment_problem` naming them, and while another account is merely at its limit the 429 stands.
345
- */
346
- acquireClaude(modelIdArg: string, policyArg: IInferenceRoutingPolicy, signalArg: AbortSignal, optionsArg?: {
347
- accountId?: string;
348
- exclude?: ReadonlySet<string>;
349
- paymentExcluded?: ReadonlySet<string>;
350
- ignoreLimits?: boolean;
351
- }): Promise<IInferenceClaudeRoute>;
578
+ * An account held with a payment problem -- Anthropic answered a request on it with a billing error (HTTP 402), or on
579
+ * a key with its organization's spend cap (HTTP 429 `enforced_spend_limit_reached`), as its usage reading says or this
580
+ * proxy saw (`reportPaymentProblem`) -- takes no request, `count_tokens` included, and is refused for the reason
581
+ * `payment_problem`. So is an account of `exclude` that `excludedBecause` names with a payment problem: the request
582
+ * left it for one. Neither counts as at its limit: when such accounts are all that is left the refusal is a 402
583
+ * `payment_problem` naming them, and while another account is merely at its limit the 429 stands. Any other account
584
+ * of `exclude` that `excludedBecause` names counts as its reason counts on the account's own judgement: a key whose
585
+ * spend could not be read is named in the 503, an account without the model leads to the 404, and every other reason
586
+ * to the 503 for want of an account -- never to the 429 of accounts at their limits.
587
+ */
588
+ acquireClaude(modelIdArg: string, policyArg: IInferenceRoutingPolicy, signalArg: AbortSignal, optionsArg?: IInferenceClaudeAcquireOptions): Promise<IInferenceClaudeRoute>;
589
+ /** `acquireClaude` among `accountsArg`, the pool's accounts or those of its better tiers. */
590
+ private routeClaude;
352
591
  /**
353
592
  * Tells the account service that Anthropic rejected the key of `generationArg`: it marks the account for a new key,
354
593
  * so the pool leaves it out from then on, and refuses the access the report asks for -- the answer expected here. A
@@ -357,17 +596,18 @@ export declare class InferenceRouter {
357
596
  */
358
597
  private rejectApiKey;
359
598
  /**
360
- * The candidate a request goes to by the placement order (`authSwitchPlacementOrder`), or the refusal that says why there is
361
- * none. A pinned request has its session's account as its one candidate, so the order only ever places new sessions.
362
- * An account at its reserve is refused like one at its limit, and the refusal names the reserve. A pinned account
599
+ * The candidate a request goes to -- of the best credential tier, then by the placement order
600
+ * (`authSwitchPlacementOrder`) -- or the refusal that says why there is none. A pinned request has its session's
601
+ * account as its one candidate, so the order only ever places new sessions. An account at its reserve is refused like one at its limit, and the refusal names the reserve. A pinned account
363
602
  * that takes no request for a reason of its own (`refusal`) is refused with that account and reason.
364
603
  */
365
604
  private select;
366
605
  /**
367
606
  * The accounts as `agl proxy status` shows them: each judged as placement would judge it on the reading `heldArg`
368
- * answers for it, which the caller read beforehand -- an Anthropic API key, which has none, as `apiKeyLimits` judges it -- with
607
+ * answers for it, which the caller read beforehand -- an API key on its month's spend (`keyLimits`), in dollars -- with
369
608
  * its cooldown, the requests it serves now, its lifecycle and where a new session of an unrestricted client of its
370
- * pool would go (`placementRank`).
609
+ * provider would go (`placementRank`): a client that names no credential, so a provider's subscriptions rank before its
610
+ * keys (`inferenceCredentialTiers`).
371
611
  */
372
612
  accountStatus(accountsArg: readonly IInferenceAccount[], heldArg: (accountIdArg: string) => plugins.authswitchAuthority.IAuthSwitchUsageSnapshot | undefined): IInferenceAccountStatus[];
373
613
  /** Counts one request on an account until its release, which may be called more than once. */
@@ -379,15 +619,22 @@ export declare class InferenceRouter {
379
619
  */
380
620
  observe(accountIdArg: string, providerArg: TInferenceProvider, headersArg: Headers, statusArg: number): void;
381
621
  /**
382
- * Anthropic refused a request on the Claude account with a billing error (HTTP 402 `billing_error`): the account is out
383
- * of rotation at once, and the account service is told (`reportPaymentProblem`). Once it holds the problem the
384
- * account's usage carries it, and that decides from then on, so the router's own mark is dropped; when the service
385
- * cannot be told -- it is not running, or an older release that does not know the report -- the mark holds for as long
386
- * as the proxy runs, and the failure is reported once. An API key's 402 is no payment problem the service holds.
622
+ * A provider refused a request on the account for a payment problem (`TInferencePaymentCause`): the account is out of
623
+ * rotation at once, and the refusal it now carries is answered. The problem is reported to the account service
624
+ * (`reportPaymentProblem`); once it holds the problem the account's usage carries it, and that decides from then on --
625
+ * until a re-check clears it, or a spend cap's month ends -- so the router's own mark is dropped. When the service
626
+ * cannot be told -- it is not running, or an older release that does not know the report or the account's kind --
627
+ * the mark holds, a spend cap until 00:00 UTC on the first day of the next month (`authSwitchSpendPeriod`), anything
628
+ * else for as long as the proxy runs, and the failure is reported once.
629
+ */
630
+ reportPaymentProblem(accountIdArg: string, causeArg: TInferencePaymentCause): IInferenceAccountUnavailable;
631
+ /**
632
+ * The payment problem an account is held with, as a refusal: by this proxy's own mark while it holds -- a spend cap's
633
+ * ends at its time -- or by the account's usage reading, which carries it once the account service holds it, with its
634
+ * cause (absent: `billing`) and, for a spend cap, when it ends; one whose spend cap ended holds nothing
635
+ * (`authSwitchPaymentProblemHeld`, the rule the account service holds it by).
387
636
  */
388
- reportPaymentProblem(accountIdArg: string): void;
389
- /** Whether a Claude account is held with a payment problem: by its usage reading, or by this proxy's own mark. */
390
- private paymentRefused;
637
+ private paymentProblem;
391
638
  /** A provider answer's `Retry-After`, in seconds from now, in either of its forms. */
392
639
  retryAfterSeconds(headersArg: Headers): number | undefined;
393
640
  /**
@@ -395,11 +642,22 @@ export declare class InferenceRouter {
395
642
  * windows. An API key has no windows to estimate.
396
643
  */
397
644
  countTokens(accountIdArg: string, usageArg: plugins.authswitchUsageTokens.IAuthSwitchTokenUsage): void;
645
+ /**
646
+ * Takes the spend of an answer an API key served on the login `loginIdArg` (`inferenceAnthropicSpend`): it counts
647
+ * against the key's limit from now on and is reported to the account service (`spend`).
648
+ */
649
+ recordSpend(accountIdArg: string, loginIdArg: string | null, recordArg: IInferenceSpendRecord): void;
650
+ /**
651
+ * Says that an answer an API key served ended before its provider stated its usage, so nothing of it counts against
652
+ * the key's limit (`IInferenceSpendLedger.uncounted`).
653
+ */
654
+ uncountedSpend(accountIdArg: string, modelArg: string): void;
398
655
  /**
399
656
  * A provider 429 on the account. Only a definitive pre-stream refusal permits another account to be tried. The
400
657
  * account cools down for the provider's `Retry-After`, at most a day; without one, or with one already due, for 60
401
658
  * seconds or until the account service holds a usage read made after the 429 (see `cooldownUntil`), which the 429
402
- * asks it to make at once. An API key, whose usage the account service does not read, cools down the 60 seconds.
659
+ * asks it to make at once. An API key, whose spend reading says nothing of the provider's rate limits, cools down
660
+ * the 60 seconds.
403
661
  */
404
662
  reportQuota(accountIdArg: string, retryAfterSecondsArg?: number): void;
405
663
  }