Skip to content

feat(opencode): killswitch — block requests when quota drops below threshold#35

Open
iceteaSA wants to merge 1 commit into
cortexkit:mainfrom
iceteaSA:feat/killswitch
Open

feat(opencode): killswitch — block requests when quota drops below threshold#35
iceteaSA wants to merge 1 commit into
cortexkit:mainfrom
iceteaSA:feat/killswitch

Conversation

@iceteaSA
Copy link
Copy Markdown
Contributor

@iceteaSA iceteaSA commented May 21, 2026

Dependency: Requires #34 (QuotaManager) to be merged first. Branch is based on feat/quota-manager.

Per-account request blocking when remaining quota drops below configurable thresholds. Returns synthetic 429 when all accounts (main + fallbacks) are below their thresholds.

Features:

  • Per-account threshold overrides (5h and 7d windows)
  • Eager quota refresh on first request for killswitch evaluation
  • Skip-main routing when main is killed (try surviving fallbacks)
  • Filter killed accounts from reactive fallback path
  • Retry-After header with earliest quota reset time + 60s buffer
  • /claude-killswitch slash command for runtime management (on/off/set thresholds)
  • Persisted to sidecar config

Files:

  • packages/core/src/killswitch.ts — new (164 lines)
  • packages/core/src/accounts.ts — killswitch types and policy functions
  • packages/opencode/src/index.ts — killswitch check in fetch handler, command registration
  • packages/opencode/src/tests/killswitch.test.ts — new (501 lines)
  • README.md + packages/opencode/README.md — killswitch documentation

Summary by cubic

Adds a per‑account killswitch and a unified QuotaManager in @cortexkit/anthropic-auth-core so OpenCode blocks low‑quota requests, shares a single quota cache, and returns clear 429s with Retry‑After when all accounts are exhausted or the quota API is backed off.

  • New Features

    • Killswitch: per‑account thresholds (5h/7d), skip main when killed, filter killed fallbacks, eager quota refresh, synthetic 429 with Retry‑After (earliest reset + 60s) when all killed, /claude-killswitch to view/toggle/set with persisted config; enable via killswitch in the sidecar; optional quota.refreshEveryNRequests.
    • QuotaManager: one path for main + fallbacks with shared cache, inflight dedup, short serial gate, exponential backoff; treats 401 as auth (no backoff); /claude-quota forces real refresh and persists; seeds from storage; every‑N and background refresh; skips request‑time refresh when backed off.
  • Bug Fixes

    • Token‑aware main quota reads (getMain(auth.access)) and token‑bound fallback cache entries prevent cross‑account reuse.
    • Scoped quota‑API backoff per route (main vs each fallback); 401 no longer arms backoff.
    • When failClosedOnUnknownQuota is set and quota API is backed off with no cached quota, return 429 with a retry-after from the backoff; count all replayable requests for the every‑N cadence and remove a shadowed counter.

Written for commit 0054553. Summary will update on new commits.

Review in cubic

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 10 files

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Fix all with cubic | Re-trigger cubic

Comment thread packages/opencode/src/index.ts
Comment thread packages/core/src/killswitch.ts
@iceteaSA iceteaSA force-pushed the feat/killswitch branch 4 times, most recently from d89dd9d to f667649 Compare May 22, 2026 17:08
@iceteaSA iceteaSA force-pushed the feat/killswitch branch 6 times, most recently from b2399cf to 4fdf006 Compare June 1, 2026 13:01
…reshold

Self-review fixes folded in:
- Token-aware fail-closed read: const mainQuota = quotaManager.getMain(auth.access)
  so a previous main account's cached quota can't satisfy the fail-closed check
  or feed the killswitch eval after a main-account switch.
- Removed a stray inner 'let sessionRequestCount = 0' + unconditional increment
  that shadowed the process-scoped counter, which had left the active-route
  fallback every-N refresh reading a never-incremented counter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant