diff --git a/surfaces/gui/e2e/usage-chip.spec.ts b/surfaces/gui/e2e/usage-chip.spec.ts index 58a18a10..04ab5325 100644 --- a/surfaces/gui/e2e/usage-chip.spec.ts +++ b/surfaces/gui/e2e/usage-chip.spec.ts @@ -33,6 +33,9 @@ test("usage chip appears after a turn and opens the breakdown popover", async ({ await expect(pop).toContainText("Claude Opus 4.8 · Anthropic"); await expect(pop).toContainText("Input"); await expect(pop).toContainText("Cache read"); + // Total input = fresh 1k + cache_read 8k + cache_write 800 (cumulative billed input). + await expect(pop).toContainText("Total input"); + await expect(pop).toContainText("9.8k"); await expect(pop).toContainText("10k tokens"); // Second turn accumulates (totals double), and the scrim click closes the popover. diff --git a/surfaces/gui/src/components/Composer.tsx b/surfaces/gui/src/components/Composer.tsx index 4ac81bdb..f446796b 100644 --- a/surfaces/gui/src/components/Composer.tsx +++ b/surfaces/gui/src/components/Composer.tsx @@ -660,9 +660,14 @@ function UsageChip({
{stat("Input", t.input)} - {stat("Output", t.output)} {t.cache_read > 0 && stat("Cache read", t.cache_read)} {t.cache_write > 0 && stat("Cache write", t.cache_write)} + {/* Cumulative billed input volume = fresh + cache reads + cache + writes (each class bills at its own rate). Shown only when a + cache split exists — otherwise it would duplicate Input. */} + {t.cache_read + t.cache_write > 0 && + stat("Total input", t.input + t.cache_read + t.cache_write)} + {stat("Output", t.output)}
))}