diff --git a/src/gateway/server-methods/usage.sessions-usage.test.ts b/src/gateway/server-methods/usage.sessions-usage.test.ts index 08a4fb1b18ac..71e8c56fdf66 100644 --- a/src/gateway/server-methods/usage.sessions-usage.test.ts +++ b/src/gateway/server-methods/usage.sessions-usage.test.ts @@ -284,7 +284,7 @@ describe("sessions.usage", () => { expect(expectDefined(sessions[0], "sessions[0] test invariant").agentId).toBe("opus"); }); - it("loads selected session summaries in one batched cache read and reports refresh status", async () => { + it("returns pending cache rows with null usage while refresh runs", async () => { vi.mocked(discoverAllSessions).mockResolvedValueOnce([ { sessionId: "s-a", @@ -304,7 +304,10 @@ describe("sessions.usage", () => { ]); vi.mocked(loadSessionCostSummariesFromCache).mockImplementation(async ({ sessions }) => ({ summaries: sessions.map((session) => { - const tokens = session.sessionId === "s-a" ? 10 : session.sessionId === "s-b" ? 20 : 30; + if (session.sessionId === "s-c") { + return null; + } + const tokens = session.sessionId === "s-a" ? 10 : 20; return { input: tokens, output: 0, @@ -339,7 +342,12 @@ describe("sessions.usage", () => { }; expect(result.cacheStatus?.status).toBe("refreshing"); expect(result.sessions.map((session) => session.sessionId)).toEqual(["s-a", "s-b", "s-c"]); - expect(result.totals.totalTokens).toBe(60); + expect(result.sessions.map((session) => session.usage?.totalTokens ?? null)).toEqual([ + 10, + 20, + null, + ]); + expect(result.totals.totalTokens).toBe(30); }); it("passes the requested timezone offset to session daily summaries", async () => { diff --git a/src/gateway/server-methods/usage.ts b/src/gateway/server-methods/usage.ts index 6110824b310a..cba554ba6f52 100644 --- a/src/gateway/server-methods/usage.ts +++ b/src/gateway/server-methods/usage.ts @@ -1445,7 +1445,8 @@ export const usageHandlers: GatewayRequestHandlers = { for (const [entryIndex, merged] of mergedEntries.entries()) { const agentId = merged.agentId; - const usage = usageByEntryIndex[entryIndex]; + // A cold or stale cache intentionally yields null until its background refresh completes. + const usage = usageByEntryIndex[entryIndex] ?? null; if (usage) { addCostUsageTotals(aggregateTotals, usage); @@ -1594,7 +1595,7 @@ export const usageHandlers: GatewayRequestHandlers = { providerOverride: merged.storeEntry?.providerOverride, modelProvider: merged.storeEntry?.modelProvider, model: merged.storeEntry?.model, - usage: expectDefined(usage, "session usage summary"), + usage, contextWeight: includeContextWeight ? (merged.storeEntry?.systemPromptReport ?? null) : undefined,