fix(stats): fold tool_calls.bytes_returned into 'With context-mode' bar

ctx_stats Section 1 rendered 'With context-mode: 1 B / 100% kept out' on tool-heavy sessions. Root cause: getRealBytesStats summed only session_events.bytes_returned (snapshot-replay only, ~0), while real MCP returns (ctx_execute/ctx_search stdout, redirect stubs) land in tool_calls.bytes_returned via incrementToolCall. Now folds tool_calls.bytes_returned across all three tiers (session/project/lifetime), so the bar shows the real bytes that entered context. Verified empirically: a live session read 0 from session_events but 423 KB from tool_calls. Conversation tier has zero overlap; lifetime overlap is ~0.4% (legacy sandbox-execute events). TDD: new 8.1b test seeds tool_calls and asserts the fold; existing tiers unchanged.
This commit is contained in:
Mert Koseoglu
2026-06-26 13:32:13 +03:00
parent a338a0d26d
commit 6d667348bb
2 changed files with 57 additions and 0 deletions
+27
View File
@@ -1274,6 +1274,17 @@ export function getRealBytesStats(opts: {
).get(opts.sessionId) as { bytes: number } | undefined;
if (snap?.bytes) snapshotBytes += Number(snap.bytes);
} catch { /* old schema */ }
try {
// MCP tool returns (ctx_execute/ctx_search stdout, redirect stubs)
// land in tool_calls.bytes_returned via incrementToolCall, NOT in
// session_events.bytes_returned (which carries snapshot-replay only).
// Folding them here is what the "With context-mode" bar measures;
// without it a tool-heavy session rendered a false "1 B / 100%".
const tc = sdb.prepare(
"SELECT COALESCE(SUM(bytes_returned), 0) AS bytes FROM tool_calls WHERE session_id = ?",
).get(opts.sessionId) as { bytes: number } | undefined;
if (tc?.bytes) bytesReturned += Number(tc.bytes);
} catch { /* old schema: no tool_calls table */ }
} else if (opts.projectDir) {
// Bug E+F: META-scoped aggregation. Take every session_id whose
// session_meta.project_dir matches, then sum ALL of those
@@ -1307,6 +1318,16 @@ export function getRealBytesStats(opts: {
).get(opts.projectDir) as { bytes: number } | undefined;
if (snap?.bytes) snapshotBytes += Number(snap.bytes);
} catch { /* old schema */ }
try {
const tc = sdb.prepare(
`SELECT COALESCE(SUM(bytes_returned), 0) AS bytes
FROM tool_calls
WHERE session_id IN (
SELECT session_id FROM session_meta WHERE project_dir = ?
)`,
).get(opts.projectDir) as { bytes: number } | undefined;
if (tc?.bytes) bytesReturned += Number(tc.bytes);
} catch { /* old schema: no tool_calls table */ }
} else {
const row = sdb.prepare(
`SELECT
@@ -1328,6 +1349,12 @@ export function getRealBytesStats(opts: {
).get() as { bytes: number } | undefined;
if (snap?.bytes) snapshotBytes += Number(snap.bytes);
} catch { /* old schema */ }
try {
const tc = sdb.prepare(
"SELECT COALESCE(SUM(bytes_returned), 0) AS bytes FROM tool_calls",
).get() as { bytes: number } | undefined;
if (tc?.bytes) bytesReturned += Number(tc.bytes);
} catch { /* old schema: no tool_calls table */ }
}
} finally {
sdb.close();
+30
View File
@@ -62,6 +62,7 @@ function seed(
sessionId: string,
events: Array<{ type: string; category: string; data: string; bytesAvoided?: number; bytesReturned?: number }>,
snapshots?: Array<{ snapshot: string }>,
toolCalls?: Array<{ tool: string; bytesReturned: number }>,
): void {
const sdb = new SessionDB({ dbPath });
try {
@@ -90,6 +91,11 @@ function seed(
sdb.upsertResume(sessionId, s.snapshot, events.length);
}
}
if (toolCalls) {
for (const tc of toolCalls) {
sdb.incrementToolCall(sessionId, tc.tool, tc.bytesReturned);
}
}
} finally {
sdb.close();
}
@@ -124,6 +130,30 @@ describe("getRealBytesStats (Phase 8 renderer source-of-truth)", () => {
expect(r.totalSavedTokens).toBeGreaterThan(9_000); // ≈ 9_500
});
test("8.1b conversation tier: folds tool_calls.bytes_returned (MCP returns) into bytesReturned", () => {
// Production reality: ctx_execute / ctx_search return bytes to context via
// the tool_calls counter (incrementToolCall / persistToolCallCounter), NOT
// session_events.bytes_returned. A session with real MCP returns but no
// snapshot replay reported bytesReturned=0 before this fix, rendering the
// Section 1 bar as "With context-mode: 1 B / 100% kept out" — false.
const dir = mkSessionsDir();
const sid = `sess-${randomUUID()}`;
const dbPath = dbPathFor(dir, "cafebabecafebabe");
seed(
dbPath,
sid,
[{ type: "tool_use", category: "file", data: "src/app.ts", bytesAvoided: 90_000, bytesReturned: 0 }],
undefined,
[{ tool: "ctx_execute", bytesReturned: 8_000 }, { tool: "ctx_search", bytesReturned: 2_000 }],
);
const r = getRealBytesStats({ sessionId: sid, sessionsDir: dir });
// 8_000 + 2_000 from tool_calls — the bytes that actually entered context.
expect(r.bytesReturned).toBe(10_000);
expect(r.bytesAvoided).toBe(90_000);
});
test("8.5 lifetime tier: omitting sessionId aggregates every session in sessionsDir", () => {
const dir = mkSessionsDir();
const sidA = `lifeA-${randomUUID()}`;