Files
my-pi/pi-condense/doc/configuration.md
T

18 KiB

Configuration reference

Full contextPrune settings, commands, footer widget states, spilled outputs, and the summarizer-model-by-plan table. See the README for the 6 knobs most people touch and the conceptual model; see PRUNING.md for the algorithm behind each setting.

Full settings JSON

Settings live under the contextPrune key in <agent-dir>/settings.json (i.e. pi's own settings file). <agent-dir> is $PI_CODING_AGENT_DIR if set, otherwise ~/.pi/agent. Each pi preset gets its own settings, so you can run different summarizer models per preset. Missing fields use the maintained my-pi defaults in memory; explicit values are preserved and extension load does not rewrite the file.

{
  "contextPrune": {
    "enabled": true,
    "showPruneStatusLine": true,
    "summarizerModel": "default",
    "summarizerThinking": "default",
    "summarizerIdleTimeoutMs": 90000,
    "summarizerMaxTimeoutMs": 300000,
    "summarizerMaxRetries": 2,
    "summarizerRetryBaseDelayMs": 1000,
    "summarizerRetryMaxDelayMs": 8000,
    "pruneOn": "agent-message",
    "batchingMode": "turn",
    "quietOversizedSkips": false,
    "minBatchChars": 1000,
    "recoveryGraceTurns": 3,
    "protectedTools": [],
    "protectedPaths": ["**/skills/**/*.md"],
    "dedupByContentHash": true,
    "autoBudgetThreshold": 0.735,
    "spillThreshold": 65536,
    "spillPreviewBytes": 2048,
    "budgetTurnDelta": 0.04,
    "chainCompression": {
      "enabled": true,
      "rollingWindow": 3,
      "stripFinalAssistantThinking": true,
      "fuseRangeSummary": true
    }
  }
}

Every key

Key Values Default Notes
enabled true / false true Master switch
showPruneStatusLine true / false true Footer widget + queued-turn notifications
summarizerModel "default" or "provider/model-id" "default" default = your active pi model. See Choosing a summarizer model
summarizerThinking default/off/minimal/low/medium/high/xhigh default Provider-specific reasoning effort knob
pruneOn agent-message / on-demand agent-message Trigger mode - see README Architecture section
batchingMode turn / agent-message turn How coarse each summary is (independent of pruneOn)
quietOversizedSkips true / false false Silences skipped-oversized / skipped-trivial info notifications
minBatchChars non-negative integer, 0 disables 1000 Pre-flush guard - batches smaller than this skip the LLM entirely
recoveryGraceTurns non-negative integer (user-turn-groups), 0 disables 3 After a context_tree_query recovery, render that tool's output verbatim for this many user-turn-groups before re-stubbing it. Enforced at render time (Phase 1 + chain-compression eligibility), not at capture time. See PRUNING.md § What Pruning Does
summarizerIdleTimeoutMs non-negative integer (ms), 0 disables 90000 Abort a summarizer attempt after this much silence. Resets on every stream event and also bounds time-to-first-token. A timeout is transient and can consume retry slots.
summarizerMaxTimeoutMs non-negative integer (ms), 0 disables 300000 Hard ceiling on one summarizer attempt. Each retry is independently bounded.
summarizerMaxRetries integer 0-10 2 Transient retries after the initial attempt. 2 means three total attempts. With a distinct summarizer model, the final slot is reserved for session-model fallback. 0 reports the first failure immediately and disables fallback rescue.
summarizerRetryBaseDelayMs non-negative integer (ms) 1000 Delay before retry 1; subsequent waits double. 0 makes retries immediate.
summarizerRetryMaxDelayMs non-negative integer (ms) 8000 Per-wait cap for exponential retry backoff. 0 makes retries immediate.
protectedTools string[] [] Never-pruned tool names (e.g. ["todowrite","todoread"]). When a protected tool's chain is range-compressed, its output is preserved verbatim inside the <compressed-chain> block as <protected-output> - protected outputs are never lost.
protectedPaths string[] ["**/skills/**/*.md"] Globs matched against a tool call's args.path; matching outputs are never pruned (same semantics as protectedTools, including <protected-output> relocation in compressed chains). Already-summarized matching reads are repaired on the next turn; chain-compressed ones are not. Set [] to disable.
dedupByContentHash true / false true Re-reads of identical (toolName, content) skip the LLM and alias the original
autoBudgetThreshold fraction 0-1, or null 0.735 Token-budget auto-flush at 73.5% of the window or 300k tokens, whichever comes first, regardless of pruneOn. null disables it. See Token-budget auto-flush
spillThreshold positive integer 65536 Minimum chars (resultText.length) for a single tool result to be spilled eagerly to a sidecar file at capture time rather than waiting for normal summarization. Non-positive / invalid values fall back to the default; to effectively disable spilling, set it above any result you expect. See Spilled outputs
spillPreviewBytes non-negative integer 2048 Head preview (bytes) kept inline in the stub and index record for a spilled result. Full body is on disk.
budgetTurnDelta fraction 0-1 0.04 Force a flush when one turn grows context by at least 4%, ORed with autoBudgetThreshold. The fraction is measured against min(context window, 300k).
chainCompression.enabled true / false true Master toggle for chain-level range compression
chainCompression.rollingWindow positive integer 3 Keep this many most-recent closed chains raw; compress older ones
chainCompression.stripFinalAssistantThinking true / false true Strip thinking blocks from the kept final text-only assistant when compressing
chainCompression.fuseRangeSummary true / false true Fuse a compressed chain's per-batch summaries into one cohesive LLM summary (one extra summarizer call per multi-batch span); off keeps the per-batch concatenation
purgeErrors.enabled true / false true Replace failed toolCall argument bodies with compact stubs after cooldown
purgeErrors.cooldownTurns positive integer 2 Turns to wait after a tool error before purging its argument body
purgeErrors.minArgChars non-negative integer 500 Only purge arg bodies at least this many characters long

See PRUNING.md § Chain Compression and PRUNING.md § Error Purge for the full algorithms.

The three pre-flush features (minBatchChars, protectedTools, dedupByContentHash) are explained in PRUNING.md § Pre-flush Pipeline & Safeguards. They run BEFORE any summarizer LLM call and can each drop a batch outright while still advancing the prune frontier.

Token-budget auto-flush

When autoBudgetThreshold is set to a value in (0, 1], the extension checks context usage at the end of every tool-using turn. If tokens reaches min(300_000, threshold * contextWindow), ALL pending batches are flushed immediately - regardless of pruneOn mode. This is an additional trigger layered on top of pruneOn, not a replacement.

  • 0.8 means 80% of the context window - it is a fraction, not a percentage. 0.8 != 80.
  • The trigger point is capped at 300,000 tokens (MAX_BUDGET_WINDOW in src/budget.ts). Without it, an advertised 1M window makes every setting unreachable: 0.9 would mean 900k tokens, so a session ends before pruning ever fires. The ceiling never binds on a model advertising 300k or less - a 256k window at 0.9 still fires at 230.4k - so only huge-window models change behavior, and they flush earlier.
  • budgetTurnDelta applies the same ceiling in the other shape: growth is measured against min(context window, 300k), because a 300k ceiling on a single turn's growth could never bind.
  • The trigger is a no-op when tokens is null (right after a provider-side compaction); it resumes once usage is known again.
  • Editable live via /pruner settings (row "Auto-flush at context %", presets Off / 60 / 70 / 80 / 90%).
  • Default null = off.

Inspired by DCP's maxContextLimit nudging; simplified to a single threshold that forces a flush rather than separate nudge/force levels.

Spilled outputs

Single tool results larger than spillThreshold chars are written to <session-dir>/<sessionId>-blobs/<key>.txt at capture time and replaced in context with a short stub (tool name, byte size, head preview, file path). <key> is the sanitized occurrence key id@resultTimestamp (@ becomes _, so bash_23@1150 -> bash_23_1150.txt), not the bare toolCallId - keying on the bare id alone would let two occurrences of one reused provider id share a path and the second overwrite the first's blob. Recovery reads the spillPath persisted on the record rather than re-deriving it from the id, so a bare-id-named sidecar (no occurrence timestamp) still resolves. See PRUNING.md § Occurrence Identity. The full body is recoverable via the native read tool at the embedded path (offset/limit supported) or via context_tree_query by id, which falls back to the inline preview if the sidecar is missing. Moving a session .jsonl without its -blobs/ directory loses only the giant-blob recovery path; bodies under spillThreshold stay inline in the index entry as usual.

Choosing a summarizer model

The default setting reuses whatever model you have active in pi - convenient but wasteful, since summary writing doesn't need a top-tier coding model. Picking the smallest/fastest model on your plan saves both latency and cost.

If the configured summarizer model suffers a transient outage while your active pi model is healthy, pi-condense automatically falls back to the session model for the duration (with a one-time notice) and probes the configured model back every few minutes - no configuration needed.

Plan Suggested summarizer
OpenAI / Codex / Copilot openai/gpt-4.1-mini, google/gemini-2.5-flash, xai/grok-3-fast
OpenRouter openrouter/qwen/qwen3-30b-a3b (cheap MoE)
Anthropic direct anthropic/claude-haiku-3-5
Google AI direct google/gemini-2.5-flash

Set it from the slash command (saves immediately):

/pruner model openai/gpt-4.1-mini
/pruner thinking low
# or both in one go:
/pruner model openai/gpt-4.1-mini:low

Summarizer timeouts and retries

Every summarizer attempt is bounded by two independent timers:

  • Idle timeout (summarizerIdleTimeoutMs, default 90s) resets on every stream event, including reasoning/thinking events. It also bounds time-to-first-token.
  • Total-duration ceiling (summarizerMaxTimeoutMs, default 300s) is a hard upper bound for one attempt.

Timeouts and provider failures are classified as transient. The initial call is followed by at most summarizerMaxRetries retries (default 2), with exponential backoff starting at summarizerRetryBaseDelayMs and capped by summarizerRetryMaxDelayMs (1s → 2s under maintained defaults). The UI reports an error only after the retry budget is exhausted. Each attempt has its own idle and ceiling timers, so the worst-case duration can span multiple attempt ceilings; user abort remains immediate, including during backoff.

Authentication failures and unusable empty/length-truncated summaries do not consume retries. With a distinct summarizerModel, the final retry slot switches to the active session model and feeds the sticky fallback controller. Set summarizerMaxRetries to 0 to disable retries and fallback rescue. Both timeout fields can independently be set to 0.

Commands

Command Effect
/pruner Interactive picker over all subcommands
/pruner settings Settings overlay (toggle / cycle every option)
/pruner on / off Enable / disable pruning
/pruner status Show mode, model, trigger, cumulative stats, and a --- context --- block (see below)
/pruner stats Detailed cumulative summarizer token/cost stats
/pruner model [id[:thinking]] Get / set summarizer model (and optionally thinking level)
/pruner thinking [level] Get / set summarizer reasoning effort
/pruner prune-on [mode] Get / set trigger mode
/pruner batching [mode] Get / set batching granularity (turn / agent-message)
/pruner protected-tools [names] Show or edit the never-pruned tool allowlist (comma- or space-separated; none clears)
/pruner protected-paths [globs] Show or edit the never-pruned path globs (none clears)
/pruner min-batch-chars [n] Show or set the pre-flush trivial-batch threshold (0 disables)
/pruner recovery-grace [n] Show or set the post-recovery verbatim grace window, in user-turn-groups (0 disables)
/pruner dedup [on|off|status] Toggle pre-flush content-hash dedup
/pruner tree Foldable browser of pruned tool calls; Ctrl-O opens the full summary in an overlay
/pruner compact Retroactively compress every eligible closed chain (bypasses rollingWindow)
/pruner now Flush pending batches immediately with a multi-row progress widget above the input
/pruner help Full help text

Tools surfaced to the LLM

context_tree_query - always available when the extension is loaded. Pruned summaries end with short refs like Summarized tool refs: \t1`, `t2`. Use `context_tree_query` with these refs to retrieve the original full outputs.The model passes those refs (or fulltoolCallIds) and gets back the original tool result text from the session index. Each per-tool bullet in the summary also carries its own inline `` tN` `` ref, so recovering a specific tool is a single hop; the footer still lists every ref as a fallback. Content-hash-deduped duplicates resolve to the original's record automatically.

Short refs (tN) always resolve 1:1 and are the primary, recommended path; a reused toolCallId returns every matching occurrence instead of silently picking one, including occurrences deduplicated to an earlier record. See PRUNING.md § Occurrence Identity for multi-occurrence recovery mechanics.

A footer widget shows the current state, controlled by showPruneStatusLine:

Every rendered state is prefixed with a single leading | divider so the segment stays visually isolated in the shared footer regardless of where other extensions' status segments land (load-order independent); there is no trailing divider - the footer's own space-join between segments already provides one.

  • | prune: OFF - disabled
  • | prune: ON - enabled, no flushes yet
  • | prune: ON . 92.0k->14.0k (-85%) - enabled; live reclaim ratio (estimated tokens before->after, percent reduction). Updates on every pruneMessages call.
  • | prune: 3 pending - batches queued, waiting for the trigger
  • | prune: summarizing... - flush in progress
  • | prune: ON . 92.0k->14.0k (-85%) . diag u1/o2 - same as above, plus a self-hiding diagnostic segment (u/m/o counters, each hidden at zero). See PRUNING.md § Diagnostics.
  • | prune: ON . think 3.2k . gap 5.1k . chain 41% - a context-pressure suffix, shown only when frontierGapTokens > 0 (nothing to append when the frontier is fully caught up). All three are chars/4 token estimates, same convention as the reclaim ratio above:
    • think - openCycleThinkingTokens: thinking-block tokens trapped in the still-open cycle (after the last text-only assistant reply), not yet eligible for pruning.
    • gap - frontierGapTokens: summarization-eligible tool-result tokens sitting past the persisted prune frontier.
    • chain - largestChainSharePct: the largest single chain's (or the open segment's) share of total branch chars. See PRUNING.md § Single-chain sessions for exact definitions.
  • | prune: recovered pending (reload) - shown at agent_end when a session reload found recoverable unflushed work but no new turn re-queued it. See PRUNING.md § Reload rearm for what "recovered" means and when it actually flushes.

Setting showPruneStatusLine: false hides the widget and silences the queued-turn notice; pruning still runs.

The status line does not show cost. Full token/cost detail is available via /pruner stats. The extension also emits cumulative session cost on the cost:external pi.events channel for external aggregators - see README § External cost channel.

/pruner status context block

When context metrics are available, /pruner status appends a --- context --- block below the settings summary:

--- context ---
thinking:     3.2k tokens (open segment)
chain share:  41%
frontier gap: 5.1k tokens
rearmed:      yes
  • thinking / chain share / frontier gap are the same openCycleThinkingTokens / largestChainSharePct / frontierGapTokens metrics shown in the footer suffix above, formatted as full labels here instead of a compact suffix.
  • rearmed: yes appears only while the reload-rearm flag is armed (cleared the moment any flush attempt runs). See PRUNING.md § Reload rearm.