# How Session Compaction Works in Pi Web: Events, Triggers, and Implementation

> Learn how session compaction in Pi Web archives chat turns into a summary and emits compaction_start and compaction_end events for UI synchronization. Explore triggers and implementation.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: internals
- Published: 2026-08-17

---

**Session compaction in Pi Web archives older chat turns into a single summary entry and emits `compaction_start` and `compaction_end` events to keep the UI synchronized.**

The **pi-web** repository manages long-running conversations by storing every turn in a Pi Agent JSON-L session file. When histories grow large, the system performs **session compaction** to summarize older messages into a lightweight entry, ensuring the interface remains responsive. Understanding the compaction trigger mechanisms and the specific events emitted allows developers to build UIs that gracefully handle these archival transitions.

## What Is Session Compaction?

Session compaction is the process of replacing a segment of historical chat messages with a single **compaction entry** containing a summarized version of that context. According to the source code in [`lib/types.ts`](https://github.com/agegr/pi-web/blob/main/lib/types.ts), the system defines a specific entry type `"compaction"` that stores the summary body alongside optional metadata about files read or modified during the conversation. This reduces the session file size while preserving essential context for the agent.

## How to Trigger a Compaction

The Pi Agent supports both manual and automatic initiation of compaction, with both paths emitting identical event signatures (differentiated only by an `auto_` prefix).

### Manual Trigger via API

To compact a session on demand, the UI sends a command through the RPC layer. In **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)**, the `AgentSessionWrapper.send()` method receives the command and forwards it to the inner Agent via `inner.compact(...)`.

```typescript
await sendAgentCommand(sessionId, {
  type: "compact",
  // optional: give the model extra instructions on how to summarise
  customInstructions: "Summarise the conversation in 2‑3 sentences."
});

```

### Automatic Compaction

The Pi SDK may automatically initiate compaction based on internal heuristics regarding session size or message count. These automatic runs emit events prefixed with `auto_`, such as `auto_compaction_start` and `auto_compaction_end`, but otherwise follow the identical lifecycle as manual compactions.

## The Compaction Lifecycle and Events Emitted

The compaction process follows a strict three-phase lifecycle defined in **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)**, emitting specific events that allow the UI to track progress.

### compaction_start Event

Immediately before the SDK begins summarizing history, it emits a **`compaction_start`** event (or **`auto_compaction_start`** for automatic runs). The `AgentSessionWrapper` subscribes to the inner Agent via `inner.subscribe` and re-emits this event to the client, signaling that a long-running operation has begun.

### Compaction Processing

During processing, the SDK reads the old entries, generates a summary string, and writes a new JSON-L entry of type `"compaction"`. This entry contains the human-readable summary plus `read-files` and `modified-files` sections. The helper **`parseCompactionSummary`** in **[`lib/compaction-summary.ts`](https://github.com/agegr/pi-web/blob/main/lib/compaction-summary.ts)** extracts these structured fields from the raw content:

```typescript
import { parseCompactionSummary } from "@/lib/compaction-summary";

const entry = /* a SessionMessageEntry of type "compaction" */;
const summary = parseCompactionSummary(entry.message.content as string);
console.log(summary.body);           // human‑readable summary text
console.log(summary.readFiles);      // files that were read during the run
console.log(summary.modifiedFiles);  // files that were written/changed

```

### compaction_end Event

Once the summary entry is successfully written to the session file, the SDK emits a **`compaction_end`** event (or **`auto_compaction_end`**). The wrapper forwards this to the client, clears the *“Stop compaction”* UI button, and updates its internal `isCompacting` flag to `false`.

## Handling Compaction Events in the UI

The React hook **[`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts)** listens for these events to manage loading states and prevent user actions during archival.

```typescript
const handleAgentEvent = useCallback((event: AgentEvent) => {
  switch (event.type) {
    case "compaction_start":
    case "auto_compaction_start":
      setIsCompacting(true);          // show “Compacting…” UI
      break;

    case "compaction_end":
    case "auto_compaction_end":
      setIsCompacting(false);         // hide the UI spinner
      break;

    // …other event cases…
  }
}, []);

```

The hook also contains reconciliation logic to handle missed end events, ensuring the `isCompacting` state remains accurate even if network issues occur during the compaction process.

## Summary

- **Session compaction** consolidates historical chat messages into a single JSON-L entry with type `"compaction"`, reducing storage overhead and maintaining UI performance.
- The system emits **`compaction_start`** and **`compaction_end`** events (prefixed with `auto_` for automatic triggers) via the `AgentSessionWrapper` subscription mechanism in **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)**.
- Manual compaction is triggered by sending a `{type: "compact"}` command with optional `customInstructions` to the agent session.
- Use **`parseCompactionSummary`** from **[`lib/compaction-summary.ts`](https://github.com/agegr/pi-web/blob/main/lib/compaction-summary.ts)** to extract summary bodies, read files, and modified files from compaction entries.
- UI components consume these events in **[`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts)** to toggle loading indicators and disable controls during active compaction.

## Frequently Asked Questions

### What events does Pi Web emit during session compaction?

Pi Web emits **`compaction_start`** immediately before summarization begins and **`compaction_end`** after the summary entry is permanently written. For automatic compactions initiated by the SDK's internal heuristics, these events are prefixed with **`auto_`** (e.g., `auto_compaction_start` and `auto_compaction_end`), allowing the UI to distinguish between user-initiated and background processes.

### How do I manually trigger a session compaction in Pi Web?

Send a command object with `type: "compact"` to the agent session via the API endpoint `POST /api/agent/[id]`. The client-side helper `sendAgentCommand` wraps this request, and the `AgentSessionWrapper.send()` method in **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)** forwards the instruction to the inner Agent's `compact()` method. You may optionally include a `customInstructions` string to guide the summarization model.

### What data is stored in a compaction entry?

A compaction entry contains a summary body describing the archived conversation, plus optional metadata listing files that were read or modified during the session. The **`parseCompactionSummary`** function in **[`lib/compaction-summary.ts`](https://github.com/agegr/pi-web/blob/main/lib/compaction-summary.ts)** extracts these fields—specifically `body`, `readFiles`, and `modifiedFiles`—from the entry's content string, providing structured access to the archived context.

### How does the UI know when compaction starts and ends?

The **[`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts)** hook subscribes to the agent's event stream and listens for `compaction_start` and `compaction_end` events (including their `auto_` variants). When these events arrive, the hook updates a React state variable `isCompacting`, which controls the visibility of loading spinners and the enabled state of the "Stop compaction" button, ensuring the interface remains synchronized with the underlying Agent state.