opencode-auth-load-balancer 0.4.0 → 0.4.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.
package/README.md CHANGED
@@ -194,7 +194,7 @@ All knobs are environment variables with sane defaults.
194
194
  | `OPENCODE_AUTH_LB_MAX_WAIT_MS` | `305000` | Bounded wait for requests that do **not** carry both an opencode session id and user-message id (provider-internal or external fetches). Session-bound user turns use durable pending instead and wait until provider quota recovers or the user cancels. Auth (`401`/`403`), disabled/re-login accounts, and network failures are never converted into quota waits. `0` makes anonymous requests fail fast. |
195
195
  | `OPENCODE_AUTH_LB_ANTHROPIC_OPUS_FALLBACK_MODEL` | *(unset — ladder mode)* | Claude Max accounts have **separate weekly caps per premium model tier** (Fable, Opus, …). When one is exhausted, that tier's requests 429 (`anthropic-ratelimit-unified-representative-claim: seven_day_fable` / `seven_day_opus` / …) even though the account's aggregate 5h/7d windows still have headroom and every other model works. Instead of cooling the **whole account** down (which cascaded every account into "cooldown"), the balancer records a **per-tier** cooldown: requests for that tier steer to accounts with tier headroom, and once the whole pool is tier-limited they descend the **fallback ladder** (next family down, best version in your provider's model list; toasted, never silent). **Unset** = ladder mode (recommended). Set to a **model id** to pin a fixed downgrade target (bypassing the ladder). Set to an **empty string** to disable (revert to the account-wide cooldown). The env name keeps `OPUS` for compatibility but applies to **every** tier. |
196
196
  | `OPENCODE_AUTH_LB_ANTHROPIC_FAMILY_ORDER` | `fable,opus,sonnet,haiku` | The fallback ladder's model families, **best first**. A tier-capped request downgrades to the highest-versioned configured model of the next family below the capped one (e.g. capped `fable` → newest `opus`; capped `opus` → newest `sonnet`). A family not in the list (a future top tier) is treated as above the first entry — so when Anthropic ships a new premium tier, a config tweak (or nothing at all, if it slots on top) keeps the ladder correct without a code change. |
197
- | `OPENCODE_AUTH_LB_ANTHROPIC_CLAUDE_CODE_VERSION` | *(unset — auto)* | Pin the Claude Code version the plugin claims to be, bypassing both the npm lookup and the built-in floor. Anthropic **gates new models on this version** — asking for a model newer than the version you report is rejected with `claude_code_version_too_old` — so by default the plugin resolves it automatically (see [Claude Code version](#claude-code-version)). Set this only to pin **forward** (a version npm hasn't tagged `latest` yet) or **back** (to reproduce a failure, or if a future release changes the request fingerprint and the newest version starts failing). Must be a plain `x.y.z`; anything else is ignored. |
197
+ | `OPENCODE_AUTH_LB_ANTHROPIC_CLAUDE_CODE_VERSION` | *(unset — auto)* | Pin the Claude Code version the plugin claims to be, bypassing both the npm lookup and the built-in floor. Anthropic **gates new models on this version** — asking for a model newer than the version you report is rejected with `claude_code_version_too_old` — so by default the plugin resolves it automatically, and relearns it from a rejection when the gate moves before npm does (see [Claude Code version](#claude-code-version)). A pin also disables that reactive recovery, since the retry would send the version you pinned. Set this only to pin **forward** (a version npm hasn't tagged `latest` yet) or **back** (to reproduce a failure, or if a future release changes the request fingerprint and the newest version starts failing). Must be a plain `x.y.z`; anything else is ignored. |
198
198
  | `OPENCODE_AUTH_LB_DIR` | — | Override the pool-file directory (handy for tests). |
199
199
  | `OPENCODE_AUTH_LB_DEBUG` | — | `1`/`true` logs each selection to stderr. |
200
200
  | `ANTHROPIC_BASE_URL` | — | Route Anthropic requests through a custom base URL. |
@@ -217,6 +217,10 @@ A hard-coded version therefore breaks on *every* model launch, so the plugin res
217
217
 
218
218
  The resolved version only ever moves **up**, so a registry blip or a rolled-back `latest` can never drag the plugin below a version already known to work; use the env pin to go down deliberately. The lookup is a single ~56-byte request fired at startup: the loader awaits only the local cache read, never the network, so neither opencode's startup nor your first request waits on npm — and a registry outage just leaves the previous version in place.
219
219
 
220
+ **The gate's own rejection is the fourth route, and the only one that cannot lag.** A cache is only as fresh as its TTL, so a model launching *inside* that window is rejected for as long as the TTL has left to run — precisely when you need the new version. No polling cadence fixes that in general either: Anthropic can gate a model before npm tags the release. So the plugin learns from the failure instead. A `claude_code_version_too_old` response is parsed for the minimum it demands, npm is asked for the real `latest` in the same breath (the minimum only clears the model that just failed), the result is cached, and the request is **transparently retried once** — with the new version in both the User-Agent and the body's `cc_version=` fingerprint. The turn succeeds instead of failing, and every later request starts from what was just learned.
221
+
222
+ The retry is spent only when the reported version actually *changed*, so a 400 for any other reason is passed straight through with its body untouched, and a pinned deployment is unaffected: under an explicit `OPENCODE_AUTH_LB_ANTHROPIC_CLAUDE_CODE_VERSION` the rejection is returned as-is, because a retry would send the very version you pinned.
223
+
220
224
  > Only the version *string* is discovered. The request fingerprint's salt is still compiled in, so if Anthropic ever changes that algorithm, pin a known-good version with the env var and open an issue.
221
225
 
222
226
  ---