Skip to content

fix(usage): read Antigravity quota from its own credential, including SSH hosts - #19209

Open
abruption wants to merge 3 commits into
stablyai:mainfrom
abruption:feat/antigravity-quota-remote-hosts
Open

abruption wants to merge 3 commits into
stablyai:mainfrom
abruption:feat/antigravity-quota-remote-hosts

Conversation

@abruption

@abruption abruption commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

Orca's "Antigravity" usage number was never actually Antigravity's. It was the Gemini Code Assist number with a different label glued on, so it showed the wrong pools — and if you had no Gemini CLI sign-in, it showed nothing at all. This PR makes Orca ask Antigravity directly, using the credential the Antigravity CLI writes for itself. Because that is just a file, Orca can also read it on a connected SSH host, so a remote dev box where you actually run agy finally reports its quota.

What Changed

  • New read path: Antigravity's own OAuth credential → loadCodeAssist with ideType: "ANTIGRAVITY" → retrieveUserQuotaSummary.
  • The credential is looked up on this machine first, then on connected SSH targets. The first host holding a usable credential wins, and the host is recorded in usageMetadata.credentialSource.
  • Buckets are keyed by group (Gemini Models, Claude and GPT models) and the reported window maps to windowMinutes (weekly → 10080, 5h → 300).
  • The existing Gemini mirror is kept as the fallback for whenever no direct read succeeds, so nothing regresses for accounts whose quota only exists as the shared Code Assist pool.

New files, all under src/main/rate-limits/:

File Responsibility
antigravity-oauth-credential.ts Parse the stored blob (plain JSON or go-keyring-base64:)
antigravity-credential-hosts.ts Find the credential across local + SSH hosts
antigravity-cloud-code-api.ts The two-step Cloud Code call
antigravity-quota-buckets.ts Group/window → RateLimitBucket
antigravity-quota-fetch.ts Orchestration → ProviderRateLimits

Why

Two details of this endpoint are easy to miss, and I think they explain why the credential path has looked broken:

1. On this account, cloudcode-pa resolved the product from the User-Agent rather than from the request body. With a UA that does not contain antigravity, loadCodeAssist still answered HTTP 200 but omitted cloudaicompanionProject, and the quota call then failed 403 — which reads exactly like a bad sign-in.

Measured against one account, same access token, ideType: "ANTIGRAVITY" fixed:

User-Agent cloudaicompanionProject tier retrieveUserQuotaSummary
(unset) / orca/1.0 / google-api-go-client/0.5 absent — unreachable
Electron's default Chromium UA present standard-tier 403
any UA containing antigravity present free-tier 200

The discriminator was only whether the UA contains the substring antigravity — case, position, and version format made no difference.

The standard-tier row is the tell: the same token resolves to a different product depending on the UA. That fits an account holding both a Code Assist and an Antigravity entitlement, where the server needs the UA to disambiguate — and it predicts that an account holding only the Antigravity entitlement would succeed regardless of UA. I have not been able to test a second account, so treat this as observed here rather than proven for every account: if you cannot reproduce the failure, that is consistent with this reading rather than against it. Pinning the UA is required in the observed case and harmless in the other.

Overriding the UA is already the house pattern here — codex-backend-auth.ts sends codex-cli, claude-oauth-usage-request.ts sends claude-code/2.1.0.

2. The response is grouped, and groups collide. Two groups routinely report identical percentages and reset times, so a percentage+reset dedupe key would silently collapse a whole model family. Buckets stay keyed by group.

Linked Issue

Addresses #11409 — usage stats not refreshing on an SSH VM dev box — for the Antigravity provider. Because this path needs only a credential file, no agy process has to be running or reachable from the client. Other providers remain host-local; this PR does not change them.

Related: #9122 describes the mirror behaviour this replaces. The local-host path there is being handled in #11536 / #16710 via the loopback LanguageServer RPC, and this PR deliberately does not touch that path — it adds no loopback probing, no process discovery, and does not modify antigravity-usage-fetcher.ts (which both of those PRs introduce). The two approaches are complementary: RPC when agy is running locally, credential when it is not.

Also touches #18837 — usage refresh in an SSH workspace overwriting AI Agent visibility with client-side CLI detection. The status-bar change here stops Antigravity from being hidden by that overwrite; the other providers still are, and the runtime-host scoping described in that thread is the general fix.

Overlap I should flag: #8064 changes the same gate in status-bar-agent-gating.ts, generalising it with a hasProviderEvidence parameter so live provider evidence outranks a stale negative detection. That is the same idea as the change here, and the two will conflict textually. They are not redundant, though: Antigravity's evidence cannot be "a successful usage fetch", because the stale-token and network verdicts this PR deliberately surfaces are not successes and would be re-hidden by that rule. Its evidence is usageMetadata.credentialSource — a sign-in was found on some host, whatever happened next. If #8064 lands first I am happy to rebase onto its parameter and pass credentialSource as the Antigravity evidence.

Visual Proof

Same profile, same connected SSH host (ubuntu@…, Linux, agy signed in there), no Gemini CLI sign-in on the client.

Before — Antigravity is absent from the usage panel entirely; the mirror has nothing to mirror.

before

After — Antigravity reports its own four windows, read from the SSH host's credential.

after

Testing

Verified end-to-end against a real signed-in Antigravity account on a remote Linux host (Tailscale, ubuntu@100.x), driving the production modules — not a stub:

parsed credential; expired=false
quota status=ok
  Gemini Models (Weekly)          | used 20% | window 10080m
  Gemini Models (5h)              | used  6% | window   300m
  Claude and GPT models (Weekly)  | used  0% | window 10080m
  Claude and GPT models (5h)      | used  0% | window   300m
session={"usedPercent":6,"windowMinutes":300,...}
weekly={"usedPercent":20,"windowMinutes":10080,...}
  • Platforms exercised: macOS client → Linux SSH host. Windows and Linux clients are unexercised; nothing in the new code is platform-specific (no shell, no child process, no path separators outside path/path.posix).

  • pnpm test src/main/rate-limits/ → 50 files / 520 tests pass.

  • pnpm test (full) → 3 pre-existing environment failures unrelated to this change (claude-tui-resume-real-binary.integration, structured-agent-session-recovery-exits, agent-session-process-identity-probe); they fail identically on main at bf4e270 on this machine.

  • oxlint clean on changed files; check:max-lines-ratchet, check:reliability-gates, check:ts-nocheck-ratchet, check:runtime-electron-ratchet, audit:code-quality:native, audit:code-quality:type-aware all pass.

  • pnpm typecheck / pnpm lint: the only failures are the two pre-existing no-dupe-keys / TS1117 errors in src/main/agent-hooks/first-work-branch-rename.test.ts, present on main and untouched here.

  • I manually tested these changes locally

  • Automated tests added/updated

New tests cover: the User-Agent contract (a regression guard — the request must identify as Antigravity), ideType, host fallback daily → stable, 401 vs 403 vs missing-project verdicts, unread-body cancellation, credential parsing in both encodings, window mapping, group-collision preservation, local-before-remote host precedence, and the unverifiable-vs-absent distinction.

AI Disclosure

Claude Opus 5, used through Claude Code, for the investigation, implementation, and tests. The User-Agent behaviour in the table above was measured against the live endpoint, not inferred by the model. All findings were verified by execution.

Review

Code review summary from the agent, against the dimensions this repo asks for:

  • Cross-platform — no child_process, no shell, no powershell.exe, no native module. Local reads use path.join; remote reads use path.posix.join against a POSIX host. No new EDR-relevant surface (docs/reference/windows-edr-posture.md untouched).
  • SSH / remote / local — resolveFilesystemRouteForHost is the only routing entry point; there is no "fall back to local" branch. A read that fails with a transport verdict (isSshRequestOutcomeUnverifiable) is reported as unchecked, never as a missing sign-in. A target the user has not connected is treated as no evidence either way rather than as an error, so an idle SSH entry cannot turn the provider red. No wire change: RateLimitRuntimeTarget is untouched, so docs/reference/remote-wire-compatibility.md does not apply.
  • Agents / integrations / git providers — additive to one provider; no shared surface changed.
  • Performance — one extra provider in the existing Promise.allSettled cycle. Remote I/O happens only for connected targets (a disconnected one returns without touching the transport), and each remote read is bounded by the relay's own request timeout. The API call carries AbortSignal.timeout(10s) combined with the caller's signal.
  • Security — no OAuth client credentials are embedded; no token refresh is performed, so no client secret is needed at all (see Notes). Credential values are never logged, and only the one well-known agy path is read — this does not scan other applications' data directories.
  • UI — no component or style changes. Existing status-bar and usage-roster rendering, new values.

Agent skill upstream boundary

  • Not applicable.

Notes

Known limitations, stated deliberately:

  • Expired credentials are not refreshed. Orca uses the stored access token while it is valid and otherwise reports that the sign-in on that host expired. This keeps any OAuth client secret out of the source tree; on a dev box where agy is in use it keeps the token current itself.
  • macOS remote hosts are out of reach. agy stores its token in the login keychain there, which a non-GUI SSH session cannot open. Linux and WSL hosts use a plain file and work.
  • Undocumented endpoint. cloudcode-pa.googleapis.com/v1internal:* is internal and may change. Failures degrade to the existing mirror rather than to an error.
  • Host selection is "first host with a usable credential, local before remote". If a multi-host preference is wanted, that seems better as a follow-up once there is a real case for it.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots attached
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • Lint, typecheck, test status reported above

Author

X: @abruptWave

…g SSH hosts

Antigravity usage was a relabelled copy of the Gemini Code Assist read, so it
showed the wrong pools and went unavailable whenever no Gemini CLI sign-in
existed. This adds a real read: the Antigravity CLI's own OAuth credential, the
Antigravity `ideType`, and `retrieveUserQuotaSummary`.

The credential is looked up on this machine first and then on connected SSH
targets, so a dev box where `agy` runs but no Antigravity process is reachable
from this machine now reports its quota. The Gemini mirror stays as the fallback
whenever no direct read succeeds.

Two details the endpoint requires that are easy to miss:

- cloudcode-pa selects the product from the User-Agent, not from the request
  body. Without "antigravity" in it, `loadCodeAssist` still answers 200 but omits
  `cloudaicompanionProject`, and the quota call then fails 403 — which reads as a
  bad sign-in. Electron's default Chromium User-Agent resolves to a different
  product with no quota grant.
- the response is grouped, and two groups routinely report identical percentages
  and reset times, so buckets stay keyed by group rather than being deduplicated.

A remote host that drops mid-read is reported as unchecked rather than as a
missing sign-in; a target that was never connected is not treated as evidence
either way.
@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 751c981e-dbfc-405c-845a-5bf602c203ae

📥 Commits

Reviewing files that changed from the base of the PR and between 8e1ac16 and 904e51c.

📒 Files selected for processing (4)
  • src/renderer/src/components/status-bar/status-bar-agent-gating.test.ts
  • src/renderer/src/components/status-bar/status-bar-agent-gating.ts
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Adds Antigravity OAuth credential parsing and discovery across local and SSH hosts. Adds a Cloud Code quota client with project resolution, quota retrieval, response validation, host fallback, timeout, and cancellation. Converts quota groups into session and weekly rate-limit windows. Integrates Antigravity fetching into refresh cycles and uses Gemini-derived limits when direct results are unavailable. Updates status-bar visibility using PATH detection or Antigravity quota snapshots. Adds coverage for credentials, API behavior, quota conversion, provider outcomes, service integration, and status-bar behavior.

Merge Risk: ⚪ Minimal · up to 904e5

Antigravity usage can now be configured and displayed independently of Gemini CLI OAuth, including remotely sourced quota states. No current merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 36 functions across 28 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately summarizes the primary change: reading Antigravity quota from its own credential, including connected SSH hosts.
Description check ✅ Passed The description is complete and follows the repository template. It explains the change and rationale, links issue #11409, includes visual proof, testing results, AI disclosure, review notes, limitati…
Linked Issues check ✅ Passed The description links the primary issue as #11409 and identifies related issues with clear scope and overlap details.
Out of Scope Changes check ✅ Passed The changes align with the stated objective. New credential, quota, SSH-host, fallback, status-bar, and test changes directly support Antigravity usage reporting.
  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 24644483-5e13-43c8-9cc3-aa183a15e85c

📥 Commits

Reviewing files that changed from the base of the PR and between d53cbed and 9b0e4af.

📒 Files selected for processing (20)
  • src/main/rate-limits/antigravity-cloud-code-api.test.ts
  • src/main/rate-limits/antigravity-cloud-code-api.ts
  • src/main/rate-limits/antigravity-credential-hosts.test.ts
  • src/main/rate-limits/antigravity-credential-hosts.ts
  • src/main/rate-limits/antigravity-oauth-credential.test.ts
  • src/main/rate-limits/antigravity-oauth-credential.ts
  • src/main/rate-limits/antigravity-quota-buckets.test.ts
  • src/main/rate-limits/antigravity-quota-buckets.ts
  • src/main/rate-limits/antigravity-quota-fetch.test.ts
  • src/main/rate-limits/antigravity-quota-fetch.ts
  • src/main/rate-limits/rate-limit-service-test-harness.ts
  • src/main/rate-limits/service-account-target-selection.test.ts
  • src/main/rate-limits/service-antigravity-usage.test.ts
  • src/main/rate-limits/service-inactive-account-previews.test.ts
  • src/main/rate-limits/service-live-claude-usage.test.ts
  • src/main/rate-limits/service-minimax-usage.test.ts
  • src/main/rate-limits/service-refresh-orchestration.test.ts
  • src/main/rate-limits/service-window-activation.test.ts
  • src/main/rate-limits/service/service-full-cycle-application.ts
  • src/main/rate-limits/service/service-full-cycle-preparation.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread src/main/rate-limits/antigravity-cloud-code-api.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The data path is sound and well-tested, but the renderer still hides the Antigravity bar in exactly the scenario this PR targets (a host where only agy exists) — see the first section below.

Reviewed changes

  • Credential parsing (antigravity-oauth-credential.ts) — parses the agy OAuth blob in both plain-JSON and go-keyring-base64: encodings, with NaN-safe expiry handling.
  • Cross-host credential lookup (antigravity-credential-hosts.ts) — local first, then connected SSH targets via resolveFilesystemRouteForHost, mapping transport failures to the unverifiable verdict and skipping unconnected/idle targets.
  • Two-step Cloud Code call (antigravity-cloud-code-api.ts) — loadCodeAssist + retrieveUserQuotaSummary against the daily/stable hosts, with the Antigravity User-Agent and daily→stable fallback.
  • Bucket mapping (antigravity-quota-buckets.ts) — group-keyed buckets, window → windowMinutes, and per-window "most constrained" summary.
  • Service integration — a 7th Promise.allSettled provider; the Gemini mirror remains the fallback whenever the direct read is not ok.

⚠️ Status-bar visibility still gates Antigravity on local CLI detection and the Gemini opt-in

The PR's headline case is a dev box where agy exists only on the SSH host, with no local sign-in. But the status-bar slot is CLI-gated on local PATH detection (isStatusBarItemAvailable('antigravity', detectedAgentIds), status-bar-agent-gating.ts:15, consumed at use-status-bar-controller.ts:141), and the toggle itself is hidden by the same check (StatusBarVisibilityMenu.tsx:77). A client with no agy on its own PATH gets a detectedAgentIds that lacks antigravity, so showAntigravity is false and the bar never renders — even when the direct read succeeds. The same files still carry "the Antigravity snapshot mirrors the Gemini fetch" comments and tie the slot to geminiCliOAuthEnabled (status-bar-provider-visibility.ts:105), which are no longer accurate.

Technical details
# Antigravity bar hidden for SSH-only hosts

## Affected sites
- src/renderer/src/components/status-bar/status-bar-agent-gating.ts:15 — `'antigravity'` is in `CLI_GATED_ITEMS`; `isStatusBarItemAvailable` returns `false` unless `agy` is detected on the local PATH.
- src/renderer/src/components/status-bar/use-status-bar-controller.ts:141 — `showAntigravity` ANDs that check into visibility.
- src/renderer/src/components/status-bar/status-bar-provider-visibility.ts:105 — `hasUsageProviderSettingsForProvider` ties the durable slot to `geminiCliOAuthEnabled`; the "mirrors the Gemini fetch" comments are now stale.
- src/renderer/src/components/status-bar/StatusBarVisibilityMenu.tsx:77 — the toggle is hidden when detection misses `antigravity`.

## Required outcome
- The Antigravity bar can appear when `agy` exists only on a connected SSH host (or a credential file exists with no local CLI), i.e. a successful credential-based read must not be suppressed by the local-CLI gate. Decide whether to drop the CLI gate for Antigravity, detect it from credential presence, or explicitly document that a local `agy` remains a requirement.
- Update the now-incorrect "mirrors the Gemini fetch" comments and the `geminiCliOAuthEnabled` coupling so the durable-signal logic reflects the new direct-read source.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Comment thread src/main/rate-limits/service/service-full-cycle-application.ts Outdated
…g Antigravity

Review follow-ups.

Host fallback never worked for the failure it existed for. `fetchFromHost` did
not catch a rejected request, so a network-level failure on the daily host left
`fetchAntigravityQuota` before the stable host was tried, and both hosts shared
one `AbortSignal.timeout`, so a slow first host handed the fallback a signal that
had already fired. Each host now gets its own deadline and its own catch, and a
caller abort stops the loop instead of burning a second attempt.

The direct read's verdicts were unreachable. Every non-`ok` result was replaced
by the Gemini mirror, so "the sign-in on <host> has expired" and "the remote host
did not answer" could never be shown — including the `unverifiable` distinction
this path exists to preserve. Those two verdicts now survive the fallback; no
sign-in anywhere, no Antigravity grant, and transient endpoint failures still
degrade to the mirror, which is the only case where it has something to say.

The status bar hid Antigravity in exactly the case this targets. The slot was
gated on local PATH detection, so a client with no `agy` of its own never
rendered the bar even when the remote read succeeded, and the slot additionally
required the Gemini CLI opt-in because the snapshot used to mirror that fetch.
A snapshot carrying quota now counts as availability on its own, and the Gemini
opt-in is no longer a precondition.
@abruption

Copy link
Copy Markdown
Contributor Author

Thanks — three real findings, all confirmed and fixed in 8e1ac16.

1. Host fallback never worked for the failure it existed for (@coderabbitai)

Correct, and worse than the comment says — there were two defects stacked:

  • fetchFromHost did not catch a rejected request, so a network-level failure on the daily host exited fetchAntigravityQuota before cloudcode-pa.googleapis.com was ever tried.
  • Both hosts shared one AbortSignal.timeout, so a slow daily host spent the budget and handed the fallback a signal that had already fired — the fallback died precisely when it was needed.

Each host now gets its own deadline and its own catch, and a caller abort stops the loop rather than burning a second attempt. My original test only covered an HTTP 500 response, never a thrown one, which is why it passed; there are now tests for outright rejection, for the per-host deadline being distinct, and for the caller-abort case.

2. Direct-read verdicts were unreachable (@pullfrog)

Also correct, and it made the part of this PR I argued hardest for into dead code. Answering the open question directly: the mirror should not win over a verdict about the user's own Antigravity sign-in.

resolveAntigravityRateLimits now splits it:

  • Surfaces — stale-token (expired or rejected sign-in) and network (remote host did not answer). Both carry their own remedy, and the second is the unverifiable distinction docs/reference/ssh-execution-boundary.md asks for; overwriting it with Gemini's numbers would imply an absence that was never established.
  • Falls back to the mirror — no sign-in on any host, an account with no Antigravity grant, and transient endpoint failures. These are the only cases where the shared-pool mirror still has something to say, and a flaky endpoint should not repaint a working bar every poll.

3. Status bar hid Antigravity in exactly the targeted case (@pullfrog)

This one would have shipped the feature with its headline case broken, and I could not have seen it locally — agy is on my client's PATH, which is why the screenshots rendered at all.

  • isAntigravityStatusBarAvailable now treats a snapshot that actually carries quota as availability, so a client with no local agy still renders the bar when the remote read succeeds. PATH detection remains the signal when there is no reading yet, so the "fresh Ubuntu, no CLI" noise the gate was added for stays suppressed.
  • The Gemini opt-in is no longer a precondition. That gate and its comment described the mirror, which is no longer how this provider is read. The stale comments in status-bar-provider-visibility.ts and use-status-bar-controller.ts are updated, and the three existing tests that encoded the old contract are rewritten rather than deleted.
  • The visibility toggle uses the same flag, so a user who turned the item off can turn it back on without local agy.

Not addressed: docstring coverage

Leaving this one. AGENTS.md asks for the opposite — "Concise/Brief Non-obvious Comments ONLY … DO NOT be verbose, explain the obvious … BE CONCISE. 1 LINE if possible" — and the surrounding src/main/rate-limits/ files run about 2% comment density with no per-function docs. Raising these files to 80% would make them inconsistent with everything around them. Happy to add docs to specific functions if a maintainer wants them.

Verification

pnpm test src/main/rate-limits/ src/renderer/src/components/status-bar/ → 100 files / 870 tests pass. tc:web clean; tc:node shows only the two pre-existing TS1117 errors in src/main/agent-hooks/first-work-branch-rename.test.ts, which are on main and untouched here. oxlint, check:max-lines-ratchet, check:reliability-gates, and both audit:code-quality:* pass.

@abruption

Copy link
Copy Markdown
Contributor Author

Softened the User-Agent claim in the description — no code change.

The previous wording stated the UA behaviour as a property of the endpoint. It is one account's measurement, and I had left the most interesting row unexplained: the same token resolves to standard-tier under Electron's Chromium UA and free-tier under an antigravity one. That reads less like a gate and more like product disambiguation on an account that holds both a Code Assist and an Antigravity entitlement — which would predict that an account holding only the Antigravity entitlement succeeds regardless of UA.

I have no second account to test that on, so the description now says so plainly, and notes that failing to reproduce the 403 is consistent with that reading rather than evidence against it. The change this PR makes is unaffected either way: pinning the UA is required in the case I can measure and harmless in the case I cannot.

If a reviewer with an Antigravity-only account is willing to run loadCodeAssist once with and once without an antigravity UA and compare cloudaicompanionProject, that would settle it — but nothing in the diff depends on the answer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/src/components/status-bar/status-bar-provider-visibility.ts (1)

76-80: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include antigravityUsageConfigured in the aggregate setting check.

When antigravityUsageConfigured is true and geminiCliOAuthEnabled is false, hasUsageProviderSettings returns false. This contradicts the Antigravity-specific rule at Lines 104-106. Aggregate callers can classify a valid Antigravity setup as absent.

Proposed fix
     settings?.geminiCliOAuthEnabled === true ||
     Boolean(settings?.opencodeSessionCookie?.trim()) ||
-    // Antigravity's durable signal requires geminiCliOAuthEnabled, so it is
-    // already covered by the gemini term above.
+    settings?.antigravityUsageConfigured === true ||
     settings?.minimaxCookieConfigured === true ||

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: efd4f15e-b1aa-4efa-9211-aa56e16f5f06

📥 Commits

Reviewing files that changed from the base of the PR and between 9b0e4af and 8e1ac16.

📒 Files selected for processing (11)
  • src/main/rate-limits/antigravity-cloud-code-api.test.ts
  • src/main/rate-limits/antigravity-cloud-code-api.ts
  • src/main/rate-limits/antigravity-usage-mirror.test.ts
  • src/main/rate-limits/antigravity-usage-mirror.ts
  • src/main/rate-limits/service/service-full-cycle-application.ts
  • src/renderer/src/components/status-bar/StatusBarVisibilityMenu.tsx
  • src/renderer/src/components/status-bar/status-bar-agent-gating.test.ts
  • src/renderer/src/components/status-bar/status-bar-agent-gating.ts
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.ts
  • src/renderer/src/components/status-bar/use-status-bar-controller.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/rate-limits/antigravity-cloud-code-api.ts
  • src/main/rate-limits/antigravity-cloud-code-api.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@abruption Thanks for the detailed confirmation.

The host fallback fix addresses the reported failure modes. Separate per-host timeouts and caller-abort short-circuiting are the correct behavior. The added rejection, deadline-isolation, and abort tests cover the previously missing cases.

I agree that broad docstring coverage would conflict with the documented repository convention. Concise comments for non-obvious behavior are sufficient here.

🐇 ✅


✏️ Learnings added
Learnt from: abruption
URL: https://github.com/stablyai/orca/pull/19209

Timestamp: 2026-09-07T05:49:34.952Z
Learning: In this repository, `AGENTS.md` requires concise comments only for non-obvious behavior. Do not request broad per-function docstring coverage in `src/main/rate-limits/` unless a maintainer requests documentation for specific functions.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

The renderer still hides Antigravity for SSH-only hosts once the credential leaves the ok state — see the inline comment. The rest of this delta is sound.

Reviewed changes

  • Surfaced terminal verdicts — resolveAntigravityRateLimits now returns the direct read for stale-token/network instead of letting the Gemini mirror overwrite them, so an expired/rejected sign-in and a lost remote host are no longer silently republished as the shared Code Assist pool.
  • Per-host deadlines — fetchAntigravityQuota builds a fresh timeout per Cloud Code host and catches rejected requests, so the daily host's failure no longer spends the stable host's budget.
  • Decoupled the slot from the local CLI and the Gemini opt-in — isAntigravityStatusBarAvailable/antigravitySlotAvailable let a snapshot carrying quota (or a detected agy) keep the bar and its toggle, and hasUsageProviderSettingsForProvider drops the geminiCliOAuthEnabled precondition.

ℹ️ Nitpicks

  • hasUsageProviderSettings in src/renderer/src/components/status-bar/status-bar-provider-visibility.ts and the matching test comment in status-bar-provider-visibility.test.ts still say Antigravity's durable signal "requires geminiCliOAuthEnabled" — stale now that the Gemini opt-in is no longer a precondition.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Comment thread src/renderer/src/components/status-bar/status-bar-agent-gating.ts Outdated
… its host drops

Two fixes from the last round cancelled each other out. The direct read's
`stale-token` and `network` verdicts are surfaced on purpose, but the status-bar
slot was gated on `status === 'ok'`, so on a client with no local `agy` the bar
disappeared the moment either verdict landed — burying the message that explains
what to do about it, in exactly the case this targets.

Availability is now a named credential source rather than a healthy status: it
marks that a sign-in was found on some host, which is what makes the slot worth
showing whatever state that sign-in is in. No sign-in anywhere still hides the
bar, and the Gemini mirror names no source, so mirror-only users keep the
local-CLI gate they had before.

Also aligns `hasUsageProviderSettings` with the per-provider rule it had drifted
from: a checked Antigravity item no longer needs the Gemini opt-in to count as
configured usage, so the setup CTA does not contradict a live Antigravity bar.
@abruption

Copy link
Copy Markdown
Contributor Author

904e51c6 — both of these are right, and the first one was self-inflicted: the two fixes from the last round cancelled each other out.

Surfaced verdicts were re-hidden for SSH-only hosts

resolveAntigravityRateLimits keeps stale-token and network on purpose, and then isAntigravityStatusBarAvailable gated the slot on status === 'ok' — so on a client with no local agy the bar vanished the moment either verdict landed, burying the message that says what to do. The two changes only looked correct in isolation.

Answering the open question: the bar should stay visible and show the expiry message. A sign-in that exists but has expired is not the same as no sign-in, and the remedy ("run the Antigravity CLI on that host") is only reachable if the bar renders.

Availability is now a named credential source rather than a healthy status:

isStatusBarItemAvailable('antigravity', detectedAgentIds) ||
  Boolean(antigravity?.usageMetadata?.credentialSource)

credentialSource is set exactly when a sign-in was located on some host, so it covers ok, stale-token, and network alike. missing-credentials sets no source and still hides the bar, which keeps the "fresh machine, no CLI" noise suppressed. The Gemini mirror names no source either, so mirror-only users keep the local-CLI gate they had before this PR — the widening applies only to a real direct read.

Stale aggregate rule

Caught by @coderabbitai as well, and it was a genuine contradiction rather than only a stale comment: hasUsageProviderSettings still folded Antigravity into the geminiCliOAuthEnabled term, so with the Gemini opt-in off it answered "no usage configured" while hasUsageProviderSettingsForProvider('antigravity', …) answered "configured". isUsageEmptyState reads the aggregate, so a user set up only for Antigravity would have been shown the setup CTA next to a live Antigravity bar. Both functions now agree, and the test comment that encoded the old rule is rewritten rather than dropped.

pnpm test src/renderer/src/components/status-bar/ → 50 files / 340 tests pass; tc:web and oxlint clean.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ All three prior findings are addressed cleanly; one rough edge remains in how the surfaced verdicts meet the status-bar gate.

Reviewed changes

  • Gave each Cloud Code host its own attempt — fetchAntigravityQuota now builds a fresh deadline per host and catches a rejected request as that host's verdict, so the daily→stable fallback actually runs on a network failure, and a caller abort stops the loop instead of burning a second attempt.
  • Made the mirror fallback verdict-aware — resolveAntigravityRateLimits surfaces stale-token and network verdicts and only falls back to the Gemini mirror for missing-sign-in, no-grant, and transient failures.
  • Stopped gating the Antigravity bar on the Gemini opt-in and local CLI — isAntigravityStatusBarAvailable treats an ok snapshot as availability and is threaded through the visibility toggle and showAntigravity; hasUsageProviderSettingsForProvider drops the geminiCliOAuthEnabled coupling.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Comment thread src/renderer/src/components/status-bar/status-bar-agent-gating.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

  • Keyed Antigravity slot availability on the credential source — isAntigravityStatusBarAvailable now treats usageMetadata.credentialSource (rather than status === 'ok') as proof a sign-in was found, so the surfaced stale-token and network verdicts keep the bar visible for clients without a local agy.
  • Made the checked item durable on its own — hasUsageProviderSettings now counts antigravityUsageConfigured, matching hasUsageProviderSettingsForProvider and dropping the stale Gemini-opt-in comment.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@abruption Thanks for the detailed fix.

The credentialSource gate preserves the Antigravity status bar for direct ok, stale-token, and network results. It still hides the status bar when no credential exists. This matches the required SSH-only behavior.

The aggregate configuration rule now matches the provider-specific rule. A configured Antigravity-only user will not see an incorrect setup CTA.

The focused test, type-check, and lint results support the changes.

🐇 ✅


✏️ Learnings added
Learnt from: abruption
URL: https://github.com/stablyai/orca/pull/19209

Timestamp: 2026-09-07T06:00:03.805Z
Learning: For Antigravity status-bar visibility in `src/renderer/src/components/status-bar/status-bar-agent-gating.ts`, `isAntigravityStatusBarAvailable` must return true when either the Antigravity CLI is detected locally or `antigravity.usageMetadata.credentialSource` exists. A credential source represents a direct credential found on a local or SSH host and keeps the status bar visible for `ok`, `stale-token`, and `network` outcomes. Mirror-only and `missing-credentials` results have no credential source and remain subject to local CLI detection.

Learnt from: abruption
URL: https://github.com/stablyai/orca/pull/19209

Timestamp: 2026-09-07T06:00:03.805Z
Learning: In `src/renderer/src/components/status-bar/status-bar-provider-visibility.ts`, Antigravity configuration must be evaluated consistently by both `hasUsageProviderSettings` and `hasUsageProviderSettingsForProvider`. `antigravityUsageConfigured` alone establishes Antigravity configuration; it must not depend on `geminiCliOAuthEnabled`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: abruption
URL: https://github.com/stablyai/orca/pull/19209

Timestamp: 2026-09-07T05:49:34.962Z
Learning: In this repository, `AGENTS.md` requires concise comments only for non-obvious behavior. Do not request broad per-function docstring coverage in `src/main/rate-limits/` unless a maintainer requests documentation for specific functions.

You are interacting with an AI system.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

  • Slot gating now keys on a named credential source — isAntigravityStatusBarAvailable admits the Antigravity slot when usageMetadata.credentialSource is present rather than only when status === 'ok', so the surfaced stale-token/network verdicts (and a plain ok) keep the bar visible for SSH-only clients, while the Gemini mirror — which never names a source — cannot widen the slot.
  • Dropped the Gemini opt-in precondition for the durable signal — hasUsageProviderSettingsForProvider('antigravity') and the aggregate hasUsageProviderSettings now treat a checked Antigravity item as durable on its own, and the stale "mirrors the Gemini fetch"/"requires geminiCliOAuthEnabled" comments are gone.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@abruption

Copy link
Copy Markdown
Contributor Author

Flagging an overlap I missed when I opened this, plus one more related issue — description updated, no code change.

#8064 changes the same gate in status-bar-agent-gating.ts that this PR touches, generalising isStatusBarItemAvailable with a hasProviderEvidence parameter so live provider evidence outranks a stale negative PATH detection. That is the same idea as the change here and the two will conflict textually. I should have found it before opening this; it has been open since August.

They are not redundant. Antigravity's evidence cannot be "a successful usage fetch": the stale-token and network verdicts this PR deliberately surfaces are not successes, and a success-shaped evidence rule would re-hide the bar exactly when those messages need to render — the bug @pullfrog caught in 8e1ac16 and I fixed in 904e51c6. So Antigravity keys on usageMetadata.credentialSource instead: a sign-in was found on some host, whatever happened next.

If #8064 lands first, I am happy to rebase onto its parameter and pass credentialSource as the Antigravity evidence rather than keeping a separate helper. Whichever order suits the maintainers.

Also linked #18837 — usage refresh in an SSH workspace overwriting AI Agent visibility with client-side detection. The status-bar change here keeps Antigravity from being hidden by that overwrite, but only Antigravity; the runtime-host scoping @AmethystLiang describes there is the general fix, and the two are compatible.

@pullfrog

pullfrog Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

No action needed. This comment is the PR author's own note announcing a description-only update (explicitly "no code change"): it flags the textual overlap with #8064 in status-bar-agent-gating.ts, explains why Antigravity keys on usageMetadata.credentialSource rather than a success-shaped evidence rule, and links #18837. I confirmed the PR description already carries both the "Overlap I should flag: #8064" section and the #18837 reference, and the working tree is clean — nothing to edit or commit.

Pullfrog  | View workflow run | via Pullfrog | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@abruption

Copy link
Copy Markdown
Contributor Author

Windows credential gap — acknowledged, no regression

A report on #16710 notes that agy 1.1.27 on Windows stores its OAuth token in Credential Manager (gemini:antigravity) rather than in a filesystem path. There is no ~/.gemini/antigravity-cli/antigravity-oauth-token file on that platform.

This PR's file-based reader will return "missing-credentials" on Windows and fall back to the Gemini mirror — identical to today's behaviour, so there is no regression. Reading from Windows Credential Manager is a possible follow-up but would expand scope here.

Adding to the known limitations in the description:

  • macOS remote hosts: login keychain inaccessible from non-GUI SSH sessions
  • Windows local: token lives in Credential Manager, not a file — graceful fallback to mirror

Also worth noting from the same report: User-Agent made no difference on a Google AI Pro (g1-pro-tier) account, which is consistent with the hedged framing already in this PR's description — the UA behaviour was observed on one account and may not apply to single-entitlement accounts.

@nwparker

Copy link
Copy Markdown
Contributor

Review verdict: Do not merge as-is; superseded by #21654 → #21678. This PR routes quota through Antigravity credential/cloud-code endpoints, while the maintained migration uses the installed CLI contract and explicit host boundaries. SSH/remote quota remains a separate follow-up, not a reason to revive this path.

This branch has not been deployed

No deployments
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.

2 participants