pi-context-epochs
v0.1.0
Published
Model-driven context epochs for Pi: same-session fresh working contexts without LLM-generated summaries. Provides new_context, context_notes and context_history primitives.
Downloads
117
Maintainers
Readme
pi-context-epochs
Model-driven context epochs for Pi: start a fresh working context inside the same session, with no LLM-generated summary, while the full transcript stays append-only, durable, searchable, and recoverable.
Inspired by OpenAI Codex's context management work (openai/codex#27488, the new_context tool and context-window replacement), implemented as a thin Pi extension using Pi's own durable compaction machinery.
Motivation
Long agentic sessions exhaust their context. The two options Pi ships are:
- Compaction (
/compact, auto-threshold): an LLM summarizes old messages. That costs a summarization call, is lossy, and replaces exact history with a paraphrase. - New session (
/new,ctx.newSession()): a different session file. Identity, notes, and continuity are lost.
Neither is "retire the working set and continue". The missing primitive is a durable context boundary: old messages leave the model's active context, the transcript keeps everything, and the session keeps its identity. Pi upstream does not yet expose ctx.newContext() (proposal, auto-closed as earendil-works/pi#8972).
This package provides that primitive as an extension by reusing Pi's own compaction projection: an epoch boundary is a real CompactionEntry whose kept-entry id can never match a real entry, so Pi itself - context reconstruction, token accounting, auto-compaction checks, /tree, fork, resume, reload - treats everything before the boundary as retired.
Pi session (one JSONL file, append-only tree)
│
├── epoch 0 user/assistant/tool messages
├── epoch 1 user/assistant/tool messages
├── epoch 2 ← active model working context
├── Notes ← model-curated, session-scoped, branch-aware
└── History ← full transcript; retired epochs are still searchableWhat the model gets
Exactly three tools (plus a /epoch command for the human):
| Tool | Purpose |
|------|---------|
| new_context | End the current epoch and continue the same session in a fresh working context. No arguments. No summary. |
| context_notes | list / read / write / delete small, session-scoped notes that survive rollovers. |
| context_history | search (lexical) and read (by stable entry ref) the durable transcript, including retired epochs. |
new_context semantics
Calling new_context arms a rollover. When the current agent run settles, the extension:
- Asks Pi to compact, and (via the
session_before_compacthook) supplies a package-owned, deterministic boundary entry - no LLM call:summary: a small<context_epoch>marker (epoch id, previous epoch id, trigger, current note keys, the verbatim latest user request as a continuation anchor, one re-grounding instruction).firstKeptEntryId: a value that can never match a real entry id, so Pi's projection keeps only the marker plus everything after it.details:{ kind: "pi-context-epochs/epoch", version: 1, epochId, previousEpochId, epochIndex, trigger, createdAt }.
- Pi appends the entry, rebuilds the agent's active context from its own session projection, and emits
session_compact(the extension verifies the persisted entry is really package-owned before continuing). - A one-line continuation nudge starts the first turn of the new epoch.
Triggers recorded in the boundary: model (the tool), manual (/epoch new), threshold (Pi auto-compaction threshold), overflow (provider context-overflow recovery).
The rollover never calls a summarizer. The only LLM cost after a rollover is the model's own continuation turn.
Notes philosophy
Notes are for non-reconstructable semantic state: explicit user decisions, rejected approaches and why, non-obvious constraints, unresolved blockers, acceptance criteria, external side effects. Things that are cheap to re-derive (source files, git, tests, package metadata, history) should be re-derived, not copied. Notes are session-scoped: they end with the session; there is no cross-session memory.
Notes persist as Pi custom entries on the session branch, so /tree and fork replay only the notes that belong to the branch you are on.
History
context_history search does case-insensitive substring matching with a simple score (occurrences + recency + role bias) over the current branch (or the whole file with scope: "session"). No embeddings, no vector DB, no background indexer - the session entries are the source of truth. Results carry stable refs (Pi entry ids). read returns one entry plus a bounded neighborhood. Reasoning/thinking blocks are never returned; tool results are tightly truncated; total read size is bounded (historyMaxChars, default 16k chars).
What the runtime gets (the model does not see this)
- Context pressure reminders - one-shot per epoch per band, computed by the runtime from
ctx.getContextUsage()against the model's realcontextWindow(no per-turn counters, no prompt churn):- soft at ≤ 25% remaining (configurable): "preserve non-reconstructable state; start a new context when the working set is no longer useful"
- critical at ≤ 10% remaining: only sent when
autoRolloveris off - otherwise that zone is owned by Pi's threshold/overflow machinery (see below), and a steered critical reminder would land stale in the fresh epoch.
- Automatic rollover - when Pi's auto-compaction threshold fires (or a provider overflow needs recovery), the extension converts the compaction into an epoch boundary instead of a summarization call. Loop-safety: after the boundary the active context is tiny, so the threshold check cannot re-fire; tests cover the no-loop property.
- Verification - after any rollover, the extension checks that the persisted compaction entry is package-owned. If another extension overwrote the compaction result, it fails closed: no continuation is sent, and the conflict is surfaced.
- Conflict scan - at startup the package warns about installed extensions known to also replace compaction results (pi-agenticoding, pi-context, pi-context-prune, billion-context-pi, pi-codex-compaction, pi-codex-compact, pi-session-continuity, pi-continue).
Why the boundary is "real"
buildContextEntries() - Pi's single projection used for prompt building, token estimation, auto-compaction decisions, reload, resume, and tree navigation - keeps, for the latest compaction on the branch: the compaction entry itself, entries from firstKeptEntryId (never matches ⇒ none), and everything appended after it. Therefore:
- Active context: marker + new-epoch messages only.
- Token accounting: estimated from the projected context; the stale-usage check in Pi's
_checkCompactionprevents re-triggering off pre-boundary usage. - Reload / resume:
SessionManager.open(file)reproduces the same projection - the boundary is in the file, not in extension memory. /tree/ fork: the boundary is an ordinary branch entry; switching branches switches the epoch sequence (and notes) to that branch.- Durability: the raw JSONL keeps every pre-boundary entry, append-only.
Installation
pi install /Users/zzzhizhi/Developer/zzzhizhia/pi-context-epochs
pi list # should show pi-context-epochsLocal path installs are referenced in place (no copying), so editing the project directory is the update; restart pi or run /reload to pick up changes.
Uninstall:
pi remove /Users/zzzhizhi/Developer/zzzhizhia/pi-context-epochsUsage
- Ask the model to "start a new context" / "roll over" - it calls
new_context(writing essential notes first). /epoch- status (epoch, usage, pressure, notes, last rollover)./epoch new- user-triggered rollover (no summary)./epoch notes//epoch history//epoch debug- notes list, epoch boundaries, diagnostics (raw vs active context sizes, boundary details, conflicts).
Headless/print mode works; continuation waits for the follow-up run to settle there.
Configuration
~/.pi/agent/context-epochs.json (optional; defaults are safe, invalid values warn and fall back):
{
"enabled": true,
"softRemainingRatio": 0.25,
"criticalRemainingRatio": 0.1,
"autoRollover": true,
"historySearchLimit": 8,
"historyReadAround": 2,
"historyMaxChars": 16000,
"noteMaxChars": 8000,
"continuationAnchorMaxChars": 2000
}Compatibility & limitations
- Manual
/compactis untouched (native summarization is preserved) unless the instructions string is the package's internal marker. - Competing compaction rewriters: last
session_before_compacthandler wins in Pi. The extension warns at startup and fails closed at verification time; it never sends a continuation for a boundary it does not own. - Tiny sessions: a rollover needs content before Pi's cut point (
keepRecentTokens, default 20k).new_contextpre-checks this and refuses gracefully on very small contexts instead of creating a meaningless boundary. - Not checkpoint/rewind: epochs replace the whole active working context generation; they do not snapshot or restore granular states, and they do not interfere with git-checkpoint style tools.
- Thinking/reasoning content is never exposed through history reads.
How this differs from Pi compaction
Pi compaction summarizes (LLM call, paraphrase). This package retires: no summary, no summarizer cost, exact history still retrievable. It rides on Pi's compaction path because that path is the one place where pi rebuilds agent state from the durable session projection - which is exactly what "same session, fresh context" needs.
Relation to Codex's experimental_mode context management
Parity: same-session fresh working context (new_context), token-budget-style sparse pressure reminders computed by the runtime, no planner/orchestrator machinery. Differences: Codex's runtime replaces the active history as a first-class session operation and recomputes usage natively; here the boundary is expressed through Pi's compaction projection (identical observable behavior, different mechanism), and Codex's budget reminder cadence/labels differ in detail. This package does not clone Codex prompts or role workflows.
Development
npm install
npm run typecheck
npm test # unit + SDK integration tests (scripted model, no network)
npm run check # typecheck + tests
npm run measure # model-facing overhead measurementLicense
MIT
