# How Pi Web Handles Session Compaction Events via Server-Sent Events (SSE)

> Discover how Pi Web handles session compaction events using Server-Sent Events SSE. Learn about real-time UI sync and automatic reconciliation for missed events in the agegr/pi-web repository.

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

---

**Pi Web streams session compaction events through a single SSE endpoint (`GET /api/agent/[id]/events`) and processes them in [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) to maintain real-time UI state synchronization, with automatic reconciliation as a fallback for missed events.**

Session compaction is a critical operation in Pi Web that compresses older conversation history to optimize storage and performance. The application uses **Server-Sent Events (SSE)** to push compaction status updates from the back-end Pi Agent to the browser in real time. This article examines the complete flow—from event definition through SSE delivery to state recovery—based on the `agegr/pi-web` source code.

## SSE Event Types for Session Compaction

Pi Web defines four compaction-related event types that flow through the SSE channel. These are registered in **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)** (lines 89–96) as part of the **running-state** event set:

| Event Type | Description |
|------------|-------------|
| `auto_compaction_start` | Automatic compaction initiated by the agent |
| `auto_compaction_end` | Automatic compaction completed |
| `compaction_start` | Manual compaction started by user action |
| `compaction_end` | Manual compaction finished |

The same events are also included in the **idle-reset** set (lines 99–104). This ensures that when any compaction end event arrives, the UI's idle timer is cleared—preventing premature disconnection during long-running compaction operations.

## How the Browser Processes Compaction Events

The React hook **[`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts)** contains the primary event handler for all SSE messages. The `handleAgentEvent` function (around lines 1210–1216) uses a switch statement to route compaction events to the appropriate state updates.

### State Update Flow

When a compaction event arrives via SSE, the handler executes this sequence:

1. **`compaction_start` or `auto_compaction_start`** — Sets `isCompacting(true)` to activate UI indicators
2. **`compaction_end` or `auto_compaction_end`** — Sets `isCompacting(false)` and triggers idle timer reset

The `isCompacting` boolean drives visible UI elements including the blue compaction status badge and the "Stop compaction" abort button.

```typescript
// Excerpt from hooks/useAgentSession.ts
const handleAgentEvent = useCallback((event: AgentEvent) => {
  switch (event.type) {
    case "auto_compaction_start":
    case "compaction_start":
      // UI enters compacting mode
      setIsCompacting(true);
      break;
    case "auto_compaction_end":
    case "compaction_end":
      // Compaction finished — clear UI state and reset idle timer
      setIsCompacting(false);
      break;
    // …additional event types…
  }
}, []);

```

## Initiating and Aborting Compaction

Compaction is triggered through the RPC command system defined in **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)**. The client sends commands to the `AgentSessionWrapper`, which forwards them to the underlying Pi Agent.

### Starting Compaction

```typescript
// Start automatic compaction from any component with session access
await sendAgentCommand(sessionId, { type: "set_auto_compaction" });

```

The `set_auto_compaction` command handler (around line 630 in [`rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/rpc-manager.ts)) configures the agent to begin compressing conversation history. When the back-end actually starts the operation, it emits `auto_compaction_start` via SSE.

### Aborting Compaction

```typescript
// Abort in-progress compaction
await sendAgentCommand(sessionId, { type: "abort_compaction" });

```

The `abort_compaction` command (line 714) terminates an active compaction run. The Pi Agent performs file-level compaction operations and guarantees that start/end event pairs are always emitted, even for aborted operations.

## Recovery from Missed Compaction Events

Network interruptions can cause the browser to miss SSE messages. Pi Web implements **periodic reconciliation** to ensure the UI never gets stuck in an incorrect compaction state.

The `reconcileAgentState` function polls the REST endpoint `/api/agent/[sid]` and synchronizes the local `isCompacting` flag with the authoritative server state:

```typescript
// Periodic reconciliation while agent connection is active
const reconcileAgentState = async (sid: string) => {
  const res = await fetch(`/api/agent/${sid}`);
  const { state } = await res.json();
  // Override local state with server truth, defaulting to false if undefined
  setIsCompacting(state?.isCompacting ?? false);
};

```

This reconciliation runs on an interval and also executes immediately after SSE reconnection, guaranteeing that temporary disconnections do not leave stale UI indicators.

## File-Level Architecture

The compaction event system spans multiple source files with distinct responsibilities:

| File | Role in Compaction Flow |
|------|------------------------|
| [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) | Defines `AgentSessionWrapper`, registers compaction events in running-state and idle-reset sets, implements command handlers for `set_auto_compaction` and `abort_compaction` |
| [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) | Consumes SSE stream, routes compaction events through `handleAgentEvent`, maintains `isCompacting` state, implements `reconcileAgentState` recovery |
| [`lib/types.ts`](https://github.com/agegr/pi-web/blob/main/lib/types.ts) | Declares `"compaction"` entry type for JSONL session files referenced by SSE events |
| [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) | Preserves compaction entry ordering during session file reads, enables accurate session reconstruction |

## Summary

- **Four event types** (`auto_compaction_start`, `auto_compaction_end`, `compaction_start`, `compaction_end`) flow through Pi Web's single SSE endpoint
- **`handleAgentEvent`** in [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) toggles the `isCompacting` boolean that drives all compaction UI
- **Idle-reset event set** ensures compaction end events always clear the disconnect timer
- **`reconcileAgentState`** polls the REST API to recover from missed SSE messages and network gaps
- **Command system** in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) exposes `set_auto_compaction` and `abort_compaction` for user-initiated control

## Frequently Asked Questions

### What happens if the browser misses a `compaction_end` event?

The idle-reset set includes `compaction_end` and `auto_compaction_end` (lines 99–104 in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)), so standard timer behavior is protected. Additionally, the `reconcileAgentState` polling mechanism fetches the current session state from `/api/agent/[sid]` and corrects `isCompacting` to match the server. This dual-layer recovery prevents UI desync.

### How does Pi Web distinguish between manual and automatic compaction?

Separate event type pairs are used: `compaction_start`/`compaction_end` for manual operations and `auto_compaction_start`/`auto_compaction_end` for agent-initiated background compaction. The `handleAgentEvent` switch statement processes both identically, but the distinction enables differentiated telemetry and user-facing messaging.

### Can compaction be aborted mid-operation?

Yes. The client sends `{ type: "abort_compaction" }` via `sendAgentCommand`, handled at line 714 in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts). The Pi Agent terminates the file-level compaction and guarantees an end event is still emitted, allowing the UI to exit compacting state cleanly.

### Where is the compaction state persisted if the page is refreshed?

The back-end Pi Agent maintains the authoritative `isCompacting` flag in session state accessible at `/api/agent/[sid]`. On page reload, the React hook re-initializes and the first `reconcileAgentState` call restores the correct compaction status before SSE streaming resumes.