Skip to content

Add Antigravity usage reporting through the CLI quota interface - #11989

Closed
steph4n-gh wants to merge 9 commits into
omacom:quattrofrom
steph4n-gh:feature/antigravity-agent-usage
Closed

steph4n-gh wants to merge 9 commits into
omacom:quattrofrom
steph4n-gh:feature/antigravity-agent-usage

Conversation

@steph4n-gh

@steph4n-gh steph4n-gh commented Sep 15, 2026 •

Copy link
Copy Markdown

Add Antigravity quota and local activity reporting to the agents panel. The collector reads agy -p "/usage" --output-format json, after requiring CLI 1.1.11 or newer, so Antigravity handles authentication, token refresh and quota requests through its read-only interface. Older or unrecognized versions never receive the slash command, and passive scans do not invoke Omarchy's installer wrapper.

Session, weekly and Claude/GPT allowances, including unused windows, come from the returned quota windows, independent of group order. Failed checks show actionable status; network failures request an early retry. Quotas and errors are never cached because the CLI response provides no account identity: every refresh checks the current sign-in, and failures leave the meters empty. The shared usage writer assigns request order before collecting and atomically publishes that sequence with the data, so older overlapping requests cannot overwrite newer valid records. A crashed collector or malformed response does not suppress another valid in-flight result, and locks are released during network calls. There are no prompt-based quota estimates or guessed plans.

Local prompts and sessions use local calendar dates and the observed CLI transcript, history and summary formats. Prompt events are merged individually using matching complete request text and nearby timestamps, excluding surrounding transcript metadata, retaining distinct events from partially overlapping logs. Antigravity's saved transcripts do not include token counts or model IDs, and /usage does not include a plan, so those values are not invented. Optional tier labels remain configurable through AGY_TIER or antigravity.json; AGY_DIR isolates custom app roots and caches.

Includes icons, provider defaults, user documentation, a sanitized CLI response fixture and regression coverage. Updated to current quattro, retaining its shared subscriptions page and provider ordering; the old default-tab change no longer applies.

Validation:

  • All 11 agent-related shell test suites pass, including CLI invocation, version gating, error handling, quota ordering, immediate account/sign-in changes, partially overlapping histories, substring and literal closing-tag cases, out-of-order publication and failed concurrent collectors, releasing the history lock before slow quota checks, custom roots, timezone boundaries and real-format history fixtures.
  • Command metadata check and git diff --check pass.
  • Verified against installed Antigravity CLI 1.2.14 and visually checked the running Omarchy panel with live quotas. The read-only quota query returned zero agent turns.
  • ./test/cli reaches an existing upstream VS Code theme assertion failure: the test expects a symlink while omarchy-theme-set-vscode now writes a regular file. Reproduced on a clean checkout of base commit 821ae589; both files are unchanged from the base branch.

Add the omarchy-agent-usage-antigravity collector to monitor Google
Antigravity usage alongside Claude, Codex, and Fireworks in the native
agents bar panel.

Authoritative subscription tier and quota limits (rolling 5-hour session,
weekly limits, and scoped 3P model allowances) are probed from the Google
Cloud Code API via Secret Service keyring credentials with rolling local
history estimation as fallback.

Also teach the agents panel to watch ~/.config/omarchy/defaults/agent so
the panel selects the user's default agent on launch.
@llstrk

llstrk commented Sep 28, 2026

Copy link
Copy Markdown

Automated AI review

Community review: Independent automated community review, unaffiliated with the Omarchy team, intended to help prepare PRs for their review.

Verified: the collector plugs into the agents plugin as intended. omarchy-agent-usage-update discovers it by file name, the record id antigravity matches the file, the record fields match what Main.qml and Panel.qml read, the icons resolve by convention, and the new tests, the neighbouring agent tests and test/cli pass on the head. With a successful (synthetic) Cloud Code response, tier and Session/Weekly limits come out as the PR describes. The Panel change selects the default agent's tab for agy, claude and codex, and an explicit tab selection still wins.

The main issue is what the panel shows when the live quota call does not succeed: failures are silent, and invented limits and a guessed plan are shown as if they were account data.

Failed quota calls are silent and show estimated limits as real ones

When the user is signed in, collect_limits leaves usageStatusText empty for a missing access token, a 401, a network failure and any other HTTP error; among these failures only a 429 that makes the whole probe fail sets it (bin/omarchy-agent-usage-antigravity:620-657). The panel shows the status card and replaces the plan line only when usageStatusText is non-empty (shell/plugins/agents/Panel.qml:207-213, :524-546), so in these cases the computed "Antigravity authentication expired…" and "Could not reach Antigravity service." texts do not appear.

Instead, when no cached window is still open, the collector emits calculate_limits: Session = prompts in the last 5 hours / 50, Weekly = prompts in the last 7 days / 250, weekly reset always now + 7 days, under the same labels as the API values (:528-598). Nothing in the PR or repository documents where 50 and 250 come from. When the user is signed in and no tier comes from AGY_TIER, the config file, the probe, the cache or an enterprise/cloud sign-in, the plan falls back to "Pro" (:355-379).

Synthetic runs (stub secret-tool, a local HTTP server standing in for Cloud Code; no cache except in the second 401 row):

Case limits shown usageStatusText Plan
200 OK API values "" from API
401, no cache estimate (Session 60%, Weekly 12%) "" Pro
401, earlier cache last cached windows "" cached
network failure estimate "" Pro
429 estimate "Antigravity limits rate limited" Pro
signed in, no access token estimate "" Pro

Related details:

  • The keyring expiry is parsed into expires_at_ms but never used (:101-113); a token that expired two hours earlier was still sent to both endpoints. agy refreshes its token proactively five minutes before expiry (agy CHANGELOG 1.1.23); since that refresh happens in agy itself, a stale keyring token is likely whenever agy has been closed for a while.
  • The 429 case pairs the rate-limit status with the help text "Run agy to authenticate."
  • If cli.log changed in the last 5 hours and its tail contains RESOURCE_EXHAUSTED or "rate limit exceeded", Session is pinned to 100% (:569-582), which turns the bar icon urgent when Antigravity is the tab shown. What agy writes there, including for the short 429s it retries on its own, was not verified.
  • calculate_limits compares timestamp with integers, so ISO-string timestamps are skipped (:551-556): 30 ISO-stamped prompts today gave a 0% estimate. collect_local_stats accepts both forms.
  • Because the estimate always returns two entries, any signed-in machine without a successful probe gets a non-empty limits list, which alone makes the Antigravity tab appear (Main.qml:219-224).

Impact: after a token expires or the network drops, the panel keeps showing plausible-looking meters and a cached or guessed ("Pro") plan with no hint that they are estimated or stale. The agents README promises that "Auth and endpoint problems replace the plan line and repeat in a card" (shell/plugins/agents/README.md:12-13); the Claude collector checks token expiry, reports "Waiting for auth" or "Sign-in expired", says "Claude limits unavailable" when nothing is cached, and sets retryAdvised on network failures (bin/omarchy-agent-usage-claude:809-846).

Suggested change: set usageStatusText for each failure (missing or expired token, 401/403, network, other HTTP errors) and give the 429 case its own help text; check expires_at_ms before probing; set retryAdvised on network failures; leave tierLabel empty when the tier is unknown (the hero then reads "Subscription"); and either drop the prompt-count estimate or label it unmistakably as an estimate.

An alternative that avoids reading credentials at all: agy 1.1.11 added agy -p "/usage" --output-format json, which the agy changelog describes as answering read-only slash commands "without starting an agent turn, spending quota, or leaving a conversation behind". The same changelog entry notes that unhandled print-mode slash commands "previously fell through as literal prompt text", and third-party stablyai/orca#23426 gates its /usage call on agy 1.1.11 or later for that reason, so a version check would be needed. This mirrors how the Codex collector asks codex app-server. Its cost per refresh was not measured.

Scoped Claude/GPT limits disappear if that group comes first

extract_limits picks the primary group with if "gemini" in name or gemini_group is None (:201-204). If "Claude and GPT models" is listed first, it becomes the primary group, is then replaced by the Gemini group, and never reaches other_groups.

groups order (synthetic run)    limits returned
Gemini, Claude/GPT              Session, Weekly, Claude/GPT Weekly (20% used)
Claude/GPT, Gemini (reversed)   Session, Weekly            <- Claude/GPT Weekly dropped

A bucket without remainingFraction is read as 1.0: a Gemini Session or Weekly bucket then shows as 0% used (:225, :234), and a scoped bucket is hidden (:250-253). If the server omits zero-valued fields, as proto3 JSON does by default, an exhausted bucket would look unused; this is a hypothesis, since no real response was captured.

Impact: third-party allowance usage can silently vanish from the panel, depending only on response order.

Suggested change: select the Gemini group by name first and treat every other group as scoped; treat a missing remainingFraction as unknown and skip that bucket.

Daily stats use UTC dates while the panel uses local dates

main uses datetime.now(timezone.utc), and collect_local_stats derives today, the 7 recent days and history dates from UTC (:383-385, :415, :496). The panel's Today row and the sync merge use the local date (Panel.qml:215-222, Main.qml:523-531), as do the Claude and Codex collectors. In a synthetic run at 23:41 UTC with TZ=Etc/GMT-14, the local date was 2026-09-28 but recentDays ended at 2026-09-27, so the panel would mark no row as Today (inferred from Panel.qml:674; the panel was not run).

Impact: for users away from UTC, today's prompts and tokens land on the wrong day for part of every day.

Suggested change: use local dates (datetime.now().astimezone(), converting timestamps to local time) as the other collectors do.

Token counts can double-count and invent a model row

In collect_local_stats (:462-506):

  • A transcript step without created_at counts toward todayTotalTokens on every scan but not toward the Today entry in recentDays. In a synthetic transcript the two differed (2150 against 1150).
  • A step without usage is estimated as characters / 4 and added on top. The PR's own fixture shows it: input tokens 122 = 120 prompt_token_count + 2 estimated from the user's message, which prompt_token_count already includes if it means what it does in the Gemini API.
  • A step without model (every USER_INPUT step in the PR's fixture) is attributed to a hard-coded gemini-3.8-flash-high, which can create a phantom model row when another model was used.

It is also unverified whether agy transcripts carry per-step usage and model at all. Third-party readers estimate tokens from transcript text or read ~/.gemini/antigravity-cli/conversations/<uuid>.db instead. If the fields are absent, every Antigravity token figure is an unlabelled estimate. A redacted transcript line from agy 1.2.x would settle it.

Impact: token totals and per-model rows can be inflated or attributed to a model that was not used.

Suggested change: count only model steps that carry usage (or read the conversations database if transcripts lack it), skip steps without created_at instead of treating them as today, and leave the model unknown rather than defaulting it.

Unverified quota request shape

Both Cloud Code POSTs send {}, discarding the cloudaicompanionProject returned by loadCodeAssist (:288, :304). For the sibling retrieveUserQuota method, Gemini CLI's RetrieveUserQuotaRequest requires project (packages/core/src/code_assist/types.ts:250-253), and third-party Antigravity quota scripts send project to retrieveUserQuotaSummary too. This is a hypothesis: no real response was captured. If the server answers {} with a differently shaped response, extract_limits returns [] and, per the first finding, no status is shown; a generic entitlement view in the same shape would show wrong numbers instead.

Impact: if the premise holds, Antigravity limits are empty or wrong for real accounts even when the probe succeeds, with no status shown.

Suggested change: send the cloudaicompanionProject returned by loadCodeAssist as project, or confirm the request shape first. A redacted capture of agy's own request, or the same account queried with and without project, would settle it.

Smaller notes

  • Sign-in detection: an id_token that fails to decode makes a valid sign-in report "Sign in to see limits" (:115-126); the decoded email is never used, so the decode can go or get its own try. The lookup also only reads the keyring, while agy supports file-stored credentials when the keyring is bypassed or unreachable (agy CHANGELOG 1.1.26, 1.1.3) and GEMINI_API_KEY sessions; those users are reported as signed out.
  • Default-agent watcher: the FileView in Panel.qml:28-34 has no onLoadFailed, so deleting ~/.config/omarchy/defaults/agent keeps the old default. From Quickshell v0.3.1 source (not run), FileView watches only the file and its parent directory, and ~/.config/omarchy/defaults/ is created only when a default is first chosen, so a first-ever default picked while the shell runs would likely not be seen until restart.
  • Documentation: manual/17-ai.md:37 still says "Claude Code, Codex, and Fireworks are covered out of the box". The README paragraph at shell/plugins/agents/README.md:60 was reflowed into a single line of about 770 characters, while the rest of the file wraps near 78 columns, and the providers example (:122-128) still lists three agents.

Optional test improvement: in a mutation run against the three changed or new test files, these mutants all survived: the live probe is never called, the probed tier is ignored, the rate-limit status is never set, the 5-hour estimate never counts a prompt, the panel never selects the default agent, and agy no longer maps to antigravity. The two assertions the PR adds to the panel test only check that the strings normalizeAgentId and omarchy/defaults/agent occur, and the scanner test asserts the "Pro" fallback. Its history fixture also uses ISO timestamps and conversation_id, while third-party readers of agy's history.jsonl see integer milliseconds and conversationId. A scanner test against a local fake CLOUD_CODE_URL covering 200, 401, 429, network failure and an expired token, a reversed-group case, and a node test that evaluates providerIndex (the repository already has run_node_test) would catch these.

Overlapping open PRs


Review information

Test scope: source review of head 2a42aa7, plus isolated runs of the PR's tests, the neighbouring agent tests, test/cli, mutation checks and synthetic collector scenarios (stub keyring, local fake Cloud Code server). The Panel selection logic was evaluated in Node from the verbatim QML source; the QML was not run in a shell. No real agy install, keyring, account or Google endpoint was used, so claims about the live API and agy's file formats rest on the agy changelog, Gemini CLI source and third-party tools, as noted.

AI process: Opus 5.5 Medium coordination and synthesis, Opus 5.5 Xhigh technical review and final fact check, GPT 6 Sol Xhigh search for related issues, Opus 5.5 Medium editorial check.

Opt out: To stop receiving these reviews, reply to this comment saying so.

@steph4n-gh steph4n-gh changed the title Add Google Antigravity usage collector and panel integration Add Antigravity usage reporting through the CLI quota interface Oct 2, 2026
@greptile-apps

greptile-apps Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Adds Antigravity agent usage reporting to the agents panel.

The PR appears safe to merge; no new actionable issue or outstanding previous finding was identified.

Summary

The PR adds Antigravity quotas and local activity to the agents panel, with version-gated CLI checks, documentation, fixtures, and tests. It also changes the shared usage writer to publish overlapping collector results in request order without holding its lock during collection.

Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Reserve request sequence under lock] --> B[Release lock and collect]
  B --> C{Valid record?}
  C -- No --> D[Leave published record unchanged]
  C -- Yes --> E[Reacquire lock]
  E --> F{Sequence newer than published?}
  F -- Yes --> G[Atomically publish sequence and record]
  F -- No --> H[Keep newer record]
Loading

Reviews (5) · Last reviewed commit: "Publish valid usage despite overlapping ..."

Comment thread bin/omarchy-agent-usage-antigravity Outdated
Comment thread bin/omarchy-agent-usage-antigravity Outdated
Comment thread bin/omarchy-agent-usage-antigravity Outdated
Comment thread bin/omarchy-agent-usage-antigravity Outdated
Comment thread bin/omarchy-agent-usage-antigravity Outdated
Comment thread bin/omarchy-agent-usage-antigravity
Comment thread bin/omarchy-agent-usage-antigravity Outdated
Comment thread bin/omarchy-agent-usage-update Outdated
@dhh

dhh commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Thank you! Brought in with your authorship in #14039 as the agy collector. The limits invented from prompt counts, the assumed plan, the estimated token counts, and the Panel.qml change were dropped, and kept limits are marked stale when a check fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants