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/frontend/src/lib/tabs.svelte.ts | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'packages/frontend/src') diff --git a/packages/frontend/src/lib/tabs.svelte.ts b/packages/frontend/src/lib/tabs.svelte.ts index 3fd7e5f..54f17d9 100644 --- a/packages/frontend/src/lib/tabs.svelte.ts +++ b/packages/frontend/src/lib/tabs.svelte.ts @@ -755,6 +755,12 @@ export function createTabStore() { keyId?: string | null; modelId?: string | null; parentTabId?: string | null; + // Backend usage aggregate (GET /tabs). Structurally identical to + // CacheStats, so it seeds `cacheStats` directly on reload. Seeding + // happens ONLY here (hydrate runs when tabs.length === 0, i.e. a true + // reload) — never on `statuses` reconnect or `turn-sealed` — so the + // persisted aggregate and in-session live `usage` events never overlap. + usageStats?: CacheStats | null; }> = []; try { const res = await fetch(`${config.apiBase}/tabs`); @@ -843,6 +849,7 @@ export function createTabStore() { chunkLimit: appSettings.chunkLimit, oldestLoadedSeq: win.oldestSeq, totalChunks: win.total, + cacheStats: row.usageStats ?? undefined, }; }); -- cgit v1.2.3