# How Session Compaction Works in Pi Web and What Events It Emits

> Discover how Pi Web session compaction streamlines chat histories and understand the compaction_start and compaction_end events that keep your UI in sync. Learn more now.

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

---

**Pi Web compresses large chat histories into summary entries via manual or automatic compaction, emitting `compaction_start`/`compaction_end` events (or `auto_compaction_start`/`auto_compaction_end`) to keep the UI synchronized.**

Session compaction in Pi Web prevents chat logs from growing unwieldy by replacing older messages with a single condensed entry. This process is implemented in the `agegr/pi-web` repository and integrates tightly with the React frontend through a well-defined event system.

## What Triggers Session Compaction

Compaction can start through two mechanisms defined in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts):

- **Manual trigger** — The UI sends a `compact` command via `POST /api/agent/[id]` with payload `{type: "compact", customInstructions?}`. The `AgentSessionWrapper.send()` method forwards this to `inner.compact(...)`.

- **Automatic trigger** — Pi's underlying SDK applies its own heuristics to initiate compaction when sessions grow large. The same event structure applies, prefixed with `auto_`.

## The Compaction Event Lifecycle

The SDK emits lifecycle events that Pi Web's wrapper captures and re-emits to clients.

### compaction_start and auto_compaction_start

Before summarization begins, the SDK emits `compaction_start` (manual) or `auto_compaction_start` (automatic). The wrapper receives this through its `inner.subscribe` callback and propagates it to connected UIs.

```typescript
case "compaction_start":
case "auto_compaction_start":
    setIsCompacting(true);
    break;

```

### Compaction Processing

During compaction, the SDK reads historical entries, generates a summary string, and writes a new JSON-L entry with type `"compaction"`. This entry contains:

- **Summary body** — Human-readable condensed history
- **readFiles** — Files accessed during the conversation
- **modifiedFiles** — Files changed during the conversation

The helper function `parseCompactionSummary` in [`lib/compaction-summary.ts`](https://github.com/agegr/pi-web/blob/main/lib/compaction-summary.ts) extracts these components:

```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);           // condensed conversation text
console.log(summary.readFiles);      // ["src/index.ts", "package.json"]
console.log(summary.modifiedFiles);  // ["src/index.ts"]

```

### compaction_end and auto_compaction_end

After writing the summary entry, the SDK emits `compaction_end` or `auto_compaction_end`. The wrapper forwards this event, clears any "Stop compaction" UI button, and updates its internal `isCompacting` flag.

## React Hook Integration

The [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) file implements the primary event handler for compaction state:

```typescript
const handleAgentEvent = useCallback((event: AgentEvent) => {
  switch (event.type) {
    case "compaction_start":
    case "auto_compaction_start":
      setIsCompacting(true);          // render loading indicator
      break;

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

    // ...additional event handling
  }
}, []);

```

The hook also performs state reconciliation to recover from missed end events, ensuring the UI never permanently shows a stuck compaction state.

## Triggering Manual Compaction

Client code can request compaction with optional custom instructions:

```typescript
await sendAgentCommand(sessionId, {
  type: "compact",
  customInstructions: "Summarize the debugging session focusing on error patterns."
});

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`lib/compaction-summary.ts`](https://github.com/agegr/pi-web/blob/main/lib/compaction-summary.ts) | Parses compaction entries into structured data |
| [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) | Defines the `compact` command and event forwarding |
| [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) | Listens for events, manages `isCompacting` state |
| [`lib/types.ts`](https://github.com/agegr/pi-web/blob/main/lib/types.ts) | Declares the `"compaction"` session entry type |

## Summary

- Session compaction replaces old messages with a single summary entry to control file size.
- **Four event types** exist: `compaction_start`, `compaction_end`, `auto_compaction_start`, `auto_compaction_end`.
- The `parseCompactionSummary` function in [`lib/compaction-summary.ts`](https://github.com/agegr/pi-web/blob/main/lib/compaction-summary.ts) extracts file lists and summary text.
- React state management lives in [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts), which toggles `isCompacting` based on event pairs.

## Frequently Asked Questions

### What is the difference between manual and automatic compaction?

Manual compaction starts when the user explicitly sends a `compact` command with optional instructions. Automatic compaction triggers when Pi's SDK detects the session has grown large enough to warrant summarization. Both emit the same event types, but automatic compaction prefixes events with `auto_`.

### What happens if a `compaction_end` event is lost?

The [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) hook implements defensive state reconciliation. It unconditionally updates the compaction state when certain events arrive, ensuring `isCompacting` resets to `false` even if the end event was dropped or the connection interrupted.

### What information does a compaction entry preserve?

Each compaction entry preserves a human-readable summary and metadata about file access patterns. The `parseCompactionSummary` function extracts `body` (summary text), `readFiles` (files read during the session), and `modifiedFiles` (files written or changed). Original message content is discarded in favor of the condensed representation.