Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.
Add Gemini collector to the Agents panel
Reported by acf1210 · filed Sep 1, 2026 · open at source
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- AreaOutside the audited area· Decided by agents · verified
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Decided by agents and verified by a second, independent agent — see the decision.
Original report
Context
Gemini CLI is a supported default agent in Omarchy (omarchy-default-agent gemini) but has no usage collector, so daily Gemini usage never appears in the omarchy.agents bar panel.
Current State
- Gemini CLI 0.57.0 installed, but local session storage is empty on the machine this spec was written on — local-stats parsing is unverified against a real file.
- Documented (not yet locally verified against a real file) session storage:
~/.gemini/tmp/<project-hash>/chats/logs.json, one hashed directory per project root (gemini-cli session-management and checkpointing docs). - A live quota mechanism exists and was verified live end-to-end (2026-09-01), using a Google OAuth token from the same account (obtained via the separately-authenticated Antigravity CLI, which shares the same Code Assist backend
cloudcode-pa.googleapis.com). The mechanism is not a singleretrieveUserQuotacall (an earlier web search pointed at that method name; it does not appear anywhere in a real, working, open-source client and was not verified) — it is two calls, confirmed by reading theantigravity-usagenpm package's own source (src/google/cloudcode.ts) and reproducing both requests live:
POST /v1internal:loadCodeAssist, body{"metadata": {"ideType": "ANTIGRAVITY", "platform": "PLATFORM_UNSPECIFIED", "pluginType": "GEMINI"}}→200, returnscloudaicompanionProject(a project id, e.g.isometric-bonfire-pqb1d) pluscurrentTier/allowedTiers.POST /v1internal:fetchAvailableModels, body{"project": "<projectId from step 1>"}→200, returnsmodelsas a dict keyed by model id (not an array), each entry shaped:
{"displayName": "Gemini 3.6 Flash (Low)", "quotaInfo": {"remainingFraction": 0.9911605, "resetTime": "2026-09-02T01:45:13Z"},
"model": "MODEL_PLACEHOLDER_M73", "apiProvider": "API_PROVIDER_GOOGLE_GEMINI", "modelProvider": "MODEL_PROVIDER_GOOGLE", ...} Both headers: Authorization: Bearer <token>, Content-Type: application/json, User-Agent: antigravity. My first attempt at this endpoint, sent without the metadata object, returned 403 SUBSCRIPTION_REQUIRED — that was a malformed-request rejection, not a real licensing gate; with the correct body it succeeded on this same (personal, unlicensed-for-Enterprise) account.
- Open question, not yet resolved: this was verified using Antigravity's own client identity (ideType: "ANTIGRAVITY", pluginType: "GEMINI"). Gemini CLI is a separate client against the same backend and may (or may not) need a different ideType/pluginType to identify itself, and may return the same account-wide quota or a differently-scoped one. Confirm by reading google-gemini/gemini-cli's own request code, or by testing once Gemini CLI is actually logged in somewhere, before hard-coding the metadata values into the collector.
- Gemini CLI's own OAuth credential lives at ~/.gemini/oauth_creds.json (standard location per third-party tooling that reads it) — absent on the machine this spec was written on, since Gemini CLI was never logged in there. The collector must degrade gracefully exactly like omarchy-agent-usage-claude does when .credentials.json is missing.
Proposed Change
Add bin/omarchy-agent-usage-gemini: local stats from ~/.gemini/tmp/*/chats/logs.json across every project hash (mirroring how the Claude collector scans ~/.claude/projects recursively), plus a best-effort live limits probe against the Code Assist quota mechanism using ~/.gemini/oauth_creds.json, degrading to stats-only with a clear usageStatusText when unauthenticated or when the two-call probe fails.
Implementation Details
New file: `bin/omarchy-agent-usage-gemini`
- Local scan:
~/.gemini/tmp/*/chats/logs.json(glob every project hash). Research task, not yet resolved by this spec: confirm the exact per-message JSON shape (role, model, token usage field names) against a real file before writing the parser — either generate one locally (a throwawaygemini -pcall) or read the authoritative field names from the open-sourcegoogle-gemini/gemini-cliparser. Do not guess field names. - Live limits: read
access_token+expiryfrom~/.gemini/oauth_creds.json(same shape verified live in this spec:{access_token, token_type, refresh_token, expiry}); if missing or expired, skip the probe (no refresh-token exchange in v1 — matches Claude's own collector, which also never refreshes, only reports "sign-in expired"). - Two-call probe against
https://cloudcode-pa.googleapis.com, headersAuthorization: Bearer <access_token>,Content-Type: application/json,User-Agent: <TBD — confirm Gemini CLI's own value, see open question above>:
POST /v1internal:loadCodeAssistwith the correctmetadataobject (exactideType/pluginTypefor Gemini CLI: open question above) → extractcloudaicompanionProject.POST /v1internal:fetchAvailableModelswith{"project": "<id from step 1>"}→ iterate themodelsdict, keep entries whereapiProvider == "API_PROVIDER_GOOGLE_GEMINI", map eachquotaInfo.remainingFraction/quotaInfo.resetTimeinto the standardlimits: [{label, percent, resetsAt}]shape (percent = 1 - remainingFraction, matching the "percent used" convention every other collector already uses;label=displayName).
- Any error at either step (401/403/429/5xx/transport failure): same graceful pattern as
probe_limits()in the Claude collector —usageStatusTextset to a short, honest reason,limits: [], local stats still shown, collector never crashes. - Same wrapper record shape as every other collector:
schemaVersion: 1, id: "gemini", name: "Gemini", updatedAt, ready, hasLocalStats, tierLabel, usageStatusText, authHelpText, limitsmerged with the stats bucket.
`shell/plugins/agents/manifest.json`: add "gemini": {"enabled": true} to defaults.providers.
`shell/plugins/agents/assets/gemini.svg` (+ -light if needed): Google's public Gemini mark.
`shell/plugins/agents/README.md`: add a row to the collector table documenting the two-call quota mechanism (loadCodeAssist + fetchAvailableModels) and the local log path.
Acceptance Criteria
- On a machine with real
~/.gemini/tmp/*/chats/logs.jsonfiles, the collector prints a valid record with correcttotalPrompts/token aggregates — must be verified against a real file before merge, since the local format is unconfirmed as of this spec. - On a machine with no Gemini session data, the collector exits cleanly with
ready: false, no crash. - With the correct
metadatabody (once Gemini CLI's ownideType/pluginTypeis confirmed),limitscontains at least one entry with apercentbetween 0 and 1 and a validresetsAt— reproducing the live result already verified in this spec (0.9911605remainingFractionon a real account, via the same backend under Antigravity's client identity). - On any error at either probe step (401/403/429/5xx/transport),
usageStatusTextcarries a short honest reason,limits: [], local stats still shown — no exception, no stack trace. omarchy agent usage-update geminiwrites onlygemini.json, leaving every sibling untouched.- No changes to Claude, Codex, Fireworks, or OpenCode collector behavior.
Testing Plan
| Layer | What | Count |
|---|---|---|
| Unit | logs.json aggregation from a fixture (once the real shape is confirmed) | +4 |
| Unit | Quota probe: loadCodeAssist+fetchAvailableModels 200/200 → limits populated (fixture from the real verified response shape); either call failing → graceful stats-only | +3 |
| Integration | omarchy-agent-usage-update gemini writes only its own file | +1 |
| Manual | Live panel shows a Gemini tab once a real session exists on a test machine | +1 |
Rollback Plan
Delete bin/omarchy-agent-usage-gemini, remove gemini from manifest.json:defaults.providers, delete stale ~/.local/state/omarchy/agents/usage/gemini.json if present.
Effort Estimate
~6h — 1h confirm the real logs.json shape + Gemini CLI's own ideType/pluginType (open research items), 2h collector script, 1h quota-probe error handling, 1h manifest/assets/README, 1h tests.
Open Research Items
- The exact
~/.gemini/tmp/*/chats/logs.jsonper-message field names are sourced from documentation, not verified against a real file (none existed on the machine this spec was written on). Before merge, either generate one real session or read the field names directly from the open-sourcegoogle-gemini/gemini-cliparser. - This spec verified the
loadCodeAssist+fetchAvailableModelsquota mechanism live, but under Antigravity's own client identity (ideType: "ANTIGRAVITY", pluginType: "GEMINI"), not Gemini CLI's. Confirm Gemini CLI's own values (likely visible ingoogle-gemini/gemini-cli's request code) before hard-coding them into the collector — using the wrong identity may return the wrong quota scope, or none at all.
Files Reference
| File | Change |
|---|---|
bin/omarchy-agent-usage-gemini | New collector |
shell/plugins/agents/manifest.json | Add gemini to defaults.providers |
shell/plugins/agents/assets/gemini.svg | New mark |
shell/plugins/agents/README.md | Document the new collector row |
bin/omarchy-agent-usage-claude (reference only) | oauth_login() / probe_limits() / collect_limits() are the pattern to mirror for graceful auth degradation |
Out of Scope
- Refresh-token exchange (v1 reports "sign-in expired" rather than silently refreshing, matching Claude's own collector).
- Any non-Code-Assist quota source (e.g., AI Studio API key usage) — Gemini CLI's primary OAuth login is the only auth path covered.
Related
- Parent epic: #9685 — Add Gemini and OpenCode monitoring to the Agents panel
- Sibling: #9683 — Add OpenCode collector to the Agents panel
- Quota mechanism (
loadCodeAssist+fetchAvailableModels) verified live end-to-end 2026-09-01 via a Google account OAuth token (Antigravity CLI's own login, same Code Assist backend), including real per-modelremainingFraction/resetTimevalues. Reference implementation read:antigravity-usagenpm package (src/google/cloudcode.ts), installed locally.
View raw text
## Context
Gemini CLI is a supported default agent in Omarchy (`omarchy-default-agent gemini`) but has no usage collector, so daily Gemini usage never appears in the `omarchy.agents` bar panel.
## Current State
- Gemini CLI 0.57.0 installed, but local session storage is empty on the machine this spec was written on — local-stats parsing is unverified against a real file.
- Documented (not yet locally verified against a real file) session storage: `~/.gemini/tmp/<project-hash>/chats/logs.json`, one hashed directory per project root (gemini-cli session-management and checkpointing docs).
- A live quota mechanism exists and was **verified live end-to-end** (2026-09-01), using a Google OAuth token from the same account (obtained via the separately-authenticated Antigravity CLI, which shares the same Code Assist backend `cloudcode-pa.googleapis.com`). The mechanism is **not** a single `retrieveUserQuota` call (an earlier web search pointed at that method name; it does not appear anywhere in a real, working, open-source client and was not verified) — it is two calls, confirmed by reading the `antigravity-usage` npm package's own source (`src/google/cloudcode.ts`) and reproducing both requests live:
1. `POST /v1internal:loadCodeAssist`, body `{"metadata": {"ideType": "ANTIGRAVITY", "platform": "PLATFORM_UNSPECIFIED", "pluginType": "GEMINI"}}` → `200`, returns `cloudaicompanionProject` (a project id, e.g. `isometric-bonfire-pqb1d`) plus `currentTier`/`allowedTiers`.
2. `POST /v1internal:fetchAvailableModels`, body `{"project": "<projectId from step 1>"}` → `200`, returns `models` as a **dict keyed by model id** (not an array), each entry shaped:
```json
{"displayName": "Gemini 3.6 Flash (Low)", "quotaInfo": {"remainingFraction": 0.9911605, "resetTime": "2026-09-02T01:45:13Z"},
"model": "MODEL_PLACEHOLDER_M73", "apiProvider": "API_PROVIDER_GOOGLE_GEMINI", "modelProvider": "MODEL_PROVIDER_GOOGLE", ...}
```
Both headers: `Authorization: Bearer <token>`, `Content-Type: application/json`, `User-Agent: antigravity`. My first attempt at this endpoint, sent without the `metadata` object, returned `403 SUBSCRIPTION_REQUIRED` — that was a malformed-request rejection, not a real licensing gate; with the correct body it succeeded on this same (personal, unlicensed-for-Enterprise) account.
- **Open question, not yet resolved**: this was verified using Antigravity's own client identity (`ideType: "ANTIGRAVITY", pluginType: "GEMINI"`). Gemini CLI is a separate client against the same backend and may (or may not) need a different `ideType`/`pluginType` to identify itself, and may return the same account-wide quota or a differently-scoped one. Confirm by reading `google-gemini/gemini-cli`'s own request code, or by testing once Gemini CLI is actually logged in somewhere, before hard-coding the metadata values into the collector.
- Gemini CLI's own OAuth credential lives at `~/.gemini/oauth_creds.json` (standard location per third-party tooling that reads it) — absent on the machine this spec was written on, since Gemini CLI was never logged in there. The collector must degrade gracefully exactly like `omarchy-agent-usage-claude` does when `.credentials.json` is missing.
## Proposed Change
Add `bin/omarchy-agent-usage-gemini`: local stats from `~/.gemini/tmp/*/chats/logs.json` across every project hash (mirroring how the Claude collector scans `~/.claude/projects` recursively), plus a best-effort live limits probe against the Code Assist quota mechanism using `~/.gemini/oauth_creds.json`, degrading to stats-only with a clear `usageStatusText` when unauthenticated or when the two-call probe fails.
### Implementation Details
**New file: `bin/omarchy-agent-usage-gemini`**
- Local scan: `~/.gemini/tmp/*/chats/logs.json` (glob every project hash). **Research task, not yet resolved by this spec**: confirm the exact per-message JSON shape (role, model, token usage field names) against a real file before writing the parser — either generate one locally (a throwaway `gemini -p` call) or read the authoritative field names from the open-source `google-gemini/gemini-cli` parser. Do not guess field names.
- Live limits: read `access_token` + `expiry` from `~/.gemini/oauth_creds.json` (same shape verified live in this spec: `{access_token, token_type, refresh_token, expiry}`); if missing or expired, skip the probe (no refresh-token exchange in v1 — matches Claude's own collector, which also never refreshes, only reports "sign-in expired").
- Two-call probe against `https://cloudcode-pa.googleapis.com`, headers `Authorization: Bearer <access_token>`, `Content-Type: application/json`, `User-Agent: <TBD — confirm Gemini CLI's own value, see open question above>`:
1. `POST /v1internal:loadCodeAssist` with the correct `metadata` object (exact `ideType`/`pluginType` for Gemini CLI: open question above) → extract `cloudaicompanionProject`.
2. `POST /v1internal:fetchAvailableModels` with `{"project": "<id from step 1>"}` → iterate the `models` dict, keep entries where `apiProvider == "API_PROVIDER_GOOGLE_GEMINI"`, map each `quotaInfo.remainingFraction`/`quotaInfo.resetTime` into the standard `limits: [{label, percent, resetsAt}]` shape (`percent = 1 - remainingFraction`, matching the "percent used" convention every other collector already uses; `label` = `displayName`).
- Any error at either step (401/403/429/5xx/transport failure): same graceful pattern as `probe_limits()` in the Claude collector — `usageStatusText` set to a short, honest reason, `limits: []`, local stats still shown, collector never crashes.
- Same wrapper record shape as every other collector: `schemaVersion: 1, id: "gemini", name: "Gemini", updatedAt, ready, hasLocalStats, tierLabel, usageStatusText, authHelpText, limits` merged with the stats bucket.
**`shell/plugins/agents/manifest.json`**: add `"gemini": {"enabled": true}` to `defaults.providers`.
**`shell/plugins/agents/assets/gemini.svg`** (+ `-light` if needed): Google's public Gemini mark.
**`shell/plugins/agents/README.md`**: add a row to the collector table documenting the two-call quota mechanism (`loadCodeAssist` + `fetchAvailableModels`) and the local log path.
## Acceptance Criteria
1. On a machine with real `~/.gemini/tmp/*/chats/logs.json` files, the collector prints a valid record with correct `totalPrompts`/token aggregates — must be verified against a real file before merge, since the local format is unconfirmed as of this spec.
2. On a machine with no Gemini session data, the collector exits cleanly with `ready: false`, no crash.
3. With the correct `metadata` body (once Gemini CLI's own `ideType`/`pluginType` is confirmed), `limits` contains at least one entry with a `percent` between 0 and 1 and a valid `resetsAt` — reproducing the live result already verified in this spec (0.9911605 `remainingFraction` on a real account, via the same backend under Antigravity's client identity).
4. On any error at either probe step (401/403/429/5xx/transport), `usageStatusText` carries a short honest reason, `limits: []`, local stats still shown — no exception, no stack trace.
5. `omarchy agent usage-update gemini` writes only `gemini.json`, leaving every sibling untouched.
6. No changes to Claude, Codex, Fireworks, or OpenCode collector behavior.
## Testing Plan
| Layer | What | Count |
|-------|------|-------|
| Unit | `logs.json` aggregation from a fixture (once the real shape is confirmed) | +4 |
| Unit | Quota probe: `loadCodeAssist`+`fetchAvailableModels` 200/200 → limits populated (fixture from the real verified response shape); either call failing → graceful stats-only | +3 |
| Integration | `omarchy-agent-usage-update gemini` writes only its own file | +1 |
| Manual | Live panel shows a Gemini tab once a real session exists on a test machine | +1 |
## Rollback Plan
Delete `bin/omarchy-agent-usage-gemini`, remove `gemini` from `manifest.json:defaults.providers`, delete stale `~/.local/state/omarchy/agents/usage/gemini.json` if present.
## Effort Estimate
~6h — 1h confirm the real `logs.json` shape + Gemini CLI's own `ideType`/`pluginType` (open research items), 2h collector script, 1h quota-probe error handling, 1h manifest/assets/README, 1h tests.
## Open Research Items
1. The exact `~/.gemini/tmp/*/chats/logs.json` per-message field names are sourced from documentation, not verified against a real file (none existed on the machine this spec was written on). Before merge, either generate one real session or read the field names directly from the open-source `google-gemini/gemini-cli` parser.
2. This spec verified the `loadCodeAssist`+`fetchAvailableModels` quota mechanism live, but under Antigravity's own client identity (`ideType: "ANTIGRAVITY", pluginType: "GEMINI"`), not Gemini CLI's. Confirm Gemini CLI's own values (likely visible in `google-gemini/gemini-cli`'s request code) before hard-coding them into the collector — using the wrong identity may return the wrong quota scope, or none at all.
## Files Reference
| File | Change |
|------|--------|
| `bin/omarchy-agent-usage-gemini` | New collector |
| `shell/plugins/agents/manifest.json` | Add `gemini` to `defaults.providers` |
| `shell/plugins/agents/assets/gemini.svg` | New mark |
| `shell/plugins/agents/README.md` | Document the new collector row |
| `bin/omarchy-agent-usage-claude` (reference only) | `oauth_login()` / `probe_limits()` / `collect_limits()` are the pattern to mirror for graceful auth degradation |
## Out of Scope
- Refresh-token exchange (v1 reports "sign-in expired" rather than silently refreshing, matching Claude's own collector).
- Any non-Code-Assist quota source (e.g., AI Studio API key usage) — Gemini CLI's primary OAuth login is the only auth path covered.
## Related
- Parent epic: #9685 — Add Gemini and OpenCode monitoring to the Agents panel
- Sibling: #9683 — Add OpenCode collector to the Agents panel
- Quota mechanism (`loadCodeAssist` + `fetchAvailableModels`) verified live end-to-end 2026-09-01 via a Google account OAuth token (Antigravity CLI's own login, same Code Assist backend), including real per-model `remainingFraction`/`resetTime` values. Reference implementation read: `antigravity-usage` npm package (`src/google/cloudcode.ts`), installed locally.
Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
Not linked to any known issue, other report or change yet.
Decision
Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.
- Out of scopeDecided by agents · verified · 2026-10-09 10:29 UTC