Skip to content

fix(usage): fetch native Antigravity quotas via Agy - #15948

Open
mikeascendx wants to merge 1 commit into
stablyai:mainfrom
mikeascendx:fix/native-antigravity-cli-usage
Open

mikeascendx wants to merge 1 commit into
stablyai:mainfrom
mikeascendx:fix/native-antigravity-cli-usage

Conversation

@mikeascendx

Copy link
Copy Markdown

ELI5

Orca currently labels a copy of Gemini CLI usage as Antigravity usage. This change asks the installed Antigravity CLI for its own quota report instead, so Orca shows the real Gemini and Claude/GPT five-hour and weekly limits without needing Gemini CLI OAuth.

What Changed

  • Added an Antigravity usage fetcher that runs the public Agy print-mode interface:

    agy -p /usage --output-format json --print-timeout 20s
    
  • Strictly parses the four native Agy quota identities: Gemini 5h/weekly and Claude/GPT 5h/weekly.

  • Converts each remaining_fraction into Orca's used percentage and preserves each independent reset timestamp.

  • Fetches Antigravity independently from Gemini in the rate-limit service.

  • Removes Gemini OAuth as a visibility prerequisite for the Antigravity usage row.

  • Keeps the existing Gemini usage integration unchanged for users who still configure it.

  • Uses the tightest five-hour bucket as the existing compact/session summary while Detailed mode renders all four named buckets.

Why

The current implementation copies the Gemini provider snapshot and changes only its provider ID, so the Antigravity row can show the wrong product's quotas or fail when legacy Gemini OAuth credentials are absent.

Agy 1.1.18 exposes its native usage payload through public non-interactive print mode. This is smaller and less coupled than the older open implementations in #11536, #12095, and #14571: it does not inspect credentials or keychains and does not discover or call a private local LanguageServer through logs, process tables, ports, TLS exceptions, or CSRF tokens.

The live command completed in about 6.5 seconds on Windows, reported zero input/output tokens, and did not change Agy history or create a conversation. Orca bounds it with cancellation, a 25-second process timeout, and a 1 MiB output cap.

Linked Issue

Fixes #9122

Visual Proof

N/A — no layout or interaction component changed. The existing named-bucket renderer is reused; a render regression test verifies all four Antigravity labels and percentages are present.

Testing

  • I manually tested these changes locally
  • Automated tests added/updated

Validated on Windows 11 with Antigravity CLI 1.1.18:

  • agy -p /usage --output-format json --print-timeout 1m: success, two groups/four buckets, zero model tokens, no history/conversation mutation
  • Focused Vitest: 4 files / 64 tests passed
  • pnpm typecheck: passed
  • Changed-file oxlint: passed
  • Full lint source-quality, type-aware quality, reliability, and max-lines stages: passed
  • Localization catalog, extraction, and coverage checks: passed
  • pnpm run build:desktop: passed
  • pnpm run build:native: passed
  • Standard pnpm test preflight is unavailable on this machine because the optional windows-native-registry package requires a Visual Studio C++ toolchain. A direct aggregate Vitest attempt encountered unrelated existing Windows failures in symlink, watcher, SSH, relay, and daemon suites; the affected Antigravity suites pass.
  • Full pnpm lint reaches the existing generated-skill-manifest check, which reports stale skill artifacts on current main; this PR does not touch skill artifacts.

AI Disclosure

OpenAI Codex (GPT-5) assisted with implementation, tests, validation, and review.

Review

  • Cross-platform: resolves agy through Orca's shared CLI resolver and starts it through the shared no-shell process wrapper, including Windows .cmd/.bat handling and hidden-console behavior.
  • SSH/remote/local: usage remains explicitly host-local, matching the current host-level Antigravity provider boundary; it does not silently run against or infer state from a remote SSH workspace.
  • Agent/integration compatibility: Gemini agent support and the existing Gemini usage provider remain unchanged. Unknown future Agy buckets are ignored while recognized buckets remain usable.
  • Performance: one cancellable process runs alongside other provider fetches, with a 20-second Agy deadline, 25-second hard process timeout, and bounded output.
  • UI: reuses the existing provider and named-bucket components; Detailed mode shows four independent buckets and compact/session surfaces retain one summary.
  • Security: fixed argument array, no shell interpolation, no credential/keychain reads, no account identifiers logged, strict JSON shape checks, and known bucket allowlisting.

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

Author X handle: @mikeascendx

The CLI-backed approach requires an Agy version that supports structured print-mode slash-command output. Older versions fail safely as unavailable/parse errors rather than falling back to Gemini data.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build all pass (see Testing for passed checks and environment/baseline blockers)

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Added Antigravity usage parsing for four quota identities. Added agy CLI fetching with timeout, output limits, abort support, and classified failures. Integrated independent Antigravity refresh state into the rate-limit service. Updated test harnesses and provider mocks. Changed Antigravity visibility to depend on its own configuration flag. Added status-bar coverage for rendering all four quota buckets.

Merge Risk: 🔵 Low · up to 01127

The change can display incorrect Antigravity quota percentages if the CLI returns an invalid fraction outside the expected range. This is a bounded parsing risk that is mergeable with explicit owner awareness and follow-up to reject invalid values; the remaining test suggestion is non-blocking.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 15 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 clearly summarizes the primary change: fetching native Antigravity quotas through the Agy CLI.
Description check ✅ Passed The description covers the required sections, explains the change and rationale, documents testing, and records known validation limitations.
Linked Issues check ✅ Passed The changes satisfy #9122 by fetching native Agy quotas, separating Antigravity from Gemini, and reporting independent quota pools and reset times.
Out of Scope Changes check ✅ Passed The implementation and test changes support native Antigravity quota fetching, independent service state, visibility, and quota rendering without unrelated scope.

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

🧹 Nitpick comments (1)
src/renderer/src/components/status-bar/UsageRosterPanel.test.tsx (1)

251-286: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the rendered percentage values.

The test checks bucket count and labels only. It does not verify the rendered 2%, 9%, 0%, and 1% values. Add assertions for these values so incorrect quota percentage rendering fails the test.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fb1007f6-0e05-4e7e-8983-9f5f642f1b64

📥 Commits

Reviewing files that changed from the base of the PR and between 5651662 and 01127da.

📒 Files selected for processing (15)
  • src/main/rate-limits/antigravity-usage-fetcher.test.ts
  • src/main/rate-limits/antigravity-usage-fetcher.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-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.ts
  • src/renderer/src/components/status-bar/UsageRosterPanel.test.tsx
  • src/renderer/src/components/status-bar/UsageRosterPanel.tsx
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.test.ts
  • src/renderer/src/components/status-bar/status-bar-provider-visibility.ts
  • src/shared/rate-limit-types.ts

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

Comment on lines +46 to +58
if (typeof value.remaining_fraction !== 'number' || !Number.isFinite(value.remaining_fraction)) {
return null
}
return {
id: value.id as AgyBucketId,
remainingFraction: value.remaining_fraction,
resetTime: parseResetTime(value.reset_time)
}
}

function toRateLimitBucket(bucket: AgyUsageBucket): RateLimitBucket {
const definition = AGY_BUCKETS[bucket.id]
const remainingFraction = Math.min(1, Math.max(0, bucket.remainingFraction))

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject out-of-range quota fractions.

Line 46 accepts values below 0 and above 1. Line 58 then converts them into valid-looking 100% or 0% usage values. Reject these values in parseAgyBucket so malformed CLI output cannot report fabricated quota data. Add tests for negative and greater-than-one fractions.

Proposed fix
-  if (typeof value.remaining_fraction !== 'number' || !Number.isFinite(value.remaining_fraction)) {
+  if (
+    typeof value.remaining_fraction !== 'number' ||
+    !Number.isFinite(value.remaining_fraction) ||
+    value.remaining_fraction < 0 ||
+    value.remaining_fraction > 1
+  ) {
     return null
   }
@@
-  const remainingFraction = Math.min(1, Math.max(0, bucket.remainingFraction))
+  const remainingFraction = bucket.remainingFraction
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (typeof value.remaining_fraction !== 'number' || !Number.isFinite(value.remaining_fraction)) {
return null
}
return {
id: value.id as AgyBucketId,
remainingFraction: value.remaining_fraction,
resetTime: parseResetTime(value.reset_time)
}
}
function toRateLimitBucket(bucket: AgyUsageBucket): RateLimitBucket {
const definition = AGY_BUCKETS[bucket.id]
const remainingFraction = Math.min(1, Math.max(0, bucket.remainingFraction))
if (
typeof value.remaining_fraction !== 'number' ||
!Number.isFinite(value.remaining_fraction) ||
value.remaining_fraction < 0 ||
value.remaining_fraction > 1
) {
return null
}
return {
id: value.id as AgyBucketId,
remainingFraction: value.remaining_fraction,
resetTime: parseResetTime(value.reset_time)
}
}
function toRateLimitBucket(bucket: AgyUsageBucket): RateLimitBucket {
const definition = AGY_BUCKETS[bucket.id]
const remainingFraction = bucket.remainingFraction

@nwparker

Copy link
Copy Markdown
Contributor

Review verdict: Do not merge as-is; superseded by #21654 → #21678. Its native CLI quota path is an earlier implementation without the current supported-version and configured-executable contract. Use the maintained stack for quota work.

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.

[Bug]: Antigravity usage is a copy of the Gemini quota — wrong pools, and refresh breaks entirely without a local Gemini CLI install

2 participants