Repository navigation
Conversation
Automated AI review
The collector's structure fits the agents-panel contract, but on stores written by current OpenCode clients it reports no local usage, and the Go meters misread low percentages. Five issues follow, the first two with the widest effect. All five also apply to the current Verified (source review plus synthetic runs of the collector):
1. Local stats skip the
|
Go API percent (rolling, weekly, monthly) |
Collector output | Panel (from Panel.qml source) |
|---|---|---|
| 1, 0, 0 | 1.0, 0.0, 0.0 | Session 100%, alarm state (≥ 0.9) |
| 0, 1, 1 | 0.0, 1.0, 1.0 | Weekly and Monthly 100% |
| 2, 1, 0 (control) | 0.02, 0.01, 0.0 | correct |
Impact: A light user sees full quota meters, the fullest window becomes the headline, and the bar icon turns urgent.
Suggested change: Divide by 100 unconditionally for this endpoint.
3. --limits-only reuses the whole cached record for up to 900 s
main() (lines 422-448) caches the complete record in opencode-scan.json, including limits and retryAdvised, and returns it for 900 s under --limits-only. collect_limits() never runs on a cache hit. The Claude collector caches only the local scan and always calls collect_limits().
t0 normal run, endpoint unreachable -> no limits, retryAdvised
t0 + 30s --limits-only (panel retry), endpoint healthy -> cached record, 0 probes, retryAdvised still set
... repeats every 30 s until the scan cache expires or a normal refresh runs
Reproduced with a stubbed endpoint. A later change from 40% to 90% usage was also not picked up by --limits-only. The panel runs --limits-only when opened and for its 30 s retry (from Main.qml/Panel.qml source).
Impact: A network failure at login can leave Go limits missing for up to 15 minutes instead of about 30 s. Opening the panel can show limits up to 15 minutes old.
Suggested change: Cache only the local scan, as the Claude collector does, and always call collect_limits(). SCAN_REUSE_SECONDS and LIMITS_ONLY_REUSE_SECONDS are defined but unused, and main() hardcodes 20 and 900.
4. Cached Go windows hide a rejected or removed key
When the probe returns 401 or 403, or the Go key is removed from auth.json, collect_limits() (lines 383-413) returns the cached windows that have not reset yet. It skips setting usageStatusText and authHelpText, and the probe cache is never cleared. main() then sets tierLabel to Go because limits are non-empty (line 440). In synthetic runs with an open cache, 401 and 403 both produced Go, three frozen meters and empty status text. Without a cache, the same responses showed "OpenCode limits unavailable" with the matching help text.
Impact: After a key is revoked or a Go subscription ends, the panel keeps showing "Go" and last-known meters with no warning, up to about a month for the monthly window. The public endpoint source treats 403 as a definite "OpenCode Go subscription required" answer.
Suggested change: On 401/403, set usageStatusText and authHelpText even while showing cached windows, or drop the fallback on 403. Derive Go from a fresh result or the saved key rather than from cached limits alone.
5. A Zen-only key is used to probe Go
access_token() (lines 269-285) falls back to any auth.json entry whose name contains "opencode". OpenCode stores a Zen key under opencode and a Go key under opencode-go (packages/tui/src/component/dialog-provider.tsx, packages/opencode/src/auth/index.ts). In OpenCode's public endpoint source, a key without a Go subscription gets 403. With a stubbed 403, the record showed "OpenCode limits unavailable" and "The saved API key has no OpenCode Go subscription behind it."
Impact: A Zen-only user sees an "OpenCode limits unavailable" warning, which contradicts the README text added here: it says users without Go get no limits section. While finding 1 stands, such a user's tab is usually hidden anyway.
Suggested change: Probe only with opencode-go, or treat a 403 on a fallback key as "no Go" without a warning.
Optional test improvement: The Claude, Codex and Fireworks collectors each have scanner or limits tests under test/shell.d/, and this collector has none. A scanner case with populated message rows and a limits case with percent: 1 reproduce findings 1 and 2 in the synthetic runs above, so they would catch those regressions.
Review information
Test scope: Source review of the collector, the agents panel (Main.qml, Panel.qml) and the sibling collectors at the PR head, with the later quattro tip checked for changes to these paths. OpenCode upstream source read at release v1.18.32, with earlier releases checked for the schema history. The Go usage endpoint's public source was read as well. The collector ran only in isolated synthetic fixtures: synthetic SQLite stores built from upstream DDL, fake keys and a stubbed endpoint. No OpenCode binary, live Go account or network request was used, so the author's live-system result was not reproduced. Panel effects come from QML source, not desktop rendering. Responses for keys routed through OpenCode's inference proxy (for example oc_sk_ keys) are not established by public source. ./test/all was not run.
AI process: Opus 5.5 Medium coordination and synthesis, independent Opus 5.5 Xhigh and GPT 6 Sol Xhigh technical assessments with targeted follow-up questions, Opus 5.5 Medium editorial check.
Opt out: To stop receiving these reviews, reply to this comment saying so.
|
Thanks! OpenCode usage is coming in through #14040, which counts only opencode's own providers so nothing doubles with the Claude and Codex collectors. Closing in favor of that. |
Why
The agents panel is one of the nicest parts of Omarchy, and OpenCode is one of the supported default agents (
omarchy default agent opencode), but the usage panel has no OpenCode tab. If you have moved entirely to OpenCode (for example OpenCode Go with GLM), the panel keeps showing a stale Claude Code tab with a sign-in warning and there is no way to see what OpenCode actually consumes.Omarchy already has the right architecture for this: the collectors and the agents panel are decoupled, and the plugin README documents that adding an agent is just shipping a collector. This PR fills the gap for OpenCode following exactly that contract.
What the collector does
session_message(~/.local/share/opencode/opencode.db); older stores have the legacymessagetable with the same data inlined in the JSON. The collector detects which shape exists (and never reads both — migrated rows would count twice). Every assistant message with token usage feeds the tokens-by-day and tokens-by-model charts; every user message counts as one prompt.GET https://opencode.ai/zen/go/v1/usageendpoint (Bearer auth). The record carries the same limit entries the claude collector produces, so the panel shows meters and "Resets in" times with no panel changes. Probe results are cached the way the claude collector caches its probe (15s reuse window; kept as the answer of record on failure, and only until each window rolls over).auth.jsonopencode writes. It never lands in a record, a cache, or the printed JSON; it only ever appears in the Authorization header of the probe. One practical note: the endpoint answers plainPython-urllibagents with 403 while ordinary clients pass, so the probe sends a custom User-Agent.Gowhen a live subscription is present,APIotherwise (pay-per-token through other providers). An OpenCode mark ships asassets/opencode.svgplus a-lighttwin for light surfaces; the mark comes from opencode's MIT-licensedpackages/identity/mark*.svg, with the filled backgrounds stripped to match the other marks.Provider enablement defaults to
truein the manifest, so the tab only appears when OpenCode was actually used.How I tested
./test/allpasses except 5 pre-existing environment failures that reproduce on a pristine checkout ofquattro(config-test,locate-test,omarchy-kernel-migration-test,snapper-test,unowned-system-paths-test— they expect an omarchy-pkgs checkout and system dirs a clean machine does not have). On my machine the record is correct against a live OpenCode Go subscription; the screenshots below are from a real system running this collector.Happy to adjust anything — record contract details, cache windows, naming, or the mark assets.
Screenshots