← Back to PRs

#14879: fix: persist session metadata to sessions.json after context pruning

by skylarkoo7 open 2026-02-12 18:46 View on GitHub →
agents stale size: L
## Summary Fixes #14857 After context pruning runs (cache-ttl mode), `sessions.json` retains stale `contextTokens` values until the next inbound message triggers a usage update. This causes Mission Control dashboards and monitoring systems to show incorrect context usage (e.g. 200k/200k when actual is much lower post-pruning). **Root cause:** The pruning extension modifies messages in-flight during the Pi context event but never writes the reduced token estimate back to the session store. The store only updates when `persistSessionUsageUpdate` runs after the next agent reply. **Fix:** After a successful prune, fire-and-forget persist the new estimated token count to `sessions.json` via `updateSessionStoreEntry`. This is best-effort -- a failure here never blocks the agent run. ## Changes | File | Change | |------|--------| | `src/agents/pi-extensions/context-pruning/runtime.ts` | Add `sessionKey` and `storePath` to `ContextPruningRuntimeValue` type | | `src/agents/pi-extensions/context-pruning/pruner.ts` | Export `estimateContextChars` and `CHARS_PER_TOKEN_ESTIMATE` for reuse | | `src/agents/pi-extensions/context-pruning/extension.ts` | After pruning, calculate new token estimate and persist to sessions.json | | `src/agents/pi-embedded-runner/extensions.ts` | Accept `sessionKey`/`storePath` params, resolve `storePath` from config, thread to runtime | | `src/agents/pi-embedded-runner/run/attempt.ts` | Pass `sessionKey` to `buildEmbeddedExtensionPaths` | | `src/agents/pi-embedded-runner/compact.ts` | Pass `sessionKey` to `buildEmbeddedExtensionPaths` | ## Tests (30 new tests across 3 files) | Test File | Tests | Coverage | |-----------|-------|----------| | `extension.test.ts` | 12 | Persistence after pruning, no-persist when session info missing, error swallowing, update callback token calculation, TTL behavior | | `runtime.test.ts` | 9 | Store/retrieve runtime, sessionKey/storePath storage, deletion, non-object guards, isolation between sessions, backward compatibility | | `pruner-exports.test.ts` | 9 | CHARS_PER_TOKEN_ESTIMATE value/type, estimateContextChars for empty/single/multiple/mixed messages, token consistency | ## Test plan - [x] No persist when no runtime registered - [x] No persist when TTL not expired - [x] No persist when messages unchanged (no pruning occurred) - [x] Persist fires after successful pruning with sessionKey + storePath - [x] No persist when sessionKey missing - [x] No persist when storePath missing - [x] Persistence errors swallowed (fire-and-forget) - [x] Update callback computes correct estimated tokens - [x] totalTokens is at least the estimated context tokens - [x] lastCacheTouchAt updated after pruning - [x] Runtime stores/retrieves sessionKey and storePath - [x] Backward compatible -- sessionKey/storePath are optional - [x] Existing session and pruning config tests still pass - [x] All 30 new tests pass <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR wires `sessionKey`/`storePath` into the context-pruning runtime and, after a successful prune in cache-TTL mode, fire-and-forget persists an updated `contextTokens` estimate to the session store so monitoring dashboards reflect post-pruning usage immediately. The main new flow is: embedded runner builds extension paths → initializes context-pruning runtime → extension prunes messages on the `context` event → computes an estimated token count from the pruned messages and calls `updateSessionStoreEntry` to patch `contextTokens`/`totalTokens`. Issue found: the embedded runner currently resolves `storePath` via `resolveStorePath()` without passing the active agent id/config store, which defaults to the *default agent* sessions store; this can cause writes to the wrong `sessions.json` when running under a non-default agent. <h3>Confidence Score: 3/5</h3> - This PR is close to mergeable but has a correctness issue in multi-agent setups that can write pruning metadata to the wrong sessions store. - Core pruning/persistence logic is straightforward and best-effort, but `storePath` resolution currently falls back to `DEFAULT_AGENT_ID` (via `resolveStorePath()` with no opts), so embedded runs for non-default agents can fail to persist or update the wrong `sessions.json` entry. - src/agents/pi-embedded-runner/extensions.ts <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs