Repository navigation
feat(agents): add OpenCode Go collector with watermark incremental scan - #6464
markbus-ai wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds OpenCode usage reporting to the model-usage panel alongside Claude Code and Codex.
Changes:
- Adds a SQLite usage scanner and OpenCode provider adapter.
- Integrates OpenCode into the panel, defaults, synchronization, and icons.
- Adds scanner and widget-contract tests plus documentation.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 8 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
shell/plugins/model-usage/Main.qml |
Registers and aggregates OpenCode usage. |
shell/plugins/model-usage/Panel.qml |
Adds OpenCode icon selection. |
shell/plugins/model-usage/README.md |
Documents OpenCode behavior and configuration. |
shell/plugins/model-usage/manifest.json |
Enables OpenCode by default. |
shell/plugins/model-usage/providers/OpenCode.qml |
Implements the provider adapter. |
shell/plugins/model-usage/scripts/opencode_usage_scanner.py |
Scans OpenCode’s SQLite session data. |
shell/plugins/model-usage/assets/opencode.svg |
Adds the dark-surface icon. |
shell/plugins/model-usage/assets/opencode-light.svg |
Adds the light-surface icon. |
test/shell.d/model-usage-opencode-scanner-test.sh |
Tests scanner aggregation and missing data. |
test/shell.d/fixtures/bar-widget-contract/shell.qml |
Tests OpenCode icon selection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| query = f""" | ||
| SELECT {", ".join(select_cols)} | ||
| FROM session | ||
| WHERE COALESCE(tokens_input, 0) | ||
| + COALESCE(tokens_output, 0) | ||
| + COALESCE(tokens_cache_read, 0) | ||
| + COALESCE(tokens_cache_write, 0) | ||
| + COALESCE(tokens_reasoning, 0) > 0 | ||
| """ |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (1)
shell/plugins/model-usage/scripts/opencode_usage_scanner.py:169
- The scanner takes the full
messagepath that the PR explicitly says it avoids: every refresh selects and JSON-parses every message, andtotalPromptsis consequently counted per assistant message rather than per usage-bearing session. Current OpenCode databases exposetokens_input,tokens_output,tokens_reasoning, and cache aggregates onsession, so this also misses the advertised millisecond-scale aggregate scan and can become expensive on large histories. Please use the session aggregates as the primary path (with a legacy message-schema fallback if needed) and update the fixture/semantics accordingly.
rows = conn.execute("SELECT id, session_id, time_created, data FROM message")
|
Hmmm. I'm a little skeptical on this. I think what we could need is the subscription status for OpenCode Zen and Go. But it needs to expose the limits. That's actually what makes this useful. |
|
@dhh Agree — the useful part is Go/Zen subscription meters, not local token totals. I dug into what OpenCode actually exposes:
So this PR’s local |
|
Maybe we can convince @thdxr to add this! |
|
@dhh The endpoint you're describing is already requested upstream — we don't need to invent the shape:
That maps 1:1 onto the session/weekly meter pair the panel already renders for Claude and Codex. If @thdxr's team exposes it to API keys, this provider becomes the real product (plan %, reset countdown, bar alarm) with the local I'd say: keep this PR as the ready-to-rebase foundation, and let's put the +1 with the Omarchy use case on #16017 so the OpenCode team sees a concrete consumer asking for it. |
|
Heads up: #6603 just landed on The Usage collection now lives in standalone collectors in The upside: adding an agent no longer touches the panel at all. Ship a collector that prints the record contract, optionally add an If you'd like to update this PR to the new structure, rebase on — Claude Fable 5, written on behalf of @dhh |
|
Closing this in favor of #6779, which implements the OpenCode Go collector in the new omarchy.agents architecture (standalone collector + assets + tests) and now uses the official usage endpoint (GET /zen/go/v1/usage) for the rolling/weekly/monthly meters dhh asked about. This PR predates the #6603 restructure that removed the model-usage plugin it targets, so keeping it open would just confuse the history. Thanks for the discussion. |
Add an omarchy-agent-usage-opencode-go collector that feeds a new Go subscription tab in the agents panel. Local token/session stats come from opencode's message database (filtered to the opencode-go provider) via a pure-SQL scan with a watermark incremental cache: a fast path serves the envelope without opening SQLite when the database is unchanged, and a changed database only rescans rows newer than the last watermark (with a 10-minute overlap band) and merges the deltas. Rate limits come from OpenCode's zen usage endpoint (session/weekly/monthly), with the key read from the opencode-go auth entry or OPENCODE_GO_API_KEY, https-only base URL validation, and a safe redirect handler that strips the Authorization header on origin changes including https->http downgrades. Hardens the cache layer: 0600/0700 permissions, O_NOFOLLOW locks, atomic temp+replace writes, stale-tmp sweep, multi-db channel selection, and graceful degradation when the cache or limits cache is unavailable. Ships the opencode-go mark (+ -light), the scanner test suite, and a README row. Refs: feat/model-usage-opencode
696158f to
3659dc9
Compare
Adds native support for OpenCode Go subscriptions in the agents panel. Queries OpenCode's official /zen/go/v1/usage API for rolling (5h), weekly, and monthly limit usage with reset timers. Unique features compared to other PRs (omacom#7157, omacom#8065, omacom#6526, omacom#6779, omacom#6464): - Pi session scanning for local token stats - Harness-agnostic key discovery (Pi, OpenCode CLI, env var) - Account-scoped metadata (scope, hasPromptStats) Key design decisions: - Uses curl instead of urllib to bypass Cloudflare TLS fingerprinting - Includes light/dark icon variants matching OpenCode brand colors - Adds test coverage for auth lookup, API parsing, and error handling
|
Automated duplication check: this pull request looks similar to #8065, which covers the same OpenCode usage collector. I keep that one open and close this one to consolidate review. If you feel this is the wrong decision, please open the PR again with a note on the difference. |
Summary
Adds an OpenCode Go collector to the
omarchy.agentsusage widget, in the standalone-collector architecture (bin/omarchy-agent-usage-<agent>prints a display-ready JSON record; the panel discovers it by convention, no panel changes needed).The collector shows the real subscription meters dhh asked for — rolling 5-hour / weekly / monthly windows as percentages with reset times from OpenCode's official usage API — plus token stats from the local
opencode.db.What's new (vs the earlier model-usage approach)
bin/omarchy-agent-usage-opencode-goshell/plugins/agents/assets/opencode-go.svg(+-light)test/shell.d/agent-usage-opencode-scanner-test.shHighlights
GET https://opencode.ai/zen/go/v1/usagewith the Go API key from~/.local/share/opencode/auth.jsonorOPENCODE_GO_API_KEY. Handles both the deployed response shape (percent/resetsAt) and the upstream-PR shape (usagePercent/resetInSec), with payload-wide percent-scale detection (no meter discontinuity after a window reset).time_created), the db/WAL mtimes, and a 10-minute overlap band with id-dedupe, so a normal refresh reads only what changed. On a 1.9 GB / 62k-row database:json_extractexact-match onrole/providerIDis the authority (LIKE prefilters are pure acceleration); rows stream as lightweight columns, raw JSON blobs never accumulate in Python.opencode-*.dbfiles it prefers the one with the newest WAL activity, so users on a release channel see the right database instead of a stale default.http://with no key sent, loud warning on non-default host), Authorization stripped on any(scheme, netloc)redirect change (including https→http downgrade), O_NOFOLLOW lock, cache files 0600 / dir 0700, emptyXDG_CACHE_HOMEfallback, cache failures always degrade to a valid record — the collector never prints an empty stdout.--forcealways rewrites a fresh scan even when the table shrank.Test plan
Notes
usageStatusText/authHelpTextand still emits a valid record with local stats. Both payload shapes are unit-tested.