From fd2a35b1691138b79b606e7961d3c78f7019722b Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:21:22 -0700 Subject: [PATCH] fix: stop reporting cache-hit rate and cost across all UI surfaces (#52717) * fix: stop reporting cache-hit rate and cost across all UI surfaces Cost estimates and cache read/write token reporting are unreliable on providers that don't surface cached_tokens (e.g. ollama-cloud, which doesn't implement prompt_tokens_details.cached_tokens), producing misleading near-zero 'cache hit' readouts and cost figures. Remove cost + cache-hit reporting from every user-facing surface; keep input/output/total token counts (provider-agnostic and accurate) and the Nous account billing UI (real account money, separate from per-conversation estimates). Surfaces: - CLI /usage + model-info: drop cost lines + cache read/write token lines - Gateway /usage + /model: drop cost + cache lines - tui_gateway/server.py: stop emitting cost_usd / cache_read in usage and subagent.complete payloads - TUI (Ink): drop cost from status bar (+ showCost plumbing), /usage panel, thinking rollup, agents overlay (incl. compare view); keep token counts - Desktop Command Center: drop cost stat, per-model cost, actual-cost hint Underlying estimate_usage_cost / format_cost / insights cost columns are left intact but no longer surfaced (display-only change, reversible). * test: update TUI + gateway + CLI tests for removed cost/cache-hit reporting - CLI /usage test asserts cost/cache lines are absent, tokens present - gateway /usage test drops cost + cache asserts; removes cost-included test - TUI subagentTree summary expectation drops the cost segment - useConfigSync + appChrome status-rule tests drop showCost prop/state --- apps/desktop/src/app/command-center/index.tsx | 23 +------- cli.py | 30 ---------- gateway/slash_commands.py | 32 ----------- tests/cli/test_cli_status_bar.py | 55 ++++--------------- tests/gateway/test_usage_command.py | 26 ++------- tui_gateway/server.py | 26 --------- .../__tests__/appChromeStatusRule.test.tsx | 8 +-- .../appChromeStatusRuleDevCredits.test.tsx | 1 - ui-tui/src/__tests__/subagentTree.test.ts | 6 +- ui-tui/src/__tests__/useConfigSync.test.ts | 3 - ui-tui/src/app/interfaces.ts | 1 - ui-tui/src/app/slash/commands/session.ts | 7 --- ui-tui/src/app/uiStore.ts | 1 - ui-tui/src/app/useConfigSync.ts | 1 - ui-tui/src/components/agentsOverlay.tsx | 20 +------ ui-tui/src/components/appChrome.tsx | 16 +----- ui-tui/src/components/appLayout.tsx | 1 - ui-tui/src/components/thinking.tsx | 13 ----- ui-tui/src/lib/subagentTree.ts | 4 -- 19 files changed, 27 insertions(+), 247 deletions(-) diff --git a/apps/desktop/src/app/command-center/index.tsx b/apps/desktop/src/app/command-center/index.tsx index 57358186a03..bade25a6549 100644 --- a/apps/desktop/src/app/command-center/index.tsx +++ b/apps/desktop/src/app/command-center/index.tsx @@ -455,20 +455,6 @@ function formatTokens(value: null | number | undefined): string { return num.toLocaleString() } -function formatCost(value: null | number | undefined): string { - const num = Number(value || 0) - - if (num === 0) { - return '$0.00' - } - - if (num < 0.01) { - return '<$0.01' - } - - return `$${num.toFixed(2)}` -} - function formatInteger(value: null | number | undefined): string { return Number(value ?? 0).toLocaleString() } @@ -525,18 +511,13 @@ function UsagePanel({ error, loading, onRefresh, period, usage }: UsagePanelProp )} -
+
- 0 ? cc.actualCost(formatCost(totals.total_actual_cost)) : undefined} - label={cc.statCost} - value={formatCost(totals.total_estimated_cost)} - />
@@ -596,7 +577,7 @@ function UsagePanel({ error, loading, onRefresh, period, usage }: UsagePanelProp rows={byModel.slice(0, 6).map(entry => ({ key: entry.model, label: entry.model, - value: `${formatTokens((entry.input_tokens || 0) + (entry.output_tokens || 0))} · ${formatCost(entry.estimated_cost)}` + value: `${formatTokens((entry.input_tokens || 0) + (entry.output_tokens || 0))}` }))} title={cc.topModels} /> diff --git a/cli.py b/cli.py index 8cf2e089a2b..7442bfe5ca2 100644 --- a/cli.py +++ b/cli.py @@ -7488,8 +7488,6 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin): if mi: if mi.max_output: _cprint(f" Max output: {mi.max_output:,} tokens") - if mi.has_cost_data(): - _cprint(f" Cost: {mi.format_cost()}") _cprint(f" Capabilities: {mi.format_capabilities()}") cache_enabled = ( @@ -7796,8 +7794,6 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin): if mi: if mi.max_output: _cprint(f" Max output: {mi.max_output:,} tokens") - if mi.has_cost_data(): - _cprint(f" Cost: {mi.format_cost()}") _cprint(f" Capabilities: {mi.format_capabilities()}") # Cache notice @@ -9113,8 +9109,6 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin): # ── Session token usage ───────────────────────────────────── input_tokens = getattr(agent, "session_input_tokens", 0) or 0 output_tokens = getattr(agent, "session_output_tokens", 0) or 0 - cache_read_tokens = getattr(agent, "session_cache_read_tokens", 0) or 0 - cache_write_tokens = getattr(agent, "session_cache_write_tokens", 0) or 0 reasoning_tokens = getattr(agent, "session_reasoning_tokens", 0) or 0 prompt = agent.session_prompt_tokens completion = agent.session_completion_tokens @@ -9127,25 +9121,12 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin): compressions = compressor.compression_count msg_count = len(self.conversation_history) - cost_result = estimate_usage_cost( - agent.model, - CanonicalUsage( - input_tokens=input_tokens, - output_tokens=output_tokens, - cache_read_tokens=cache_read_tokens, - cache_write_tokens=cache_write_tokens, - ), - provider=getattr(agent, "provider", None), - base_url=getattr(agent, "base_url", None), - ) elapsed = format_duration_compact((datetime.now() - self.session_start).total_seconds()) print(" 📊 Session Token Usage") print(f" {'─' * 40}") print(f" Model: {agent.model}") print(f" Input tokens: {input_tokens:>10,}") - print(f" Cache read tokens: {cache_read_tokens:>10,}") - print(f" Cache write tokens: {cache_write_tokens:>10,}") print(f" Output tokens: {output_tokens:>10,}") if reasoning_tokens: print(f" ↳ Reasoning (subset): {reasoning_tokens:>10,}") @@ -9154,21 +9135,10 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin): print(f" Total tokens: {total:>10,}") print(f" API calls: {calls:>10,}") print(f" Session duration: {elapsed:>10}") - print(f" Cost status: {cost_result.status:>10}") - print(f" Cost source: {cost_result.source:>10}") - if cost_result.amount_usd is not None: - prefix = "~" if cost_result.status == "estimated" else "" - print(f" Total cost: {prefix}${float(cost_result.amount_usd):>10.4f}") - elif cost_result.status == "included": - print(f" Total cost: {'included':>10}") - else: - print(f" Total cost: {'n/a':>10}") print(f" {'─' * 40}") print(f" Current context: {last_prompt:,} / {ctx_len:,} ({pct:.0f}%)") print(f" Messages: {msg_count}") print(f" Compressions: {compressions}") - if cost_result.status == "unknown": - print(f" Note: Pricing unknown for {agent.model}") # Account limits -- fetched off-thread with a hard timeout so slow # provider APIs don't hang the prompt. diff --git a/gateway/slash_commands.py b/gateway/slash_commands.py index 24754336b36..b2b8089b51b 100644 --- a/gateway/slash_commands.py +++ b/gateway/slash_commands.py @@ -1391,8 +1391,6 @@ class GatewaySlashCommandsMixin: if mi: if mi.max_output: lines.append(t("gateway.model.max_output_label", tokens=f"{mi.max_output:,}")) - if mi.has_cost_data(): - lines.append(t("gateway.model.cost_label", cost=mi.format_cost())) lines.append(t("gateway.model.capabilities_label", capabilities=mi.format_capabilities())) if result.warning_message: lines.append(t("gateway.model.warning_prefix", warning=result.warning_message)) @@ -1628,8 +1626,6 @@ class GatewaySlashCommandsMixin: if mi: if mi.max_output: lines.append(t("gateway.model.max_output_label", tokens=f"{mi.max_output:,}")) - if mi.has_cost_data(): - lines.append(t("gateway.model.cost_label", cost=mi.format_cost())) lines.append(t("gateway.model.capabilities_label", capabilities=mi.format_capabilities())) # Cache notice @@ -3514,42 +3510,14 @@ class GatewaySlashCommandsMixin: # Session token usage — detailed breakdown matching CLI input_tokens = getattr(agent, "session_input_tokens", 0) or 0 output_tokens = getattr(agent, "session_output_tokens", 0) or 0 - cache_read = getattr(agent, "session_cache_read_tokens", 0) or 0 - cache_write = getattr(agent, "session_cache_write_tokens", 0) or 0 lines.append(t("gateway.usage.header_session")) lines.append(t("gateway.usage.label_model", model=agent.model)) lines.append(t("gateway.usage.label_input_tokens", count=f"{input_tokens:,}")) - if cache_read: - lines.append(t("gateway.usage.label_cache_read", count=f"{cache_read:,}")) - if cache_write: - lines.append(t("gateway.usage.label_cache_write", count=f"{cache_write:,}")) lines.append(t("gateway.usage.label_output_tokens", count=f"{output_tokens:,}")) lines.append(t("gateway.usage.label_total", count=f"{agent.session_total_tokens:,}")) lines.append(t("gateway.usage.label_api_calls", count=agent.session_api_calls)) - # Cost estimation - try: - from agent.usage_pricing import CanonicalUsage, estimate_usage_cost - cost_result = estimate_usage_cost( - agent.model, - CanonicalUsage( - input_tokens=input_tokens, - output_tokens=output_tokens, - cache_read_tokens=cache_read, - cache_write_tokens=cache_write, - ), - provider=getattr(agent, "provider", None), - base_url=getattr(agent, "base_url", None), - ) - if cost_result.amount_usd is not None: - prefix = "~" if cost_result.status == "estimated" else "" - lines.append(t("gateway.usage.label_cost", prefix=prefix, amount=f"{float(cost_result.amount_usd):.4f}")) - elif cost_result.status == "included": - lines.append(t("gateway.usage.label_cost_included")) - except Exception: - pass - # Context window and compressions ctx = agent.context_compressor if ctx.last_prompt_tokens: diff --git a/tests/cli/test_cli_status_bar.py b/tests/cli/test_cli_status_bar.py index e27ade6af7d..3edc1e94c80 100644 --- a/tests/cli/test_cli_status_bar.py +++ b/tests/cli/test_cli_status_bar.py @@ -500,7 +500,7 @@ class TestCLIStatusBar: class TestCLIUsageReport: - def test_show_usage_includes_estimated_cost(self, capsys): + def test_show_usage_omits_cost_reporting(self, capsys): cli_obj = _attach_agent( _make_cli(), prompt_tokens=10_230, @@ -516,52 +516,19 @@ class TestCLIUsageReport: cli_obj._show_usage() output = capsys.readouterr().out + # Token counts and session metadata still shown. assert "Model:" in output - assert "Cost status:" in output - assert "Cost source:" in output - assert "Total cost:" in output - assert "$" in output - assert "0.064" in output + assert "Input tokens:" in output + assert "Output tokens:" in output + assert "Total tokens:" in output assert "Session duration:" in output assert "Compressions:" in output - - def test_show_usage_marks_unknown_pricing(self, capsys): - cli_obj = _attach_agent( - _make_cli(model="local/my-custom-model"), - prompt_tokens=1_000, - completion_tokens=500, - total_tokens=1_500, - api_calls=1, - context_tokens=1_000, - context_length=32_000, - ) - cli_obj.verbose = False - - cli_obj._show_usage() - output = capsys.readouterr().out - - assert "Total cost:" in output - assert "n/a" in output - assert "Pricing unknown for local/my-custom-model" in output - - def test_zero_priced_provider_models_stay_unknown(self, capsys): - cli_obj = _attach_agent( - _make_cli(model="glm-5"), - prompt_tokens=1_000, - completion_tokens=500, - total_tokens=1_500, - api_calls=1, - context_tokens=1_000, - context_length=32_000, - ) - cli_obj.verbose = False - - cli_obj._show_usage() - output = capsys.readouterr().out - - assert "Total cost:" in output - assert "n/a" in output - assert "Pricing unknown for glm-5" in output + # Cost and cache-hit reporting is removed everywhere. + assert "Total cost:" not in output + assert "Cost status:" not in output + assert "Cost source:" not in output + assert "Cache read tokens:" not in output + assert "Cache write tokens:" not in output class TestStatusBarWidthSource: diff --git a/tests/gateway/test_usage_command.py b/tests/gateway/test_usage_command.py index 9fbb80e3123..d58c57613dd 100644 --- a/tests/gateway/test_usage_command.py +++ b/tests/gateway/test_usage_command.py @@ -76,20 +76,20 @@ class TestUsageCachedAgent: runner = _make_runner(SK, cached_agent=agent) event = MagicMock() - with patch("agent.rate_limit_tracker.format_rate_limit_compact", return_value="RPM: 50/60"), \ - patch("agent.usage_pricing.estimate_usage_cost") as mock_cost: - mock_cost.return_value = MagicMock(amount_usd=0.1234, status="estimated") + with patch("agent.rate_limit_tracker.format_rate_limit_compact", return_value="RPM: 50/60"): result = await runner._handle_usage_command(event) assert "claude-sonnet-4.6" in result assert "35,000" in result # input tokens assert "10,000" in result # output tokens - assert "5,000" in result # cache read - assert "2,000" in result # cache write assert "50,000" in result # total - assert "$0.1234" in result assert "30,000" in result # context assert "Compressions: 1" in result + # Cost and cache-hit reporting is removed everywhere. + assert "$" not in result + assert "Cache read" not in result + assert "Cache write" not in result + assert "Cost" not in result @pytest.mark.asyncio async def test_running_agent_preferred_over_cache(self): @@ -161,20 +161,6 @@ class TestUsageCachedAgent: assert "Cache read" not in result assert "Cache write" not in result - @pytest.mark.asyncio - async def test_cost_included_status(self): - """Subscription-included providers show 'included' instead of dollar amount.""" - agent = _make_mock_agent(provider="openai-codex") - runner = _make_runner(SK, cached_agent=agent) - event = MagicMock() - - with patch("agent.rate_limit_tracker.format_rate_limit_compact", return_value="RPM: 50/60"), \ - patch("agent.usage_pricing.estimate_usage_cost") as mock_cost: - mock_cost.return_value = MagicMock(amount_usd=None, status="included") - result = await runner._handle_usage_command(event) - - assert "Cost: included" in result - class TestUsageAccountSection: """Account-limits section appended to /usage output (PR #2486).""" diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 24299a82ceb..e94692ddc93 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -2790,8 +2790,6 @@ def _get_usage(agent) -> dict: "model": getattr(agent, "model", "") or "", "input": g("session_input_tokens", "session_prompt_tokens"), "output": g("session_output_tokens", "session_completion_tokens"), - "cache_read": g("session_cache_read_tokens"), - "cache_write": g("session_cache_write_tokens"), "reasoning": g("session_reasoning_tokens"), "prompt": g("session_prompt_tokens"), "completion": g("session_completion_tokens"), @@ -2815,25 +2813,6 @@ def _get_usage(agent) -> dict: usage["active_subagents"] = _async_active_count() except Exception: pass - try: - from agent.usage_pricing import CanonicalUsage, estimate_usage_cost - - cost = estimate_usage_cost( - usage["model"], - CanonicalUsage( - input_tokens=usage["input"], - output_tokens=usage["output"], - cache_read_tokens=usage["cache_read"], - cache_write_tokens=usage["cache_write"], - ), - provider=getattr(agent, "provider", None), - base_url=getattr(agent, "base_url", None), - ) - usage["cost_status"] = cost.status - if cost.amount_usd is not None: - usage["cost_usd"] = float(cost.amount_usd) - except Exception: - pass # Dev-only live credits-spent readout (L0 usage-aware-credits). Gated on # HERMES_DEV_CREDITS so the payload stays clean when the flag is off. if is_truthy_value(os.environ.get("HERMES_DEV_CREDITS")): @@ -3289,11 +3268,6 @@ def _on_tool_progress( payload[int_key] = int(val) except (TypeError, ValueError): pass - if _kwargs.get("cost_usd") is not None: - try: - payload["cost_usd"] = float(_kwargs["cost_usd"]) - except (TypeError, ValueError): - pass if _kwargs.get("files_read"): payload["files_read"] = [str(p) for p in _kwargs["files_read"]] if _kwargs.get("files_written"): diff --git a/ui-tui/src/__tests__/appChromeStatusRule.test.tsx b/ui-tui/src/__tests__/appChromeStatusRule.test.tsx index c7f2a00eefc..de823162df2 100644 --- a/ui-tui/src/__tests__/appChromeStatusRule.test.tsx +++ b/ui-tui/src/__tests__/appChromeStatusRule.test.tsx @@ -96,7 +96,6 @@ const baseProps = { liveSessionCount: 0, model: 'opus-4.8', sessionStartedAt: null, - showCost: false, status: 'ready', statusColor: DEFAULT_THEME.color.ok, t: DEFAULT_THEME, @@ -157,7 +156,6 @@ describe('StatusRule session count click target', () => { model: 'kimi-k2.6', onSessionCountClick: openSwitcher, sessionStartedAt: null, - showCost: false, status: 'ready', statusColor: DEFAULT_THEME.color.ok, t: DEFAULT_THEME, @@ -183,12 +181,11 @@ describe('StatusRule session count click target', () => { model: 'opus-4.8', onSessionCountClick: vi.fn(), sessionStartedAt: Date.now() - 60_000, - showCost: true, status: 'ready', statusColor: DEFAULT_THEME.color.ok, t: DEFAULT_THEME, turnStartedAt: null, - usage: { context_max: 200_000, context_percent: 25, context_used: 50_000, cost_usd: 0.5, total: 50_000 }, + usage: { calls: 0, context_max: 200_000, context_percent: 25, context_used: 50_000, input: 0, output: 0, total: 50_000 }, voiceLabel: 'voice off' }) @@ -197,9 +194,8 @@ describe('StatusRule session count click target', () => { // Must-keep essentials survive intact … expect(rendered).toContain('ready') expect(rendered).toContain('opus 4.8') - // … while the low-value tail (session count, cost) is dropped, not truncated. + // … while the low-value tail (session count) is dropped, not truncated. expect(rendered).not.toContain('3 sessions') - expect(rendered).not.toContain('$0.5000') }) }) diff --git a/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx b/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx index 514ff5f5c7c..7d04cfe2758 100644 --- a/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +++ b/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx @@ -45,7 +45,6 @@ const baseProps = { liveSessionCount: 0, model: 'opus-4.8', sessionStartedAt: null, - showCost: false, status: 'ready', statusColor: DEFAULT_THEME.color.ok, t: DEFAULT_THEME, diff --git a/ui-tui/src/__tests__/subagentTree.test.ts b/ui-tui/src/__tests__/subagentTree.test.ts index bd892d7ac07..863646a8e52 100644 --- a/ui-tui/src/__tests__/subagentTree.test.ts +++ b/ui-tui/src/__tests__/subagentTree.test.ts @@ -139,8 +139,8 @@ describe('fmtCost + fmtTokens', () => { }) }) -describe('formatSummary with tokens + cost', () => { - it('includes token + cost when present', () => { +describe('formatSummary with tokens', () => { + it('includes tokens but not cost', () => { expect( formatSummary({ activeCount: 0, @@ -154,7 +154,7 @@ describe('formatSummary with tokens + cost', () => { totalDuration: 30, totalTools: 14 }) - ).toBe('d2 · 3 agents · 14 tools · 30s · 10k tok · $0.42') + ).toBe('d2 · 3 agents · 14 tools · 30s · 10k tok') }) }) diff --git a/ui-tui/src/__tests__/useConfigSync.test.ts b/ui-tui/src/__tests__/useConfigSync.test.ts index 2a6f7262456..c82984bac4d 100644 --- a/ui-tui/src/__tests__/useConfigSync.test.ts +++ b/ui-tui/src/__tests__/useConfigSync.test.ts @@ -26,7 +26,6 @@ describe('applyDisplay', () => { bell_on_complete: true, details_mode: 'expanded', inline_diffs: false, - show_cost: true, show_reasoning: true, streaming: false, tui_compact: true, @@ -42,7 +41,6 @@ describe('applyDisplay', () => { expect(s.compact).toBe(true) expect(s.detailsMode).toBe('expanded') expect(s.inlineDiffs).toBe(false) - expect(s.showCost).toBe(true) expect(s.showReasoning).toBe(true) expect(s.statusBar).toBe('off') expect(s.streaming).toBe(false) @@ -66,7 +64,6 @@ describe('applyDisplay', () => { const s = $uiState.get() expect(setBell).toHaveBeenCalledWith(false) expect(s.inlineDiffs).toBe(true) - expect(s.showCost).toBe(false) expect(s.showReasoning).toBe(false) expect(s.statusBar).toBe('top') expect(s.streaming).toBe(true) diff --git a/ui-tui/src/app/interfaces.ts b/ui-tui/src/app/interfaces.ts index 463372a3522..cd8789d44ec 100644 --- a/ui-tui/src/app/interfaces.ts +++ b/ui-tui/src/app/interfaces.ts @@ -173,7 +173,6 @@ export interface UiState { sections: SectionVisibility sessionTitle: string - showCost: boolean showReasoning: boolean indicatorStyle: IndicatorStyle sid: null | string diff --git a/ui-tui/src/app/slash/commands/session.ts b/ui-tui/src/app/slash/commands/session.ts index a8b4b3ca4e1..27848eaf69d 100644 --- a/ui-tui/src/app/slash/commands/session.ts +++ b/ui-tui/src/app/slash/commands/session.ts @@ -593,22 +593,15 @@ export const sessionCommands: SlashCommand[] = [ } const f = (v: number | undefined) => (v ?? 0).toLocaleString() - const cost = r.cost_usd != null ? `${r.cost_status === 'estimated' ? '~' : ''}$${r.cost_usd.toFixed(4)}` : null const rows: [string, string][] = [ ['Model', r.model ?? ''], ['Input tokens', f(r.input)], - ['Cache read tokens', f(r.cache_read)], - ['Cache write tokens', f(r.cache_write)], ['Output tokens', f(r.output)], ['Total tokens', f(r.total)], ['API calls', f(r.calls)] ] - if (cost) { - rows.push(['Cost', cost]) - } - const sections: PanelSection[] = [{ rows }] if (r.context_max) { diff --git a/ui-tui/src/app/uiStore.ts b/ui-tui/src/app/uiStore.ts index 470f4264b94..b9d62fbe14c 100644 --- a/ui-tui/src/app/uiStore.ts +++ b/ui-tui/src/app/uiStore.ts @@ -23,7 +23,6 @@ const buildUiState = (): UiState => ({ pasteCollapseChars: 2000, sections: {}, sessionTitle: '', - showCost: false, showReasoning: false, sid: null, status: 'summoning hermes…', diff --git a/ui-tui/src/app/useConfigSync.ts b/ui-tui/src/app/useConfigSync.ts index f159bbbd17b..6d964213f25 100644 --- a/ui-tui/src/app/useConfigSync.ts +++ b/ui-tui/src/app/useConfigSync.ts @@ -213,7 +213,6 @@ export const applyDisplay = ( pasteCollapseLines: _pasteCollapseLinesFromConfig(cfg), pasteCollapseChars: _pasteCollapseCharsFromConfig(cfg), sections: resolveSections(d.sections), - showCost: !!d.show_cost, showReasoning: !!d.show_reasoning, statusBar: normalizeStatusBar(d.tui_statusbar), streaming: d.streaming !== false diff --git a/ui-tui/src/components/agentsOverlay.tsx b/ui-tui/src/components/agentsOverlay.tsx index 497230c3934..b04c20551d8 100644 --- a/ui-tui/src/components/agentsOverlay.tsx +++ b/ui-tui/src/components/agentsOverlay.tsx @@ -18,7 +18,6 @@ import { buildSubagentTree, descendantIds, flattenTree, - fmtCost, fmtDuration, fmtTokens, formatSummary, @@ -407,8 +406,6 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) const outputTokens = item.outputTokens ?? 0 const localTokens = inputTokens + outputTokens const subtreeTokens = agg.inputTokens + agg.outputTokens - localTokens - const localCost = item.costUsd ?? 0 - const subtreeCost = agg.costUsd - localCost const filesRead = item.filesRead ?? [] const filesWritten = item.filesWritten ?? [] @@ -442,7 +439,7 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) {item.apiCalls ? : null} - {localTokens > 0 || localCost > 0 ? ( + {localTokens > 0 ? ( {localTokens > 0 ? ( ) : null} - {localCost > 0 ? ( - - {fmtCost(localCost)} - {subtreeCost >= 0.01 ? ` · subtree +${fmtCost(subtreeCost)}` : ''} - - } - /> - ) : null} - {subtreeTokens > 0 ? : null} ) : null} @@ -650,7 +634,6 @@ function DiffView({ const round = (n: number) => String(Math.round(n)) const sumTokens = (x: typeof aTotals) => x.inputTokens + x.outputTokens - const dollars = (n: number) => fmtCost(n) || '$0.00' return ( @@ -683,7 +666,6 @@ function DiffView({ {diffMetricLine('duration', aTotals.totalDuration, bTotals.totalDuration, n => `${n.toFixed(1)}s`)} {diffMetricLine('tokens', sumTokens(aTotals), sumTokens(bTotals), fmtTokens)} - {diffMetricLine('cost', aTotals.costUsd, bTotals.costUsd, dollars)} ) diff --git a/ui-tui/src/components/appChrome.tsx b/ui-tui/src/components/appChrome.tsx index b3ec8bff21b..ed0588f5420 100644 --- a/ui-tui/src/components/appChrome.tsx +++ b/ui-tui/src/components/appChrome.tsx @@ -248,7 +248,6 @@ export interface StatusBarSegments { bg: boolean compactCtx: boolean compressions: boolean - cost: boolean duration: boolean subagents: boolean voice: boolean @@ -264,8 +263,7 @@ export function statusBarSegments(cols: number): StatusBarSegments { compressions: w >= 80, voice: w >= 84, bg: w >= 88, - subagents: w >= 92, - cost: w >= 96 + subagents: w >= 92 } } @@ -420,7 +418,6 @@ export function StatusRule({ lastTurnEndedAt, liveSessionCount, sessionStartedAt, - showCost, turnStartedAt, voiceLabel, onSessionCountClick, @@ -494,7 +491,6 @@ export function StatusRule({ const sessionCountText = liveSessionCount > 0 ? statusSessionCountLabel(liveSessionCount) : '' const compressions = typeof usage.compressions === 'number' ? usage.compressions : 0 - const costText = typeof usage.cost_usd === 'number' ? `$${usage.cost_usd.toFixed(4)}` : '' // Dev-only readout (HERMES_DEV_CREDITS). The server omits the key entirely unless the // flag is on, so this segment self-hides for normal users. micros→cents is allowed money // math (display formatting) — never parseFloat a *_usd. Signed: a mid-session top-up that @@ -516,8 +512,7 @@ export function StatusRule({ const showBg = segs.bg && bgCount > 0 && fits(SEP + stringWidth(`${bgCount} bg`)) const subagentCount = typeof usage.active_subagents === 'number' ? usage.active_subagents : 0 const showSubagents = segs.subagents && subagentCount > 0 && fits(SEP + stringWidth(`⛓ ${subagentCount}`)) - const showCostSeg = segs.cost && showCost && !!costText && fits(SEP + stringWidth(costText)) - // No segs flag / no showCost coupling — it's a server-gated dev readout, lowest priority, + // Dev-gated readout (HERMES_DEV_CREDITS), lowest priority, // so it consumes tail budget LAST and drops first on a narrow terminal. const showDevCredits = !!devCreditsText && fits(SEP + stringWidth(devCreditsText)) @@ -629,12 +624,6 @@ export function StatusRule({ ⛓ {subagentCount} ) : null} - {showCostSeg ? ( - - {' │ '} - {costText} - - ) : null} {showDevCredits ? ( {' │ '} @@ -772,7 +761,6 @@ interface StatusRuleProps { indicatorStyle?: IndicatorStyle notice?: Notice | null sessionStartedAt?: null | number - showCost: boolean status: string statusColor: string t: Theme diff --git a/ui-tui/src/components/appLayout.tsx b/ui-tui/src/components/appLayout.tsx index 7fa5dec1886..d3cd8383d52 100644 --- a/ui-tui/src/components/appLayout.tsx +++ b/ui-tui/src/components/appLayout.tsx @@ -394,7 +394,6 @@ const StatusRulePane = memo(function StatusRulePane({ notice={ui.notice} onSessionCountClick={() => patchOverlayState({ sessions: true })} sessionStartedAt={status.sessionStartedAt} - showCost={ui.showCost} status={ui.status} statusColor={status.statusColor} t={ui.theme} diff --git a/ui-tui/src/components/thinking.tsx b/ui-tui/src/components/thinking.tsx index ce90cca2138..016c99138af 100644 --- a/ui-tui/src/components/thinking.tsx +++ b/ui-tui/src/components/thinking.tsx @@ -6,7 +6,6 @@ import { THINKING_COT_MAX } from '../config/limits.js' import { sectionMode } from '../domain/details.js' import { buildSubagentTree, - fmtCost, fmtTokens, formatSummary as formatSpawnSummary, hotnessBucket, @@ -361,12 +360,6 @@ function SubagentAccordion({ rollupBits.push(`${fmtTokens(localTokens)} tok`) } - const localCost = item.costUsd ?? 0 - - if (localCost > 0) { - rollupBits.push(fmtCost(localCost)) - } - const filesLocal = (item.filesWritten?.length ?? 0) + (item.filesRead?.length ?? 0) if (filesLocal > 0) { @@ -380,12 +373,6 @@ function SubagentAccordion({ rollupBits.push(`+${subtreeTools}t sub`) } - const subCost = aggregate.costUsd - localCost - - if (subCost >= 0.01) { - rollupBits.push(`+${fmtCost(subCost)} sub`) - } - if (aggregate.activeCount > 0 && item.status !== 'running') { rollupBits.push(`⚡${aggregate.activeCount}`) } diff --git a/ui-tui/src/lib/subagentTree.ts b/ui-tui/src/lib/subagentTree.ts index 513559b8076..3770bd2003f 100644 --- a/ui-tui/src/lib/subagentTree.ts +++ b/ui-tui/src/lib/subagentTree.ts @@ -252,10 +252,6 @@ export function formatSummary(totals: SubagentAggregate): string { pieces.push(`${fmtTokens(tokens)} tok`) } - if (totals.costUsd > 0) { - pieces.push(fmtCost(totals.costUsd)) - } - if (totals.activeCount > 0) { pieces.push(`⚡${totals.activeCount}`) }