Fixed
-
๐ก๏ธ #146 โ OpenRouter / rate-limited providers no longer burned the user's daily quota. Health-probe pings are now throttled at two orthogonal levels so a single overloaded model can no longer wipe out an OpenRouter (or similar) daily request limit. Issue submitted by
@ia-S-on.- Per-provider quota circuit-breaker โ New module
src/core/provider-cooldown.jsexposes an in-memory pause map. When a probe returns HTTP429, FCM parses theRetry-Afterheader (and falls back to the OpenRouter-style body"Rate limit exceeded, please try again N seconds later."), then pauses every model of that provider until the window expires. Probe pressure drops from ~30 req/min/provider to ~0 req/min/provider for the entire duration. Affected code:src/core/ping.js(trigger onresp.status === 429),src/tui/app.js(skip paused providers inrunPingCycle). - Per-model broken cooldown with exponential backoff โ
src/core/probe-cache.jsnow cachesbrokenentries too.isCacheFresh()andgetModelsDueForProbe()honour a newbrokenCooldownMs(failureCount)ladder (30 s โ 1 m โ 2 m โ 5 m, then plateau at 5 min).recordProbeResults()tracksconsecutiveFailures(increments onbroken, resets to0onok). This caps worst-case probe pressure on a permanently-broken model at ~0.2 req/min instead of the previous ~30 req/min. - Live footer chip โ When one or more providers are paused, the TUI footer now shows e.g.
โธ openrouter 14h ยท โธ ovhcloud 34s(warm amber background, live countdown that auto-rolls to s/m/h/d). New state fieldstate.pausedProviders(defaults to{}) + render-table chip fed bypausedProvidersoption. Behaviour verified on first run โ OVHcloud was correctly paused for 34 s on a real 429 during smoke testing.
- Per-provider quota circuit-breaker โ New module
Added
- ๐ README health-check warning โ New section
โ ๏ธ Health checks consume provider quotabetween the providers table and the Tier scale. Explains the 1k/day OpenRouter cap, links the v0.5.65 fix points, and offers the immediate workaround (remove the provider's key from Settings โ anonymous probes still work for liveness checks). - ๐งช 18 new unit tests in
test/test.jscovering both layers (Issue #146: provider quota circuit-breaker+Issue #146: per-model broken-cooldown backoff). Three pre-existing tests intest/probe-cache.test.jsupdated to reflect the newbroken-is-fresh-during-cooldown contract.
Changed
- ๐
pnpm testsuite: 797 / 797 passing (was 779 / 779 in 0.5.64; net +18 new tests + 3 updated). - ๐
node --checkclean on every touched file; TUI smoke-tested in tmux (no runtime regression). - ๐ Footer's existing probe-cache chip is unchanged in behaviour but gains the sibling paused-providers chip immediately to its right.
Migration / workaround
If you were already hitting the 429 from issue #146 before upgrading, simply upgrading to v0.5.65 fixes the runaway probe loop โ no manual step required. The provider will be auto-paused on its next probe.
If you want zero authenticated probes for a particular provider (e.g. you only use OpenRouter for ad-hoc calls and want FCM to leave the key alone), leave the key empty in Settings โ anonymous probes still tell us whether each model is up and will not burn your personal daily quota.
Files
- New:
src/core/provider-cooldown.js(โ220 lines). - Modified:
src/core/ping.js(import + 429 trigger),src/core/probe-cache.js(constants,brokenCooldownMs,isCacheFresh,getModelsDueForProbe,recordProbeResults),src/tui/app.js(import +runPingCycleskip + paused snapshot),src/tui/tui-state.js(pausedProviders: {}on state),src/tui/render-table.js(new chip + thread option through),README.md(warning section),test/test.js(18 new tests),test/probe-cache.test.js(3 updated),package.json(0.5.64โ0.5.65), this changelog.
Breaking changes
None. All changes are additive and behaviour-preserving for users who were not already rate-limited.