Repository navigation
feat: show plan limits and spend in the sidebar - #1026
Merged
Merged
Conversation
The sidebar footer becomes one status strip: you, a ring per agent plan draining as its lowest window runs out, and the machine, which also shows when it is offline. The rings open a panel with each plan's windows, the week's compute cost by backend, and a daily graph of agent or compute spend. The composer's context ring becomes a menu with the compaction point, the chat's plan and its cost. A Codex plan holding a usage reset offers to use it, after a confirmation that shows what is left. Plan limits come from the agents themselves (Claude Code's get_usage, Codex's account/rateLimits/read) and are always shown as what is left. Agent cost is what each agent reports, or models.dev's price for agents that report only tokens, fetched once a day. Compute cost uses the price each backend quoted when the run launched. Nothing is a hardcoded price, and costs stay on the machine. Closes #807. Assisted-by: claude-opus-5.5
Contributor
[Medium impact] The changes reach local plan reads, account usage, and dashboard spend views. Fix the missing finished-job costs and required read-error display before merging. Findings
Fix with agent prompt### Issue 1
src/local/spend.rs:149-151
A paid job can finish between provider checks without ever being observed as running. That leaves `started_at` empty, so `running_time` drops it from the weekly totals. `chat_compute` still counts the run but adds neither cost nor an unpriced warning.
Keep finished jobs with an unknown start visible as unpriced, or recover their running time before totaling them.
### Issue 2
ui/src/queries/settings.ts:206-210
`staleTime` does not schedule another read. After the first report, an idle sidebar can keep showing the same limits and spend indefinitely. `observed` also drops limit changes received within the fresh minute without scheduling a later read.
Add a bounded refresh while the sidebar is visible, or schedule a read when a limit change is deferred.
### Issue 3
src/local/codex.rs:1049-1055
`live_client` can return a child before its startup handshake finishes. `ensure_with_spawn` puts the child in `inner` before calling `handshake`, and `account_request` immediately writes through the returned client. Unlike `reusable_client`, this lookup does not hold `spawn_lock`.
Wait for startup to finish before returning the child. Otherwise, usage reads and resets can send account requests before `initialize` completes, risking a rejected request.
### Issue 4
ui/src/components/StatusStrip.tsx:24-26
If the first `/api/usage` request fails, `report` is empty and the usage segment disappears. The user sees the same result as a machine with no usage, with no error or retry.
This violates the repository requirements “Unknown is not absent” and “Every failure reaches someone who can act on it.” Show the read error and a retry action before merging. Keep the existing quiet fallback for an older server’s 404.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.SummaryAdds plan limits and spend to the sidebar and chat composer.
Completed paid jobs can be omitted from spend when their start was never observed. The review also found missing refresh scheduling, a Codex startup race, and hidden first-read errors. Checks were source-based; no runtime tests or visual checks were performed.
|
A paid job first seen after it finished counts as unpriced instead of vanishing from spend, and a backend with priced and unpriced runs says so beside its total. Limits a running chat reports within a minute of the last read wait for that snapshot to go stale instead of being dropped, also when a read finishes while they wait. The strip reads usage again every five minutes while visible; a usage read never reaches a Codex app-server before its handshake; and a failed read keeps the segment, dimmed, with the last values, the error and Try again, instead of hiding it or showing stale numbers as current. Assisted-by: claude-opus-5.5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The sidebar footer becomes one status strip: you, a ring per Claude Code or Codex plan that drains as its lowest window runs out, and the machine, which also shows when it is offline. The rings open a panel with each plan's windows (always shown as what's left), a Codex usage reset when the account holds one, the week's compute cost by backend, and a daily AI or compute spend graph. The composer's context ring becomes a menu with the compaction point, the chat's plan and, when billed per token, its cost.
The choices that are expensive to undo:
get_usagecontrol request on a throwaway child, Codex'saccount/rateLimits/readthrough the chat's running app-server when there is one. orx never reads their tokens. Claude has no public way to use its resets, so only Codex's are offered.cost_micros,cost_currencyandrecorded_atonchat_usage_samples, andprice_jsononruns: the price a backend quoted when the run launched, never rewritten. Contract fixtures cover both directions.GET /api/usage, andPOST /api/usage/refreshand/api/usage/resetbehind the Origin guard.It adds about 7,000 lines: roughly 3,000 are tests and 700 are strings across the 11 catalogs. Two size reviews (Opus and GPT-6.1 Sol) found it about right, and their cuts are in.
Tested on macOS against the dev server in WebKit and Chromium, and in the desktop window: real Codex on ChatGPT Pro (plan, and its one reset credit read but never spent) and Claude Code on an API key (chat cost). Not tested: a Claude subscription, agent profiles, a priced compute run, spending a reset, and the Windows and Linux windows.
Closes #807.
Proof: the full Rust, UI and e2e suites on this diff, and the running app in WebKit and Chromium against this machine's real Codex plan and agent costs (screenshots below).
Reviewed with sol-loop: 3 rounds with GPT-6.1 Sol on high. Sol verdict: approved
Assisted-by: claude-opus-5.5