v0.20.0 — Pi parity fixes, mid-turn cache stability, key-files redesign
What's new
Fixed Pi parity gaps with OpenCode
- Long-running Pi sessions durably compact. Pi's compaction marker now writes correctly across the visible message branch, so historian publications actually shrink
getBranch()on every run instead of accumulating forever. Tool-heavy sessions that previously grew without bound until provider overflow now stay healthy through normal scheduled compaction. - Pi understands its own context pressure. Scheduler thresholds, force-materialization at 85%, and emergency block at 95% now compute pressure from input tokens only (matching the wire payload), respect provider-reported overflow limits, and reset stale persisted state on restart and model switch.
- Pi has a built-in
todowritetool. Magic Context registers atodowritetool for Pi sessions matching OpenCode's wire shape. Synthetic todowrite injection on cache-busting passes preserves your task list across compaction the same way it does on OpenCode. - Pi gets project orientation content.
<key-files>,<project-docs>, and<user-profile>blocks now render in Pi's system prompt the same way they do on OpenCode, using the same project-scoped state.
This required a major refactor of the Pi context handler. The full Pi e2e suite (37 active scenarios) passes end to end.
Mid-turn execute decisions defer to turn boundaries
Anthropic prompt-cache continuity matters most during the multi-step tool-use turns that fill the middle of a long session. Previously, if the scheduler decided to execute heuristic cleanup and queued tool drops mid-turn, that cleanup ran on the very next defer pass while the assistant was still working through its tool calls — busting the cache and forcing the rest of the turn through a cold prompt.
Mid-turn execute decisions are now deferred until the turn ends. The scheduler still detects when execute would normally fire and records the intent durably; at the end of the next pass that finishes execute-gated work successfully, the deferred flag is cleared. Safety paths (force-materialize at 85%, explicit /ctx-flush, subagents) bypass the deferral as before.
Net effect: multi-step turns now keep their cached prefix through tool calls instead of each step paying a cache-rebuild cost.
Key files are now project-scoped, symbol-stitched, and version-invalidated
The pinned-key-files system has been redesigned end to end:
- Project-scoped. Key files live in a project table, not session storage, so every session in the same project sees the same orientation set without re-evaluation.
- AFT-stitched content. The dreamer asks an AFT-enabled subagent to produce one structured stitched block per file — outline plus selected full implementations — instead of dumping raw bytes. Files you read frequently get accurate symbol-level context, not whole-file dumps that blow the budget.
- Version-invalidated cache. A shared version counter coordinates injection caches across OpenCode and Pi. Dreamer updates atomically replace the rows and bump the version in one transaction; running sessions pick up the new content on their next cache-busting pass.
- Pi parity. Pi sessions render the same
<key-files>block as OpenCode.
Disk drift is handled gracefully: stale content updates queue without busting the in-memory rendered block. Unreadable files start with stale_reason='missing' and stay out of injection until they're available.
Pi 0.74 is the new minimum
Pi has moved to the @earendil-works/* package scope and github.com/earendil-works/pi-mono. Pi 0.74 or newer is required for the v0.20 plugin. The unified CLI ships with engines.node >= 24 to match Pi's runtime.
If you're on an older Pi:
# Update Pi
pi update --self
# Or via npm
npm install -g @earendil-works/pi-coding-agent@latestWhat's fixed
-
magic-context doctor --forceon cold caches. When OpenCode's plugin cache had a populatednode_modules/but no rootpackage.json(e.g. an interrupted install), the auto-update path failed to resolve the install context and silently skipped the upgrade. Doctor now seeds a minimal root package file so subsequent updates succeed (issue #73). -
OpenCode session deletes no longer pass a directory query. The
client.session.deletecall for hidden child sessions (historian, dreamer, sidekick, compressor) was passing adirectoryparameter that OpenCode middleware used for plugin context selection, creating a small risk of cross-session deletion on unusual path-normalization cases (issue #72). -
Dreamer key-files lease. The post-task key-files phase ran a long AFT-and-LLM pipeline outside the dreamer's lease-renewal loop, so on slower models the lease could expire mid-task and the final commit would be rejected. Lease renewal now wraps that phase.
-
ensureColumnrace under concurrent startup. When OpenCode and Pi started simultaneously against a pre-upgrade database, the column-add path could fail withduplicate columnif both processes raced. The helper now treats a sibling-won race as success. -
Pi restart no longer leaves a stuck historian flag. If Pi was killed mid-historian, the
compartment_in_progressflag would survive restart and block future historian runs. The flag now clears on init when no historian is actually in flight. -
Context usage caching staleness. Persisted pressure was preferred even when it predated a model change, occasionally causing scheduler decisions against the wrong context limit on the first turn after switching models. First-pass and model-switch invalidation now clear stale state before the scheduler reads.
-
todowritepriority is now genuinely optional. Pi sessions where the model omittedpriorityin a todowrite call had their snapshots silently dropped because the normalizer requiredpriority. Missing priority now defaults to"medium"(matching OpenCode behavior) so capture and replay always work. -
Auto-update install context resolution. Same as
doctor --forceabove — applies to the in-session auto-update hook too.
Upgrade
# OpenCode
npx @cortexkit/magic-context@latest doctor --force
# Pi (requires Pi >= 0.74)
pi update --self
npx @cortexkit/magic-context@latest setup --harness piIf you're upgrading from a version older than 0.19, run doctor --force to refresh the cached plugin install.