# Preemptive Compaction Trigger

`preemptive-compaction.ts` supplements Pi 0.82's built-in auto-compaction. It evaluates the threshold at completed-turn boundaries and uses only Pi's existing extension operations; it does not modify Pi itself.

Pi itself triggers only after a completed assistant turn when:

```text
contextTokens > contextWindow - reserveTokens
```

With a 272,000-token model and Pi's default `reserveTokens: 16384`, that is about **255,616 tokens**. The package's normal default remains 85% (**231,200 tokens**); native-threshold mode is opt-in.

## Resolution order

The effective trigger is resolved at every completed-turn boundary. It reads both:

```text
~/.pi/agent/settings.json              # global
<current-working-directory>/.pi/settings.json  # trusted project override
```

using Pi's standard project-over-global merge rule. Resolution is:

1. `compaction.enabled: false` or `preemptive-compaction.enabled: false` — disables this extension; it never bypasses an automatic-compaction opt-out.
2. `PI_PREEMPTIVE_COMPACTION_TOKENS` — positive integer token threshold for one launch.
3. `PI_PREEMPTIVE_COMPACTION_RATIO` — ratio strictly between `0` and `1` for one launch.
4. `preemptive-compaction.usePiNativeThreshold: true` — mirrors Pi's effective `contextWindow - reserveTokens` trigger at the completed-turn boundary. If no explicit reserve is configured, Pi's default `16,384` reserve is used. Native comparison remains strict (`tokens > threshold`) unless the maximum-output safety cap lowers it to the last safe inclusive boundary.
5. `preemptive-compaction.thresholdTokens` or `.thresholdRatio` — the explicit package threshold.
6. Active Observational Memory configuration — `compactAfterTokens` or ratio-mode `compactAfterTokensRatio`, unless it is `passive: true`.
7. Explicit Pi `compaction.reserveTokens` — converted to Pi's own trigger: `contextWindow - reserveTokens`.
8. Default: **85%** of the active model's context window, with a 32,768-token floor. Explicit user-provided thresholds are not raised to that floor; they are used as configured.

At 272,000 tokens, the default is **231,200 tokens**. An explicit `preemptive-compaction.thresholdRatio` takes priority over Observational Memory's threshold.

The effective trigger is also capped below the active model's configured maximum response budget: Pi's own accounting includes the assistant response that just finished, so enough space must remain for the next response. With the current 16,384-token maximum output, the cap is `255,616` for a 272,000-token model. The 85% default is below that cap.

The extension checks each `turn_end` boundary. Pi emits this only after all tool calls in the current batch have finished and their tool results have been recorded. If the threshold is reached, the extension requests `ctx.compact()`. After success it submits a hidden continuation only for a normal tool-use turn and only when no newer pending input is detected; user input takes precedence. Error, aborted, length-limited, no-tool, and fully terminating tool turns are not auto-resumed. Completed tools are not intentionally rerun.

`agent_settled` remains a fallback for restored sessions and usage/retry edge cases. The extension observes `session_before_compact` and yields when another compaction has already begun. Pi provides no public atomic compaction lock, so this is cooperative suppression rather than a cross-extension ownership guarantee.

Pi 0.82 does not expose its internal native threshold decision at `turn_end`; it checks native auto-compaction after `agent_end` or before the next user prompt. Consequently, the extension mirrors threshold arithmetic locally at the completed-turn boundary; this calculation incurs no model cost. Set `preemptive-compaction.usePiNativeThreshold: true` to mirror Pi's effective reserve threshold, including its default `16,384`, or leave it false to retain the package's 85% default. Actual compaction still uses Pi's `ctx.compact()` pipeline and the normal `session_before_compact` result chain.

The extension only requests `ctx.compact()` and returns no `session_before_compact` result. Therefore the existing remote-compaction, observational-memory, and other compaction hooks remain responsible for the actual summary, opaque routing, and fallback behavior.

Its user-facing messages keep those responsibilities separate: the trigger logs when it queues work, successful completion logs the selected compaction method, and continuation logging says whether the hidden continuation was submitted or superseded by newer pending input. Failure/cancellation suppresses automatic retry while the branch leaf is unchanged and re-arms after the branch advances.

## Rendered transcript cleanup

After successful compaction, old TUI-rendered components can be trimmed while always retaining the newest rendered compaction result and a configurable recent tail. This is enabled by default and does not delete or rewrite session entries or LLM context. A forced redraw—which can clear terminal scrollback—is requested only if a component was actually removed.

```json
{
  "preemptive-compaction": {
    "clearRenderedContextAfterCompaction": true,
    "renderedContextKeepRecentItems": 24
  }
}
```

Set `clearRenderedContextAfterCompaction` to `false` to disable it. `renderedContextKeepRecentItems: 0` retains the newest compaction result without an extra rendered tail. This option applies only to TUI mode.

## Examples

Global or project Pi settings:

```json
{
  "preemptive-compaction": {
    "enabled": true,
    "thresholdRatio": 0.85,
    "usePiNativeThreshold": false
  }
}
```

Or an absolute threshold:

```json
{
  "preemptive-compaction": {
    "thresholdTokens": 230000
  }
}
```

A one-launch override:

```bash
PI_PREEMPTIVE_COMPACTION_TOKENS=230000 pi
# or
PI_PREEMPTIVE_COMPACTION_RATIO=0.85 pi
```

Restart Pi or run `/reload` after updating the package code. Settings are read again at every completed-turn boundary.
