pi-condense
v2.10.5
Published
Pi coding-agent extension that summarizes completed tool-call batches, replaces raw outputs with short stubs, compresses closed tool-call chains, and recovers any original on demand via context_tree_query.
Maintainers
Readme
pi-condense
A Pi coding-agent extension that keeps long agent sessions cheap by pruning context - the context-economy layer of the pi agent toolkit (see below).
The problem
Every long agent session accumulates raw tool output - file reads, command dumps, search results - that the model already used and will never need again verbatim. Left in context, it degrades reasoning on later turns, inflates the cost of every subsequent request, and pushes a smaller/cheaper model past the point where it can still drive. Provider prompt-caching does not fix this on its own: naive trimming actively fights it, because rewriting the prompt on every turn busts the cache you were relying on to keep costs down.
pi-condense replaces finished tool-call batches with short, recoverable summaries, timed around exactly that caching problem. Nothing is deleted - the session file on disk is untouched, and any summarized result can be pulled back verbatim via the context_tree_query tool. Net effect: long sessions stay affordable, and a smaller/cheaper driver model stays viable for longer.
Why this, not naive trimming
- Recoverable, not lossy. Originals are archived and addressable by a short ref. Summarizing only changes what the model sees by default - not what it can retrieve on demand.
- Batched against the cache, not per turn. Pruning fires once per finished unit of work (configurable), so the prompt prefix stays stable in between and providers keep serving it from cache. Pruning every turn instead would bust the cache on every single turn - the opposite of the intended savings.
The full argument, with diagrams, is in PRUNING.md; this README stays at the "why and how to use it" level.
Part of the pi agent toolkit
Four independent extensions for the pi coding agent, each owning one concern of running agents seriously:
- pi-quiver - capabilities (fetch, doc conversion, session tools)
- pi-cohort - coordination (delegate to focused child agents)
- pi-condense - context economy (this repo): prune context, keep it recoverable
- pi-gauntlet - process (the gated brainstorm->ship workflow)
No code dependency either way. The practical coupling: pi-condense is what keeps a long pi-cohort fan-out or a long pi-gauntlet gated run affordable as it grows, and both can surface pi-condense's live cost via the shared cost:external channel (see External cost channel).
Mental model
Two-layer memory, not deletion:
- Hot: a compact summary of a finished batch, kept in active context.
- Cold: the full original tool results, archived in a session-local index and addressable by a short ref (
t1,t2, ...).
The model reads the hot summary by default and calls context_tree_query when it actually needs the cold original back. See PRUNING.md for the full before/after diagrams and the prefix-cache mechanics behind the batching schedule.
flowchart LR
B["finished tool-call batch<br/>(t1, t2, t3 ...)"] --> S[summarize into short stub]
S --> H["hot: model sees the stub"]
B -.archived, session file untouched.-> C["cold: originals by ref"]
H -->|"context_tree_query(t2)"| C
C -.restores original.-> HQuick example
pi install npm:pi-condense/pruner on # enable pruning (off by default)
/pruner model openai/gpt-4.1-mini # pick a cheap summarizer
/pruner status # see mode, model, trigger, cumulative statsArchitecture
| Trigger mode | Fires | Cache impact |
|---|---|---|
| agent-message (default) | When the agent sends a final text-only reply | ~1 cache rewrite per task batch |
| on-demand | Only when you run /pruner now | None until you ask |
With the default agent-message trigger (and autoBudgetThreshold/budgetTurnDelta unset), a non-interactive (pi -p) session sees its first flush only at the final reply - set autoBudgetThreshold (e.g. 0.8) so flushes also fire mid-run. This is a property of single-prompt sessions, not a defect in the default.
Before any summarizer call, a pre-flush pipeline can drop or redirect a batch at zero LLM cost: protected tools/paths are never touched, content-hash duplicates are aliased to the original, batches too small to be worth summarizing are skipped outright, and oversized single results are spilled straight to a sidecar file. Closed tool-call chains older than a rolling window are additionally range-compressed. Full pipeline and each safeguard: PRUNING.md § Pre-flush Pipeline & Safeguards, § Chain Compression.
External cost channel
Every summarizer cost update is emitted on the shared pi.events channel cost:external (source: "pi-condense", cumulative per session, live only - not persisted, not re-seeded on restart). This is a generic channel: pi-condense is a producer, not the owner. pi-cohort is the canonical consumer, folding it into a single Σ$ total alongside its own subagent costs.
Key concepts
| Term | Meaning |
|---|---|
| Stub | The short breadcrumb ([Summarized in pruner summary, ref \t1`...]) that replaces a pruned tool result in context |
| context_tree_query | The tool the model calls to recover a stubbed original by ref (tN) or toolCallId. A reused id returns every matching occurrence, not just one, including any that were content-deduplicated to an earlier record - see [PRUNING.md § Occurrence Identity](PRUNING.md#occurrence-identity) |
| Batch vs chain | A batch is one flush's worth of tool calls; a chain is a longer closed sequence eligible for range compression |
| Prune frontier | The last attempted prune boundary - advances even on a skip, so nothing is reconsidered twice |
| Diagnostics (diag u/m/o/b) | A self-hiding status-line segment surfacing prune-time degradations: u= unresolved chain range,m= detection/render id mismatch (informational, does not change what's dropped),o= orphan tool-result sweep,b= a zero-coverage chain with nothing left to backfill (genuine span mismatch, see below). Each letter's count is omitted when zero; the whole segment disappears when all four are zero. Backing session entries arecontext-prune-diagnostic - see below |
| Context metrics (thinking/chain share/frontier gap) | Open-cycle thinking tokens, largest-chain share, frontier gap - what the pruner cannot (yet) reclaim, notably in single-chain sessions. Shown on /pruner status, never on the footer. See below and [PRUNING.md § Single-chain sessions](PRUNING.md#single-chain-sessions) |
| Prompt-cache interaction | Why batching (not per-turn pruning) is the default - see [PRUNING.md](PRUNING.md#how-prefix-caching-works) |
| cost:external` | The shared cost-reporting channel pi-condense emits on (see above) |
Diagnostic entries (context-prune-diagnostic)
The status-line diag u<N>/m<N>/o<N>/b<N> segment above is backed by context-prune-diagnostic session entries - session-log-only, never added to what the model sees. Full mechanics: PRUNING.md § Diagnostics.
Uncovered chains compress too
/pruner compact and the automatic flush both compress eligible chains even when no per-batch summary ever covered them - a trivial batch, an oversized-skip, a fully-deduped batch, or a plain capture miss all used to strand the chain permanently with a no-summary skip. These chains now get a deterministic, zero-LLM-cost stub body (call count, tool histogram, span duration, working t<N> refs) instead; the raw tool outputs are archived exactly like the covered path and stay recoverable via context_tree_query. Exception: a zero-coverage chain whose middle calls are all protected stays uncompressed (plain no-summary skip, no diagnostic) - every output would relocate verbatim into the synthetic body anyway, so compressing saves nothing. Full mechanics: PRUNING.md § Deterministic fallback (uncovered chains).
Limitation: a chain stranded in an otherwise-idle session is not healed by /pruner now on an empty queue (the flush returns early before chain detection runs at all) - it heals on the next flush that has any work, or immediately via /pruner compact.
Context metrics (context-prune-flush-metrics)
Three metrics the pruner cannot yet reclaim - open-cycle thinking tokens, largest-chain share (%), frontier gap tokens - surface in two places, both backed by computeContextMetrics (src/context-metrics.ts). They are deliberately kept off the footer status line, which stays limited to prune state, reclaim, and diagnostics:
/pruner statusprints a--- context ---block:thinking:,chain share:,frontier gap:, plus arearmed: yesline while a reload-rearm probe (below) has recoverable work armed.- Each flush attempt (every outcome, including empty/error) writes one
context-prune-flush-metricssession entry with the pre-flush snapshot - session-log-only, never added to what the model sees, and not reconstructed on reload.
These are most informative for long single-chain sessions where Phase 3 (chain compression) never gets a closed chain to act on - see PRUNING.md § Single-chain sessions for the limitation and config guidance, and PRUNING.md § Reload rearm for how a reload with recoverable pending work re-arms the automatic flush trigger.
When to use / when NOT to use
Use it for: long coding or research sessions where tool output dominates the prompt; setups deliberately running a smaller/cheaper driver model; pi-cohort fan-outs or pi-gauntlet runs where cost compounds across many turns or many children.
Don't reach for it when: the session is short and one-shot - there is nothing accumulated to prune, only latency to add. It also doesn't replace a provider's own native context-compaction feature if you already rely on that, and it doesn't reduce the cost of the current turn's tool calls - only of history that has already been produced.
Limitations
- Pruning only applies to batches captured while enabled. Enabling mid-session does not retroactively summarize earlier turns.
- Summarizer calls run synchronously inside the turn boundary, so they add latency proportional to the summarizer model's response time. Pick a fast one.
- Content-hash dedup only matches against records already in the indexer (cross-flush); two identical outputs within the same flush both go through the summarizer.
- The tree browser (
/pruner tree) does not inline original tool outputs - usecontext_tree_queryfor that.
Install
Published to npm as pi-condense.
User scope (all repos under your pi profile):
pi install npm:pi-condenseProject scope (current repo only, committable via .pi/settings.json):
pi install -l npm:pi-condenseTry without installing:
pi -e npm:pi-condenseFrom a local checkout (for hacking on the extension itself):
git clone [email protected]:jjuraszek/pi-condense.git ~/repos/pi-condense
cd ~/path/to/your/repo
pi install -l ~/repos/pi-condense
# or one-shot, no install:
pi -e ~/repos/pi-condense/index.tsPin a specific version with npm:[email protected]. Upgrade by re-running pi install. Remove with pi remove pi-condense. Once installed, the extension auto-loads on every pi invocation; no flags needed. See CHANGELOG.md for release history.
By default the extension is off. /pruner on enables it and it stays enabled across sessions in the same pi agent directory.
Configuration - the knobs most people touch
Settings live under contextPrune in <agent-dir>/settings.json ($PI_CODING_AGENT_DIR if set, else ~/.pi/agent). Each pi preset gets its own settings. A settings.json that cannot be read as a JSON object is never overwritten by a /pruner change: the change applies to the current session and an error notification names the file.
| Key | Default | Notes |
|---|---|---|
| enabled | false | Master switch (or just use /pruner on) |
| summarizerModel | "default" | Pin a cheap model instead of reusing your active one - see the plan-by-plan table in doc/configuration.md |
| pruneOn | agent-message | Trigger mode - see Architecture above |
| autoBudgetThreshold | null | Fraction (e.g. 0.8) of the context window that force-flushes everything regardless of pruneOn; the trigger point is capped at 300k tokens |
| frontierGapThresholdTokens | null | Opt-in absolute-token flush trigger: fires at turn_end once the un-pruned tail past the prune frontier reaches N tokens, regardless of window size; recommended starting value 80000 |
| protectedTools / protectedPaths | [] / ["**/skills/**/*.md", "**/gauntlet-overrides.md"] | Tool names / path globs that are never summarized; only the newest read per protected path stays verbatim (older reads of the same path are stubbed once the prompt cache is cold anyway) |
| spillThreshold | 65536 | Chars above which a single oversized result spills straight to a sidecar file |
The default also protects reads of pi-gauntlet's per-repo gauntlet-overrides.md so the repo's harness contract stays available for gate decisions after pruning.
The full settings JSON, every key, the commands table, footer widget states, spilled-output details, and the summarizer-model-by-plan table live in doc/configuration.md.
Relationship to the rest of the platform
pi-condense is the context-economy layer: it has no code dependency on the other three, but it is what keeps a long pi-cohort parallel fan-out or a long pi-gauntlet gated run affordable as they grow, and research on summarization-based context management suggests it can also make a smaller driver model hold up better on long tasks (see PRUNING.md § Research Evidence - a cited hypothesis, not a benchmark run in this repo).
Roadmap
No committed roadmap beyond what's already tracked in CHANGELOG.md; proposals and in-progress work show up there and in repo issues first.
Contributing
See CONTRIBUTING.md - issues follow a Context / Problem / Idea / Acceptance Criteria template; PRs run the pi-gauntlet workflow (one-liners exempt from ceremony, never from keeping docs truthful).
Support
If this saves you tokens, buy me a coffee.
Lineage
Adds pre-flush safeguards, agent-message batching, chain compression, and an npm release flow on top of the original approach from championswimmer/pi-context-prune.
References
- Anthropic prompt caching: https://docs.claude.com/en/docs/build-with-claude/prompt-caching
- AWS Bedrock prompt caching: https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
- OpenAI prompt caching: https://platform.openai.com/docs/guides/prompt-caching
- Research backing summarization-based context management: see PRUNING.md § Research Evidence
License
MIT - see LICENSE.
