diff --git a/apps/vis/server/src/lib/context-projector.ts b/apps/vis/server/src/lib/context-projector.ts index 81569f9e3..57372bc96 100644 --- a/apps/vis/server/src/lib/context-projector.ts +++ b/apps/vis/server/src/lib/context-projector.ts @@ -170,8 +170,14 @@ export function projectContext(entries: ReadonlyArray): ContextProjec }]; break; case 'usage.record': { - const scope = (rec.usageScope ?? 'session') as 'session' | 'turn'; - addUsage(usage.byScope[scope], rec.usage); + // `byScope.session` is the cumulative session total — every usage + // record, matching agent-core's UsageRecorder.total / byModel — while + // `byScope.turn` is the turn-scoped subset. The main agent loop records + // every step as 'turn', so partitioning records into one bucket OR the + // other left `byScope.session` near-empty and under-reported the Context + // tab token bar, which reads `byScope.session`. + addUsage(usage.byScope.session, rec.usage); + if ((rec.usageScope ?? 'session') === 'turn') addUsage(usage.byScope.turn, rec.usage); if (!usage.byModel[rec.model]) usage.byModel[rec.model] = { ...ZERO }; addUsage(usage.byModel[rec.model]!, rec.usage); break; diff --git a/apps/vis/server/test/lib/context-projector.test.ts b/apps/vis/server/test/lib/context-projector.test.ts index 1d63f1d85..014dd7d75 100644 --- a/apps/vis/server/test/lib/context-projector.test.ts +++ b/apps/vis/server/test/lib/context-projector.test.ts @@ -25,6 +25,13 @@ describe('context-projector', () => { expect(proj.usage.byScope.turn).toEqual({ inputOther: 10, output: 5, inputCacheRead: 0, inputCacheCreation: 0, }); + // `byScope.session` is the cumulative session total — the sum of every + // usage record (matching `byModel`), not just records explicitly scoped + // 'session'. The main loop records every step as 'turn', so before the fix + // this bucket stayed {0,0,0,0} and the Context tab token bar read empty. + expect(proj.usage.byScope.session).toEqual({ + inputOther: 10, output: 5, inputCacheRead: 0, inputCacheCreation: 0, + }); expect(proj.usage.byModel['kimi-k2']).toEqual({ inputOther: 10, output: 5, inputCacheRead: 0, inputCacheCreation: 0, });