Repository navigation
fix(antigravity): read native quota only on supported CLI versions - #21654
Conversation
Adopts native usage parsing and display from PR #20797, adding a pre-invocation version guard and legacy group labels. Co-authored-by: Abhishek Tiwari <69499+abti@users.noreply.github.com>
|
That's a great feature, I woudl like to see it is merged |
|
Windows data point for the version guard, since the checks so far were run against the installed macOS Environment: Orca 1.4.205, Windows 11 x64, agy 1.2.7, Antigravity IDE not running, no Gemini CLI credentials on disk.
{
"status": "SUCCESS",
"num_turns": 0,
"usage": {"input_tokens":0,"output_tokens":0,"thinking_tokens":0,"cache_read_tokens":0,"total_tokens":0},
"command": {
"name": "usage",
"data": {
"groups": [
{
"name": "Gemini Models",
"description": "Models within this group: Gemini Flash, Gemini Pro",
"buckets": [
{"id":"gemini-weekly","name":"Weekly Limit Remaining","window":"weekly",
"remaining_fraction":0.8182741403579712,"reset_time":"2026-09-23T03:12:00Z"},
{"id":"gemini-5h","name":"Five Hour Limit Remaining","window":"5h",
"remaining_fraction":0,"reset_time":"2026-09-19T11:20:06Z"}
]
},
{
"name": "Claude and GPT models",
"description": "Models within this group: Claude Opus, Claude Sonnet, GPT-OSS",
"buckets": [
{"id":"3p-weekly","name":"Weekly Limit Remaining","window":"weekly",
"remaining_fraction":1,"reset_time":"2026-09-26T10:57:33Z"},
{"id":"3p-5h","name":"Five Hour Limit Remaining","window":"5h",
"remaining_fraction":1,"reset_time":"2026-09-19T15:57:33Z"}
]
}
]
}
}
}Worth noting for the rendering side: buckets are per model pool, not per model. One Windows-specific gotcha a macOS run will not surface. Invoking I did not chase the cause, but the failure mode is the part that matters here: an empty read paired with a success exit code is indistinguishable from "the CLI reported no quota", so it would surface as Happy to run a Windows build of this branch against agy 1.2.7 and report back. |
|
I also validated the current head (
I did notice one integration gap with the stacked #21653. That PR allows I have a narrow patch ready that resolves only a direct |
|
Closed #19385, which read quota from the language server's loopback port: that path now returns |
|
Independent verification update:
Scoped verdict: Approve after stack order for version-gated native quota fetching, stacked on #21653. The populated live quota and rendered usage proof are documented in #21678. Windows/WSL/SSH, paired-runtime, and multiple-account quota remain unverified. |
|
Independent verification (2026-09-20):
Verdict: approve after stack order, with #21678 for configured-executable/visibility behavior. This verifies the supported local CLI quota path; Windows/WSL/SSH/paired-runtime and multi-account quota remain outside the proof. No merge performed. |
ELI5 / evidence / trade-offs auditELI5: Orca reads Antigravity's reported quota instead of guessing usage from chat text. Proof: Authenticated agy User-facing trade-off: The quota is only as accurate and available as agy's endpoint; unsupported hosts show no invented numbers. This note scopes the claim to the evidence above; it does not claim untested remote, SSH, WSL, or vendor combinations. |
There was a problem hiding this comment.
ℹ️ No critical issues — a few minor suggestions inline, plus one latent stale route.
Reviewed changes
- Independent Antigravity fetch replaces the Gemini mirror.
antigravity-usage-mirror.ts(and its test) is deleted; a newantigravity-usage-fetcher.tsresolvesagy, gates onhasReachedAppVersion(version, '1.1.11'), then readsagy --print /usage --output-format json, mapping groups/buckets intoProviderRateLimits.buckets. - Service wiring. Antigravity joins the
Promise.allSettledbatch and flows throughapplyStalePolicy; its result may now beerror, so recent buckets are retained through transient failures instead of flapping tounavailable. - Shared types.
RateLimitWindow.windowLabelandRateLimitBucket.{id,groupName,groupDescription}are added as optional fields, so mixed-version wire readers stay compatible. - Grouped renderer rendering. Antigravity windows are grouped by
groupNameinUsageRosterPanel,StatusBarProviderSegment, andProviderPanel, with newantigravity-usage-format.tsandusage-section-selection.tshelpers and native5h/wk/group labels. - Visibility is decoupled from Gemini OAuth.
antigravityUsageConfigured(status item + detected CLI) is now the whole durable signal instatus-bar-provider-visibility.tsanduse-status-bar-controller.ts.
ℹ️ Antigravity still routes to Gemini's OAuth settings
getUsageProviderAccountsSectionId('antigravity') still returns accounts-gemini, guarded by a comment asserting "Antigravity usage currently shares Gemini's OAuth configuration" and by a passing test. After this PR the Gemini CLI OAuth opt-in has no effect on Antigravity usage, which is read from agy, so that pane no longer configures what the row represents. The route is unreachable through the roster today, so no user hits it yet, but the branch and its test encode the invariant this PR removed and will misroute Antigravity the first time the row's click path changes.
Technical details
# Antigravity accounts route points at Gemini OAuth
## Affected sites
- `src/renderer/src/components/status-bar/usage-provider-settings-target.ts:11-14` — `case 'antigravity'` falls through to `return 'accounts-gemini'` with the now-false comment.
- `src/renderer/src/components/status-bar/usage-provider-settings-target.test.ts:9` — asserts `getUsageProviderAccountsSectionId('antigravity') === 'accounts-gemini'`.
- `src/renderer/src/components/status-bar/use-status-bar-controller.ts:217-225` — the route is consumed by `handleOpenProviderAccounts`.
## Why it is latent today
- `StatusBarSurface.tsx:173-212` always supplies `renderRow`, which wraps every non-Claude/Codex provider in `ProviderDetailsMenu`, so the default `onOpenProvider` row in `UsageRosterPanel.tsx:350` is not reached for Antigravity.
- The sign-in row path needs `state.kind === 'sign-in'`, which `usage-roster-row-state.ts:20-31` only returns for `failureKind === 'missing-credentials'` (or an error string match). Every Antigravity failure result sets a `failureKind`, so it never qualifies.
## Required outcome
- Decide the correct accounts destination for Antigravity now that it has no Gemini OAuth dependency (a settings section for the `agy` CLI, or `null` like Kimi), update the comment, and align the test.Metadata
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
| record.code === 'ETIMEDOUT' || | ||
| record.code === 'ERR_CHILD_PROCESS_TIMEOUT' || | ||
| record.killed === true || | ||
| record.signal === 'SIGTERM' |
There was a problem hiding this comment.
This treats any signal-killed process as a timeout. execFileCaptureToTermination sets killed for any non-null exit signal, so an agy that crashes or is stopped externally is surfaced to the user as "could not be refreshed before the agy CLI timed out". run-process.ts:346 documents this exact trap — a real deadline already arrives as code: 'ETIMEDOUT' from createTimeoutError. Consider keeping the timeout message for the timeout codes only and wording the signal case separately.
| signal | ||
| })), | ||
| fetchGeminiRateLimits(geminiCliOAuthEnabled), | ||
| fetchAntigravityRateLimits(signal), |
There was a problem hiding this comment.
fetchAntigravityRateLimits resolves agy from PATH and spawns --version plus /usage on every cycle, with no settings opt-in — unlike the Gemini fetcher, main has no knowledge of the Antigravity status-bar item, so a user with agy installed but the bar unchecked still gets both processes each refresh. Is that intended, or should the fetch be skipped unless the provider is actually surfaced? (The PR notes the CLI is now the user's opt-in signal, which main cannot currently see.)
| // 'unavailable' until the user opts into Gemini CLI OAuth. Without that | ||
| // gate the default-on checked item would pin a permanently dead bar. | ||
| return settings.antigravityUsageConfigured === true && settings.geminiCliOAuthEnabled === true | ||
| return settings.antigravityUsageConfigured === true |
There was a problem hiding this comment.
The UsageProviderSettings comment just above (lines 11-15) still says Antigravity durability "further requires geminiCliOAuthEnabled" and that its snapshot "mirrors the Gemini fetch". This line drops that gate, so the comment now documents removed behavior — worth updating so the type doesn't contradict the code.
| // Why: stale-retention would otherwise show Gemini numbers as "Refresh failed" Antigravity usage. | ||
| expect(service.getState().antigravity?.status).toBe('unavailable') | ||
| expect(service.getState().antigravity?.session).toBeNull() | ||
| // Why: stale-retention must not substitute Gemini numbers for Antigravity. |
There was a problem hiding this comment.
The test name (never leaves a cached Antigravity snapshot in the error retry lane, line 98) no longer matches what it asserts: lines 115-116 now require the 18% snapshot to be retained alongside status: 'error'. Renaming it (e.g. "retains the last Antigravity snapshot through a transient failure") would keep the intent readable.

Latest verification — 2026-09-20: The actual production fetcher now returns two populated native quota buckets, and the rendered Usage menu shows Gemini Models weekly 10% and Claude/GPT weekly 0%. Live proof. This supersedes the earlier empty-groups-only limitation. #21678 adds custom executable support. Multiple accounts and Windows/WSL/SSH quota remain unverified. All checks at the unchanged PR head are green. Earlier screenshots and test notes below describe their original runs.
ELI5
Antigravity and Gemini no longer share a quota meter in Orca. Orca asks Antigravity for its own numbers. If the CLI supplies no numbers, Orca says usage is unavailable. Older CLIs are refused before the quota command because they can treat it as a paid model request.
What Changed
Replaces the Gemini-derived Antigravity snapshot with bounded
agy --versionandagy --print /usage --output-format jsoncalls. Only a recognized version at least 1.1.11 reaches/usage; cancellation stops the read. Shows independent Gemini-model and Claude/GPT quota groups, their session/weekly windows, and stale/error states. Group names remain in bucket labels for clients that ignore the new optional metadata.Adopts the implementation from #20797 (
03bce76ef0dca7f7309f1598953a4f45a92c595d), adding the version guard, legacy labels, test isolation, and style adjustments. Stacked on #21653.Why
The Gemini quota endpoint can reject accounts while Antigravity has its own quota source. Using the CLI avoids extracting OAuth clients or reading credentials ourselves. Checking the executable version on every refresh avoids reusing a positive capability result after an executable downgrade; the existing service controls refresh frequency.
Linked Issue
Addresses #19561 and the quota portion of #19587; related #17345 and #9122. Does not add an Accounts login/card or close those broader issues.
Visual Proof
After, actual installed agy 1.2.7 on macOS: clicked Refresh rate limits in the rebuilt hidden Electron app, then opened the Antigravity usage panel. This API-key configuration returns no buckets.
Populated display fixture, not live OAuth quota: temporary renderer state with four distinct percentages demonstrates group/window separation. Restored actual state after capture.
No before screenshot captured. The previous Gemini-derived behavior is documented by the removed mirror implementation and its tests.
Testing
201 focused fetcher/service/status-bar tests pass. A separate live test passed against the guarded production fetcher and installed agy 1.2.7. Regression tests prove 1.1.10, 1.1.11 prerelease, and unknown versions never receive
/usage; 1.1.11 and newer supported examples do. The older-version token-spend evidence is from the review on #20797, not a repeated old-version invocation here.Full typecheck and changed-code quality gate pass. Dev main/preload/renderer and embedded web build launched successfully in background mode. Full release build and broad CI remain pending.
AI Disclosure
Review
Self-reviewed fetch bounds, cancellation, missing/malformed data, separate quota groups, stale results, and mixed-version labels. This remains a draft pending CI and further platform/account verification.
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Verified on local macOS only. Live populated native quota now passes. Windows/WSL/SSH and paired-runtime quota UI remain unverified. The fetch runs on the service host with its installed CLI/auth; it does not select credentials by the viewed workspace. Custom launch-command overrides are not used by this fetcher. New bucket fields are optional, and legacy names retain group identity. No authentication or global hook settings changed.
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)