Skip to content

feat(opencode): add killswitch indicators to TUI sidebar#39

Open
iceteaSA wants to merge 14 commits into
cortexkit:mainfrom
iceteaSA:feat/killswitch-sidebar
Open

feat(opencode): add killswitch indicators to TUI sidebar#39
iceteaSA wants to merge 14 commits into
cortexkit:mainfrom
iceteaSA:feat/killswitch-sidebar

Conversation

@iceteaSA
Copy link
Copy Markdown
Contributor

@iceteaSA iceteaSA commented May 21, 2026

Dependency: Requires #35 (killswitch) to be merged first. Rebased onto upstream/main (v1.5.0) now that #37 (TUI sidebar) is merged.

Adds killswitch awareness to the TUI sidebar widget (the integration of #35 + the merged sidebar):

  • blocked (red) state for killswitch-killed main and fallback accounts.
  • Quota-backoff and refresh-backoff state display.
  • A Killswitch row in the Health section and inclusion in the LIMITED badge.
  • writeSidebarState() computes killswitch policy for every account using fresh, token-aware QuotaManager reads (never the stale request-start snapshot).

Files:

  • packages/opencode/src/sidebar-state.ts — adds killed + backoff fields
  • packages/opencode/src/tui.tsx — blocked indicator + kill status section
  • packages/opencode/src/index.ts — killswitch-aware sidebar state

Summary by cubic

Adds a killswitch that hard‑blocks accounts below remaining‑quota thresholds and wires it into routing and the TUI sidebar; /claude-killswitch manages thresholds. Killed accounts never route, and when none are routable we return a synthetic 429 with retry-after.

  • New Features

    • Killswitch: eager quota refresh, single getRoutableFallbackAccounts filter across all routes, and a hard 429 with retry-after from the earliest reset; non‑replayable bodies never fall through to killed accounts.
    • Sidebar: collapsible header with version, compact active‑account row (5h usage and fast mode), red blocked indicator (⊘), and a Health row listing killswitch‑blocked accounts; killed flags for main/fallbacks, resolveActiveAccount(), and background fallback storage changes re‑render via onFallbackStorageChanged.
    • Commands: /claude-killswitch for status/on/off/set; changes are persisted via @cortexkit/anthropic-auth-core.
  • Bug Fixes

    • Keep the active fallback selected after async main quota refresh; keep the sidebar visible when data clears while collapsed; suppress a11y lint on the interactive header box.
    • Token‑aware reads for fail‑closed and killswitch checks prevent stale quota from previous accounts from affecting decisions; restore process‑scoped request counter so every‑N quota refreshes trigger correctly.

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

Review in cubic

Greptile Summary

This PR wires the killswitch feature (#35) into the TUI sidebar and the main fetch handler. writeSidebarState now computes a killed flag for every account via token-aware QuotaManager reads, and a new refreshSidebarQuota helper preserves the last routing decision across async quota refreshes. The fetch handler gains an eager quota-refresh step before the killswitch evaluation, a single getRoutableFallbackAccounts function used across all routing paths, and a hard 429 with retry-after when no routable account survives.

  • Sidebar changes (sidebar-state.ts, tui.tsx): adds a killed boolean to main and fallback state, a collapsible header with a collapse/expand toggle, a red indicator in the collapsed row, and a Killswitch row in the Health section of the expanded view.
  • Fetch-handler changes (index.ts): replaces raw getUsableFallbackAccounts calls with getRoutableFallbackAccounts (killswitch-aware) on all routing paths; non-replayable bodies with a killed main account correctly 429 immediately.
  • New /claude-killswitch command (killswitch.ts): status/on/off/set sub-commands with per-account threshold overrides, persisted via setKillswitchPersistent.

Confidence Score: 5/5

Safe to merge; the killswitch routing logic is well-tested and the previously flagged issues are addressed.

The core routing changes are correct and covered by targeted integration tests. The only findings are in the /claude-killswitch command text output layer: account IDs not escaped in markdown tables, threshold values above 100 accepted without error, and set sub-command silently enabling the killswitch.

packages/core/src/killswitch.ts — command input parsing and status table rendering

Important Files Changed

Filename Overview
packages/core/src/killswitch.ts New file: killswitch command parsing, status table builder, and executeKillswitchCommand. Missing threshold range validation (0-100) and account-ID sanitization for the markdown status table.
packages/core/src/accounts.ts Adds KillswitchConfig type, killswitchPassesPolicy, killswitchRetryAfterSeconds, and setKillswitchPersistent. Logic is sound; the sawUnknownWindow + failClosedOnUnknownQuota interplay is handled correctly.
packages/opencode/src/index.ts Killswitch gate integrated into fetch handler: eager quota refresh, getRoutableFallbackAccounts replaces raw getUsableFallbackAccounts, hard 429 when no survivors. Previously flagged issues (stale mainQuota on first request, allFallbacksKilled vs survivingFallbacks mismatch, non-replayable bypass) are addressed.
packages/opencode/src/sidebar-state.ts Adds killed flag to SidebarAccountState and SidebarState.main; adds resolveActiveAccount helper for collapsed view. Straightforward and well-tested.
packages/opencode/src/tui.tsx Collapsible header, red ⊘ blocked indicator in collapsed row, and Killswitch Health row in expanded view. The Killswitch StatRow is a sibling of (not inside) the degraded Show block, but since killedNames().length > 0 always implies degraded(), the Health header always precedes the row correctly.
packages/opencode/src/tests/index.test.ts Adds integration tests for sidebar clobber regression, background fallback refresh, and killswitch fetch gate. Good coverage of the new paths.
packages/opencode/src/tests/killswitch.test.ts New test file covering parseKillswitchCommandAction, killswitchPassesPolicy, killswitchRetryAfterSeconds, executeKillswitchCommand, and setKillswitchPersistent. Comprehensive edge-case coverage.
packages/opencode/src/tests/sidebar-state.test.ts New tests for resolveActiveAccount covering main, fallback, disabled fallback, and killed-account cases. Clean.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B{failClosedOnUnknownQuota + no quota + backed off?}
    B -- Yes --> C[Return 429 Quota API unavailable]
    B -- No --> D{killswitch enabled?}
    D -- No --> K[Send to main]
    D -- Yes --> E[Eager quota refresh if needsRefresh]
    E --> F[Re-read mainQuota from QuotaManager]
    F --> G{killswitchPassesPolicy main quota?}
    G -- Passes --> K
    G -- Fails / killed --> H{isReplayableRequest?}
    H -- No --> I[survivingFallbacks = empty]
    H -- Yes --> J[getRoutableFallbackAccounts usable + killswitch-passing]
    I --> L{survivingFallbacks length > 0?}
    J --> L
    L -- Yes --> M[tryUsableFallbackAccounts]
    M --> N{fallbackResponse?}
    N -- Yes --> O[Return fallback response]
    N -- No --> P[killswitchRetryAfterSeconds]
    L -- No --> P
    P --> Q[Return 429 hard-block]
    K --> R[sendWithAccessToken main]
Loading

Reviews (10): Last reviewed commit: "Merge branch 'fix/sidebar-fallback-quota..." | Re-trigger Greptile

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 16 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/core/src/accounts.ts Outdated
Comment thread packages/opencode/src/tui.tsx Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 2 times, most recently from 84f44e5 to 583586a Compare May 21, 2026 20:20
@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 21, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 4 times, most recently from 09b34c3 to 8339edc Compare May 22, 2026 17:08
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 10 times, most recently from e86d9d3 to a8d6404 Compare June 3, 2026 18:24
Comment thread packages/opencode/src/index.ts Outdated
Comment thread packages/opencode/src/index.ts Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch from a8d6404 to ecffae9 Compare June 3, 2026 18:58
Comment thread packages/opencode/src/index.ts Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 4 times, most recently from 4988ae1 to 78b02b4 Compare June 3, 2026 19:53
Comment thread packages/opencode/src/index.ts Outdated
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch 3 times, most recently from 5b995dd to eaf91c9 Compare June 3, 2026 20:54
…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.
Layer killswitch awareness onto the restyled sidebar (killed state in
SidebarState + writeSidebarState via killswitchPassesPolicy, blocked status
word, Killswitch health row, degraded/LIMITED inclusion).

Also restores the process-scoped 'let sessionRequestCount' (a prior cascade
had flipped it to const, which left the active-route fallback every-N refresh
reading a never-incremented counter).
@iceteaSA iceteaSA force-pushed the feat/killswitch-sidebar branch from eaf91c9 to 3147ad3 Compare June 3, 2026 21:03
@iceteaSA
Copy link
Copy Markdown
Contributor Author

iceteaSA commented Jun 4, 2026

Updated: this branch now carries the sidebar quota fix (#57) and the collapsible sidebar (#58), and integrates the collapsed view with the killswitch indicators — the collapsed active-account row shows a red ⊘ (instead of the usage dot) when that account is killswitch-blocked; resolveActiveAccount exposes killed. #57 and #58's commits appear here until they merge. Merge order: #57#58 → this PR.

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