Skip to content

fix(rate-limits): read real Antigravity quota from the agy CLI, not the Gemini mirror - #24073

Merged
nwparker merged 4 commits into
mainfrom
nwparker/agy-real-quota
Oct 1, 2026
Merged

nwparker merged 4 commits into
mainfrom
nwparker/agy-real-quota

Conversation

@nwparker

@nwparker nwparker commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

ELI5

Orca's "Antigravity" usage number was never Antigravity's. It was the Gemini CLI's quota with the label changed. This asks the Antigravity CLI itself, which is the only thing that can answer.

What Changed

Before: the main process built the Antigravity reading out of the Gemini one — literally { ...gemini, provider: 'antigravity' } behind a small wrapper. The user saw Gemini CLI per-model buckets ("Pro", "Flash", …) on a 60-minute window, the weekly limit was always blank, and if @google/gemini-cli was not installed the segment said "Token refresh failed" forever.

After: Orca runs agy -p "/usage" --output-format json on the machine that owns execution and publishes what agy reports — one named pool per model group, on the window agy actually meters.

The mechanism:

  • antigravity-usage-response.ts parses agy's print-mode envelope (command.data.groups[].buckets[]). It reads command.data, not the human-readable response text, because the text rounds the fraction to a whole percent and drops both the bucket ids and disabled.
  • agy's window is mapped to Orca's minute counts (weekly → 10080, 5h → 300). An unrecognised window stays a named bucket with no duration rather than being forced into one of the two, so a future agy window cannot silently be drawn as a weekly limit.
  • A disabled bucket is dropped instead of being drawn as 0% used — Antigravity (agy) quota shown incorrectly — weekly limit never displayed (reads a 60-min per-model window) #22511 hit an account whose gemini-5h pool was disabled while gemini-weekly was exhausted, and showing the disabled pool would report headroom the account does not have.
  • Each window is summarised by its most constrained group, because the tier is out of Antigravity when either group is out.
  • antigravity-usage-fetcher.ts resolves agy with the existing resolveCommandOnLocalPath (no which/where subprocess) against the login-shell PATH, spawns it through runProcess, and classifies the outcome: absent CLI and signed-out are unavailable with an actionable message; timeout and unreadable payload are error.
  • The fetch runs on its own promise, like Grok/Cursor/Zcode, so its ~2.5 s does not lengthen every other provider's cycle.
  • deriveSessionSummary moved out of gemini-bucket-formatting.ts into a provider-neutral rate-limit-bucket-summary.ts; both providers now share one implementation.
  • The Gemini mirror module is deleted.

Why

agy keeps its Google credential in the OS keyring (macOS: service gemini, account antigravity) and mints its own token against daily-cloudcode-pa. Nothing outside agy can authenticate its quota endpoint, so the CLI is the source of truth rather than a convenience.

Alternatives I investigated and rejected:

  • Reading the keychain item and calling the quota API directly. Needs Orca to reimplement agy's token exchange and per-platform credential wrappers, and breaks whenever agy rotates either. The credential is also platform-specific, so it needs three backends before it works anywhere but macOS.
  • Talking to agy's embedded language server. It exists (Language server listening on random port at 60441 for HTTPS (gRPC)) but the port is random per run and there is no server while agy is not running, so a poll on a cadence has nothing to talk to most of the time.
  • Appending the quota flags to the user's configured Antigravity launch command. A launch command may be a wrapper script, carry its own flags, or be a shell pipeline; appending -p /usage to that runs the wrong program or puts the slash command in the wrong argv slot. The fetch resolves the plain executable itself.

The quota-spend guard. In print mode an unrecognised slash command is not an error — agy sends the text to the model as an ordinary prompt. So on a build of agy that does not know /usage, a 15-minute poll would quietly start a conversation and spend the user's quota every cycle, while Orca reported "did not report a quota" the whole time. The envelope distinguishes the two cases: a command reply carries an empty conversation_id and num_turns: 0; a prompt carries a conversation id and at least one turn. When Orca sees a turn, it latches Antigravity usage as unsupported and never probes again for the life of the process — because the evidence is the cost, and rediscovering it on a cadence would keep paying for the same answer. A successful parse is checked first, so a real reading can never trip the latch.

I chose a runtime capability check over a version floor because I could not source when /usage was added — agy's changelog shows it being improved around 1.2.6 but not introduced. A guard that detects the actual failure needs no version table and cannot go stale. (#23426 took the version-gate route, which is what prompted this.)

One thing worth flagging for reviewers: do not add --disable-slash-commands to this invocation. It looks like a hardening flag and it is the opposite — it stops agy expanding /usage as a command, so the text is sent to the model as an ordinary prompt. The call then starts a conversation, spends quota, and on an account near its limit returns RESOURCE_EXHAUSTED (429) instead of a reading. I did this by accident while validating and burned a real weekly pool; the comment in antigravity-usage-command.ts and the opt-in real-CLI suite both exist to stop it happening again.

Linked Issue

Fixes #9122
Fixes #22511
Fixes #14515

Unblocks the data source that #16704 and #21071 need. Related #17345, #19587 (Accounts surface, separate change).

Three earlier reports of the same root cause were closed without the data source being fixed, and this is what they were describing — worth reopening or closing as duplicates of the above: #19561 ("Usage unavailable"), #14227 ("Refresh failed — missing usage fetcher"), #7809 (segment disappears while the toggle is on).

Visual Proof

No renderer change — the status bar and Accounts pane render ProviderRateLimits as they already did; this PR changes what that record contains. The published record, read from a live agy 1.2.11 on macOS with one pool genuinely exhausted:

{
  "status": "ok",
  "session": null,
  "weekly": { "usedPercent": 100, "windowMinutes": 10080, "resetsAt": 1791361098000 },
  "buckets": [
    { "name": "Gemini Models",         "usedPercent": 100, "windowMinutes": 10080 },
    { "name": "Claude and GPT models", "usedPercent": 0,   "windowMinutes": 10080 }
  ],
  "meta": { "source": "cli", "credentialSource": "antigravity-cli" }
}

That is the #22511 account state: one group out of weekly quota, the other untouched. Before this change the same account reported weekly: null and a list of Gemini CLI model buckets, so a user would route work to Antigravity and hit a 429 mid-task.

Testing

Validated against the real CLI, not a remembered payload — agy 1.2.11, macOS arm64:

  • agy -p "/usage" --output-format json and agy -p "/quota" … both answer as command.name: "usage"; the envelope reports num_turns: 0, every token counter 0, and an empty conversation_id, so a poll spends no quota and starts no conversation.
  • Cost measured over three consecutive warm runs: 2.39 s / 2.10 s / 2.56 s. That is why the fetch sits on its own promise.
  • Captured both a full pool (remaining_fraction: 1) and an exhausted one (remaining_fraction: 0) and confirmed the published record for each.
  • ORCA_REAL_AGY_CLI_TEST=1 pnpm test src/main/rate-limits/antigravity-usage-real-cli.test.ts — passes against the live CLI (opt-in, off by default, same shape as the existing real-Claude-CLI suite). This is the check that caught the --disable-slash-commands mistake.
  • pnpm test src/main/rate-limits/ — all passing. 35 new tests across the parser and fetcher, including the verbatim envelope agy produced when it answered /usage as a prompt, including the verbatim agy 1.2.11 payload as a fixture.
  • pnpm tc:node, oxlint, pnpm run check:code-quality:changed — clean.

Host coverage via orca host list:

  • macOS (local, darwin) — signed in, fully exercised as above.

  • Linux (ssh:openclaw) — agy is not installed, verified through an orca terminal on that host. That is the cli-unavailable path, and it is why the absent-CLI case reports unavailable with "not found on this machine" instead of an error that keeps retrying.

  • Windows — not exercised locally; the spawn goes through runProcess and the lookup through resolveCommandOnLocalPath, both of which already carry the Windows argument-encoding and PATHEXT rules. Windows verification is the main thing I would like help with.

  • I manually tested these changes locally

  • Automated tests added/updated, or explained why not below

Review

SSH boundary: this fetch runs wherever the rate-limit service runs, which is the execution host for an orca serve runtime and the desktop for an SSH-attached workspace. That matches every non-Claude/Codex provider today (Gemini, Cursor, Grok, Zcode all read local credentials), so this PR does not change the boundary — it does not fix it either. Making usage follow an SSH execution host is #19209 / #16466 and deliberately out of scope here.

Performance: one extra ~2.5 s child process per refresh cycle, off the critical path. No polling change.

Backwards compatibility: ProviderRateLimits is unchanged — no new fields, no wire change, so a paired old client decodes this exactly as before and simply sees better numbers. What the host publishes changes, which reaches old clients; that is the intended fix and it degrades safely (a client that ignored buckets still reads weekly).

Security: no token material is read, logged, or returned. The fetch never touches the keychain; agy does.

Agent skill upstream boundary

  • Not applicable.

Notes

This supersedes a long queue of PRs aimed at the same bug. I read all of them before building, and the design here takes the mechanism the agy-CLI group converged on, plus specifics each of them contributed:

Approaches I did not adopt but which mapped the design space, and whose reasoning is in the Why section: #21999 (@Mivr), #20797 (@abti-ai), #22055 (@werlang), #18167 (@eojonathan) on keychain-and-API; #20933 (@lippdev), #23382 (@yuta27jojo-rgb), #14571 (@leomleao) on the local language server; #19209 (@abruption) on the SSH-host case; #20388 (@bluetomlee), #19588 (@artile), #7952 (@andrecristodev), #23761 (@Tai-DT) on the Accounts surface.

Issue reporters whose diagnosis this is built on: @liuyifeng92 (#9122, traced the mirror and the @google/gemini-cli refresh dependency) and @rsuzukimktdigital (#22511, documented the groups[].buckets[] contract including disabled, which is where the disabled-bucket handling comes from).

Co-authored-by: baioccheg-bit baioccheg-bit@users.noreply.github.com
Co-authored-by: mikeascendx mikeascendx@users.noreply.github.com
Co-authored-by: isairz isairz@users.noreply.github.com
Co-authored-by: lurunzi lurunzi@users.noreply.github.com
Co-authored-by: brennanb2025 brennanb2025@users.noreply.github.com
Co-authored-by: liuyifeng92 liuyifeng92@users.noreply.github.com
Co-authored-by: rsuzukimktdigital rsuzukimktdigital@users.noreply.github.com

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • Visual proof: N/A with reason above (no renderer change; published record included)
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • oxlint, pnpm tc:node, pnpm test src/main/rate-limits/ pass locally

Orca published a successful Gemini `retrieveUserQuota` read under the
antigravity provider id. That reported Gemini CLI per-model buckets on a
60-minute window, so Antigravity's real pools were never shown, the weekly
window was always null, and the segment depended on an installed
@google/gemini-cli for token refresh that an Antigravity user has no reason
to have.

Read the quota from `agy -p "/usage" --output-format json` instead, which is
the only caller that can authenticate it — agy keeps its credential in the
OS keyring and mints its own token against daily-cloudcode-pa.

Fixes #9122
Fixes #22511
Without the stub, each RateLimitService suite spawned the developer's real
`agy` and resolved a login shell, which turned service-window-activation
from 214 ms into 14 s and broke its fake-timer fetch counts.
…a read

The flag stops agy expanding `/usage` as a command, so the text goes to the
model as an ordinary prompt: the call starts a conversation, spends quota, and
on an account near its limit answers RESOURCE_EXHAUSTED (429) instead of a
reading. Adds an opt-in real-CLI suite that catches exactly this.
@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Switches Antigravity quota source from Gemini mirror to direct CLI.

The PR does not yet appear safe to merge because weekly-only Antigravity quota can leave the default status-bar segment without a number.

Findings

  1. P1 Weekly usage disappears ▶
  2. P2 Sign-in opens wrong account ▶
  3. P2 Signed-out guidance stays hidden ▶

Summary

This PR replaces Gemini-mirrored Antigravity usage with quota reported by the agy CLI.

  • Parses model-group buckets and summarizes their limiting windows.
  • Fetches Antigravity independently of Gemini and stops polling when a response shows that /usage ran as a model prompt.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Refresh[Rate-limit refresh] --> Agy[Run agy /usage]
  Agy --> Parse[Parse usage envelope]
  Parse -->|Valid quota| Publish[Publish Antigravity pools]
  Parse -->|Model turn| Latch[Stop further agy polls]
  Parse -->|Other failure| Error[Publish failure]
Loading

Reviews (2) · Last reviewed commit: "fix(rate-limits): stop polling agy once ..."

Comment on lines +152 to +154
session: reading.session,
weekly: reading.weekly,
buckets: reading.buckets.map(({ id: _id, ...bucket }) => bucket),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Weekly usage disappears When agy reports weekly-only pools, this publishes named buckets and a weekly reading but no session reading. The status bar’s verbose view filters out those bucket names and falls back only to session or monthly usage. As a result, the default Antigravity segment shows no number even when a weekly pool is exhausted.

Comment on lines +130 to +135
if (output.toLowerCase().includes(NOT_SIGNED_IN_MARKER)) {
return unavailable(
'Antigravity usage is not available. Sign in with `agy` to report this account’s quota.',
'missing-credentials',
now()
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Sign-in opens wrong account A signed-out agy account now produces Antigravity-specific sign-in guidance, but the Antigravity sign-in action still opens Gemini’s Accounts section. That sends users to a credential that cannot satisfy this fetch, making the new guidance harder to act on.

Comment on lines +130 to +135
if (output.toLowerCase().includes(NOT_SIGNED_IN_MARKER)) {
return unavailable(
'Antigravity usage is not available. Sign in with `agy` to report this account’s quota.',
'missing-credentials',
now()
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Signed-out guidance stays hidden Antigravity no longer depends on Gemini OAuth, but a signed-out agy account returns unavailable. The renderer still requires Gemini OAuth to display an unavailable Antigravity slot. With agy installed and Gemini OAuth off, the slot and its new sign-in guidance remain hidden.

@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

This PR replaces the Antigravity Gemini mirror with a real agy fetch, but several renderer assumptions were only true because Antigravity was Gemini, and they were not updated. The most visible: the default status-bar segment no longer shows a number for Antigravity at all.

Reviewed changes

  • New agy read. antigravity-usage-command.ts / antigravity-usage-fetcher.ts resolve agy on the login-shell PATH, run -p "/usage" --output-format json --print-timeout 20s through runProcess, and classify absent-CLI/signed-out as unavailable, timeout/parse as error.
  • New parser. antigravity-usage-response.ts reads command.data.groups[].buckets[] (not the lossy response text), maps weekly→10080 / 5h→300, drops disabled buckets, names buckets by group, and summarises each window by its most-constrained group.
  • Shared summariser. deriveMostConstrainedWindow moves to rate-limit-bucket-summary.ts; Gemini and Antigravity share it.
  • Service wiring. antigravityResultPromise is a sibling promise settled alongside grok/cursor/zcode; the mirror module and its test are deleted; the test harness and seven service suites stub the new fetcher.

⚠️ Antigravity's real pools never reach the default status-bar segment

The fetch now publishes group-named buckets ("Gemini Models", "Claude and GPT models") with session: null and weekly populated, but the verbose segment's bucket allowlist is Gemini's old model names, and its fallback window omits weekly. The result is that the default (verbose) Antigravity chip renders an icon and an unlabeled bar with no percentage — a regression, since the deleted mirror always carried Gemini's non-null session and Gemini-named buckets. The usage roster panel is fine; only the inline segment drops it.

Technical details
# Antigravity inline status-bar segment renders nothing

## Affected sites
- `src/renderer/src/components/status-bar/StatusBarProviderSegment.tsx:181-205` — when `p.buckets` is non-empty it filters by `isVisibleStatusBarBucket` and falls back only to `p.session ?? p.monthly`. Antigravity's buckets are named by group and `session` is null, so `visibleBuckets` is empty and `fallbackWindow` is null.
- `src/renderer/src/components/status-bar/StatusBarProviderSegment.tsx:166-172` — the allowlist is Gemini's `Flash`/`Pro`/`1.5 Pro` plus Cursor pools.
- `src/main/rate-limits/antigravity-usage-response.ts:90-95` — where the group name is minted (`formatBucketName`).

## Required outcome
- The default (verbose) Antigravity segment must show its real pool(s) or at least a labelled window, not an unlabelled bar.

## Suggested approach
- Add the Antigravity group names to `STATUS_BAR_BUCKET_NAMES`, or make the fallback `p.session ?? p.weekly ?? p.monthly`. Since the buckets *are* the whole meter for this provider, surfacing them is the intent.

## Open questions for the human
- With `usageTightestOnly`/compact the segment shows `tightest.label` (a group name), so is the intended inline presentation the two group pools, or a single "weekly" chip?

⚠️ The renderer still gates Antigravity on the Gemini OAuth opt-in

Main no longer gates the Antigravity fetch on geminiCliOAuthEnabled, but the renderer still does. With the opt-in off, an unavailable Antigravity snapshot (no agy installed, or signed out) is hidden, so the new "Sign in with agy" / "CLI not found" guidance never appears; the durable-settings check also only counts Antigravity as configured via the Gemini term. The sign-in affordance additionally routes to the Gemini accounts section, which cannot sign a user into agy.

Technical details
# Renderer still keys Antigravity off Gemini OAuth

## Affected sites
- `src/renderer/src/components/status-bar/status-bar-provider-visibility.ts:116-121` — requires `antigravityUsageConfigured === true && geminiCliOAuthEnabled === true`.
- `src/renderer/src/components/status-bar/status-bar-provider-visibility.ts:82-86, 94-100` — `hasUsageProviderSettings` comment/term assume Antigravity is covered by the Gemini term.
- `src/renderer/src/components/status-bar/use-status-bar-controller.ts:99-102` — comment codifies the same coupling.
- `src/renderer/src/components/status-bar/usage-provider-settings-target.ts:11-14` — Antigravity → `accounts-gemini`.

## Required outcome
- An Antigravity-only user (agy installed, Gemini OAuth off) sees the bar and its actionable guidance, and the sign-in CTA points somewhere that can actually authenticate `agy`.

## Open questions for the human
- Is a dedicated Antigravity accounts section intended (cf. #17345/#19587), or should the CTA be suppressed until one exists?

ℹ️ No minimum agy version gate or fallback for the print-mode read

The invocation is hardcoded with no capability probe or version gate, and the load-bearing print-mode behaviour is versioned. In the upstream google-antigravity/antigravity-cli CHANGELOG, --print-timeout predates this command, --output-format was added in 1.1.8, and the read-only print-mode slash commands (so -p "/usage" does not start a turn) were added in 1.1.11. On an older CLI the command has no defined outcome, and per the author's own note an unexpanded slash command is sent as an ordinary prompt — the exact billed-turn failure the comment warns about. Worth documenting the minimum supported version and defining the fallback.

Technical details
# Version compatibility of the agy print-mode read

## Affected sites
- `src/main/rate-limits/antigravity-usage-command.ts:14-27` — single hardcoded argv, no version/capability check.

## Evidence
- `--print-timeout`: present by agy 1.1.1 (superseded semantics in 1.1.28, 1.2.6, 1.2.9).
- `--output-format`: added in 1.1.8.
- read-only print-mode `/usage` (no agent turn, no quota): added in 1.1.11.
- Source: `google-antigravity/antigravity-cli` `CHANGELOG.md`.

## Required outcome
- Define behaviour for a CLI predating the read-only `/usage` contract so it cannot silently fall into a billed turn.

## Suggested approach (optional)
- Document the minimum supported `agy` version, and consider a capability probe (inspect `--help`/version) before treating absence as "not installed". Note the repo's `docs/reference/antigravity-readiness-evidence.md` warns that `agy --version` and the TUI banner disagree, so any numeric gate is unreliable.

ℹ️ Nitpicks

  • src/main/rate-limits/antigravity-usage-command.ts:53 — isPlainAntigravityExecutable is exported but never called; its doc comment describes a rule the fetcher (which always resolves agy directly) does not enforce. Remove it or wire it in.
  • src/main/rate-limits/antigravity-usage-fetcher.ts:44-46 — the comment says "'unavailable' and not 'error' for every failure", but the sibling failed() helper returns status: 'error'. Consider rewording so it describes only the absent-CLI/signed-out case.
  • src/shared/rate-limit-types.ts:4 — the windowMinutes doc still says "300 (5h) or 10080 (7d)", but the parser now emits 0 for an unrecognised agy window.

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

* to that either runs the wrong program or feeds the slash command to the wrong argv slot. The quota
* read resolves the plain executable itself instead.
*/
export function isPlainAntigravityExecutable(command: string): boolean {

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.

isPlainAntigravityExecutable is exported but never called (repo-wide grep finds only this definition). Its doc comment describes a rule the fetcher does not enforce — the fetcher always resolves the plain agy executable itself. Either delete it or use it where a configured launch command is considered.

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: bc7a5047-c224-4a40-9130-0030fe5c667a

📥 Commits

Reviewing files that changed from the base of the PR and between 661835e and bc30bae.

📒 Files selected for processing (3)
  • src/main/rate-limits/antigravity-usage-fetcher.test.ts
  • src/main/rate-limits/antigravity-usage-fetcher.ts
  • src/main/rate-limits/antigravity-usage-response.ts

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


📝 Walkthrough

Walkthrough

The change adds an agy-based Antigravity quota parser and fetcher, then publishes its result independently from Gemini during rate-limit refresh. It also adds a shared helper for selecting the most constrained quota window and updates Gemini formatting to use it. Tests cover parsing, CLI outcomes, refresh behavior, and an opt-in real-CLI run.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to bc30b

Antigravity usage now comes from agy independently of Gemini. The supplied evidence identifies no actionable merge-blocking risk.

Security Architecture Review

Security architecture risk: 🔵 Low · up to bc30b

The new background quota check uses fixed arguments and bounded execution. However, an incompatible CLI can turn the intended read-only check into a quota-consuming request before subsequent checks are disabled. Remote-host routing and account-switch behavior were not fully verified.

Retained concerns

  • Low · security · inferred: The background quota check establishes command compatibility only after executing authenticated print mode. If the installed CLI interprets /usage as a prompt, the first check can consume a model turn. The latch prevents subsequent sequential attempts in that process, but restarting without upgrading permits another attempt. This behavior is newly reachable through quota refresh rather than Gemini-result mirroring; the supported exposure is unintended quota consumption, not arbitrary prompt injection or tool execution.
Security review details

Security Blast Radius

  • inferred — The supported direct exposure is the OS user executing the rate-limit service and the account selected by that user’s agy credentials. The inspected call carries no tenant, workspace, prompt or remote-target parameter; broader remotely triggerable exposure remains unverified.

Security Findings and Attack Paths

  • inferred — An incompatible installed CLI can interpret the fixed /usage request as a model prompt. Detection occurs after execution and suppresses later sequential checks only for the current process. No attacker-controlled prompt, arbitrary tool invocation or credential-exfiltration path was established.

Trust Boundaries and Controls

  • observed — The production caller supplies only cancellation, while command arguments are fixed. Execution uses a 20-second CLI deadline, a 30-second process timeout and a 512 KiB capture limit. The shared spawn wrapper disables shell mode and handles Windows command shims separately.

Resilience and Maintainability Implications

  • observed — Cancellation stops the subprocess but can settle with captured output. Although the service prevents aborted-result publication, the fetcher can still update its unsupported-command latch from that output. The published Antigravity metadata also contains no account provenance or generation; external account-change invalidation was not established.

Hardening Proposals

  • proposed — Establish a non-billing capability or supported-version check before authenticated print-mode invocation, and retain incompatibility decisions keyed to executable version across restarts. This would avoid learning command incompatibility through a potentially quota-consuming request.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR satisfies the independent Antigravity source and CLI-decoupling requirements in [#9122]. It also implements weekly and 5-hour group buckets, disabled-bucket filtering, constrained-window summar… Implement AI-credit parsing and representation for Antigravity usage, connect it to the displayed rate-limit data, and add automated tests for available, exhausted, and unavailable AI-credit values.
Docstring Coverage ⚠️ Warning Docstring coverage is 52.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 19 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes remain connected to the linked issue objectives. The provider-neutral session-summary helper supports the new Antigravity path and the existing Gemini path. Service orchestration changes e…
Title check ✅ Passed The title clearly and concisely describes the primary change: fetching real Antigravity quota from the agy CLI instead of mirroring Gemini quota.
Description check ✅ Passed The description is complete and relevant. It covers the user impact, implementation, rationale, linked issues, testing, visual proof, security, compatibility, platform coverage, and checklist items.
Full details: Linked Issues check

Explanation

The PR satisfies the independent Antigravity source and CLI-decoupling requirements in [#9122]. It also implements weekly and 5-hour group buckets, disabled-bucket filtering, constrained-window summaries, independent fetching, and automated tests for [#22511] and [#14515]. However, [#22511] also requires reporting AI credits. The changed parser and fetcher summaries describe group buckets only and do not implement AI-credit parsing or display. This requirement remains unmet.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

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.

In print mode an unrecognised slash command is not an error — agy sends the
text to the model. On a build that does not know `/usage`, polling would start
a conversation and spend the user's quota every cycle while Orca reported no
quota. The envelope distinguishes the two: a command reply has an empty
conversation_id and num_turns 0.

A successful parse is checked first, so a real reading can never trip the latch.

@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 in this delta.

Reviewed changes

  • Added an unsupported-command latch. A module-level usageCommandUnsupported flag short-circuits the fetch to unavailable with an "update agy and restart Orca" message once charged, with a test-only resetAntigravityUsageSupportForTests to clear it.
  • Detects a model-turn reply. New didRunModelTurn / stdoutShowsModelTurn in antigravity-usage-response.ts treat num_turns > 0 or a non-empty conversation_id as evidence agy ran the slash command as a prompt.
  • Latches only after a failed parse. The fetcher checks the successful-parse branch first, so a real reading can never trip the latch; transient/unparsable answers keep retrying.
  • Tests. Covers the latch firing, single-spawn across repeated fetches, no-latch-when-parsed, and no-latch-on-transient-failure. All 35 rate-limit parser/fetcher tests pass locally.

The single production caller (service-full-cycle-preparation.ts:161) runs once per cycle on the local login-shell PATH, so the process-global latch does not leak across hosts or accounts, and the dedicated agy binary makes the "this version cannot answer" verdict a per-process property. Confirmed the latch cannot be set from the observed 1.2.11 command envelope (num_turns: 0, empty conversation_id) because a successful parse returns before the turn check.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@nwparker
nwparker merged commit a76a0bc into main Oct 1, 2026
33 checks passed
lurunzi added a commit to lurunzi/orca that referenced this pull request Oct 1, 2026
Merge stablyai/orca main up to 78daf71 on top of the 2026-10-01 sync and fork/main 49d4264.

- Antigravity quota: upstream stablyai#24073/stablyai#24074 land their own agy-CLI quota reader and
  model-group pools; the fork's earlier implementation, its configured-command override and its
  status-bar extras (grouped compact metrics, reset countdown, grouped tooltip/roster) are removed
  in favour of upstream's.
- Agent exit confirmation: keep the fork rule that only a shell in the foreground closes chat,
  with Windows PTY job proof (07da011, 7a40146), on top of upstream's hook-presence verdicts;
  upstream's wsl.exe/tmux and owner-less silence cases are adapted to that rule.
- Claude adapter: prompt suggestion bookkeeping folded into upstream's child-record observation.
- Coordinator identity loader opens the orchestration database only if it exists; upstream's
  idle-edge mail lookup otherwise created it for every structured session.
- Fork capabilities advertised via shared/agent-provider-runtime-capabilities.ts, prompt-suggestion
  reducer wrapper and frame-field alias keep upstream files under max-lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant