better-ccflare 3.5.53 → 3.5.55

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
@@ -781,34 +781,40 @@ Full documentation available in [`docs/`](docs/):
781
781
  **Not supported:**
782
782
  - ARM32 devices (Raspberry Pi Zero, Pi 1, Pi 2, or 32-bit OS)
783
783
 
784
+ ## Sponsors
785
+
786
+ | | |
787
+ |---|---|
788
+ | <a href="https://signpath.io/"><img src="https://avatars.githubusercontent.com/u/34448643?s=200&v=4" alt="SignPath" height="40"></a> | Free code signing on Windows provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). |
789
+
784
790
  ## Acknowledgements
785
791
 
786
792
  Inspired by [snipeship/ccflare](https://github.com/snipeship/ccflare) - thanks for the original idea and implementation!
787
793
 
788
- **Special thanks to our contributors:**
789
- - [@bitcoin4cashqc](https://github.com/bitcoin4cashqc) - SSL/HTTPS support implementation with comprehensive documentation
790
- - [@anonym-uz](https://github.com/anonym-uz) - Critical auto-pause bug fix, analytics performance optimizations, request body truncation, and incremental vacuum implementation
791
- - [@makhweeb](https://github.com/makhweeb) - Enhanced request handling and analytics improvements
792
- - [@jw409](https://github.com/jw409) - Fixed OAuth account addition in WSL2 and compiled binaries by replacing unreliable prompt() with readline; systemd deployment guide, BUN_JSC_* crash loop analysis, and preflight environment validator (PR #106)
793
- - [@materemias](https://github.com/materemias) - Testing and validation of Vertex AI provider implementation, thorough debugging of OAuth API key authentication (issue #54), requesting and validating AWS Bedrock support (issue #49), extensive testing of new releases and features; fix request details modal hydration race where payload row committed after parent request row, adding lazy re-fetch via `/api/requests/payload/:id` with 404 fallback (PR #186)
794
- - [@tqtensor](https://github.com/tqtensor) - Comprehensive memory leak fix preventing OOM kills with smart chunk capping, memory monitoring, and optimized cleanup (PR #67)
795
- - [@lunetics](https://github.com/lunetics) - Force-reset rate limit feature allowing manual clearing of stale rate-limit locks via API, CLI, and dashboard with immediate usage polling (PR #68), OOM kill prevention with periodic data retention cleanup, 3-day default retention, and time-scoped stats queries (PR #70), model registry sync removing retired models and adding sonnet-4.6 CLI shortcut (PR #71); migrated Anthropic usage handling to the new `limits[]` payload shape — per-account display (session/weekly/per-model rows with severity and overage-credit meter), account ranking/utilization and capacity-guard reads, and model-aware proactive throttling so a per-model weekly cap (e.g. Fable) only throttles matching-model requests instead of the whole account, with flat-window fallback throughout ([#296](https://github.com/tombii/better-ccflare/pull/296)); surfaced usage-window exhaustion that was invisible to both `/health` and the accounts dashboard during the 2026-07-09 incident — added a `usage_exhausted` pool counter (subset of `routable`), extracted `rateLimitStatus` display precedence into a tested pure helper (usage exhaustion > unified header snapshot > legacy cooldown > OK), and a shared staleness guard so a known-past window reset is never reported as exhausted on either surface, with characterization tests pinning the incident's account-wide-bench-to-pool-flap chain ([#299](https://github.com/tombii/better-ccflare/pull/299)); fixed the agent interceptor silently rewriting subagent request models to stale/retired ids via a hardcoded alias map, and unconditionally overriding explicit per-call model choices — rewrites now fire only on an explicit DB preference, with a live Anthropic model catalog (`GET /v1/models`, weekly refresh, console-API accounts only by default to avoid OAuth ban risk, passive capture from pass-through traffic) vetting rewrite targets and backing dashboard model dropdowns/validation; added `original_model`/`applied_model` request-row provenance and an `x-better-ccflare-model-rewrite` response header, post-rewrite-aware account/combo routing, agent model provenance (`modelSource`, revert-to-inherit) with a redesigned two-mechanism dashboard edit dialog separating the agent-file default from the runtime preference override ([#300](https://github.com/tombii/better-ccflare/pull/300)); persisted `requires_reauth` detection for dead OAuth refresh tokens after a real incident where two accounts' tokens silently failed for ~43h undetected — preserves the machine-readable OAuth error code ahead of `error_description` in the Anthropic/Codex/xAI providers (previously discarded, so detection could never match), routing exclusion at the single `isAccountAvailable` choke point plus all proactive refresh paths, clear-sites across every reauth flow (CLI, dashboard, Qwen/Codex HTTP handlers), a new `auth_failure` webhook/SSE alert with cooldown dedup, and a dashboard "Needs authentication" badge, backed by ~120 new tests ([#311](https://github.com/tombii/better-ccflare/pull/311)); added opt-in model-scoped capacity routing (`MODEL_SCOPED_CAPACITY_ROUTING=exhausted`) that filters accounts whose per-model-family weekly cap is exhausted, combining telemetry-based `weekly_scoped` usage windows with a reactive 5-minute in-memory negative cache fed by observed `out_of_credits` 429 responses (provenance-tagged `recent_upstream_rejection` vs. `telemetry_confirmed` for honest client messaging), plus a new `session-drain-soonest` load-balancing strategy (`LB_STRATEGY=session-drain-soonest`) that preserves session-stickiness while ranking fresh-selection candidates by earliest future `weekly_all` reset to consume capacity before it expires ([#325](https://github.com/tombii/better-ccflare/pull/325)); Routing settings card on the Settings tab wiring the strategy selector and model-scoped capacity routing toggle from PR #325 to the dashboard — a shared `resolveEnvFileSetting<T>()` resolver backs `getStrategySource()`/`getModelScopedCapacityRoutingSource()` so the env>file>default precedence can't drift between the getter and its source twin, both controls show an env-locked badge and disable themselves when overridden by `LB_STRATEGY`/`MODEL_SCOPED_CAPACITY_ROUTING`, an active-but-unlisted strategy renders as a disabled "(current)" item instead of a blank Select that could be silently overwritten, and a strategy switch now also invalidates the accounts query so the "Primary" badge updates immediately instead of drifting for up to 60s ([#329](https://github.com/tombii/better-ccflare/pull/329)); guarded logger `JSON.stringify` call sites (`Logger.formatMessage`, `LogFileWriter.write`) against circular references and `BigInt` values that previously threw uncaught out of routine `log.debug/info/warn/error` calls into caller code, with a sanitized fallback that preserves `ts`/`level`/`msg` and byte-identical output for normal serializable events ([#330](https://github.com/tombii/better-ccflare/pull/330)); fixed a false auto-pause chain where a legitimately usage-throttled account's `five_hour` window reset triggered an auto-refresh probe, but `applyUsageThrottling` had no exemption for internal synthetic probes and returned better-ccflare's own 529 back to the scheduler, which counted it as a genuine endpoint failure toward `FAILURE_THRESHOLD` — exempted requests carrying the pre-existing `x-better-ccflare-auto-refresh`/`x-better-ccflare-keepalive` loop-prevention headers from usage throttling entirely, and stopped counting a 529 probe response toward the consecutive-failure pause threshold as defense in depth, with TDD regression tests proving both failure modes pre-fix ([#331](https://github.com/tombii/better-ccflare/pull/331)); `docs/routing-architecture.md`, a technical reference documenting the account-selection pipeline end-to-end — the four load-balancing strategies, usage-throttling's pacing-line model, model-scoped capacity routing, and auto-fallback — each with a plain-English explanation and a Mermaid flowchart cross-checked against source, including accuracy fixes from two rounds of Greptile review (combo-routed requests skip model-scoped weekly windows; `out_of_credits`/keepalive 429s don't apply an account cooldown; a request with no `clientSessionId` isn't recorded as a sticky session-affinity mapping; `checkForAutoFallbackAccounts` and `wouldAutoUnpause` are two distinct eligibility rules, not one shared rule) plus a correction for the synthetic-probe throttle exemption landed by PR #331 after this doc branched ([#332](https://github.com/tombii/better-ccflare/pull/332)); fixed the `build:linux-*`/`build:macos-*`/`build:windows-x64` platform-specific build scripts omitting the `--define __BETTER_CCFLARE_VERSION__` flag that the plain `build` script sets, causing locally-built platform binaries to misreport their version via the unrelated `CLAUDE_CLI_VERSION` fallback in `getVersionSync()` ([#333](https://github.com/tombii/better-ccflare/pull/333)); closed a security hole where the `x-better-ccflare-auto-refresh`/`x-better-ccflare-keepalive` internal-probe markers were trusted from any client, letting a forged marker bypass usage-throttling, suppress per-account rate-limit cooldowns, and defeat the 529 in-place retry safety net — gated all probe-marker read-sites behind a process-local secret (`crypto.randomUUID()`, minted at startup and threaded through `ProxyContext`) that only the two schedulers' own loopback probes carry, with the secret stripped from request history, cache-body storage, and the outbound provider request path so it can never leak ([#336](https://github.com/tombii/better-ccflare/pull/336)); separated transient 529-overload cooldown handling from the 429 rate-limit ramp after a production incident where reset-less 529 bursts drove healthy accounts to a 5-minute cooldown ceiling purely on server-side overload signals — a reset-less 529 now takes a fixed short cooldown (`CCFLARE_OVERLOAD_COOLDOWN_MS`) instead of the exponential ramp, a 529-with-reset caps window-scale reset values at overload scale rather than the 429 ceiling, neither 529 variant advances the `consecutive_rate_limits` streak, the single-flight recovery-probe gate arms on the persisted overload reason so an all-529 account still gets a single-flight probe, an all-suppressed candidate pool retries the highest-priority account ungated instead of failing with a hard 503, and the capacity-exhaustion response distinguishes real weekly exhaustion from an eligible account merely cooling down ([#342](https://github.com/tombii/better-ccflare/pull/342)); diagnosed the root cause of orphaned `Response.clone()` tee branches on the 529-overload check path (issue #354) with a comprehensive test suite covering every affected call site (PR #355) — landed as the two still-open header-only `parseRateLimit` clone removals and disposal of the terminal-529 check clone via the existing `drainBody`/`cancelDiscardedResponseBody` helpers (consistent with the Bun no-op-leak finding from PR #353), plus the analogous unreleased `parseUsage` streaming clone in `updateAccountMetadata` ([#355](https://github.com/tombii/better-ccflare/pull/355)); found the same orphaned-tee defect class surviving at the sites #355 deliberately left alone — three body-inspecting error classifiers (`isModelUnavailableError`, `isInvalidThinkingSignatureError`, `isCacheControlRejectionError`) cloned before their content-type gate and stranded that copy on every non-JSON body (ordinary for providers like Qwen), and `OpenAICompatibleProvider.processResponse` had the inversion — its *incoming* response was the orphan, on every successful JSON reply from every openai-compatible provider — fixed by cloning only after the gate and cancelling the discarded original once conversion succeeds, with 9 regression tests pinning both the disposal and the unchanged classifications ([#358](https://github.com/tombii/better-ccflare/pull/358)); fixed `makeProxyRequest` replacing any caller-supplied abort signal with its own timeout-only controller that becomes unreachable once headers arrive, so a client disconnect mid-stream (idle watchdog, Ctrl-C, dropped network) never aborted the upstream fetch — `reader.cancel()` measured as a no-op on Bun, only `abort()` closes the socket, leaking one upstream connection and burning quota for as long as the provider held it open — combined the caller's signal with the header-phase timeout via `AbortSignal.any`, routed every upstream call through one `forwardUpstream` helper that passes the signal explicitly (since provider body transforms rebuild the Request from its URL and drop any signal it carried), and taught the top-level catch to distinguish a client-disconnect `AbortError` from a genuine account failure — returning 499 immediately instead of walking every remaining account and fallback route for a request nobody is listening to ([#360](https://github.com/tombii/better-ccflare/pull/360)); fixed the request-payload endpoint reporting "Request not found" whenever a payload was merely absent — payloads are legitimately missing when capture is disabled or when the usage collector releases long-running requests' payloads after `REQUEST_PAYLOAD_RETENTION_MS` to bound memory, a distinction that cost real debugging time chasing a 340-second request that was in the table the whole time — the 404 now explains a payload isn't stored and why one can legitimately be absent, with the status and JSON shape unchanged ([#362](https://github.com/tombii/better-ccflare/pull/362)); persisted the client session id (body `metadata.user_id`) with each request row after a stream-stall investigation misattributed 43 requests from a locally spawned subagent to a user's test run — the session id was already used for session-affinity routing but never stored, so a session's own subagents were indistinguishable from the session itself in request history; threaded it through the usage collector and all `forwardToClient`/synthetic-failure `saveRequest` call sites with preserve-first `COALESCE` semantics so error-path re-saves can't blank out attribution the main path recorded, sanitized on write (control-character strip, 200-char cap, mirroring `sanitizeProjectName`) since the value was previously only routing state and had no bound on stored length, and fixed the shared `toRequest`/`toRequestResponse` mappers that were silently dropping the field for any caller besides the hand-built HTTP handler — hardened through two rounds of independent adversarial review (Codex, Kimi) that caught a missing session id on 4 of 6 failure-recording paths and the mapper gap, with 9 new tests plus the full database/proxy suite green ([#363](https://github.com/tombii/better-ccflare/pull/363)); surfaced `stream_terminal_state` through `/api/requests` and the live dashboard summary after a day lost diagnosing four stalled requests that all read as healthy `statusCode: 200` — the column was already persisted but no route read it back, so a stream that died mid-content, was cancelled by the client, or was recovered was indistinguishable from a clean response; both write surfaces (REST read path and the live SSE summary) narrow the unconstrained TEXT column through a shared guard instead of an unchecked cast, an unrecognized value surfaces as `"unknown"` rather than collapsing into the same `undefined` a non-streaming request produces, and the narrowing helpers ship from a new cycle-free `@better-ccflare/types/request` subpath since evaluating the package barrel first trips a pre-existing types↔core import cycle — hardened through three rounds of independent adversarial review (Codex, Kimi, Gemini) that caught the unchecked cast, the unknown-state conflation, and an unhardened live-summary path ([#364](https://github.com/tombii/better-ccflare/pull/364)); fixed spurious usage-window resets from upstream `resets_at` timestamp jitter — providers recompute the reset time per response and it jitters by fractions of a second around the same instant, which `notifyWindowReset`'s any-forward-movement check misread as a real rollover on nearly every poll (99.4% of 1564 detections over 48h of production traffic across three Anthropic accounts, max 1.879s jitter vs. genuine rollovers all advancing exactly 5.00h), each false positive spuriously resetting the session-affinity state the `session`/`session-drain-soonest` strategies depend on; added a `WINDOW_RESET_MIN_ADVANCE_MS` (60s) minimum-advance threshold sitting in the empty gap between jitter and real rollovers, backed by 4 new regression tests ([#381](https://github.com/tombii/better-ccflare/pull/381))
796
- - [@troykelly](https://github.com/troykelly) - Comprehensive PostgreSQL compatibility fixes including boolean type handling, identifier case preservation, BIGINT string coercion, UNION ALL type alignment, HAVING clause compatibility, parameter ordering corrections, worker initialization, and connection pooling (issue #81); detailed bug report and root cause analysis for `/api/accounts` Invalid Date error affecting PostgreSQL BIGINT columns (issue #88)
797
- - [@cowwoc](https://github.com/cowwoc) - Compact reliability fixes, space breakdown after cleanup, requests tab without payloads (PR #149); clear stale rate_limited_until when usage API shows capacity restored (PR #150); prevent manually-paused accounts from being selected as auto-fallback candidates (PR #151); balance new sessions by utilization within same-priority accounts using water-filling algorithm (PR #152); fix ModuleNotFound crash in compiled binary when using Compact Database by embedding vacuum-worker at build time (PR #155); expected usage position indicator on rate limit bars showing projected pacing vs. reset window (PR #156); show explicit rate-limited state when usage data unavailable on startup (PR #161); deduplicate concurrent fetchAndCache calls per account to prevent redundant Anthropic requests (PR #159); make GET /api/accounts cache-only and await usage fetch in refresh endpoint, eliminating blind 5s timeout (PR #162); mark account rate-limited when all models exhausted to prevent stale-state retry loops (PR #163); use retry-after header for dynamic model-exhaustion cooldown instead of hardcoded 1 hour (PR #164); remove implicit sonnet catch-all in getModelList preventing silent unexpected model remaps (PR #165); reduce log noise by aggregating auto-unpause skip messages and suppressing identity model mapping logs (PR #167); reasoning effort compatibility layer for OpenAI/Codex routes with deterministic downgrade mapping and count_tokens path support (PR #172, implemented in PR #188); unified Codex model mapping path (PR #203)
798
- - [@wonkooklee](https://github.com/wonkooklee) - Fixed ZlibError on startup caused by Bun auto-decompressing upstream 404 responses while leaving stale `content-encoding: gzip` headers intact — applied `withSanitizedProxyHeaders` to the model-not-found early-return path, matching every other forward path (PR #243)
799
- - [@Cotch22](https://github.com/Cotch22) - Fixed garbled non-ASCII text in request details modal by decoding base64 bodies as UTF-8 via `TextDecoder` instead of raw `atob()` (PR #246)
800
- - [@zenprocess](https://github.com/zenprocess) - LeastUsed load-balancing strategy selecting accounts by lowest utilization to prevent burst pool-exhaustion (PR #193); fix keepalive 429s incorrectly cooling accounts by skipping cooldown application on synthetic keepalive replays, and shorten the no-reset fallback cooldown from 1 hour to 60 seconds (overridable via env) to prevent mass account lockout on burst contention (PR #196); optional `X-Anthropic-Agent-Id` header for explicit per-agent attribution ([#260](https://github.com/tombii/better-ccflare/pull/260)); detect and record premature SSE termination for all Anthropic-Messages-shaped streams — generalized the terminal-recovery wrapper to cover every anthropic-compatible provider (not just native Anthropic), added a `stream_terminal_state` column distinguishing complete/recovered/error/truncated/client-cancelled outcomes, and fixed success-rate metrics silently recording truncated mid-stream responses as successful ([#344](https://github.com/tombii/better-ccflare/pull/344)); fixed usage-exhausted accounts staying in the selection pool and getting cycled through every model instead of being skipped, by filtering accounts whose representative usage window is fully exhausted out of `getOrderedAccounts()` before the load-balancing strategy ever sees them, with `isAccountAvailable` gaining an optional usage-snapshot parameter so the same exhaustion check backs both the forced-account and combo-slot selection paths ([#345](https://github.com/tombii/better-ccflare/pull/345)); fixed dashboard double-submit creating duplicate accounts and the by-name delete cascade that could wipe every account sharing a name — added a DB-level `UNIQUE(name, provider, custom_endpoint)` guard across all add paths (dashboard, CLI, OAuth), switched account deletion to be genuinely id-scoped everywhere, and added a non-destructive migration that merges existing duplicate rows (credentials, counters, flags, cooldown state) into a single survivor before enforcing uniqueness on both the SQLite and PostgreSQL paths ([#343](https://github.com/tombii/better-ccflare/pull/343)); added MiniMax Token Plan usage polling mirroring the zai/xai fetcher pattern, deriving each window's interval length from its own `end_time - start_time`, inverting the API's remaining-percent into utilization, and using only the `general` model-class row (never falling back to an unrelated quota pool like `video`) so a healthy account can't be misreported as exhausted ([#346](https://github.com/tombii/better-ccflare/pull/346)); fixed the dashboard's `no_account` bucket always reporting 0% success and being un-clickable — `COALESCE(account_used, ...)` in the success-rate query and an `IS NULL OR = ...` drill-down filter so NULL-encoded and legacy `'no_account'`-literal rows both collapse into the same bucket, plus a self-reported PostgreSQL production incident (GROUP BY functional-dependency violation from the placeholder renumberer) diagnosed and fixed same-day with a verified `GROUP BY 1` rewrite, BIGINT-as-string coercions across several repositories, and a new `DATABASE_URL`-gated live PostgreSQL query harness ([#352](https://github.com/tombii/better-ccflare/pull/352)); added a startup-time multi-instance guard warning the operator (or optionally refusing to start, via `BETTER_CCFLARE_MULTI_INSTANCE=refuse`) when another live ccflare process shares the same database — a `instance_heartbeats` table (ported to both SQLite and PostgreSQL) is written and refreshed on a 5s tick per instance, with rows older than 30s treated as a dead predecessor so a crash never blocks a legitimate restart, naming the seven categories of in-process coordination state (session affinity, usage caches, refresh mutex, circuit breaker, etc.) that silently diverge across instances sharing one database ([#376](https://github.com/tombii/better-ccflare/pull/376)); hardened MiniMax Token Plan status-code handling — replaced the `!== 0` failure check with a positive success allowlist distinguishing known failures (auth/quota) from unrecognized codes so a future MiniMax success code other than `0` can't silently read as a permanent failure, and fixed `registerMinimaxUsagePolling` silently skipping accounts with a missing/empty API key with no log signal, which previously surfaced as a misleading "no Minimax accounts found" diagnosis when accounts existed but lacked credentials ([#350](https://github.com/tombii/better-ccflare/pull/350)); gated agent workspace auto-discovery in `interceptAndModifyRequest` behind an opt-in `BETTER_CCFLARE_AGENT_WORKSPACE_AUTODISCOVER` flag (off by default), mirroring the existing `BETTER_CCFLARE_DISCOVER_PLUGIN_AGENTS` precedent — stops every request in containerized/server deploys from regex-hunting filesystem paths out of client system prompts and burning a hot-path `existsSync` + `registerWorkspace()` cache refresh that could never succeed against the local allowlist anyway, while already-persisted workspaces continue to load normally ([#361](https://github.com/tombii/better-ccflare/pull/361)); fixed Bun's native off-heap body retention (issue #273) on abandoned proxy `Response`s — benchmarked and disproved the prior `body.cancel()` mitigation as a no-op on every released Bun (indistinguishable RSS growth with or without it, confirmed upstream at oven-sh/bun#35093), then replaced it with a chunked drain (`drainBody`) that actually releases the native backing store, wired across all 12 failover/retry discard sites plus the previously-unreleased `extractUsageInfo` response clone in `updateAccountMetadata`; also caught and disclosed a flaw in the PR's own real-network leak harness after merge (bad auth token producing 144-byte responses, plus shared-process RSS measurement masking the signal), replacing it with an isolated-process rig and cautioning that its own median result is inconclusive at n=6 ([#353](https://github.com/tombii/better-ccflare/pull/353)); made the pool-exhausted 503 usage-aware after a production incident where every account was usage-capped (not rate-limited) for 116 minutes — `/health` kept reporting `routable: 3` and every 503 carried a flat `Retry-After: 60`, which combined with `CLAUDE_CODE_MAX_RETRIES=5` to kill client retries in ~300s; added a `usage_exhausted` reason to the pool-exhausted response ladder, derived `next_available_at`/`Retry-After` from the earliest of cooldown expiry and known usage reset with a 600s (UsageCache TTL) floor when no reset is known, and made `/health`'s `routable` count usage-aware to match what the proxy will actually route ([#365](https://github.com/tombii/better-ccflare/pull/365)); added a per-account/per-provider circuit breaker (closed/open/half-open) around the proxy's failure handling, tripping on sustained upstream failures independently of rate-limit/usage state and exposing a distinct `circuit_open` pool-exhaustion reason with its own 30s Retry-After cooldown floor — composed against the concurrently-landed usage-aware pool-exhaustion work (#365) so `circuit_open` still reports the longer, more honest wait when a breaker-tripped account also has a known usage or rate-limit recovery time, rather than undercutting it with the bare 30s floor ([#349](https://github.com/tombii/better-ccflare/pull/349)); surfaced full anomaly-detector totals and truncation state in the Insights dashboard — a new per-detector `AnomalyDetectorSummary` (`totalCount`/`truncated`) exposes the untrimmed detected count alongside the existing top-N sample so an operator can tell "50 of 50" (healthy) from "50 of 3,247" (a real spike) instead of a silently saturated cap, with a new `AnomaliesView` panel surfacing severity ordering, a truncation badge, and threshold text derived live from response `meta` rather than hard-coded; also fixed a self-caught regression where sanitizing the `project` field for display before anomaly detection collapsed distinct same-63-char-prefix projects into one runaway-loop group — moved the sanitizer to the output boundary so detection always sees the raw, already-length-capped DB value ([#369](https://github.com/tombii/better-ccflare/pull/369)); added an optional build-time `CCFLARE_BUILD_SUFFIX` env var so patched/forked builds report `<version>+<suffix>` instead of an indistinguishable stock version string, surfaced through the CLI `--version` flag, the server startup banner, and the dashboard bundle, with a strict no-op (byte-identical output) when unset ([#371](https://github.com/tombii/better-ccflare/pull/371)); fixed MiniMax Token Plan and Alibaba Coding Plan accounts collapsing to a single most-restrictive usage bar instead of separate 5-hour/7-day rows — added the missing providers to the dashboard's weekly-usage allow-list and a shape-based probe in `RateLimitProgress` disambiguating MiniMax's canonical camelCase `resetAt` windows from the Anthropic-style snake_case `resets_at` shape, so each window now renders its own utilization instead of the top-level `Math.max`-across-windows fallback hiding real headroom ([#372](https://github.com/tombii/better-ccflare/pull/372)); fixed `runaway_loop` anomaly detector firing CRITICAL alerts on healthy fleet parallelism (issue #367) — refined the detector's group key from `(account, model, project)` to `(account, model, project, agentUsed)` so independently-routed workers sharing one account+model+project no longer collapse into a single bucket and trip each other, threaded `agentUsed` through the row interface and detection path, added `x-claude-code-session-id` as a last-resort attribution fallback in the agent interceptor (after explicit-header and system-prompt detection) with a distinct `session_header` attribution source so claude-cli traffic gains per-session identity with no client change, and made the `loopMinRequests` threshold configurable with project preserved in the cooldown/alert dedup ID so distinct projects on the same account/model can't suppress each other's alerts ([#370](https://github.com/tombii/better-ccflare/pull/370)); deployment guide section documenting the single-instance-per-database operator rule — names the seven categories of in-process coordination state (session affinity, usage caches, refresh mutex, circuit breaker, etc.) that silently diverge when two instances share a database, explains why the divergence produces no error, and gives concrete alternatives (cold-handoff rolling restarts instead of true blue/green, and account-partitioned horizontal scaling with one database per instance); also flagged the pre-existing Kubernetes example's contradiction with the new rule for maintainer resolution rather than editing it unilaterally ([#377](https://github.com/tombii/better-ccflare/pull/377)); added build-time provenance (`version`, `git_sha`, `git_ref`, `build_date`) to the `/health` response so operators can verify the running image matches the deploy branch HEAD, with each field falling back to `"unknown"` when unset so the shape stays stable ([#380](https://github.com/tombii/better-ccflare/pull/380)); diagnosed the PostgreSQL `instance_heartbeats` integer-out-of-range crash loop (issue #383) with a production-tested repro, root cause, and workaround, then hardened the multi-instance guard's refuse-path cleanup against transient DELETE failures (SQLITE_BUSY, brief PG connection blips) that could otherwise leave an orphaned heartbeat row and cause a fast restart to falsely refuse startup again — added `clearHeartbeatWithRetry` with bounded exponential backoff (3 attempts, 25/50/100ms), used only on the refuse path since the shutdown-path stopper has no benefit from retrying a dying process ([#387](https://github.com/tombii/better-ccflare/pull/387)); replaced deployment-specific hostnames, an operator filesystem path, and internal issue-tracker identifiers with generic wording across comments and test descriptions in 12 files, preserving engineering meaning with zero behavior change ([#385](https://github.com/tombii/better-ccflare/pull/385)); fixed three real defects masking each other in `bun test`'s full-suite run — cross-file `mock.module()` pollution in `oauth.test.ts`/`qwen-account-reauth.test.ts` silently nuking other tests' `@better-ccflare/proxy`/provider exports for the rest of the process (fixed by capturing and restoring the real module around each mock), an unhandled `PRAGMA wal_checkpoint` rejection surfacing as "Unhandled error between tests" when a test's scratch DB was already unlinked before `close()` ran (made best-effort), and a CLI TLS test missing `--ssl-cert`; then routed 19 hardcoded `/tmp/test-*.db` paths through `TMPDIR` so the suite passes under sandboxed CI, and cleaned up per-test CLI database + WAL/SHM siblings to stop them accumulating across runs (issue #107, [PR #386](https://github.com/tombii/better-ccflare/pull/386)); closed the remaining gap flagged by greptile-apps on merged PR #196 — the mid-stream SSE rate-limit path in `response-handler.ts` was missing the cache-keepalive cooldown exemption already applied at the other 3 sites in `proxy-operations.ts` and `response-processor.ts`, so a keepalive replay's 200 OK response later emitting a mid-stream `event: error` frame could still cool an account that should have been exempt; applied the same `isInternalProbe(headers, ctx, 'keepalive')` idiom with the anti-forgery secret gate preserved, with regression tests pinning both exempt error types and the forged-header case ([PR #388](https://github.com/tombii/better-ccflare/pull/388))
801
- - [@robsonek](https://github.com/robsonek) - Stopped accounts being benched for a request-scoped Anthropic 429 that reports `x-should-retry: true` with no rate-limit metadata — added a fail-closed, header-only `isRetryable429` predicate (scanning all header names by `anthropic-ratelimit-`/`x-ratelimit-` prefix so a future window shape is automatically declined) that classifies such responses as `windowless_429`, failing over per-request with no account cooldown and no consecutive-429 increment instead of draining the pool account-by-account until force-reset was required (PR #375)
802
- - [@issmirnov](https://github.com/issmirnov) - Persisted analytics view controls (view mode, time range, metric, model breakdown, filters) in the URL query string and localStorage so selections survive reloads and are shareable via link, with defensive decoding and cumulative-view coupling enforced centrally (PR #252); round the Success Rate Trend chart's Y-axis tick labels and tooltip to zero decimals via `formatPercentage(..., 0)` so values like `99.1666...%` no longer overflow their container, and add a human-readable "Success Rate" line name (PR #280)
803
- - [@CorentinLumineau](https://github.com/CorentinLumineau) - Plugin agent discovery scanning Claude Code plugin directories for agent definitions alongside the built-in agents path, with manifest parsing, name validation, and dashboard display (PR #197)
804
- - [@d4rken](https://github.com/d4rken) - fixed dashboard "Active" badge showing the most-recently-used account instead of the one the load balancer would actually pick next — introduced a side-effect-free `peek()` to `LoadBalancingStrategy` (shared `peek-availability.ts` mirrors auto-unpause logic without DB writes), dashboard accounts endpoint stamps `isPrimary: true` on the predicted pick, badge renamed from "Active" to "Primary"; also inlined the account-row controls previously hidden behind a `MoreHorizontal` dropdown into flat action buttons (PR #218); Dashboard account row reorganization into wrap-friendly multi-row layout with overflow kebab menu, restored tooltip context for moved actions, and deduped re-auth condition (PR #204); surfaced API key mutation errors inline in create/delete/toggle dialogs, prevented first-key lockout by disabling admin checkbox during loading with fail-safe isFirstKey guard, and added dismissible error banner for role/toggle failures (PR #205); fixed OAuth account creation silently discarding the priority value by adding a priority column to oauth_sessions and threading it through init → session → callback → account creation, plus replaced the constraint-dropping `CREATE TABLE … AS SELECT` tier-drop rebuild with an explicit schema-preserving migration (PR #206); surfaced update-check failure reason in dashboard tile by including the underlying error message in the backend 500 response body, switching to structured Logger, and displaying the error message under "Check Failed" in the frontend sidebar tile (PR #207); exempted /api/version/check from authentication so the dashboard update tile works correctly when API keys are configured (PR #208); replaced bare error-code list with grouped error UI featuring per-(errorCode, account) dismissal, time-window selector (1h/24h/7d/all) persisted in localStorage, clickable rows opening a details modal with human-readable descriptions and recovery info, and a CTE+window-function backend query joining account rate-limit state (PR #209); added combined-quota "5h Pool" and "7d Pool" metric tiles to the Overview dashboard aggregating per-account utilization across all eligible accounts, with worst-account subline, popover breakdown by contributing/exhausted/excluded/fallback accounts, at-risk projection, and next-reset time (PR #210); fixed SIGTERM race where interval-manager's synchronous process.exit(0) short-circuited async shutdown, dropping in-flight requests and pending DB writes — removed the early exit, added HTTP drain via serverInstance.stop(), reordered shutdown sequence (schedulers → HTTP drain → usage worker → DB flush), and added a 30s watchdog to bound total shutdown time (PR #214); fixed API key copy button silently failing on plain-HTTP hosts by adding a `copyText` helper that prefers the async Clipboard API and falls back to an off-screen textarea + `execCommand` for non-secure contexts, and routed `CopyButton` and the inline copy buttons in `ApiKeysTab` through it so the checkmark animation works in both cases (PR #215); fixed post-processor worker timing out on large databases (multi-GB) by passing `fastMode=true` to skip the redundant PRAGMA integrity_check — the main thread already ran it, and re-running it in the worker blew past the 10s startup deadline causing silent request loss (PR #220); replaced the bare recent-errors string list with a grouped error card featuring per-(errorCode, account) dismissal, time-window selector (1h/24h/7d/all), clickable rows opening a details modal with human-readable descriptions, recovery info, and provider-aware copy for model_fallback_429 that escalates to error only when no other accounts are available (PR #212); redesigned request history row layout into a two-row card that never wraps the top-line summary, switched the list view to the summary-only endpoint to eliminate the ~12MB per-page-load body fetch, and fixed the API key and account filter dropdowns to source options from dedicated endpoints so deleted-but-historical entries remain selectable (PR #221); replaced fs.copyFileSync migration backup with VACUUM INTO + atomic rename via a .partial temp file, producing a defragmented consistent snapshot and ensuring the named backup only appears complete (PR #222); added bounded backup retention — after each migration backup, prunes `.backup.<ts>` files to keep the newest N (default 3, configurable via `BETTER_CCFLARE_MIGRATION_BACKUP_KEEP`, 0 to disable), sorting by name-embedded timestamp to survive rsync/clock-drift, skipping operator-renamed files with non-integer suffixes (PR #223); bumped the post-processor worker startup timeout from 10s to 60s and made it overridable via `CF_WORKER_STARTUP_TIMEOUT_MS`, preventing silent analytics loss on large (multi-GB) databases where the per-handle PRAGMA work blew past the old deadline (PR #224); narrowed the migration backup gate from a 41-condition OR to only the three genuinely irreversible operations (refresh_token NOT NULL rebuild, account_tier drop, oauth_sessions.tier drop), eliminating multi-GB backup copies on every restart when only additive ALTER TABLE ADD COLUMN migrations are pending (PR #225); resolved all 42 error-level Biome diagnostics across dashboard components, hooks, and openai-formats tests — replaced `noNonNullAssertedOptionalChain` patterns in tests with explicit non-null assertions after `toBeDefined()`, converted `forEach` callbacks to `for…of` to fix `useIterableCallbackReturn`, added scoped `biome-ignore` comments with explanations for `noArrayIndexKey` cases where stable ids aren't available, and fixed two a11y errors in ErrorBoundary (SVG role/aria-label/title, button type attribute) (PR #226); replaced the blocking startup `PRAGMA integrity_check` (which froze the event loop for 94s on a 7.6 GiB DB) with a dual-timer background scheduler running `quick_check` every 6h and a full `integrity_check` + `foreign_key_check` every 24h in a dedicated `bun:sqlite` worker, added a sticky-corrupt rule so a quick `ok` cannot mask a full corruption finding, exposed the status via an expanded `/api/storage` response and a new `POST /api/storage/integrity/check` on-demand endpoint, and surfaced it in the Overview dashboard via a `StorageIntegrityCard` and sticky `StorageIntegrityBanner` on corruption — also adds `foreign_key_check` to the full probe since `integrity_check` per SQLite docs does not verify foreign keys (PR #227); fixed Logger.{error,warn,info,debug} silently emitting `{}` for Error instances by normalizing Errors to plain objects with name/message/stack/cause before serialization — affects 159 call sites including the "Failed to intercept/modify request" line in agent-interceptor — and demoted all per-request INFO logs in agent-interceptor and extractSystemPrompt to DEBUG, plus skipped the spurious `~/.claude/.claude/agents` path construction for global CLAUDE.md entries since global agents are already loaded unconditionally by AgentRegistry (PR #228); fixed `LOG_LEVEL=DEBUG` being silently ignored — `LogLevel.DEBUG === 0` is falsy so `getLogLevelFromEnv() || level` fell back to the constructor default (INFO), suppressing all debug output even when explicitly configured; switched to `??` so the env override applies for DEBUG too, and added regression tests covering DEBUG/WARN/ERROR/unset/unknown values plus the `silentConsole` side-effect (PR #229); eliminated the blocking full VACUUM trap — hourly retention cleanup and "Compact now" called `incrementalVacuum` which silently fell back to a live full VACUUM on existing DBs (auto_vacuum≠INCREMENTAL), freezing the proxy for minutes every hour — replaced with: `configureSqlite` and `ensureSchema` set `PRAGMA auto_vacuum = INCREMENTAL` on fresh DBs, new `bootstrapAutoVacuum()` migrates existing mode-0 DBs once at startup before HTTP binds (one-time VACUUM cost, never blocks live traffic), `incrementalVacuum()` now dispatches `PRAGMA incremental_vacuum(N)` to a worker thread on mode-2 DBs or logs and returns otherwise (no destructive fallback), drops hourly N from 200000 to 8000 (~32 MiB) to keep writer-slot hold sub-100ms, adds consecutive-skip escalation after 3 missed ticks, and gates the auto_vacuum PRAGMA on mode=0 only to preserve operator-set FULL mode; also removed the dashboard "Compact now" button/endpoint (same hang trap from a different surface) while retaining `bun run cli --compact` with a writer-lock probe that refuses if a running service holds it; and dropped the stale `fastMode` constructor arg from DatabaseOperations after PR #227's integrity-scheduler rework made it redundant (PR #230); fixed `mmapSize=0` being silently ignored in `configureSqlite` — the `> 0` guard meant the default `mmapSize: 0` fell through to bun:sqlite's built-in mmap default (~15 GiB observed on a 15 GiB DB), causing the OOM-killing VACUUM scenario from #230 to recur; also applied memory-bounded PRAGMAs (`mmap_size=0`, `cache_size=-2000`, `temp_store=FILE`) to the full-VACUUM worker thread opened by `compact()` / `--compact`, which had the same gap (PR #231); bounded AsyncDbWriter retention to stop a post-processor worker memory leak — split the single job queue into separate metadata (count-capped at 2000) and payload (count-capped at 1000, byte-capped at 100 MB) queues, added a MAX_JOBS_PER_TICK=50 / MAX_DRAIN_MS_PER_TICK=250ms dual budget so processQueue yields to the event loop instead of monopolizing it, added round-robin scheduling to prevent starvation between queue types, added canAcceptPayload() preflight to skip serialization under backpressure, and surfaced per-queue lengths, byte budget, oldest-job age, and per-kind drop counters in getHealth() (PR #234); enabled the refresh-usage button for Codex OAuth accounts — sends one minimal `/responses` request (bounded to `max_output_tokens: 1`, `reasoning.effort: "minimal"`) to capture `x-codex-*` rate-limit headers, cancels the response body after the header snapshot, deduplicates concurrent clicks per-account with a shared in-flight promise, and wires the result into the existing usageCache + accounts.rate_limit_reset flow; polling intentionally excluded to avoid burning quota at 90s cadence (PR #219); added "Avg / day" and "Avg / week" burn-rate sub-rows to the Plan Value metric card on the Overview dashboard — computed server-side from fixed 7-day and 30-day windows (filter-independent) with a clamped divisor via `effectiveBurnRateDays` so thin history doesn't inflate averages, plus a generic `subRows` prop on `MetricCard` for the UI (PR #235); routed Anthropic 529 overloaded_error responses through the existing rate-limit failover machinery so configured fallback providers take over during an overload instead of forwarding the 529 directly to the client — `parseRateLimit` treats 529 as rate-limited, parsing Retry-After (delta-seconds and HTTP-date) and x-ratelimit-reset with a `clampResetTime` helper that caps reset times to 24h to prevent hostile headers from permanently disabling an account; mid-stream `SseRateLimitSniffer` fires on `overloaded_error` frames for Anthropic-shape providers (anthropic/claude-oauth) only with a line-anchored regex to prevent false positives; two new `RateLimitReason` values (`upstream_529_overloaded_with_reset` / `upstream_529_overloaded_no_reset`) surface accurate audit attribution in the dashboard; `returnRateLimitedResponseOnExhaustion` forwards the original 529 upstream response to the client when the full account pool is exhausted so callers see the real error instead of a generic 503 (PR #236); fixed auto-refresh scheduler probing manually-paused accounts in an endless loop — the eligibility SQL excluded paused accounts only when `auto_pause_on_overage_enabled=0`, ignoring `pause_reason`, while the auto-resume guard in `sendDummyMessage` only un-pauses accounts where `pause_reason IN (NULL,'overage')`; aligned the SQL eligibility query to a positive allowlist (`paused=0 OR (overage_enabled=1 AND pause_reason IN (NULL,'overage'))`) and tightened `isOveragePaused` in `account-selector.ts` to require the same condition, so manual, failure-threshold, and peak_hours pauses are left completely alone; adds a regression test that re-executes the live scheduler SQL against a seeded in-memory SQLite DB covering all seven pause-reason scenarios (PR #237); replaced flat per-account 429 cooldown with adaptive exponential backoff — `consecutive_rate_limits` counter increments atomically in SQL on each 429 and drives `min(upstream_reset, BASE × 2^(n−1))` cooldown (capped at MAX, default 30s base / 5min max), counter resets after 5min of clean operation or on successful token refresh, all four 429 paths (response-processor, model_fallback_429, all_models_exhausted_429, mid-stream SSE sniffer) route through a single `applyRateLimitCooldown` helper, tunables overridable via `CCFLARE_RATE_LIMIT_BACKOFF_BASE_MS` / `CCFLARE_RATE_LIMIT_BACKOFF_MAX_MS` / `CCFLARE_RATE_LIMIT_RESET_STABILITY_MS` (PR #213)
805
- - [@zionts](https://github.com/zionts) — fix false "integrity check failed" banner on large DBs; adaptive incremental vacuum; payload retention default reduction ([#259](https://github.com/tombii/better-ccflare/pull/259)); session-affinity load-balancing strategy for per-client sticky routing with least-used spread ([#285](https://github.com/tombii/better-ccflare/pull/285))
806
- - [@StartupBros](https://github.com/StartupBros) — surface Codex SSE `error`/`response.failed` events to clients instead of fabricating empty success turns ([#274](https://github.com/tombii/better-ccflare/pull/274)); synthesize safe `count_tokens` responses for Codex without upstream calls or OAuth refresh ([#275](https://github.com/tombii/better-ccflare/pull/275)); sanitize Codex tool-call inputs at the Anthropic compatibility boundary ([#277](https://github.com/tombii/better-ccflare/pull/277)); align Codex tool-call semantics so tool loops work correctly with Anthropic-compatible clients ([#278](https://github.com/tombii/better-ccflare/pull/278)); inject a continuation nudge after Codex replays Skill tool results so the model resumes the original request instead of stalling ([#279](https://github.com/tombii/better-ccflare/pull/279)); add native xAI/Grok OAuth provider with token refresh, Grok Build credits polling via gRPC-web, and full dashboard integration ([#281](https://github.com/tombii/better-ccflare/pull/281)); session volume circuit breaker guarding against runaway subagent fan-out — per-session rolling-hour request counting with a warn threshold (default 300/h) and opt-in reject threshold, Anthropic-shaped 429 before account selection burns upstream quota, and Codex Responses session-identity propagation so `/v1/responses` traffic is governed too ([#303](https://github.com/tombii/better-ccflare/pull/303)); opt-in OpenAI `prompt_cache_key` for Codex requests with per-conversation partitioning (session id + instructions + first input item) to keep concurrent subagent conversations from thrashing a single cache key, gated to OpenAI's own hosts so custom/self-hosted OpenAI-compatible endpoints are unaffected ([#306](https://github.com/tombii/better-ccflare/pull/306)); enabled the GPT-5.6 model family (sol/terra/luna) on the Codex provider by advertising codex-cli 0.144.1 in request headers and adding context-window metadata (372k for 5.6, 272k for 5.5, 128k for 5.3-codex-spark) with longest-prefix fallback so dated/suffixed model variants still resolve to their family's window ([#304](https://github.com/tombii/better-ccflare/pull/304)); recover from missing Anthropic `message_stop` frames on native `/v1/messages` streams — after a terminal `message_delta` with a non-null `stop_reason`, a fail-closed SSE observer gives the upstream a 10s grace period before synthesizing the missing terminator, avoiding Claude Code's ~300s stall watchdog as the only recovery path ([#307](https://github.com/tombii/better-ccflare/pull/307)); fix Codex on-demand usage refresh for ChatGPT subscription accounts by omitting `max_output_tokens` on the canonical subscription endpoint (which rejects that field with a 400), while preserving it for custom API-compatible endpoints ([#310](https://github.com/tombii/better-ccflare/pull/310)); track project and agent attribution sources — low-cardinality provenance labels (`header_project`/`path_project`/`heading_project`/`none` and `header_agent`/`prompt_agent`/`none`) showing whether request attribution came from an explicit header, workspace path, prompt heading, or agent detection, with a conservative slug validator that rejects secret/hex/credential/hostname-shaped headings and a source-authority-ranked UPSERT so a lower-confidence re-save can never clobber a higher-confidence attribution ([#313](https://github.com/tombii/better-ccflare/pull/313)); gracefully drain in-flight SSE/proxy responses on shutdown instead of severing them mid-stream — `serverInstance.stop()` refuses new connections while active streams run to completion within a configurable drain budget (`CCFLARE_SHUTDOWN_DRAIN_MS`, default 60s), force-closes whatever remains via tracked stream wrappers (Bun 1.x cannot escalate an earlier graceful `stop()` with `stop(true)`), and awaits stream settlement before usage-collector drain so late finalizers still record usage; deduplicated CLI vs. server signal-handler ownership so the CLI's `process.exit()` no longer races the server's drain ([#314](https://github.com/tombii/better-ccflare/pull/314)); made remote models.dev pricing catalogue fetches single-flight and time-bounded — concurrent cost estimates share one in-flight request, and a stalled catalogue fetch aborts after 10s and falls back to bundled pricing instead of hanging request finalization ([#315](https://github.com/tombii/better-ccflare/pull/315)); hardened UsageCollector's finalization lifecycle — active SSE streams now survive past the old fixed eviction window as long as chunks keep arriving, payload memory is bounded by global active/pending byte budgets with exact-sized typed-array copies, detached finalizers atomically take ownership of request state before their first `await` so capacity eviction or ID reuse can't corrupt a concurrent pricing flow, pricing estimation is deadline-bounded via `CF_PRICING_TIMEOUT_MS`, and `drain()` forces pending pricing futures to zero-cost and loops until finalization work is genuinely quiescent instead of trusting a single `allSettled` snapshot ([#316](https://github.com/tombii/better-ccflare/pull/316)); hardened Codex Anthropic-to-Responses request translation — explicit `tool_choice` mapping (`auto`/`any`/`none`/named tool → Codex equivalents, applied independent of whether `tools` are present) with a `ValidationError` for unknown types or names, `disable_parallel_tool_use` forwarded as `parallel_tool_calls: false`, robust `tool_result` serialization handling non-text/null content and oversized payloads with `is_error` preservation, source-order-preserving message conversion so Codex sees the same block chronology the client sent, and a single-tail Skill continuation nudge that fires exactly once regardless of parallel fan-out ([#317](https://github.com/tombii/better-ccflare/pull/317)); ambient `.d.ts` stubs for the gitignored, build-generated database worker modules so `bun run typecheck` succeeds on a clean checkout before `bun run build` has produced the real files ([#320](https://github.com/tombii/better-ccflare/pull/320)); surface WARN and ERROR log lines on the console in headless `bun start` mode via a module-level `setConsoleLogging` override, fixing a silent-monitoring gap where those lines only reached the log bus/file and never the terminal or journald ([#321](https://github.com/tombii/better-ccflare/pull/321)); extracted the PostgreSQL ADD COLUMN duplicate-column race handling in `runMigrationsPg` into an exported, independently-tested `addColumnTolerant` helper covering the full SQLSTATE `42701` discrimination branch matrix ([#322](https://github.com/tombii/better-ccflare/pull/322)); single-flight recovery probes after a mature rate-limit cooldown expires — once an account has racked up 5+ consecutive 429s, only one process-local request is admitted to probe it within a 2-minute lease window after cooldown expiry, and concurrent requests selecting the same account fall through to the next account instead of stampeding it and re-triggering the same 429 storm, with a bounded self-heal on leaked leases and an eviction cap on the in-memory lease map ([#323](https://github.com/tombii/better-ccflare/pull/323)); fixed Codex response-side protocol fidelity — `response.incomplete` events (and `response.completed` carrying `status: "incomplete"`) now map to the correct Anthropic terminal (`refusal` for `content_filter`, `max_tokens` otherwise) instead of a fabricated `end_turn`/`tool_use`; a broader `CODEX_ERROR_TYPE_BY_CODE` table classifies quota/throttle/policy/subscription error codes to their correct Anthropic error type and HTTP status instead of collapsing them into a generic `api_error`, with a message-regex fallback recognizing context-overflow even without the literal error code; and `normalizeCodexInputUsage` excludes cached tokens from `input_tokens` so cache reads are no longer double-counted against Anthropic's additive usage semantics ([#324](https://github.com/tombii/better-ccflare/pull/324)); `GET /api/sessions/:sessionId/account` for local status-line integrations — a DB-backed lookup of the most recent `requests` row for a client session's `account_used`, reusing `createAccountsListHandler` so the response can never drift from `GET /api/accounts`, with a bounded/sanitized session-id path segment matching the write path's own sanitization and a structurally-exact GET-only auth exemption ([#396](https://github.com/tombii/better-ccflare/pull/396)); broke a `@better-ccflare/types` ↔ `@better-ccflare/core` module-init cycle that crashed on any entry point loading the types barrel first, by exporting `./models`/`./model-mappings` as core subpaths and importing those leaves directly from `types/agent.ts` instead of the core barrel ([#397](https://github.com/tombii/better-ccflare/pull/397)); opt-in xAI cache-native conversation identity — a stable `x-grok-conv-id` header hashed from the client session id, plus sticky per-conversation account affinity so a conversation keeps hitting the same xAI account's cache partition ([#398](https://github.com/tombii/better-ccflare/pull/398))
807
- - [@goldmedal](https://github.com/goldmedal) — fixed a spent `extra_usage` overage pool wrongly benching an otherwise-routable account with a permanent (no-reset) `usage_exhausted` state — split the admission-gating utilization (`getRepresentativeUtilizationForProvider`, excludes `extra_usage`) from a new ranking-only utilization (`getRankingUtilizationForProvider`, includes it) so `SessionStrategy`'s water-filling sort still sees overage headroom while admission no longer does, aligned reset derivation with the gate window, and fixed the accounts-list status label reusing the same signal ([#405](https://github.com/tombii/better-ccflare/pull/405), fixes [#404](https://github.com/tombii/better-ccflare/issues/404))
808
- - [@robsonek](https://github.com/robsonek) — Usage History dashboard tab: per-account, per-window usage utilization time series with server-computed exhaustion prediction, persisted via a callback-injected snapshot hook on the existing `/oauth/usage` poll (no new Anthropic API calls), dual SQLite/PostgreSQL migration, retention pruning, and jitter-tolerant window segmentation ([#294](https://github.com/tombii/better-ccflare/pull/294))
809
- - [@flex-seongmin](https://github.com/flex-seongmin) — Explicit outbound forward-proxy support (`BETTER_CCFLARE_OUTBOUND_PROXY`) for enterprise TLS-intercepting egress gateways — a global Bun fetch wrapper installed at both server and CLI startup so provider requests, OAuth flows, usage polling, and webhooks are all covered without threading a `proxy` option through every call site, with always-exempt loopback destinations (full `127.0.0.0/8` range), caller-supplied `proxy` precedence, strict no-op when unset, and credential-safe logging on both the enable path and invalid-URL rejection path ([#334](https://github.com/tombii/better-ccflare/pull/334))
810
- - [@CooLowbro](https://github.com/CooLowbro) — Opt-in guard (`CCFLARE_DISABLE_COMBO_SESSION_FALLBACK`) preventing exhausted combo-routed requests from silently falling through into normal `SessionStrategy` routing, so isolated combo pools (e.g. Anthropic-only vs. Codex-only) stay isolated instead of spilling over — applied at both the account-selection prefilter and the post-failover fallback point, with rejected requests still recorded through the usage collector as `combo_session_fallback_disabled` so failure metrics remain visible ([#339](https://github.com/tombii/better-ccflare/pull/339)); added the Fable model family to the Codex and Qwen default model maps — `CodexProvider.mapModel` had no `fable` branch so the map entry alone would have sat unreachable, with a regression test pinning the fix ([#394](https://github.com/tombii/better-ccflare/pull/394)); let a combo slot forward the client's own model instead of forcing a rewrite — an empty `model` becomes a pure account filter, gated to providers that accept client-supplied Claude model ids (`PROVIDERS_ACCEPTING_CLIENT_MODEL`) and validated on both the create and update routes to close a create-then-empty bypass, with the per-slot capacity filter falling back to the request's model when the slot is passthrough and the dashboard blocking an invalid empty-model slot for incompatible providers ([#395](https://github.com/tombii/better-ccflare/pull/395)); `GET /api/models?provider=<x>` plus a shared dashboard model picker so the combo slot and account-mapping fields stop being free text — for Codex it lists the models ccflare already knows (`CODEX_KNOWN_MODELS`, derived from the context-window table) alongside the OpenAI section of the cached models.dev catalogue marked `reference`, deduplicated by id with the stronger marking winning, degrading to the builtins with a warning (never failing the request) when the catalogue is unavailable, and keeping the two groups visually distinct since catalogue membership proves a model exists but not that the account's plan may call it ([#399](https://github.com/tombii/better-ccflare/pull/399)); replaced the compiled Codex default-model map with defaults derived from each account's own live model listing (`chatgpt.com/backend-api/codex/models`, the same endpoint the Codex CLI reads) — resolution order becomes operator override → this account's listing → another account's listing (labelled `shared`, since two of three real accounts answer 401 on their own listing while serving traffic fine) → the provider's built-in map, with tiering following the listing's own `priority` ordering instead of a hand-maintained table, `visibility: hide` entries (routing aliases, the auto-review model) filtered out, and an editable override surface (Settings → Advanced) kept reachable even before the first successful discovery ([#403](https://github.com/tombii/better-ccflare/pull/403))
811
- - [@vansh2408](https://github.com/vansh2408) — Fixed Codex-routed requests failing with `400 Invalid JSON schema: regex lookaround is not supported` whenever an MCP tool's input schema used a regex `pattern` with lookaround (e.g. an email-validation tool) — extended the existing OpenAI schema sanitizer (renamed `removeUriFormat` → `sanitizeSchemaForOpenAI`) with a lookaround-detecting rule that safely drops the unsupported `pattern` while preserving the field's `type: "string"`, and applied it to the Codex provider's tool conversion, which also brought the pre-existing `$schema`/`format: "uri"` scrubbing to that path for the first time ([#359](https://github.com/tombii/better-ccflare/pull/359))
794
+ **Special thanks to our contributors** (see [full details](./docs/acknowledgements.md)):
795
+
796
+ - [@bitcoin4cashqc](https://github.com/bitcoin4cashqc)
797
+ - [@anonym-uz](https://github.com/anonym-uz)
798
+ - [@makhweeb](https://github.com/makhweeb)
799
+ - [@jw409](https://github.com/jw409) — [#106](https://github.com/tombii/better-ccflare/pull/106)
800
+ - [@materemias](https://github.com/materemias) — [#49](https://github.com/tombii/better-ccflare/pull/49), [#54](https://github.com/tombii/better-ccflare/pull/54), [#186](https://github.com/tombii/better-ccflare/pull/186)
801
+ - [@tqtensor](https://github.com/tqtensor) — [#67](https://github.com/tombii/better-ccflare/pull/67)
802
+ - [@lunetics](https://github.com/lunetics) — [#68](https://github.com/tombii/better-ccflare/pull/68), [#70](https://github.com/tombii/better-ccflare/pull/70), [#71](https://github.com/tombii/better-ccflare/pull/71), [#296](https://github.com/tombii/better-ccflare/pull/296), [#299](https://github.com/tombii/better-ccflare/pull/299), [#300](https://github.com/tombii/better-ccflare/pull/300), [#311](https://github.com/tombii/better-ccflare/pull/311), [#325](https://github.com/tombii/better-ccflare/pull/325), [#329](https://github.com/tombii/better-ccflare/pull/329), [#330](https://github.com/tombii/better-ccflare/pull/330), [#331](https://github.com/tombii/better-ccflare/pull/331), [#332](https://github.com/tombii/better-ccflare/pull/332), [#333](https://github.com/tombii/better-ccflare/pull/333), [#336](https://github.com/tombii/better-ccflare/pull/336), [#342](https://github.com/tombii/better-ccflare/pull/342), [#353](https://github.com/tombii/better-ccflare/pull/353), [#354](https://github.com/tombii/better-ccflare/pull/354), [#355](https://github.com/tombii/better-ccflare/pull/355), [#358](https://github.com/tombii/better-ccflare/pull/358), [#360](https://github.com/tombii/better-ccflare/pull/360), [#362](https://github.com/tombii/better-ccflare/pull/362), [#363](https://github.com/tombii/better-ccflare/pull/363), [#364](https://github.com/tombii/better-ccflare/pull/364), [#381](https://github.com/tombii/better-ccflare/pull/381)
803
+ - [@troykelly](https://github.com/troykelly) — [#81](https://github.com/tombii/better-ccflare/pull/81), [#88](https://github.com/tombii/better-ccflare/pull/88)
804
+ - [@cowwoc](https://github.com/cowwoc) — [#149](https://github.com/tombii/better-ccflare/pull/149), [#150](https://github.com/tombii/better-ccflare/pull/150), [#151](https://github.com/tombii/better-ccflare/pull/151), [#152](https://github.com/tombii/better-ccflare/pull/152), [#155](https://github.com/tombii/better-ccflare/pull/155), [#156](https://github.com/tombii/better-ccflare/pull/156), [#159](https://github.com/tombii/better-ccflare/pull/159), [#161](https://github.com/tombii/better-ccflare/pull/161), [#162](https://github.com/tombii/better-ccflare/pull/162), [#163](https://github.com/tombii/better-ccflare/pull/163), [#164](https://github.com/tombii/better-ccflare/pull/164), [#165](https://github.com/tombii/better-ccflare/pull/165), [#167](https://github.com/tombii/better-ccflare/pull/167), [#172](https://github.com/tombii/better-ccflare/pull/172), [#188](https://github.com/tombii/better-ccflare/pull/188), [#203](https://github.com/tombii/better-ccflare/pull/203)
805
+ - [@wonkooklee](https://github.com/wonkooklee) — [#243](https://github.com/tombii/better-ccflare/pull/243)
806
+ - [@Cotch22](https://github.com/Cotch22) — [#246](https://github.com/tombii/better-ccflare/pull/246)
807
+ - [@zenprocess](https://github.com/zenprocess) — [#107](https://github.com/tombii/better-ccflare/pull/107), [#193](https://github.com/tombii/better-ccflare/pull/193), [#196](https://github.com/tombii/better-ccflare/pull/196), [#260](https://github.com/tombii/better-ccflare/pull/260), [#273](https://github.com/tombii/better-ccflare/pull/273), [#343](https://github.com/tombii/better-ccflare/pull/343), [#344](https://github.com/tombii/better-ccflare/pull/344), [#345](https://github.com/tombii/better-ccflare/pull/345), [#346](https://github.com/tombii/better-ccflare/pull/346), [#349](https://github.com/tombii/better-ccflare/pull/349), [#350](https://github.com/tombii/better-ccflare/pull/350), [#352](https://github.com/tombii/better-ccflare/pull/352), [#353](https://github.com/tombii/better-ccflare/pull/353), [#361](https://github.com/tombii/better-ccflare/pull/361), [#365](https://github.com/tombii/better-ccflare/pull/365), [#367](https://github.com/tombii/better-ccflare/pull/367), [#369](https://github.com/tombii/better-ccflare/pull/369), [#370](https://github.com/tombii/better-ccflare/pull/370), [#371](https://github.com/tombii/better-ccflare/pull/371), [#372](https://github.com/tombii/better-ccflare/pull/372), [#376](https://github.com/tombii/better-ccflare/pull/376), [#377](https://github.com/tombii/better-ccflare/pull/377), [#380](https://github.com/tombii/better-ccflare/pull/380), [#383](https://github.com/tombii/better-ccflare/pull/383), [#385](https://github.com/tombii/better-ccflare/pull/385), [#386](https://github.com/tombii/better-ccflare/pull/386), [#387](https://github.com/tombii/better-ccflare/pull/387), [#388](https://github.com/tombii/better-ccflare/pull/388)
808
+ - [@robsonek](https://github.com/robsonek) — [#294](https://github.com/tombii/better-ccflare/pull/294), [#375](https://github.com/tombii/better-ccflare/pull/375), [#407](https://github.com/tombii/better-ccflare/pull/407)
809
+ - [@issmirnov](https://github.com/issmirnov) — [#252](https://github.com/tombii/better-ccflare/pull/252), [#280](https://github.com/tombii/better-ccflare/pull/280)
810
+ - [@CorentinLumineau](https://github.com/CorentinLumineau) — [#197](https://github.com/tombii/better-ccflare/pull/197)
811
+ - [@d4rken](https://github.com/d4rken) — [#204](https://github.com/tombii/better-ccflare/pull/204), [#205](https://github.com/tombii/better-ccflare/pull/205), [#206](https://github.com/tombii/better-ccflare/pull/206), [#207](https://github.com/tombii/better-ccflare/pull/207), [#208](https://github.com/tombii/better-ccflare/pull/208), [#209](https://github.com/tombii/better-ccflare/pull/209), [#210](https://github.com/tombii/better-ccflare/pull/210), [#212](https://github.com/tombii/better-ccflare/pull/212), [#213](https://github.com/tombii/better-ccflare/pull/213), [#214](https://github.com/tombii/better-ccflare/pull/214), [#215](https://github.com/tombii/better-ccflare/pull/215), [#218](https://github.com/tombii/better-ccflare/pull/218), [#219](https://github.com/tombii/better-ccflare/pull/219), [#220](https://github.com/tombii/better-ccflare/pull/220), [#221](https://github.com/tombii/better-ccflare/pull/221), [#222](https://github.com/tombii/better-ccflare/pull/222), [#223](https://github.com/tombii/better-ccflare/pull/223), [#224](https://github.com/tombii/better-ccflare/pull/224), [#225](https://github.com/tombii/better-ccflare/pull/225), [#226](https://github.com/tombii/better-ccflare/pull/226), [#227](https://github.com/tombii/better-ccflare/pull/227), [#228](https://github.com/tombii/better-ccflare/pull/228), [#229](https://github.com/tombii/better-ccflare/pull/229), [#230](https://github.com/tombii/better-ccflare/pull/230), [#231](https://github.com/tombii/better-ccflare/pull/231), [#234](https://github.com/tombii/better-ccflare/pull/234), [#235](https://github.com/tombii/better-ccflare/pull/235), [#236](https://github.com/tombii/better-ccflare/pull/236), [#237](https://github.com/tombii/better-ccflare/pull/237)
812
+ - [@zionts](https://github.com/zionts) — [#259](https://github.com/tombii/better-ccflare/pull/259), [#285](https://github.com/tombii/better-ccflare/pull/285)
813
+ - [@StartupBros](https://github.com/StartupBros) — [#274](https://github.com/tombii/better-ccflare/pull/274), [#275](https://github.com/tombii/better-ccflare/pull/275), [#277](https://github.com/tombii/better-ccflare/pull/277), [#278](https://github.com/tombii/better-ccflare/pull/278), [#279](https://github.com/tombii/better-ccflare/pull/279), [#281](https://github.com/tombii/better-ccflare/pull/281), [#303](https://github.com/tombii/better-ccflare/pull/303), [#304](https://github.com/tombii/better-ccflare/pull/304), [#306](https://github.com/tombii/better-ccflare/pull/306), [#307](https://github.com/tombii/better-ccflare/pull/307), [#310](https://github.com/tombii/better-ccflare/pull/310), [#313](https://github.com/tombii/better-ccflare/pull/313), [#314](https://github.com/tombii/better-ccflare/pull/314), [#315](https://github.com/tombii/better-ccflare/pull/315), [#316](https://github.com/tombii/better-ccflare/pull/316), [#317](https://github.com/tombii/better-ccflare/pull/317), [#320](https://github.com/tombii/better-ccflare/pull/320), [#321](https://github.com/tombii/better-ccflare/pull/321), [#322](https://github.com/tombii/better-ccflare/pull/322), [#323](https://github.com/tombii/better-ccflare/pull/323), [#324](https://github.com/tombii/better-ccflare/pull/324), [#396](https://github.com/tombii/better-ccflare/pull/396), [#397](https://github.com/tombii/better-ccflare/pull/397), [#398](https://github.com/tombii/better-ccflare/pull/398), [#408](https://github.com/tombii/better-ccflare/pull/408), [#409](https://github.com/tombii/better-ccflare/pull/409)
814
+ - [@goldmedal](https://github.com/goldmedal) — [#405](https://github.com/tombii/better-ccflare/pull/405), fixes [#404](https://github.com/tombii/better-ccflare/issues/404)
815
+ - [@flex-seongmin](https://github.com/flex-seongmin) — [#334](https://github.com/tombii/better-ccflare/pull/334)
816
+ - [@CooLowbro](https://github.com/CooLowbro) — [#339](https://github.com/tombii/better-ccflare/pull/339), [#394](https://github.com/tombii/better-ccflare/pull/394), [#395](https://github.com/tombii/better-ccflare/pull/395), [#399](https://github.com/tombii/better-ccflare/pull/399), [#403](https://github.com/tombii/better-ccflare/pull/403)
817
+ - [@vansh2408](https://github.com/vansh2408) — [#359](https://github.com/tombii/better-ccflare/pull/359)
812
818
 
813
819
  ## Contributing
814
820
 
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-ccflare",
3
- "version": "3.5.53",
3
+ "version": "3.5.55",
4
4
  "description": "Load balancer proxy for Claude API with intelligent distribution across multiple OAuth accounts to avoid rate limiting",
5
5
  "license": "MIT",
6
6
  "repository": {