This is a experimental project. Note that, for the current architectures, normal compaction is better for all, except long horizon memory.
A pi extension that replaces monolithic context compaction with a persistent, agent-controlled variable store.
Memory ≠ Context. Everything that happens in a session is stored as a variable in realtime (full content, never truncated). Context management — deciding what stays visible — happens only at compaction time, and the agent makes those decisions itself.
Every message (user, assistant, tool result) is stored as a variable the moment it happens: full content, kind, size, in message order. Content capture only — no summaries, no decisions. The store is always complete, even if compaction never fires or the session dies.
At each compaction boundary a separate compaction agent (an LLM call) decides, per message:
- PIN — full content stays visible verbatim (decayable or persistent)
- ARCHIVE — only a dense summary stays in context
- DROP — no representation; content stays fully recoverable in the store
It writes per-entry summaries while the content is in view, records
depends_on edges, and rolls old archives into historical rollups when
the archive section grows too large.
The model-visible compacted message is a deterministic hybrid context snapshot: current task, current state, decisions and constraints, open work, active topics, a compact memory index, and verbatim pins. It is a state document, not one bullet per message. The compaction model returns structured JSON; the extension owns the final Markdown layout and token enforcement.
Guarantees:
- Nothing is deleted — archived/dropped/rolled-up content is always recoverable; the raw session JSONL is the final floor.
- Completeness — every candidate ends in exactly one state; unhandled candidates default to archive.
- Pin integrity — persistent pins are rendered directly from the store, verbatim, and only an explicit main-agent decision can create them.
- Bounded snapshots — normal compacted state has a configurable token target; verbatim pin content is tracked separately and never silently cut.
- Stable identities — variable ids/seq are fixed at message time, so dependencies and tool references never go stale.
- Cache-friendly — one context rewrite per compaction; summaries are written once, never rewritten.
- Graceful fallback — any failure (unparseable output, missing model, store error) falls back to pi's default compaction.
Published on npm as pi-ctx-vars:
pi install npm:pi-ctx-vars # install globally (user settings)
pi install -l npm:pi-ctx-vars # install for the current project
pi -e npm:pi-ctx-vars # try it for one run, no installOr from source:
pi -e /path/to/pi-ctx-vars/extension/index.tsDependencies: Node 22+ (uses node:sqlite).
| Tool | Purpose |
|---|---|
context_pin(target, decay?) |
Protect a variable (var_N) or raw entry so its full content survives compaction. decay=false = persistent (only the main agent can unpin). |
context_unpin(target) |
Release a pin (main agent only). |
context_drop(target) |
Remove representation from context; content stays in the store. |
context_archive(target, summary?) |
Reduce to summary-only representation. |
context_read(var_id) |
Load a variable's full content into context (does NOT pin). |
context_query(sql) |
Read-only SQL over the store (SELECT/WITH only) — the way to search and inspect variables. Tables: variables, depends_on, rollups, decisions. |
Configure under contextVars in ~/.pi/agent/settings.json or .pi/settings.json
(CTXV_* env vars override):
| Key | Default | Meaning |
|---|---|---|
enabled |
true |
Master switch |
realtimePopulation |
true |
Store every message as a variable as it happens |
archiveKeepIndividual |
30 |
Keep this many archive summaries individually before rolling up |
summaryMaxChars |
500 |
Per-entry archive summary cap |
summaryTokenBudget |
3500 |
Model-visible snapshot target (tokens), excluding verbatim pins |
snapshotMaxTopics |
10 |
Maximum synthesized active-topic sections |
rollupMaxChars |
2000 |
Historical rollup summary cap |
packetTokenBudget |
15000 |
Compaction-agent input packet budget (tokens) |
compactionModel |
null |
"provider/model" for the compaction agent (default: session model) |
compactionMaxTokens |
4000 |
Compaction agent output cap |
pinnedMaxChars |
40000 |
Warning threshold for large pins; pins remain verbatim |
queryRowCap |
50 |
context_query result cap |
storeDir |
null |
Override store location (default: next to the session file) |
debugLogPath |
null |
Write a debug log |
cd extension
npm install
npx tsc # typecheck
cd .. && node --experimental-strip-types --test test/unit-*.mjsMIT