* fix(rate-limits): read Kimi usage credentials from the configured WSL runtime
Kimi's usage fetch always read the Windows host's ~/.kimi-code, so a Kimi CLI running inside WSL rotated only the WSL-side token and the status bar was stuck on 'Run Kimi to refresh'.
Resolve the Kimi home from the local-account runtime target (mirroring Codex's getDefaultWslDistro()/getWslHome() UNC pattern), pinned to host off Windows, and keep KIMI_CODE_HOME host-only.
Fixes#12370
Co-authored-by: Orca <help@stably.ai>
* fix(rate-limits): bound and offload the Kimi credentials read for WSL homes
Adopted from @cengiz-io's #12372: read credentials through
createAuthFilesystemOperation (async, per-path dedup, AbortSignal bound) so a
stopped distro degrades to an error instead of parking Electron main on a UNC
read. ENOTDIR joins ENOENT as "not signed in" to keep existsSync parity, and the
WSL runtime target is now probed with the async wsl.exe helpers.
Co-authored-by: Orca <help@stably.ai>
* test(rate-limits): build Kimi credential-path expectations with path.join
The host-home assertions hardcoded POSIX separators, so they only passed on
a POSIX runner — on a Windows dev machine `join` emits backslashes and all
four cases failed (three assertion mismatches plus a WSL-suite fixture whose
map key never matched the path the fetcher read).
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
* fix(codex): make quota probes credential-safe
Codex OAuth uses rotating refresh tokens, and Orca's quota probes spawned
real codex app-server processes inside live credential homes, hard-killed
them at a 10s deadline (cold starts run 10-25s), re-probed every inactive
account on each switch, and deselected accounts on torn auth.json reads.
- arm the RPC read deadline only after initialize responds (30s/40s boot
budget), and terminate probes via stdin EOF + SIGTERM with a bounded
drain before any hard kill; resolve only once the child exits
- serialize Orca-spawned codex processes per credential home (probe vs
probe, probe vs commit-message/PR-fields/branch-name/model-discovery)
- keep the inactive-probe debounce across account switches and stagger
inactive probes; the active account still refreshes immediately
- grade credential reads (present/missing/unreadable/no-credential) and
require absence to outlive a grace window before deselecting
* fix(codex): close remaining credential races
* fix(codex): keep failed probes under home lock
* fix(codex): observe probe pipe failures
* fix(codex): await Windows generation tree kills
* fix(codex): preserve incomplete shared credentials
* chore(dead-code): drop 2k lines of unreachable exports and orphan modules
Ran knip across every build entry (main, preload, renderer, popout, web,
cli, relay, workers, forked sidecars, config scripts) and removed what no
entry graph can reach.
- 11 orphan modules nothing imported, plus one test that only covered them
- 159 unused exports/types, with their now-dead helpers, imports and tests
Each candidate was verified against dynamic references before deletion.
42 knip hits were false positives and are kept: shared modules consumed by
the mobile/ workspace, the src/shared/plugins/** public API, vendored
shadcn primitives, and relay wire-protocol constants held for compatibility.
Adds knip.json + `pnpm audit:dead-code` so this stays measurable.
Verified: pnpm typecheck, pnpm lint, and 2081 tests across the 73 affected
test files all pass.
* chore(dead-code): move knip config under config/
Root-level additions are blocked by the root directory guard.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* chore: condense code comments
* chore: shorten more code comments
* clarify PTY agent session descendant cleanup behavior
Refine the comment on ptyAgentSessionIds to more accurately describe
when agent sessions sweep their descendant process trees and note the
exception on immediate Windows shutdown.
* fix(codex): cache weekly-only accounts when switching Codex accounts
refreshForCodexAccountChange snapshotted the outgoing account only when
this.state.codex.session was populated. Weekly-only plans report no session
window, so their snapshot was dropped and the account switcher's inline bars
rendered empty for exactly those accounts.
Accept a populated weekly window as well. #10136 made this reachable: before
duration-based classification, a weekly-only quota landed in the session slot,
so the gate happened to pass.
Claude is intentionally untouched; it has no weekly-only plan shape.
* test(rate-limits): pin that a windowless outgoing Codex account is not cached
The widened weekly-only gate had no test for its lower bound: replacing it with a bare truthy check on state.codex passed all 70 tests, which would cache an empty fetching placeholder and render a blank inline bar row in the switcher.
* fix(rate-limits): keep Codex PTY reset text for weekly-only plans
The PTY /status fallback parses '5h limit' and 'Weekly limit' lines by
label, but the extracted reset text was only ever attached to the
session window. Codex plans without a 5h session bucket (e.g. current
Pro) produce a weekly-only parse, so the reset time the CLI printed was
silently dropped. Fall back to the weekly window when no session window
exists.
* review: parse Codex PTY reset text per window into resetsAt
* review: make Codex PTY status fallback work on codex >=0.145
* review: harden PTY status parse against model-scoped rows and styled output
* fix(rate-limits): strip private PTY control sequences
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
The managed token refresh is deferred while a live claude session owns
the credentials, but nothing reacted to the last live PTY exiting: the
deferred result is a terminal-classified error whose retry lane backs
off to the 15-minute cadence and is window-focus-gated, so the usage
panel stayed on "Waiting for Claude session" long after the blocker was
gone.
Notify on the live-PTY 1->0 transition (covering both markClaudePtyExited
and confirmSeededClaudeLivePtys releasing the last dead seeded id) and
force a claude-only refetch iff the current state is deferred.
Closes#9324
* fix(rate-limits): unstick Claude "Limited" usage and feed live usage from session statuslines
The OAuth usage endpoint's 429 Retry-After (~50 min) was ignored, so the
30s-15min automated retry lanes kept landing inside the throttle window and
the status bar stayed on a bare "Limited" indefinitely while Claude itself
worked fine.
- Respect Retry-After on 429: carry it through usageMetadata.retryAtMs and
gate automated refetches (activation lane, poll cycles) until it expires;
user-directed refreshes still bypass.
- Keep the last-known usage snapshot visible through rate-limited windows
(24h) instead of dropping it after the generic 30-minute stale threshold.
- Add a managed Claude statusLine command that forwards each session's
rate_limits (Claude Code >=2.1.80) to a new /statusline/claude loopback
route, feeding live usage windows with zero usage-endpoint calls; OAuth
polling pauses while the live feed is fresh. User-owned statusLine
settings are never overwritten.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(rate-limits): keep last-known window when a statusline post carries only one
Statusline payloads may report five_hour and seven_day independently; a
partial post must not wipe the other bar to null. Also document the
seconds-vs-ms epoch heuristic.
Addresses CodeRabbit review on #9617.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(rate-limits): unstick Claude usage with live statusline feed
The OAuth polling endpoint is rate-limited; Claude's status often shows
"Limited" until the next poll cycle, even when quota remains. Live posts
from the statusline command update usage within 100ms, eliminating false
"Limited" displays during active sessions.
Manages install lifecycle via marker to respect user deletions. Handles
Windows payload buffering and guards before curl spawn. Protects against
live-post/OAuth-fetch races and cross-attribution during account switches.
Gracefully tolerates schema drift in statusline parsing.
* test(rate-limits): assert stale outgoing post doesn't affect incoming
Capture usedPercent before ingesting and assert it remains unchanged,
rather than checking for a specific value. This is more precise and less
brittle when testing session switch isolation.
---------
Co-authored-by: Dzmitry Bachko <dbachko@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* fix(accounts): follow the runtime for WSL provider account detection (#9537)
On Windows + WSL, provider-account detection (usage recognition and the
status-bar account switcher) was pinned to the Windows host even when the
project runs in WSL, so WSL accounts were never recognized and the WSL
switcher group never appeared.
Root cause: `localAccountRuntime` hard-defaulted to 'host', which
short-circuited `getInitialClaude/CodexRateLimitTarget` before the existing
"follow the global Windows runtime default" branch could run. That branch was
therefore dead for every real user.
Fix: add an 'auto' value for `localAccountRuntime`, make it the default, and
migrate the untouched legacy 'host' default to 'auto' once (guarded by
`localAccountRuntimeDefaultedToAutoForAllUsers`; explicit 'wsl' is preserved).
'auto' resolves via a shared `resolveLocalAccountRuntimeTarget` helper: on a
windows-host default it stays host (no behavior change); on a WSL default it
follows WSL, so WSL accounts are recognized and the WSL group appears.
Wired the shared resolver into the managed-account default target, the
status-bar WSL-group gate, and the Accounts settings location toggle.
Note: detection follows the global Windows runtime default, not the live
active project's runtime (the fetch target is a single global value); the
latter is a larger follow-up.
* fix(accounts): align auto runtime consumers
* fix(accounts): keep runtime polling aligned with settings
---------
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* feat(status-bar): consolidate agent usage into a single roster popover
The footer usage cluster is now one quiet, borderless control: each agent
shows its tightest window as neutral text (letter badges at icon-only
width), and clicking anywhere opens a consolidated Usage popover listing
every agent worst-first — icon, name, plan, soonest reset, and per-window
threshold-colored bars. Claude/Codex rows drill into their existing
account switcher, runtime toggle, and Codex reset credits via a submenu
variant of ProviderDetailsMenu; all other providers drill into their
detail panel. Row actions and the footer links route to Settings.
Also: barColor's <60% band goes green -> neutral so color is reserved for
approaching limits (threshold color now lives only in the popover, the
always-visible bar stays monochrome), Codex plan_type is surfaced as
ProviderRateLimits.planType for the "Codex · Plus" label, and the Fable
weekly window is labeled "Fable" so it no longer collides with "wk".
* fix(status-bar): single-line sign-in row + review dedup
Signed-out roster rows now read as one line (name · "not signed in" ·
right-aligned Sign in) instead of a floating button over an orphaned
second line. Review follow-ups: the non-submenu ProviderDetailsMenu
branch reuses the extracted panelBody, and the icon-only letter badge is
one shared component so its has-data dot condition can't drift.
* fix(status-bar): harden usage roster interactions
* chore: remove unrelated formatting change
* chore(skills): refresh bundle manifest for rc.2
* fix(status-bar): preserve usage roster semantics
* fix(status-bar): preserve roster menu interactions
* chore: remove unrelated formatting changes
* fix(status-bar): keep usage reset countdowns live
* feat(status-bar): toggle compact usage summary
* fix(status-bar): simplify usage mode toggle
* feat(status-bar): replace usage footer toggle with Compact/Detailed segmented control
Swap the bottom-of-popover on/off switch for a SettingsSegmentedControl at
the top of the Usage popover (view-switcher pattern), so both modes are
named and discoverable on first open. Reuses the repo's canonical
Compact/Detailed vocabulary from the Workspace card-layout control.
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.
Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.
Area: main — core runtime, ipc, daemon, pty, providers. 73 files changed, 3206 insertions(+), 10475 deletions(-).
Co-authored-by: Orca <help@stably.ai>
* Surface Grok unified-billing monthly usage instead of a permanent warning
Unified-billing Grok accounts have no weekly credits: the
/billing?format=credits view returns a config without
creditUsagePercent, so the status bar was stuck on 'Grok billing
response did not include credit usage' even though the account has a
real quota. The default (format-less) /billing view reports it as an
included monthly budget (monthlyLimit/used with the billing period).
When the credits view has no weekly credit usage, read the default view
and surface monthly usage as the provider's 30-day window (already
supported by the tooltip and chip visibility for OpenCode Go). If the
fallback read fails, the previous 'unavailable' presentation stands
rather than escalating to an error chip.
* Review fixes: chip renders monthly-only usage; fallback failures keep stale data
- StatusBar ProviderSegment: monthly window is chip-visible when it is the
sole window (Grok unified billing); fetching/error no-data guards and the
icon-only dot now count monthly, matching tooltip.tsx. OpenCode Go chips
are unchanged (monthly stays tooltip-only next to session/weekly).
- grok-fetcher: monthly-fallback request failures propagate as 'error' so
applyStalePolicy keeps the last good monthly snapshot; 'unavailable' is
reserved for a successful response without monthly fields.
* Settings: show Grok monthly usage row for unified-billing accounts
Why: the Grok accounts section only rendered the weekly-credits row, so
unified-billing accounts showed a signed-in state with no usage at all.
* Use generated localization keys for Grok monthly copy
* Stop usage chips flashing and back off retries for failing providers
Two behaviors made the status bar unusable when any provider was
persistently failing (bad auth, unsupported plan):
1. Every refetch repainted all providers as 'fetching', so a settled
error chip flashed to a loading "…" chip and back on every cycle.
withFetchingStatus now keeps settled states (ok/error/unavailable)
visible until the new result lands; only providers with no settled
state (first load, explicit account-switch clear) show loading.
2. Error providers on the fast activation-retry lane (claude/codex/
grok) were retried every 30s on any focus/show/restore event —
forever. Repeated hits drove Claude's tight-budget usage endpoint
into 429s, flipping the chip between 'Limited' and its actual error.
Retries now back off exponentially per consecutive applied failure
(30s, 60s, 120s, … capped at the 15-minute poll cadence) and reset
on success or account/target switch.
* Count full fetches as failure-lane retries and keep Grok pane refresh feedback
- Stamp failing providers' activation-retry clocks when a stale-driven full
fetch runs, so the individual failure lane does not fire a redundant retry
right after the full fetch already retried them.
- GrokUsagePane: manual refresh spinner/disable is now renderer-local, since
settled snapshots no longer repaint as 'fetching' during refetches.
- Strengthen the backoff-reset test so it distinguishes a reset streak from a
stale retry timestamp (CodeRabbit), and add a regression test for the
full-fetch retry stamping.
* Fall back to legacy keychain when stale scoped Claude credentials 401
Claude Code maintains the legacy 'Claude Code-credentials' Keychain item
for the default config dir, but a scoped item (service suffixed with
sha256(configDir)) can be left behind by sessions that ran with
CLAUDE_CONFIG_DIR set. Once its access token expires, nothing refreshes
it: readFromKeychain returned the scoped token unconditionally, every
usage fetch 401'd as 'stale-token', and system-default auth has no CLI
recovery lane — so the status bar showed 'Refreshing sign-in' forever
while the legacy item held a perfectly valid token.
Two changes:
- readFromKeychain: an actual access token from the legacy item now
beats scoped refresh-only credentials (Orca cannot refresh tokens
itself, so refresh-only must not shadow a working token).
- On a stale-token OAuth failure with scoped-keychain credentials, retry
once with the legacy item's token before classifying the failure.
Host system-default auth only — managed/WSL credentials never fall
back to the host user's legacy keychain item.
The legacy retry runs before CLI repair, so a readable working token is
preferred over launching a hidden 25s claude PTY.
* Pin WSL-gate and same-token short-circuit for legacy keychain retry
The legacy-keychain retry must never answer a WSL target with the host
user's keychain account, and must not double the usage request when the
legacy item mirrors the failed scoped token. Add tests locking both.
* Classify Codex app-server chatgpt-auth-required as an auth error
When auth.json holds only an OPENAI_API_KEY (no ChatGPT tokens), the
app-server RPC rejects account/rateLimits/read with "chatgpt
authentication required to read rate limits". That string matched none
of CODEX_AUTH_ERROR_PATTERNS, so fetchCodexRateLimits fell through to
the hidden PTY /status probe, which cannot render usage for such
accounts and burned the full 15s PTY timeout on every refresh cycle,
surfacing as a permanent "Refresh failed — PTY timeout" status chip.
Classify the message as an auth error so the RPC result is returned
directly (fast, accurate) and no PTY is spawned.
* Keep auth-required usage errors from rendering as a rate-limit Limited label
The new Codex app-server error 'chatgpt authentication required to read
rate limits' mentions rate limits only as the object it failed to read,
but the status bar's rate-limit classifier matched the phrase and
labeled the chip 'Limited'. Classify authentication-required messages as
auth failures so they get the standard softened refresh copy instead.
* fix(grok): prefer auth.x.ai session and mention Grok in OSC52 toast
auth.json can hold multiple issuer keys; Object.values order could pick a
stale entry before the live auth.x.ai OIDC session. Prefer auth.x.ai keys
and fall back only when none exist. Also list Grok in the OSC52 blocked
clipboard toast so Grok TUI copy failures point at the same setting.
* fix(grok): harden preferred auth session selection
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
* fix(rate-limits): debounce auto-refresh off last successful fetch
A failed post-update/relaunch fetch stamped the single debounce
timestamp the same as a success, so refreshIfWindowActive() no-oped
every focus/show/restore and the deferred-startup retry for the next
5 minutes — leaving the status bar stuck on "Refresh failed" until the
15-minute background poll (which on Windows also requires the relaunched
window to be focused).
Key the debounce off the last SUCCESSFUL read instead: only stamp
lastSuccessfulFetchAt when a provider returned fresh 'ok' data, so a
failed fetch no longer suppresses auto-recovery on the next window
activation. Removes the now-unused lastFetchAt field.
Fixes#5355
Co-authored-by: Orca <help@stably.ai>
* fix(rate-limits): narrow active refresh retries
* fix(rate-limits): refresh after live Claude credential handoff
* fix(rate-limits): cover antigravity provider in active refresh plan
Co-authored-by: Orca <help@stably.ai>
* refactor(rate-limits): compile-enforce active-refresh provider coverage
Co-authored-by: Orca <help@stably.ai>
* fix(rate-limits): keep durable non-Claude/Codex error retries on the 5-minute full-fetch cadence
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* fix(ports): stop live-port indicator flickering on transient scan failures
A worktree's plug icon renders only when it has ports. The scanner polls
each execution host every 30s and, on a transient failure (SSH/IPC latency),
substituted an empty "unavailable" scan — dropping that host's ports for the
cycle and blinking the affected row's live-port indicator off, then on again
next poll.
Debounce per-host failures: reuse the host's last good scan until failures
reach a tolerance, so a single dropped poll no longer zeroes the row. A
reachable host reporting no ports has no unavailableReason, so a genuine port
close still clears immediately. Logic extracted to a pure helper with tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ports): stop port scan re-init loop that flickered live-port icons
The scan poll effect depended on the `refresh` callback identity, which gets a
fresh reference whenever repos/settings are replaced (frequent in a busy
workspace). Each change re-ran the effect, which reset the scan to null and
kicked an immediate re-scan — a tight ~600ms loop that blinked the plug icon
off/on even though the scanned ports never changed.
Drive the poll and advertised-url effects off the latest refresh via a ref and
key them on stable value-signatures (enable state, active scan key, host-set
signature, runtime kind) instead of the callback identity. Incidental store
churn no longer re-inits the scanner; it still polls every 30s, reacts to
advertised-url events, and rescans on a real host-set or runtime change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Address PR review feedback (#5510)
- Prune the per-host failure counter for failed-only hosts that disappear,
not just hosts present in lastGood — avoids an unbounded failures map and a
stale failure streak if the host reappears
- Canonicalize scanTargetsSignature by sorting so a reordered-but-unchanged
host set does not re-init the scan and reintroduce flicker
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ports): keep host scans targeted and stable
* test(ports): cover scan debounce state
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
* fix(grok): restore clipboard and native-chat parity
Grok CLI already supports argv prompts, OSC 52 copy, and image paste chips.
Orca was blocking those paths: stdin-after-start keystroke injection, OSC 52
writes default-off, image-attachment denylist, and native-chat allowlist.
- Launch Grok with positional argv prompts
- Default OSC 52 TUI clipboard writes on (still user-toggleable)
- Treat Grok as image-attachment capable
- Parse ~/.grok/.../chat_history.jsonl for native chat
OSC 52 clipboard *query* remains ignored by design (host clipboard exfil risk);
xAI docs only require OSC 52 write for remote copy.
* fix(grok): sync OSC 52 docs and locale catalog with default-on
Update terminalAllowOsc52Clipboard type docs for the true default, and
refresh locale strings so settings UI mentions Grok alongside other TUIs.
* fix(grok): tool hook matcher, StopFailure, previews, AskUser waiting
Grok tool-event matchers are real regexes; bare `*` failed as match-all.
Install `.*` for Pre/Post tool hooks, add StopFailure for API-error ends,
recognize Grok-native tool input keys, and map ask_user_question PreToolUse
to waiting with interactivePrompt (Kimi-style live card path).
* fix(grok): resolve chat_history under GROK_HOME and long-cwd layouts
Centralize Grok session path helpers so hooks and native-chat honor
GROK_HOME and find chat_history.jsonl by session id when the cwd group
is slug-encoded (encoded name > 255 bytes) instead of only
encodeURIComponent(cwd).
* fix(terminal): keep Kitty keyboard for Grok on Windows ConPTY
Local Windows ConPTY withholds KKP so CSI-u-blind CLIs (e.g. Antigravity)
keep Enter/nav working (#2434). Grok needs KKP for Ctrl+Enter interject and
modified-Enter newline chords; blanking the advertisement for Orca-launched
Grok left those actions broken.
- Prefer KKP when tuiAgent is grok despite ConPTY withhold
- Wire launchAgent from tab/startup into keyboard protocol options
* fix(grok): restore OSC52 default-off, split decoders, honor GROK_HOME hooks
- Keep terminalAllowOsc52Clipboard default false (clipboard exfil risk)
- Split transcript-line-decoders under max-lines without suppressions
- Install local Grok hooks under resolveGrokHomeDir() / GROK_HOME
* refactor(grok): share CLI home resolution
* fix(grok): harden terminal and native chat integration
* test(grok): align CI coverage with native chat support
---------
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
* fix(status-bar): keep antigravity usage visible
* feat(rate-limits): add Grok usage status
* feat(grok): add managed usage accounts
* test(rate-limits): isolate Codex PTY fallback fetches
* fix(grok): address CodeRabbit review on usage status PR
- Guard auth.json read/parse so missing files return null
- Thread AbortSignal through Grok rate-limit fetch and ACP auth
- Pass isRemote:false for floating-terminal agent launch env
* fix(grok): wire ACP abort after child listeners to avoid TDZ
* fix(status-bar): address CodeRabbit on combined Grok/Antigravity usage
- Pin WSL shell distro when resolving managed GROK_HOME (parity with Codex)
- Refresh only Grok on account change via fetchGrokOnly
- Consolidate usage status-bar toggle catalogs; add Grok locale keys
- Fix Grok aria-label, feature-interaction tracking, and test fixture
* fix(settings): drop duplicate usage status-bar toggle catalog
Remove the leftover re-export path so Appearance search has a single
source of truth for usage provider toggles (including Antigravity + Grok).
* fix(settings): restore AccountsPane and locale UTF-8 after merge
Re-do AccountsPane three-way merge with binary-safe git objects and keep
both Grok accounts + remote provider client imports. Rebuild locale JSON
merges so em dash/ellipsis/middle-dot strings are no longer mojibake.
* fix(i18n): add missing comma after merged status-bar locale keys
* test: add antigravity to GrokUsagePane rate-limit fixture
RateLimitState gained a required antigravity field; the pane fixture must
carry it to typecheck.
* fix(status-bar): gate Antigravity durable visibility on Gemini OAuth opt-in
The Antigravity snapshot mirrors the Gemini fetch, which is permanently
'unavailable' until the user opts into Gemini CLI OAuth. Without this gate
the default-on checked item plus a detected agy CLI pinned a dead 'A --'
bar (Gemini itself hides in that state) and suppressed the usage setup CTA
for users who configured nothing.
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* feat(rate-limits): Grok CLI OAuth weekly credit usage in status bar
Read ~/.grok/auth.json (read-only), fetch billing credits via cli-chat-proxy, and surface Grok in Settings, status bar toggles, and rate-limit polling alongside other usage providers.
* fix(grok): clarify comments and address CodeRabbit review
- Shorten Why comments per AGENTS.md; fix billing period end fallback.
- Share GrokAccountStatus type; hash-based locale keys; reload why in Settings.
* docs(grok): plain-language comments and Settings copy
* feat(stats): subscription usage section with Grok in Stats & Usage
Surface rate-limit weekly credits in Settings > Stats & Usage and link to Accounts for setup.
* feat(stats): Grok tab in Usage Analytics dropdown
* refactor(stats): drop Subscription usage block; align Grok pane with Codex
* fix(grok): align settings copy and visibility tests
* fix(grok): add localization catalog entries
* fix(grok): avoid eager usage refresh fanout
* fix(grok): harden usage refresh visibility
* test(grok): cover account status privacy boundary
* fix(grok): target refreshes and redact auth errors
* fix(grok): hide usage UI for signed-out users and align empty states
- Treat a token-less auth.json (e.g. after grok logout) as signed out
instead of surfacing a permanent status-bar error.
- Map billing responses without credit usage to 'unavailable' so plans
with no weekly credits hide the bar like Claude API-key billing.
- Gate the grok status-bar item and toggle on CLI PATH detection,
matching claude/codex/gemini/kimi.
- Guard an empty GROK_CLI_CHAT_PROXY_BASE_URL from producing a
relative billing URL.
- Drop dead minimax/kimi Stats & Usage search keywords left from the
removed subscription section.
- Add missing grok search keyword catalog entries and translate the
English-stubbed stats keywords in es/ja/ko/zh.
* test(ipc): mock grok account registrar in register-core-handlers test
---------
Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
* Support WSL Codex settings promotion and harden config write-back
- Enable settings promotion for WSL runtimes using per-distro baselines.
- Create parent directories if missing to prevent promotion ENOENTs.
- Keep restrictive permissions (0600) and follow symlinks on promote.
- Respect CRLF line endings when inserting keys into CRLF config files.
- Skip redundant baseline file writes when settings are unchanged.
- Include the release scan report for the 1.4.131-rc2 prep.
* Refactor sleeping agent wake flow and fetch rate limits via backend
- Background-mount only targeted terminal tabs during passive wake to
prevent spawning unnecessary PTYs for unvisited tabs.
- Latch edge-triggered wake requests that arrive mid-hibernation and
track active claims to prevent double-resuming a provider session.
- Query the ChatGPT wham usage backend API directly with fetch for
rate limits, avoiding launching Codex or WSL login shells.
- Asynchronously probe and serialize WSL auth files with timeouts to
prevent synchronous I/O from stalling Electron's main process.
- Fix config promotion edge cases such as missing parent directories,
dangling symlinks, and atomic write permission widening.
* Support WSL dotfile-symlink write-back and lengthen redeem timeout
- Preserve symlinked Codex config on WSL by writing through the
existing file instead of atomic-rename, since \\wsl$ symlink
metadata isn't reliably detected and rename would clobber the link.
- Tighten new ~/.codex directory creation to 0700 (holds auth.json).
- Give explicit reset-credit redemption a 30s backend timeout instead
of the 10s background-poll default, since it's user-triggered.
- Read sleeping-agent session state from the worktree's actual
execution-host partition instead of always the local one, so the
headless-wake check works correctly for SSH-hosted worktrees.
- Isolate serve-sim watcher tests from the real $TMPDIR/serve-sim
state file to avoid leaking unrelated events.
Enable three unicorn rules — one correctness, two performance — and fix every
existing violation repo-wide so the rules pass as errors.
prefer-number-properties (76 sites)
- parseInt/parseFloat/NaN -> Number.* : safe aliases (autofixed).
- isNaN -> Number.isNaN (12 sites, hand-converted): global isNaN coerces its
argument, Number.isNaN does not. Verified every call site already passes a
number (Number.parseInt results, number-typed fields, Date.getTime()), so the
conversion is behavior-preserving today and guards against a future non-numeric
argument silently coercing.
prefer-array-find (26 sites)
- .filter(pred)[0] -> .find(pred); .filter(pred).at(-1) / .pop() -> .findLast(pred).
Drops the intermediate array and short-circuits.
prefer-array-index-of (5 sites)
- .findIndex(x => x === v) -> .indexOf(v).
Verified: typecheck (node/cli/web) clean, 53 affected suites pass (1679 tests),
oxlint clean repo-wide. mobile/ uses findLast safely (already ships ES2023
.toReversed()); config scripts and e2e helpers run on Node 24.
Replace the hand-rolled `AbortController` + `setTimeout(() => controller.abort())`
+ `clearTimeout` in `finally` pattern with `AbortSignal.timeout(ms)` across the
main-process fetchers, updaters, and hosted-provider clients. This removes a
timer-leak footgun (a thrown/early-returned path that skips the finally leaks the
timer) and ~3-4 lines of bookkeeping per site. `AbortSignal.timeout` is Node
17.3+ (Electron main is Node 22+).
Two sites compose a caller-cancel signal with the timeout via `AbortSignal.any`
(Node 20.3+) instead of a manual abort listener:
- git/fork-sync.ts: also fixes a latent bug — the caller's `options.signal` was
spread into the git options then immediately clobbered by `signal:
controller.signal`, so caller cancellation was silently dropped. `AbortSignal.any`
restores it.
- rate-limits/claude-fetcher.ts (fetchViaOAuth external signal).
hosted-review-api-request.ts: `AbortSignal.timeout()` rejects with a
`TimeoutError`, not an `AbortError`, so the timeout-detection branch is updated
(otherwise `timedOut` would never be set).
minimax-fetcher.test.ts: its timeout test drove the abort with fake timers, which
cannot advance `AbortSignal.timeout`'s internal timer. Rewritten to fire the
timeout with an already-aborted signal so it genuinely exercises the abort path.
Deliberately NOT migrated:
- src/relay/git-handler.ts: the relay targets Node 18 (`build-relay.mjs`,
MIN_NODE_MAJOR = 18); `AbortSignal.any` needs Node 20.3+, and timeout-only would
drop the request context signal.
- ipc/feedback.ts: its timeout-driven fallback is verified with fake timers, which
can't advance `AbortSignal.timeout`; kept on the manual pattern.
* Add MiniMax rate-limit tracking and secure cookie storage
* Securely store MiniMax session cookies using an encrypted envelope format and local file hardening.
* Fetch rate limits in an isolated session partition and clear the cookie jar before and after requests to prevent leakage.
* Add a default-on "minimax" status bar item to display subscription usage.
* Expose minimax configuration settings (group ID and models) in settings panes and sync them via the runtime client.
* Isolate MiniMax config resolver and decryption failures from affecting other rate-limit providers.
* Redact MiniMax secrets with whitespace around colons
Update redactMiniMaxSecret to allow and match optional whitespace
surrounding the colon when redacting quoted cookie values. This matches
the spacing tolerance used during parsing.
* Address PR review: harden cookie read, validate IPC, add tests
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* Harden rate-limit PTY cwd and abort background usage probes on stop
Resolve a runaway CPU incident where background Claude rate-limit usage
probes could inherit a root working directory (such as `/` or `C:\`) and
index the entire disk.
- Force hidden Claude and Codex usage PTYs to spawn inside a small,
dedicated directory under user data or temp.
- Thread AbortSignal through rate-limit fetchers to immediately kill
and reap in-flight PTY processes when RateLimitService stops.
- Harden daemon and local PTY defaults to reject root-like working
directories for automated agent startups.
* Consolidate PTY path safety checks to prevent runaway CPU
Centralize `isRootLikePath` into a dedicated PTY path safety module
to prevent terminal launches and hidden usage probes from executing
in filesystem or drive roots, avoiding unbounded file discovery.
- Centralize path validation supporting POSIX, Windows, and UNC roots.
- Fail loudly if no safe default working directory is available.
- Enforce bounded directories for hidden usage PTYs and WSL probes.
- Check for aborted signals during rate-limit CLI repairs.
* Gate agent startup on the effective PTY cwd after default fallback
Co-authored-by: Orca <help@stably.ai>
* test(repos): verify origin/HEAD target in SSH base-ref tests
Co-authored-by: Orca <help@stably.ai>
* Pin HOME to assert exact default PTY cwd in spawn test
Pin `process.env.HOME` in the fallback test for LocalPtyProvider to
assert the exact resolved candidate rather than just checking that it is
non-root-like. This catches potential regressions where an unintended
home directory is picked, and uses a try/finally block to safely restore
the environment afterwards.
---------
Co-authored-by: Orca <help@stably.ai>
Quality pass on main-process/build PRs merged 2026-07-03:
- ios-emulator-backend: resolve the serve-sim executable via a lazily-cached getter
instead of eagerly in the constructor. The bridge is built before the main window
is shown, so the one-time recursive copy + xattr subprocess (first launch after each
version bump) no longer blocks macOS startup for a feature that may go unused (#7174).
- index: collapse the two near-identical `{webContentsId, until}` reload flags
(expectedRendererReload / recoveryReloadInFlight) into one `createWebContentsTimedFlag`
primitive; behavior preserved, including consume-on-read for the recovery reload (#7290).
- check-reliability-gates: coerce gate.commands/testFiles/platforms/providers with an
`asArray` helper before `.includes`, so a hand-edited manifest with a missing/mistyped
field reports a validation failure instead of throwing an uncaught TypeError; extract
`hasCompleteRedGreenEvidence` for the duplicated status check (#7295).
- claude-pty: derive FABLE_WEEKLY_LABEL_RE from WEEKLY_RE.source so a future weekly-
wording change stays in one place and can't reopen the parsing gap it just closed.
- macos-tcc-login-shell: trim the 30-line flag-by-flag JSDoc to the two non-obvious whys
(TCC identity, env(1) SHELL re-assertion) per the repo comment guidance (#7003).
Typecheck, oxlint, oxfmt, `check:reliability-gates`, and touched unit suites all pass.
Fixes the PTY fallback path so Claude Fable usage carries a concrete reset timestamp and the existing status tooltip renders the Fable reset countdown.
Claude Code 2.1.199 renders the Fable plan bucket as Current week (Fable), while Orca only parsed a standalone Fable heading. Accept weekly-style Fable headings as the distinct Fable weekly meter while keeping broader Fable-weekly copy as a section boundary to avoid false positives.
Validation:
- pnpm exec vitest run --config config/vitest.config.ts src/main/rate-limits/claude-pty.test.ts src/main/rate-limits/claude-fetcher.test.ts src/main/rate-limits/service.test.ts src/renderer/src/components/status-bar/tooltip.test.ts src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
- pnpm run typecheck
- pnpm exec oxlint src/main/rate-limits/claude-pty.ts src/main/rate-limits/claude-pty.test.ts src/main/rate-limits/claude-fetcher.ts src/main/rate-limits/claude-fetcher.test.ts src/main/rate-limits/service.ts src/main/rate-limits/service.test.ts src/renderer/src/components/status-bar/tooltip.test.ts src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
- git diff --check HEAD~1..HEAD
- live local Claude fetcher returned fableWeekly: 62