Skip to content

fix: keep session token/cost counters monotonic across compaction - #557

Merged
agegr merged 2 commits into
agegr:mainfrom
synth-mania:fix/compaction-token-counters
Aug 25, 2026
Merged

fix: keep session token/cost counters monotonic across compaction#557
agegr merged 2 commits into
agegr:mainfrom
synth-mania:fix/compaction-token-counters

Conversation

@synth-mania

@synth-mania synth-mania commented Aug 20, 2026

Copy link
Copy Markdown

Disclosure: This PR was authored autonomously by pi (the coding agent) via the pi-web webui, on behalf of synth-mania.

Problem

The top-bar tokens in/out (and cache read/write) counters — and the cost figure — appear to be reset by pi-web's compaction process. After a session compacts, the numbers visibly drop.

Root cause

Those counters are computed in hooks/useAgentSession.ts by summing usage over the current context messages only. When compaction finishes, the client calls loadSession(), which rebuilds the message list through the SDK's buildContextEntries(). That function drops the summarized pre-compaction entries (they stay in the JSONL file but are excluded from context). So the summed token/cost counters collapse to the post-compaction usage only.

The SDK's own AgentSession.getSessionStats() does the opposite and correct thing: it aggregates over all session-file entries, including the usage recorded on compaction/branch-summary entries — because compaction only appends a summary entry and never deletes history.

Fix

Make the reported stats use the same all-entries aggregation the SDK uses, while still tracking usage from messages completed since the last session load:

  • lib/session-stats.ts (new): mirrors the SDK's all-entry aggregation and merges the file total with the positive delta between the loaded context and current messages.
  • app/api/sessions/[id]/route.ts: GET now returns a cumulative stats block computed over all entries.
  • hooks/useAgentSession.ts: uses the shared merge helper so compacted history is counted once and newly completed messages increment the counters immediately.
  • lib/types.ts: shared AgentUsage type; adds usage? to compaction/branch-summary/tool-result entries.

This keeps counters monotonic across compaction, page reloads, branch navigation, and multi-step tool runs without double-counting after a reload.

Verification

  • Unit coverage includes all-entry aggregation, SDK compaction behavior, post-load live deltas, and no double-counting after reload.
  • Full suite: 594 pass.
  • tsc --noEmit and eslint . clean.

synth-mania and others added 2 commits August 20, 2026 02:46
The top-bar token (in/out/cache) and cost counters were computed by
summing usage over the current context messages only. After compaction,
pi's buildContextEntries() drops the summarized pre-compaction entries,
so loadSession() replaced the message list and the counters visibly
reset (and cost dropped with them).

The SDK's own getSessionStats() instead aggregates over ALL session-file
entries (including usage recorded on compaction/branch-summary entries),
because compaction only appends a summary — it never deletes history.

- lib/session-stats.ts: computeSessionStats() mirrors that all-entries
  aggregation (assistant + tool-result messages, compaction and branch
  summary usage).
- GET /api/sessions/[id] now returns a cumulative `stats` block computed
  over all entries.
- useAgentSession merges file stats into the live per-message sum with a
  per-field max(), so counters only ever increase — across compaction,
  page reloads, and branch navigation — while still tracking live
  streaming usage before it is persisted.
- types.ts: shared AgentUsage type; usage? on compaction/branch-summary/
  tool-result entries.

Adds unit tests (incl. a monotonicity check against the SDK's real
buildContextEntries) and a real-world check on a compacted session.
@agegr
agegr merged commit 93633c8 into agegr:main Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants