Earendil Pi Message Compaction: Managing Long Sessions in the AgentSession Pipeline

Earendil Pi automatically compacts conversation history into summaries when sessions exceed the LLM's context window, using an abort-aware pipeline that extensions can intercept and customize.

The earendil-works/pi repository implements intelligent message compaction to prevent context overflow during extended coding sessions. When conversations grow beyond the token limits of the underlying language model, the system summarizes older messages while preserving conversation continuity. This mechanism is orchestrated by the AgentSession class and is fully extensible through event hooks.

How Earendil Pi Message Compaction Works

Every conversational turn in Earendil Pi is stored as a session entry. When cumulative token usage approaches the model's limits, the runtime triggers an automatic compaction that replaces older messages with a concise summary. The system tracks compaction state using an AbortController (_compactionAbortController) to ensure operations can be cancelled mid-flight.

The Three Triggers for Auto-Compaction

The session checks assistant token usage after each turn (see the auto-compaction check around line 1780 in packages/coding-agent/src/core/agent-session.ts). Three specific conditions initiate compaction:

  • Overflow ("overflow"): When the LLM returns a context overflow error, the session discards the offending message, runs compaction, and automatically retries the request.
  • Threshold ("threshold"): When accumulated tokens exceed the reserved amount configured in settings.compaction.reserveTokens, compaction runs without interrupting the user flow.
  • Manual ("manual"): Explicit invocation via session.compact() when users or extensions need on-demand summarization.

The Compaction Event Lifecycle

The compaction pipeline emits granular events that extensions and monitoring tools can observe:

  • session_before_compact: Emitted immediately before compaction begins, carrying a signal for cancellation.
  • compaction_start: Confirms the compaction decision and reason.
  • session_compact: Fires after summary generation but before storage, containing the compactionEntry.
  • compaction_end: Signals completion (success, error, or cancellation) with aborted and willRetry flags.

Core Implementation in AgentSession

The compaction logic resides in packages/coding-agent/src/core/agent-session.ts. The AgentSession class coordinates the entire workflow across approximately 130 lines of orchestration code (lines 1607–1737).

The compact() method creates the abort controller, emits lifecycle events, and delegates summarization to the compaction module. A critical guard exists around line 1786 where getLatestCompactionEntry() prevents stale usage data from triggering redundant compactions.

Customizing Compaction with Extension Hooks

Extensions can intercept compaction via the session_before_compact event. This hook receives an abort signal and the ID of the first message to be kept, allowing complete replacement of the generated summary or cancellation of the operation.

// packages/coding-agent/examples/extensions/custom-compaction.ts
pi.on("session_before_compact", async (event, ctx) => {
  // Cancel the compaction if we don’t want it for this session
  if (shouldSkipCompaction(event)) {
    event.signal.abort();
    return { cancel: true };
  }

  // Provide a custom summary instead of the default one
  return {
    compaction: {
      summary: "User-provided short recap",
      firstKeptEntryId: event.firstKeptEntryId,
      tokensBefore: event.tokensBefore,
    },
  };
});

The runtime checks the extension result (extensionResult?.compaction) and stores the provided object as the new compaction entry if present (lines 1640–1665).

The Compaction Summarization Pipeline

The actual LLM-driven summarization lives in packages/coding-agent/src/core/compaction/index.ts. The compact helper function receives the abort signal, invokes the language model with a system prompt requesting concise summarization, and returns a CompactionResult containing:

  • summary: The generated condensed text.
  • firstKeptEntryId: The ID of the first retained message post-compaction.
  • tokensBefore: Token count of the session before compaction.
  • details: Optional debugging metadata.

Session Manager Storage and Retrieval

All session entries—including compactions—are persisted by the SessionManager. Two key methods manage compaction state:

  • appendCompaction(summary, firstKeptId, tokensBefore): Stores a new compaction entry in the session log.
  • getLatestCompactionEntry(): Retrieves the most recent compaction to prevent duplicate triggers and calculate current context usage accurately.

Monitoring and Metrics

The repository includes analytics tooling to track compaction effectiveness. scripts/session-context-stats.mjs aggregates compaction metadata across session logs, counting sessions with compactions and calculating the compaction rate (displayed in the context-usage summary lines 332–335). This metric appears in CI dashboards to monitor long-session handling efficiency.

Practical Code Examples

Manually trigger compaction from a REPL or extension:

// Manually compact a session with a custom directive
await pi.session.compact("Summarise the last 10 turns");

Configure automatic threshold-based compaction:

// Auto-compact when token usage exceeds 120k (reserving 100k)
pi.setSettings({
  compaction: { enabled: true, reserveTokens: 100_000 },
});

Register a custom compaction extension:

import "./extensions/custom-compaction.ts"; // Registers the hook shown above

Summary

  • Earendil Pi uses the AgentSession class in packages/coding-agent/src/core/agent-session.ts to automatically compact conversation history when approaching LLM token limits.
  • Compaction triggers include overflow errors, configurable token thresholds, and manual invocations, each emitting distinct lifecycle events.
  • Extensions can customize or cancel compaction via the session_before_compact hook, which receives an AbortController signal and entry metadata.
  • The CompactionResult structure preserves the summary, first kept entry ID, and token statistics for accurate context window management.
  • The SessionManager prevents redundant compactions by tracking the latest entry, while session-context-stats.mjs provides metrics on compaction rates across sessions.

Frequently Asked Questions

What triggers automatic message compaction in Earendil Pi?

Automatic compaction triggers in three scenarios: when the LLM returns a context overflow error ("overflow"), when accumulated tokens exceed the configured reserve threshold ("threshold"), or when explicitly requested via session.compact() ("manual"). The system checks token usage after each assistant turn around line 1780 of agent-session.ts.

How can I customize the compaction behavior in Earendil Pi?

Register an extension listener for the session_before_compact event. The handler receives a signal that can abort the operation and can return a custom compaction object with your own summary, firstKeptEntryId, and tokensBefore values. If the handler returns { cancel: true }, the compaction is skipped entirely.

What data structure does a compaction entry contain?

Each compaction entry follows the CompactionResult interface from packages/coding-agent/src/core/compaction/index.ts, containing a text summary, the firstKeptEntryId referencing the first retained message, the tokensBefore count for metrics, and optional details for debugging. The SessionManager stores this via appendCompaction().

How does Earendil Pi prevent duplicate compaction runs?

The AgentSession queries getLatestCompactionEntry() before triggering auto-compaction (around line 1786). This ensures the system uses fresh usage calculations rather than stale data that might have been invalidated by previous compactions, preventing redundant summarization cycles.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →