From b734eb96bf0af267fdfbef85df51940ca0b4e8c7 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Tue, 2 Jun 2026 13:08:36 +0900 Subject: feat: persist per-tab token/cache usage across reload Persist usage as invisible type:"usage" chunk rows (side channel): - core: add "usage" ChunkType + UsageData; exclude usage rows from getChunksForTab/getTotalChunkCount; add getUsageStatsForTab aggregate (exported from barrel); defensive skip in groupRowsToMessages. - api: agent-manager accumulates per-attempt usageRows and flushes them in the same atomic appendChunks call as the turn's content (discarded on a superseded fallback attempt). GET /tabs enriches rows with usageStats. - frontend: hydrateFromBackend seeds cacheStats from usageStats (reload only; no re-seed on statuses reconnect, so no double-count with live events). Tests: core DB-backed usage persistence/aggregate; api usage-row-per-event + fallback discard; routes GET /tabs usageStats; frontend hydrate seed + no-double-count + live-accumulation-after-seed. 495 -> 509 passing. --- packages/core/src/chunks/transform.ts | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'packages/core/src/chunks/transform.ts') diff --git a/packages/core/src/chunks/transform.ts b/packages/core/src/chunks/transform.ts index a4c6fc8..e8f4a18 100644 --- a/packages/core/src/chunks/transform.ts +++ b/packages/core/src/chunks/transform.ts @@ -209,6 +209,11 @@ export function groupRowsToMessages(rows: ChunkRow[]): MessageRow[] { continue; } + // Usage rows are an invisible side channel (persisted for the backend + // aggregate only). They're already query-excluded from getChunksForTab, + // so this is defensive insurance: never let one leak into render grouping. + if (row.type === "usage") continue; + // assistant / tool rows → part of the current assistant message const c = ensureAssistant(row); switch (row.type) { -- cgit v1.2.3