Add daily cost and plan usage history for OpenCode Go#2296
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d6c1a90b91
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| case .mistral: | ||
| snapshot?.mistralUsage?.toCostUsageTokenSnapshot(historyDays: self.settings.costUsageHistoryDays) | ||
| case .opencodego: | ||
| snapshot?.opencodegoUsage?.toCostUsageTokenSnapshot(historyDays: self.settings.costUsageHistoryDays) |
There was a problem hiding this comment.
Suppress empty OpenCode Go cost snapshots
When OpenCode Go comes from the web strategy in explicit Web mode, or when the local database has no cost rows, opencodegoUsage is still present but daily is empty because no local cost history was read. This new provider-derived projection is non-nil anyway, so refreshTokenUsage records it as a successful token snapshot and the menu can render a Cost row with only “Today: — / Last N days: —” while makeCostHistorySubmenu returns nil. Gate this on !opencodegoUsage.daily.isEmpty or surface the no-data state so Web-only/no-history users do not get a dead Cost row.
Useful? React with 👍 / 👎.
|
Codex review: needs real behavior proof before merge. Reviewed July 18, 2026, 4:22 PM ET / 20:22 UTC. Summary Reproducibility: not applicable. This PR adds a provider capability rather than reporting a broken existing contract. The remaining validation question is whether the new source-mode behavior runs as documented in a packaged app. Review metrics: 2 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Keep the documented source boundary: expose local daily cost only from the local fallback path, preserve Web-only behavior without local reads, and merge once packaged-app proof demonstrates both paths. Do we have a high-confidence way to reproduce the issue? Not applicable: this PR adds a provider capability rather than reporting a broken existing contract. The remaining validation question is whether the new source-mode behavior runs as documented in a packaged app. Is this the best way to solve the issue? Yes in design: reusing the established plan-history and cost-chart surfaces while keeping workspace-scoped web data separate from device-wide local history is the narrow maintainable path. The final runtime boundary still needs direct evidence. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 841de0de552c. Label changesLabel justifications:
Evidence reviewedWhat I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (8 earlier review cycles)
|
Mirrors the Codex/Claude menu structure: the top rate-limit pane stays submenu-free, a "Plan Usage" row hover-expands into 5-hour/weekly/monthly history tabs, and a "Cost" row hover-expands into a daily $ chart sourced from the local opencode-go SQLite history (~/.local/share/opencode/opencode.db). The web-sourced fetch strategy best-effort enriches its rolling/weekly percentages with the local daily buckets (opencode.ai has no daily- granularity endpoint), but skips that local read entirely when the user explicitly selects Web-only source mode. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Web-only source mode and machines without a readable local database leave opencodegoUsage present but daily empty, which previously still produced a non-nil CostUsageTokenSnapshot and surfaced a Cost row with no history to show. Return nil until daily entries exist instead. Addresses PR review feedback on steipete#2296. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
d6c1a90 to
c2e0bbd
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c2e0bbdcab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| tokenCostMenuSectionEnabled: !UsageMenuCardView.Model.usesProviderCostHistoryAsPrimaryDashboard(target) && | ||
| self.settings.costSummaryShowsSubmenu(for: target), |
There was a problem hiding this comment.
Include OpenCode Go in inline cost rendering
When the user has Cost summary set to Inline only, costSummaryShowsSubmenu(for:) is false, so this new gate disables the generic Cost row for OpenCode Go even when opencodegoUsage.daily is populated. The only remaining inline cost renderer is the allow-list in InlineUsageDashboardContent.inlineUsageDashboard ([.codex, .claude, .vertexai, .bedrock, .cursor]), which does not include .opencodego, so OpenCode Go cost history disappears entirely until the user switches to Submenu/Both. Add OpenCode Go to the inline cost dashboard path or keep its cost row enabled in inline-only mode.
Useful? React with 👍 / 👎.
opencodego was missing from the shared CostUsageTokenSnapshot inline dashboard path (matching Codex/Claude/Vertex/Bedrock/Cursor), so users with Cost summary set to "Inline only" saw no OpenCode Go cost data at all: the Cost row is suppressed in that style, and there was no inline fallback either. Adds opencodego to that path so it now matches those providers exactly, including showing both the Cost row and the inline chart together in "Both" style. Addresses PR review feedback on steipete#2296. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 54517230ac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // dedicated top-pane submenu (openai/mistral) skip the generic "Cost" row; other | ||
| // `tokenCostRequiresProviderSnapshot` providers (e.g. opencodego) show real rate-limit | ||
| // bars in the top pane instead and need the dedicated row to surface cost at all. | ||
| tokenCostMenuSectionEnabled: !UsageMenuCardView.Model.usesProviderCostHistoryAsPrimaryDashboard(target) && |
There was a problem hiding this comment.
Keep Groq's cost submenu reachable
When the selected provider is Groq and Cost summary is set to Submenu/Both with daily history, this predicate is false because usesProviderCostHistoryAsPrimaryDashboard(.groq) returns true. Groq is not handled by the top-pane submenu path in makeUsageSubmenu (it only special-cases OpenAI/Mistral/Zai), so the sectioned menu gets tokenUsage == nil, no menuCardCost row is added, and the standalone Cost item is skipped because the web items were already handled; the Groq cost history chart becomes unreachable. Keep Groq out of this gate or route its usage row to makeCostHistorySubmenu.
Useful? React with 👍 / 👎.
tokenCostMenuSectionEnabled reused usesProviderCostHistoryAsPrimaryDashboard to exclude openai/mistral from the generic "Cost" row. That predicate is shared with an unrelated concern (inline-dashboard eligibility) and later gained .groq for its own reasons, which would have silently excluded groq from this row too. Replace it with an explicit openai/mistral check so this gate no longer tracks membership changes made for other purposes. In practice groq's tokenCost.supportsTokenCost is false, so its Cost row was already unreachable independent of this gate — verified with a test locking in that groq's cost data stays visible via the inline dashboard regardless of how this predicate is defined. Addresses PR review feedback on steipete#2296. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The name implied a provider-native top-pane submenu (matching hasOpenAIAPIUsageSubmenu's naming), but opencodego satisfies this via its collapsible "Cost" row, not a top-pane submenu. Unlike the two predicate-reuse bugs fixed earlier in this PR, this reuse of tokenCostRequiresProviderSnapshot is intentional: any provider whose cost is sourced by projecting a UsageSnapshot field can only render that cost through addMenuCardSections's sectioned layout, so the two concepts are genuinely coupled here. Renamed to say what it actually gates and documented why the reuse is safe, so a future reader doesn't mistake it for the same bug class. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 825e2f868d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| private func shouldRecordPlanUtilizationHistory(for provider: UsageProvider) -> Bool { | ||
| switch provider { | ||
| case .codex, .claude, .antigravity: | ||
| case .codex, .claude, .antigravity, .opencodego: |
There was a problem hiding this comment.
Scope OpenCode Go history by source and workspace
When Auto mode falls back from workspace-scoped web usage to the device-wide local estimate, or the configured workspace changes, these samples are all persisted because both OpenCode Go paths produce UsageSnapshots with identity: nil; resolvePlanUtilizationAccountKey therefore puts them in the same unscoped bucket. The Plan Usage chart consequently connects percentages belonging to different scopes/accounts. Attach a stable workspace/source owner to the snapshot, or avoid recording identityless OpenCode Go samples.
Useful? React with 👍 / 👎.
Chipagosfinest
left a comment
There was a problem hiding this comment.
Adversarial release-gate verdict: keep this separate from #2307 and do not merge the current head yet. The latest unresolved source/workspace ownership finding at #2296 (comment) can connect plan-history samples from different scopes because both OpenCode Go paths persist identityless snapshots into the same bucket. The earlier UI regressions appear addressed, but this ownership boundary plus the Auto-mode local-enrichment decision and Web-only/Inline-only runtime proof should be closed before merge. Exact-head macOS CI is also still queued.
|
Update for current head |
|
Merged as Verification:
Maintainer fixes preserve step-level request timestamps, keep successful workspace-scoped web snapshots free of device-wide local cost data, and store the monthly plan lane under the provider-neutral Live OpenCode Go account/database verification was unavailable, as expected for this review. No real provider probe or Keychain-backed signed-bundle test was run; parser, SQLite fixture, snapshot, menu-model, plan-history, and full CI coverage were used instead. |
Summary
CostHistoryChartMenuViewCodex/Claude use) sourced from the localopencode-goSQLite history at~/.local/share/opencode/opencode.db.step-finishparts.Screenshots
Plan usage pane



Cost pane
Inline graph
Test plan
swift build(CodexBar, CodexBarCore, CodexBarCLI) — cleanswift testsuite — all tests touching the changed files pass; the only failures are pre-existing timing-sensitive PTY/Keychain tests unrelated to this diffswiftformat --lint/swiftlintclean on all touched filesCodexBar.appbuild against a realopencode-golocal databaseCo-Authored-By: Claude Sonnet 5 noreply@anthropic.com