agl 40.4.0 → 40.5.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.
- package/changelog.md +27 -0
- package/dist_serve/bundle.js +2228 -2177
- package/dist_serve/bundle.js.map +2 -2
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/aglapi/aglapisurface.document.js +2 -2
- package/dist_ts/classes.accountsactions.d.ts +11 -1
- package/dist_ts/classes.accountsactions.js +59 -2
- package/dist_ts/classes.controller.js +5 -5
- package/dist_ts/classes.flexaccountaccess.js +3 -2
- package/dist_ts/classes.harnessreleases.d.ts +14 -4
- package/dist_ts/classes.harnessreleases.js +28 -4
- package/dist_ts/classes.inferencegateway.d.ts +28 -3
- package/dist_ts/classes.inferencegateway.js +177 -61
- package/dist_ts/classes.inferenceplacement.d.ts +5 -3
- package/dist_ts/classes.inferenceplacement.js +2 -2
- package/dist_ts/classes.inferencerouter.d.ts +349 -91
- package/dist_ts/classes.inferencerouter.js +804 -297
- package/dist_ts/classes.inferenceservice.d.ts +14 -2
- package/dist_ts/classes.inferenceservice.js +53 -14
- package/dist_ts/classes.inferencespend.d.ts +339 -0
- package/dist_ts/classes.inferencespend.js +815 -0
- package/dist_ts/classes.inferenceusagecache.d.ts +8 -0
- package/dist_ts/classes.inferenceusagecache.js +9 -5
- package/dist_ts/classes.inferenceusagetap.d.ts +50 -9
- package/dist_ts/classes.inferenceusagetap.js +185 -31
- package/dist_ts/classes.proxycontrol.d.ts +3 -2
- package/dist_ts/classes.proxycontrol.js +1 -2
- package/dist_ts/classes.proxydaemon.js +3 -2
- package/dist_ts/classes.proxystore.d.ts +6 -0
- package/dist_ts/classes.proxystore.js +11 -2
- package/dist_ts/fleet/classes.claudeworkers.d.ts +5 -4
- package/dist_ts/fleet/classes.claudeworkers.js +10 -9
- package/dist_ts/functions.claudecoderelease.d.ts +12 -6
- package/dist_ts/functions.claudecoderelease.js +13 -7
- package/dist_ts/functions.inferencecli.js +12 -8
- package/dist_ts/interfaces.auth.d.ts +1 -1
- package/dist_ts/interfaces.auth.js +3 -1
- package/dist_ts/plugins.d.ts +3 -1
- package/dist_ts/plugins.js +7 -2
- package/dist_ts_authswitch/authority-apikeys.d.ts +32 -0
- package/dist_ts_authswitch/authority-apikeys.js +65 -0
- package/dist_ts_authswitch/authority-contract.d.ts +142 -28
- package/dist_ts_authswitch/authority-contract.js +48 -1
- package/dist_ts_authswitch/authority-overview.js +8 -1
- package/dist_ts_authswitch/authority-placement.d.ts +7 -2
- package/dist_ts_authswitch/authority-placement.js +10 -3
- package/dist_ts_authswitch/authority-runtime-contract.d.ts +2 -2
- package/dist_ts_authswitch/authority-spend.d.ts +103 -0
- package/dist_ts_authswitch/authority-spend.js +110 -0
- package/dist_ts_authswitch/authority-subscription.d.ts +12 -1
- package/dist_ts_authswitch/authority-subscription.js +24 -1
- package/dist_ts_authswitch/authority-usagelimit.d.ts +7 -4
- package/dist_ts_authswitch/authority-usagelimit.js +89 -39
- package/dist_ts_authswitch/authority-usagewindows.d.ts +4 -1
- package/dist_ts_authswitch/authority-usagewindows.js +41 -16
- package/dist_ts_authswitch/classes.authoritybroker.d.ts +30 -19
- package/dist_ts_authswitch/classes.authoritybroker.js +152 -61
- package/dist_ts_authswitch/classes.authoritycli.d.ts +9 -4
- package/dist_ts_authswitch/classes.authoritycli.js +94 -29
- package/dist_ts_authswitch/classes.authorityclient.d.ts +24 -7
- package/dist_ts_authswitch/classes.authorityclient.js +31 -11
- package/dist_ts_authswitch/classes.authoritydaemon.js +39 -12
- package/dist_ts_authswitch/classes.authoritydatabase.d.ts +28 -5
- package/dist_ts_authswitch/classes.authoritydatabase.js +171 -9
- package/dist_ts_authswitch/classes.authorityimport.js +2 -2
- package/dist_ts_authswitch/classes.authoritymodels.d.ts +71 -3
- package/dist_ts_authswitch/classes.authoritymodels.js +249 -20
- package/dist_ts_authswitch/classes.authorityregistry.js +4 -2
- package/dist_ts_authswitch/classes.authorityusage.d.ts +69 -7
- package/dist_ts_authswitch/classes.authorityusage.js +118 -4
- package/dist_ts_authswitch/classes.claudenative.d.ts +16 -7
- package/dist_ts_authswitch/classes.claudenative.js +29 -16
- package/dist_ts_authswitch/classes.claudesignin.js +5 -2
- package/dist_ts_authswitch/testsupport.d.ts +24 -5
- package/dist_ts_authswitch/testsupport.js +56 -12
- package/dist_ts_authswitch/ts_migration/0011_account_api_key_fields.d.ts +12 -0
- package/dist_ts_authswitch/ts_migration/0011_account_api_key_fields.js +21 -0
- package/dist_ts_authswitch/ts_migration/index.js +3 -1
- package/dist_ts_interfaces/functions.authorityscope.d.ts +18 -7
- package/dist_ts_interfaces/functions.authorityscope.js +35 -8
- package/dist_ts_interfaces/interfaces.d.ts +1 -1
- package/dist_ts_interfaces/interfaces.js +1 -1
- package/dist_ts_interfaces/plugins.d.ts +2 -1
- package/dist_ts_interfaces/plugins.js +7 -4
- package/dist_ts_interfaces/requests.accounts.d.ts +56 -0
- package/dist_ts_interfaces/requests.accounts.js +11 -1
- package/dist_ts_interfaces/requests.d.ts +2 -2
- package/dist_ts_interfaces/requests.js +1 -1
- package/docs/authswitch.md +75 -12
- package/package.json +5 -5
- package/readme.md +198 -21
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/aglapi/aglapisurface.document.ts +2 -2
- package/ts/classes.accountsactions.ts +75 -1
- package/ts/classes.controller.ts +4 -4
- package/ts/classes.flexaccountaccess.ts +2 -1
- package/ts/classes.harnessreleases.ts +25 -4
- package/ts/classes.inferencegateway.ts +184 -64
- package/ts/classes.inferenceplacement.ts +6 -4
- package/ts/classes.inferencerouter.ts +1026 -314
- package/ts/classes.inferenceservice.ts +56 -13
- package/ts/classes.inferencespend.ts +870 -0
- package/ts/classes.inferenceusagecache.ts +16 -4
- package/ts/classes.inferenceusagetap.ts +159 -34
- package/ts/classes.proxycontrol.ts +3 -3
- package/ts/classes.proxydaemon.ts +2 -1
- package/ts/classes.proxystore.ts +11 -1
- package/ts/fleet/classes.claudeworkers.ts +9 -8
- package/ts/functions.claudecoderelease.ts +13 -6
- package/ts/functions.inferencecli.ts +11 -7
- package/ts/interfaces.auth.ts +2 -0
- package/ts/plugins.ts +7 -0
- package/ts_authswitch/authority-apikeys.ts +85 -0
- package/ts_authswitch/authority-contract.ts +167 -29
- package/ts_authswitch/authority-overview.ts +7 -0
- package/ts_authswitch/authority-placement.ts +12 -4
- package/ts_authswitch/authority-runtime-contract.ts +2 -2
- package/ts_authswitch/authority-spend.ts +168 -0
- package/ts_authswitch/authority-subscription.ts +28 -1
- package/ts_authswitch/authority-usagelimit.ts +95 -40
- package/ts_authswitch/authority-usagewindows.ts +42 -17
- package/ts_authswitch/classes.authoritybroker.ts +161 -68
- package/ts_authswitch/classes.authoritycli.ts +108 -32
- package/ts_authswitch/classes.authorityclient.ts +41 -10
- package/ts_authswitch/classes.authoritydaemon.ts +43 -13
- package/ts_authswitch/classes.authoritydatabase.ts +178 -8
- package/ts_authswitch/classes.authorityimport.ts +1 -1
- package/ts_authswitch/classes.authoritymodels.ts +165 -19
- package/ts_authswitch/classes.authorityregistry.ts +3 -0
- package/ts_authswitch/classes.authorityusage.ts +171 -10
- package/ts_authswitch/classes.claudenative.ts +28 -15
- package/ts_authswitch/classes.claudesignin.ts +4 -1
- package/ts_authswitch/native-contracts.md +81 -20
- package/ts_authswitch/readme.md +1 -1
- package/ts_authswitch/testsupport.ts +58 -11
- package/ts_authswitch/ts_migration/0011_account_api_key_fields.ts +20 -0
- package/ts_authswitch/ts_migration/index.ts +2 -0
- package/ts_interfaces/functions.authorityscope.ts +43 -8
- package/ts_interfaces/interfaces.ts +1 -1
- package/ts_interfaces/plugins.ts +6 -3
- package/ts_interfaces/requests.accounts.ts +48 -0
- package/ts_interfaces/requests.ts +4 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/accounts.plugins.ts +3 -1
- package/ts_web/accounts.templates.ts +20 -11
- package/ts_web/classes.accountsreadmodel.ts +322 -70
- package/ts_web/elements.accounts.ts +219 -20
- package/ts_web/elements.codexconnections.ts +3 -1
- package/ts_web/plugins.ts +4 -2
package/changelog.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-10-23 - 40.5.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Serve Claude Code from the Anthropic API keys when no Claude account can take its request (AGL-299). A Claude pool whose lease names no credential -- Claude Code's own, `agl proxy run claude`, the `claude` client -- now spans two tiers, the Claude accounts first and the Anthropic API keys after them (`inferenceCredentialTiers`): a key takes a request only when no Claude account can, for whatever reason (at its limit or reserve, cooling down, without a usage reading, model list or access, or with a payment problem), and a session on a key returns to a Claude account at the next request one can take, with no waiting period, journaled with the new placement reason `returned`. A pool that names its credential keeps to it (Flex's API-key pool never takes a Claude account), and a lease with a plan restriction never reaches a key. With every account and key at its limit the 429 carries the earliest reset of either. The pool's models are its best tier's with a readable account: the Claude accounts' while one answers its model list, else the keys'. `agl proxy status` ranks a provider's subscriptions before its keys, and `nextPlacement` names the account a client that names no credential is placed on next, a key included. Downgrade: an earlier AGL's `agl proxy status` fails while a `returned` placement is among the latest 20 (routing is unaffected; the entry ages out of the 30-day journal).
|
|
8
|
+
- Serve Codex's Responses requests from the OpenAI API keys when no ChatGPT account can take them (AGL-299). A ChatGPT pool whose lease names no credential spans the ChatGPT accounts first and the OpenAI API keys after them, and one that names `credential: 'api_key'` the keys alone (a lease or catalog request of `{ provider: 'openai', credential: 'api_key' }` is now admitted). A key takes Responses requests (`/v1/responses`) only, never a translated Messages request, and the Codex catalog stays the ChatGPT accounts' (a pool of keys alone answers Codex's `/models` 503, and Codex runs on its bundled catalog). A request goes to OpenAI's API (`https://api.openai.com/v1/responses`) on the key's own connection (flexharness `createOpenAiApiKeyConnection`), with only its `model` rewritten and a Codex turn token only to the account that issued it; a key OpenAI rejects answers 503 `account_unauthorized` in its own words. A key's model list is OpenAI's `GET /v1/models`, held a minute like a Claude account's, and the proxy's own model list falls to the keys' models the registry describes when no ChatGPT account answers its catalog. Routing judges a key as an Anthropic key is judged (one shared judgement for both providers' keys and the Claude accounts): its payment problem, its month's spend, spend recorded nowhere yet, its cool-down, a price for the request and its model list, and a key whose access fails as the request goes out is held for it and the request goes to the next candidate (a pinned one is refused naming the key and the reason), while a ChatGPT account's failure still fails the request; the request's own accounts left for a reason of theirs (`excludedBecause`) count by that reason on the ChatGPT pool too. A session on a key moves on the key's limit or OpenAI's quota refusal with its conversation state reset (`x-agl-account-state-reset`), is refused 503 for the key's own other reasons, and returns to a ChatGPT account only with a request that carries no account state. Codex's `usage_limit_reached` names each key held by its spend limit. `agl proxy status` judges an OpenAI key's model list as a request does, so its rank and `nextPlacement.openai` come from the same check placement uses. Downgrade: an earlier proxy fails to start on a store that keeps a lease of `{ provider: 'openai', credential: 'api_key' }` -- one held when the proxy stopped for the downgrade -- since its stored-lease check refuses that policy; AGL itself takes no such lease (`agl proxy run` and the controller name no OpenAI credential), only a control client that asks for one. Stop that client before downgrading: closing its connection ends the lease. If an earlier proxy's start already failed, start this release once with the client stopped and wait out the lease's 60-second reclaim window, which ends it, then downgrade. A key OpenAI refuses with 403 during a request answers that request with 403 `provider_forbidden`; the key is not held and the session does not move.
|
|
9
|
+
- Take an API key its provider refuses for billing or spend out of rotation (AGL-299). The gateway reads up to 64 KiB of a key's 402 or 429 before a byte reaches the client: Anthropic's 402 `billing_error`, Anthropic's 429 with `error.details.error_code: enforced_spend_limit_reached` (the organization's usage-tier monthly spend cap, held until 00:00 UTC on the first day of the next month) and OpenAI's 429 whose `error.code` is `credit_balance_exhausted`, `organization_spend_limit_exceeded`, `project_spend_limit_exceeded` or `organization_usage_limit_exceeded`, or whose `error.type` is `insufficient_quota`, are payment problems: the request goes to another account before a byte reaches the client (journaled `unavailable`), and with none the client gets the provider's own answer unchanged; later requests get the proxy's 402 `payment_problem`, which names each account, what it was refused for and what ends it. The proxy reports a key's problem to the accounts service with its cause (`authswitch.authority.usage.paymentProblem` `cause: spend_cap|quota`; none for a billing error, so a Claude account's report is unchanged), which holds it on the key's usage, a spend cap until it ends and anything else until a re-check; the proxy reads every key's usage for it, and holds the problem itself while the service cannot (a spend cap until it ends, anything else until the proxy restarts). `agl proxy status` shows such a key unavailable with its `cause` and, for a spend cap, `retryAt`; a key's 402 no longer passes through. A spend limit set in the Claude Console (HTTP 400) is not read as a payment problem. The ChatGPT backend -- its Codex catalog, model list and Responses endpoint -- is reached only through ChatGPT sign-ins, whatever credential a policy names, so a pool of OpenAI API keys alone answers Codex's `/models` with 503 `no_eligible_accounts`.
|
|
10
|
+
- Stop placing requests on an API key at its monthly spend limit (AGL-299). The inference proxy judges each OpenAI or Anthropic key on its month's spend as the accounts service answers it, by the one usage-limit rule: a key at its limit, or with an answer of the month that could not be priced, takes no request until 00:00 UTC on the first of the next month, and its sessions move to another key or, for Claude Code, to a Claude account; with every account and key held, the 429 carries the earliest reset and names each key held by its spend limit with its month in dollars. A key with a limit whose spend cannot be read takes no request (`unavailable.reason: spend_unread`), and a 503 names it, a session's own key included; a key without a limit keeps serving and has no spend window. A key with a limit is placed by what is left of its month, ahead of the keys without one; a reading whose month ended counts as the new month's, with its whole limit left until that month's end. `agl proxy status` shows a key's month in place of a maximum usage -- `available ($12.40 of $50.00 this month)`, `available ($3.20 this month, no spend limit)`, `at its spend limit` (or `held until its month ends` for an unpriced answer) until the month's end, unavailable while its spend cannot be read, held with its payment cause, or cooling down -- and what is left of its limit as its headroom.
|
|
11
|
+
- Add OpenAI and Anthropic API keys as accounts, from **Settings → Accounts** or the command line (AGL-299). **Add account** offers **OpenAI API key** and **Anthropic API key** beside ChatGPT and Claude; the key's dialog asks for the key (masked, with no autofill, at most 512 characters, never written into the page), an optional name and an optional monthly spend limit, stays locked while the provider is asked and keeps a refusal inside it, and once the key is added the screen says the spend limit the account has; `authswitch account add openai-api-key|anthropic-api-key [--label <label>] [--spend-limit <dollars>]` reads the key from standard input. A key that begins the way the other provider's keys do (`sk-ant-` for an OpenAI key; `sk-proj-`, `sk-svcacct-`, `sk-admin-` for an Anthropic key) is refused before anything is sent. The accounts service then asks the provider for the models the key may use and holds nothing unless it accepts the key: a rejected key, an OpenAI key that may not read models (HTTP 403, with what to grant it) and a provider that cannot be asked are refused with the reason, and no answer, log or audit record repeats the provider's words or the key; OpenAI's model list is read through @modelprofile.com/flexharness-providers 16.3.0 (`listOpenAiApiKeyModels`, the key as the bearer alone, no redirect followed). The test support's fake provider (`ts_authswitch/testsupport`) answers OpenAI's model list too, for the keys `issueOpenAiApiKey` issues. The key is sealed like every other credential, one sealed kind per provider (`anthropicApiKey`, unchanged, and `openaiApiKey`), and never shown again; the account names it only by how it ends (`keyHint`, `…` and its last four characters), and an Anthropic key added before keeps no hint. Each key is an account of its own (tenancy `api-key`) with the new login purpose `openai_api_key` beside `anthropic_api_key`; both back the inference proxy's gateway alone. Adding a held key again is refused; adding a removed or rejected key again brings its account back, with its month's spend and its spend limit unless the add names another. Removing a key does not revoke it at the provider, which its removal says. The Accounts screen lists the keys under **OpenAI API keys** and **Anthropic API keys**, after the sign-ins, each row named "OpenAI API key ending …abcd"; the overview counts the keys together (`2 ChatGPT · 1 Claude · 3 API keys`). A key's page offers **Rename**, its **Spend limit** under Limits, **Show latest usage**, **Re-check** while a payment problem holds and **Remove**; a key has no maximum usage, reserve lift, Codex switch, login test or new sign-in, and an OpenAI key is no Codex connection a project can be set to. A key its provider no longer accepts reads **Key rejected** and says to add a key it accepts. The Anthropic key's login reads **Anthropic API key** (was "Anthropic API key for Flex"). Controller requests `controller.accounts.apikey.add` and `controller.accounts.spendlimit` (audited as `accounts.apikey.add` and `accounts.spendlimit`, which carry no key); authswitch requests `authswitch.authority.apikey.add` (now with `providerId: 'openai'` and `spendLimitMicroUsd`) and `authswitch.authority.spendlimit`. Controller protocol 44.
|
|
12
|
+
- Give each API key a monthly spend limit (AGL-299). A key's limit is a whole number of cents from $0.01 to $1,000,000 per calendar month in UTC (`spendLimitMicroUsd` on the account, `authSwitchSpendPeriod`), set under **Spend limit** on the key's page (its own **Save**, and **No limit**) or `authswitch account set <account-id> --spend-limit <dollars>|--clear-spend-limit`; a maximum usage and a reserve lift are refused on a key (`invalid_input`), and a spend limit on a sign-in. It counts AGL's own traffic only: AGL's inference proxy prices each answer it relays on a key once, by the model that answered and the service tier the provider reported, and reports it to the accounts service (`authswitch.authority.usage.spend`, at most 64 answers, each with its own id), which records each report whole in one transaction in the key's monthly ledger (two new collections, `authswitch_authority_spend_entries` and `authswitch_authority_spend_periods`); a repeated answer changes nothing, and an answer by a recorded id with other content refuses the report. Only the current and the previous month are kept. A key's usage is its month's spend (reading `kind: 'spend'`: spent, limit, answers, unpriced answers, when the month resets), and the one usage-limit rule holds a key with a limit at it, or with an answer that could not be priced, until the month ends; the answers already running at the crossing finish, so a key can end its month that much over. A removed key keeps its month. The account is announced as changed when a key's limit state flips, its month's first spend lands or its limit changes. On the Accounts screen a key's row shows its month as its one meter, in dollars (a key without a limit shows its spend alone), and its page a **Monthly spend** meter with "$12.40 of $50.00 this month · AGL traffic only · as of 14:02" (or "Spent $12.40 this month · no limit · as of 14:02") under it, in the attention colour from 80% of the limit. In the screen's status order a key's spend limit is its usage limit: at it the row reads **At spend limit · back …** (**Unpriced answer · back …** for an answer that could not be priced), grey like any limit and counted as held back by usage, and the page says **At its spend limit, resets …**; a payment problem comes first, **Spend cap reached** for the provider's spend cap, and a rejected key next. The figure is read again on **Refresh**, on **Show latest usage** and when the account is announced as changed, never on a timer. `authswitch list` shows a key's month in dollars. The provider's own spend limit stays the backstop for everything else that uses the key: set AGL's below it. A key held with a payment problem says its cause (a billing error, a spend cap, a used-up credit or spend limit), and **Re-check** (`authswitch account recheck`) lifts its hold at once -- a spend cap's too, before the first of the month -- without asking the provider (`outcome: 'cleared'`); if the provider still refuses, the next request moves before a byte reaches the client and the hold is set again. Downgrade: there is none past this release except restoring the accounts service's store (`~/.local/share/authswitch/authority`) from a backup taken before the upgrade. Its first start creates the two spend collections and gives every account `spendLimitMicroUsd` and `keyHint` (migration `0011_account_api_key_fields`), and a key's payment problem is held on its usage record; an older accounts service refuses to start on any of them.
|
|
13
|
+
- The proxy prices each answer an Anthropic key serves once, as it ends, with the flexharness 16.3.0 price table (`priceModelCall`, `getAnthropicModelPrice`, `getAnthropicRequestPrice`, `readAnthropicMessagesUsage`): by the model that answered and what its usage states, with the fees of the server tools the request declared, priced by exact type, a stream's merged from `message_start` and `message_delta`. An answer it cannot price -- a model the table does not list, Priority Tier, a cache lifetime, region or server tool without a price, usage it cannot read -- counts as unpriced and goes to the proxy's journal; a stream the client cuts counts the usage it stated by then (Anthropic may bill more; the Console limit is the backstop), and one cut before it stated any usage counts nothing.
|
|
14
|
+
- The proxy prices each answer an OpenAI key serves once, as it ends (`inferenceOpenAiSpend`: flexharness `readOpenAiResponsesUsage`, `getOpenAiModelPrice`, `priceModelCall`): by the model and service tier the response states, with every `web_search_call` and `file_search_call` item one call of its tool whatever its action (AGL's figure can exceed OpenAI's bill), a completed, incomplete or failed answer on the usage it states. An answer as a model the table does not list (a dated snapshot of a GPT-6 model included), with no service tier, or whose final event is over 8 MiB counts as unpriced and goes to the journal, naming the model. An answer that ends without OpenAI stating its usage -- the client left, the stream was cut, or the response failed stating none -- counts nothing and no usage is estimated; the journal says so for each one (`uncounted`), so AGL's figure can be below OpenAI's bill and the OpenAI project's budget is the backstop. A key with a limit is sent the checked body encoded again with the tier it was checked for (`default` for `auto` or none; an explicit tier only with a price for the model, `fast` naming Fast mode) and takes no request with an unpriced model or tier, `background: true`, or a server tool without an exact call fee (`web_search_preview`, code interpreter, image generation, hosted MCP or tool search, a container shell); functions, custom tools, the local shell, apply patch, computer use and client-run tool search (`execution: "client"`, as Codex 0.163 sends it) cost tokens only. `test/e2e/e2e.proxyspendcodex.node.ts` is the release gate: real Codex on a limited key, a key at its limit through the ledger, and pending spend across a proxy restart.
|
|
15
|
+
- The answer counts against the key's limit at once: the proxy writes it to its own database (`agl_inference_spend`, no expiry) at once, apart from the reports, and reports it to the accounts service whether written yet or not (`authswitch.authority.usage.spend`, one key's answers on one login per report, at most 64, each once), on the login the answer was served on (carried on the route from the pool read), also once the key was removed, and an answer no login can be named for counts and holds a limited key until one is; it judges a key on the reading plus what it still holds, of the current UTC month only, across a proxy restart or a crash too; a reading whose month ended starts the new month at nothing under its limit (`authSwitchSpendWithPending`). The acknowledgement holds the accounts service's answer and drops the answers in one step.
|
|
16
|
+
- A report the accounts service refuses (`invalid_input`, or a malformed request) is held, never dropped: the answers keep counting, the key with a limit takes nothing (`unavailable.reason: spend_unrecorded`, and a 503 names it), the refusal goes to the journal, and the answers are sent again every ten minutes and at every start. So is a key with a limit while one of its answers is held in neither the proxy's database, which could not write it, nor the ledger. Any other failure, `Internal server error` included, is retried, waiting from 5 seconds up to 5 minutes, said once.
|
|
17
|
+
- A key with a limit is sent the request body as the price check read it, encoded again with `service_tier: "standard_only"`, also when the client named it (subscriptions and keys without a limit keep the client's bytes), and takes no request the price table cannot price (a model, another tier, a speed or `inference_geo`, a server tool whose exact type has no price, such as code execution, the advisor, an undated tool search alias or the MCP connector): the request goes to an account that can take it, else it gets 400 `invalid_request_error` naming what has no price and the fix (`model_unpriced`). `count_tokens` is exempt. A key without a limit takes such a request and counts its answer as unpriced.
|
|
18
|
+
- Downgrade: an earlier proxy ignores `agl_inference_spend`, so the answers it holds are neither counted nor reported until this release runs again.
|
|
19
|
+
- AGL verified Claude Code 2.1.306 (AGL-686). Only a release newer than 2.1.306 is now mentioned to the driver, once, and the mention names 2.1.306 as the release AGL verified. The fleet still runs 2.1.282 and every newer release; its floor stays 2.1.282 until the fleet stops handling that release's output.
|
|
20
|
+
- A release an earlier AGL recorded but never mentioned is mentioned at start only while it is still newer than 2.1.306; otherwise its record is dropped, so no start says "Claude Code 2.1.306 is newer than 2.1.306".
|
|
21
|
+
- The accounts service lists 2.1.306 and 2.1.288, the release AGL's own Claude chats run through the Agent SDK, among the releases whose credential handling it read. Its usage read still takes every release from 2.1.282 on.
|
|
22
|
+
- The check is in `ts_authswitch/native-contracts.md` ("Claude Code compatibility check"). 2.1.306 keeps every fleet item. Its new `api-request` transcript records are about a quarter of what it writes, and the fleet's 4 MiB window still holds hours of a worker's turns.
|
|
23
|
+
|
|
24
|
+
### Fixes
|
|
25
|
+
|
|
26
|
+
- The accounts service refuses Claude Code's usage read while it runs itself with `CLAUDE_SECURESTORAGE_CONFIG_DIR` naming a directory other than Claude Code's home (AGL-773). A Claude sign-in (`authswitch account add claude`, and the command the Accounts screen shows) now sets the variable to the sign-in's own home: inherited from the terminal, it sent the new login into another store, over the login that store held, possibly Claude Code's own, and the sign-in expired without adding the account.
|
|
27
|
+
- Make the Accounts screen's **Needs you** count readable in the light theme (AGL-299). The tile's figure was set in the warning fill colour (systemOrange, about 2.2:1 on white); it now takes the catalog's warning text colour (`--dees-color-text-warning`, 6.4:1 on white), as the screen's other warning text does. The dark theme looks the same.
|
|
28
|
+
- Refuse a Claude session whose own account cannot take its request, with nothing else left, with a 503 saying why (AGL-299). When the session's account takes no request for a reason of its own -- its usage or model list cannot be read, it needs a fresh sign-in, the account service answers no access for it, or a key's spend cannot be read -- and no other account can take the request, the proxy now answers 503 `no_eligible_accounts` (no eligible account has current usage, an available sign-in and access to the requested model, naming every key whose spend could not be read, the session's own included) instead of a 429 claiming every account is at its limit. An account without the requested model leads to the 404 as on its own, and a payment problem to the 402 as before.
|
|
29
|
+
|
|
3
30
|
## 2026-10-22 - 40.4.0
|
|
4
31
|
|
|
5
32
|
### Features
|