← Back to PRs

#21610: fix(context-pruning): pi-extensions never built + null lastTouch skips prune after restart

by xnohat open 2026-02-20 04:45 View on GitHub →
agents size: XS
## 3 bugs found & fixed via live debugging on Raspberry Pi 4 --- ### Bug 1 (build): `pi-extensions/` never built — pruning never ran in production `resolvePiExtensionPath()` resolves to `<root>/pi-extensions/<id>.js` but `tsdown.config.ts` had no entry for these files. Both `context-pruning` and `compaction-safeguard` were silently never loaded. **Fix:** Add tsdown entry → `pi-extensions/`. Add to `.gitignore` (build artifact). Fix `.gitignore` pattern to `/pi-extensions` (was incorrectly also ignoring `src/agents/pi-extensions/`). --- ### Bug 2 (logic): `!lastTouch` falsy check skips pruning after restart After restart, `readLastCacheTtlTimestamp` returns `null` (correctly skips zero-timestamp entries). But `extension.ts` used `!lastTouch` which is `true` for both `null` and `0` → pruning skipped indefinitely after every restart. ```ts // Before if (!lastTouch || ttlMs <= 0) return undefined; // null treated same as 0 // After if (ttlMs <= 0) return undefined; if (lastTouch !== null && Date.now() - lastTouch < ttlMs) return undefined; // null = cold cache → prune immediately ``` --- ### Bug 3 (estimate): `CHARS_PER_TOKEN_ESTIMATE = 4` causes pruner to under-fire Pruner computes `charWindow = contextTokens × CHARS_PER_TOKEN`. With `= 4`, charWindow is overestimated → fill ratio underestimated → pruner stops too early. Empirical measurement from a real session (code/JSON/shell tool results): - `~384k chars / ~158k tokens ≈ 2.44 chars/token` With `= 4`: ratio = `384k / 800k = 48%` → below `hardClearRatio=0.5` → no hard-clear With `= 2.5`: ratio = `384k / 500k = 77%` → above `hardClearRatio=0.5` → hard-clear runs ✅ Note: prose ≈ 4 chars/token, but tool results (code, JSON, shell) tokenize more efficiently at ~2.5. --- ## How it was found Debugged live on a Raspberry Pi 4 running OpenClaw 2026.2.18 — noticed context pruning never reducing context size even after extended idle. Traced through session JSONL entries, extension loader, pruner source, and Anthropic token usage data.

Most Similar PRs