feat(desktop): show running session cost in the desktop status bar (#67765) - #67834
feat(desktop): show running session cost in the desktop status bar (#67765)#67834DavidMetcalfe wants to merge 1 commit into
Conversation
…tainer review) Draft placeholder commit to back the PR linked from NousResearch#67765. The implementation itself lands in a follow-up commit once a maintainer weighs in on chip placement and config-toggle conventions. See the PR body for the full design rationale.
72cb503 to
95a042d
Compare
|
Thanks for documenting the intended scope before implementation. Problems
Suggested changes
Automated hermes-sweeper review. |
|
@teknium1 Thanks for the review — and for calling out the prior policy from This PR's design is a narrowing of that policy, not a reversal. Here's how: The dependency PR #67790 (sticky The session-cost chip would then render with an accuracy label reflecting that status: The rehydration PR #67770 ensures the cost counter persists across gateway restarts (reads from Open question: Does the team consider this narrowing approach (accuracy-labeled cost chip, gated on the sticky status ladder) an acceptable direction, or do you prefer a different path — e.g., gating the chip only on an explicit I'm happy either way — just want to confirm the policy direction before writing code. |
|
@teknium1 @OutThisLife Just checking in on this draft — no rush, but the open questions from my reply to the review (Jul 25) are still unanswered, and I'd like to implement once the direction is set:
The full scope is in the PR body and issue #67765. For what it's worth, the pricing data feeding the estimates (#85388, DeepSeek peak/off-peak) is rebased, mergeable, and CI-green, so the chip's numbers will be accurate once that lands. Happy to adjust the scope (e.g., chip without the accuracy label) if the team prefers — just say the word and I'll implement on this branch. |
Status: DRAFT — implementation pending
This PR is being filed as a draft to align on direction before implementation work begins. The brief below describes the intended scope. Source changes will follow in a follow-up commit (or a follow-up PR) once a maintainer weighs in on the placement and config-toggle conventions.
Why draft-first
Per the brief: the chip's core behavior (1, 4, 5 in the Acceptance section) is independent and can ship anytime. The accuracy-label behavior (2, 3) depends on the underlying data being honest, which is being addressed by PR #67770 (rehydration) and PR #67790 (priority ladder).
If you'd prefer the chip land strictly after those two PRs land in main, that's fine — say so and I'll close this draft and re-file when the dependencies are merged. If you're comfortable with the chip landing independently (with the graceful-degradation behavior described below), say so and I'll implement on this branch.
Problem
The desktop status bar shows context-window usage (
apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx:101, 145-146) but doesn't show what the active session has cost so far. The agents panel shows per-subagent cost (apps/desktop/src/app/agents/index.tsx:219) but the main session — which is what the user actually pays attention to — has no running dollar readout.This is a polish gap, not a missing-feature: the backend already computes, accumulates, and persists per-call cost in real time. The wire payload just doesn't carry it.
What's already there (verified)
session_model_usagetable athermes_state.py:836-856— per-call token deltas (input / output / cache-read / cache-write / reasoning) andestimated_cost_usdkeyed by(session_id, model, billing_provider, billing_base_url, billing_mode, task). Issues Session model/billing fields don't reflect mid-session model switches → incorrect metrics #51607 (mid-session/modelattribution) and Auxiliary model API calls are invisible in analytics / workspace dashboard #23270 (aux-task isolation) already solved at the schema level.agent/usage_pricing.py:105-810(_OFFICIAL_DOCS_PRICING) — 50+ snapshot entries for Anthropic 4.0–4.8, OpenAI GPT-5.6/4o/4.1/o3, DeepSeek v4/chat/reasoner, Google Gemini 2.5-pro, Fireworks variants, with separate rates for cache-read and cache-write.agent/usage_pricing.py:997-1024(get_pricing_entry) — OpenRouter and any other provider with abase_urlreturns live rates from{base_url}/models. Snapshot is the fallback.agent/conversation_loop.py:2353-2369andagent/codex_runtime.py:77, 157—update_token_counts()is called after every API response.apps/desktop/src/store/session.ts:305-310($currentUsageatom) →apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx:101, 129(read inuseStatusbarItems) → derivescontextUsageandcontextBarchips. Same atom can carrycost_usd.apps/desktop/src/i18n/en.ts:1167(actualCost).What's missing
tui_gateway/server.py:3569-3626(_get_usage) returnsmodel,input,output,reasoning,prompt,completion,total,calls,context_*— nocost_usd, nocost_status.apps/desktop/src/types/hermes.ts:434-443(UsageStats) declarescost_usd?: number(optional, never populated today).Acceptance
This PR is done when:
context-usagefor any session with non-zerocost_usd.display.show_session_costconfig toggle (hidden when set tofalse).Items 1, 4, 5 can land in any order (independently testable). Items 2 and 3 require the linked bugs.
Graceful degradation (this PR is independent of #67770 and #67764)
If landed alone (without #67762 and #67764):
$X.XX est.based on the latest call's cost and status.$0.00until the next API call lands (cosmetic; thedisplay.show_session_cost: falsetoggle lets users opt out).None of these are crashes. Users with
display.show_session_cost: falseare unaffected. No silent cost data corruption is possible because the chip is read-only on the wire payload.Open questions for reviewers
apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx:432-439betweencontext-usageandsession-timer, mirroring thecontext-usageitem shape. Acceptable, or do you prefer composer pill / sidebar tab?~/Library/Application Support/Hermes/composer-images/composer_2026-07-19_21-17-23-010_da5ada.png). At viewports narrower than ~900px should the chip (a) collapse to just$${cost}dropping the status indicator, (b) overflow into ellipsis with the full label in thetitletooltip, or (c) hide below a breakpoint? I prefer (a).display.*matches thedisplay.*settings convention. Anything else (e.g.,feature_flags.*,ui.*) preferred?Notes
apps/desktop/AGENTS.md,npx tsc -b .reports 11 pre-existing TypeScript errors insrc/lib/incremental-external-store-runtime.tsandnpx vitest runreports 65 failing test files / 382 failing tests with the same root cause (pnpm hoisting / duplicate@assistant-ui/corepackages). These are pre-existing onmainat commit36f2a966cand unrelated to cost tracking. Implementation will land unit tests with the existing status bar test files that pass cleanly today (apps/desktop/src/store/session-color.test.ts,apps/desktop/src/store/projects.test.ts).AGENTS.md).Closes #67765
Task list (from the implementation brief)
agent.session_estimated_cost_usdandagent.session_cost_statusfrom SQLite on agent construction. Without this, the chip's value will visibly reset to$0.00mid-session after any gateway restart.COALESCE(cost_status, cost_status)semantics acrosshermes_state.py:2887, 2908, 3087, plus the unconditional in-memory assignments atagent/conversation_loop.py:2321andagent/codex_runtime.py:150, with a sticky priority ladder (actualandincludedare sticky; estimated/unknown reflect the latest call).tui_gateway/server.py:3569-3626(_get_usage) to readagent.session_estimated_cost_usdandagent.session_cost_statusand emit them ascost_usdandcost_statusin the returned dict.apps/desktop/src/types/hermes.ts:434-443(UsageStats) withcost_status?: "actual" | "estimated" | "included" | "unknown"(the field is already optional).usageCostLabel(currentUsage)helper toapps/desktop/src/lib/statusbar.ts:42-48(mirror theusageContextLabelpattern).session-costitem incoreRightStatusbarItemsatapps/desktop/src/app/shell/hooks/use-statusbar-items.tsx:432-439betweencontext-usageandsession-timer, mirroring thecontext-usageshape.openSessionCost: stringto thestatusbarblock inapps/desktop/src/i18n/en.ts:2196-2226and equivalents inzh.ts,zh-hant.ts,ja.ts; updateapps/desktop/src/i18n/types.ts:1788+.estimatedCost: (cost: string) => stringtoapps/desktop/src/i18n/en.ts:~1166alongside existingactualCost; type definition atapps/desktop/src/i18n/types.ts:1028.display.show_session_cost: boolconfig toggle (defaulttrue) to the config schema and surface it in the settings panel ifdisplay.*toggles are user-facing there.$${cost}without the status indicator, OR add CSStext-overflow: clipto truncate gracefully.Previous PRs in this series