# How Pi-Web Handles SSE Reconnection After Page Refresh During Streaming

> Discover how Pi-Web seamlessly restores SSE reconnection after a page refresh. Learn how it fetches session state to resume streaming exactly where you left off.

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

---

**When a user refreshes the browser during an active agent stream, Pi-Web automatically restores the Server-Sent Events (SSE) connection by fetching the current session state from `GET /api/agent/[id]` and reconnecting to the events endpoint if `isStreaming` is true, ensuring the UI resumes receiving the generation exactly where it left off.**

The `agegr/pi-web` repository implements a resilient streaming architecture that survives browser refreshes without interrupting ongoing AI agent generations. This functionality relies on a coordinated sequence between the client-side React components and the Next.js API routes that maintain persistent session state.

## Detecting Active Streams on Component Mount

When the `ChatWindow` component mounts—or remounts after a page refresh—it immediately queries the server to determine if a generation is still in progress. The component calls **GET `/api/agent/[id]`** to retrieve the current session state, which includes the critical boolean flag `state.isStreaming`.

If this flag returns `true`, it indicates that an agent is actively generating a response on the server side, even though the client connection was severed by the refresh. The component then passes this signal to the `useAgentSession` hook to initiate reconnection.

```tsx
// components/ChatWindow.tsx
useEffect(() => {
  // On mount – fetch the latest session state
  fetch(`/api/agent/${sessionId}`)
    .then(r => r.json())
    .then(state => {
      // If a generation was in progress, reconnect the SSE stream
      if (state.isStreaming) {
        startSse(sessionId);               // opens /api/agent/[id]/events
        setThinkingLevel(state.thinkingLevel);
        setIsCompacting(state.isCompacting);
      }
    });
}, [sessionId]);

```

## Restoring the SSE Connection via useAgentSession

The `useAgentSession` hook contains the lifecycle management for SSE connections. When instructed to reconnect, it opens a new **EventSource** connection to **GET `/api/agent/[id]/events`**, which streams `agent_message`, `agent_end`, and compaction events from the server.

This endpoint, defined in `app/api/agent/[id]/events/route.ts`, maintains the live stream of tokens from the running agent process. By reconnecting to this endpoint, the client receives the remainder of the generation that occurred during the disconnection window.

```ts
// hooks/useAgentSession.ts
function startSse(id: string) {
  const evtSource = new EventSource(`/api/agent/${id}/events`);
  evtSource.onmessage = e => handleAgentEvent(JSON.parse(e.data));
  // …cleanup logic omitted…
}

```

## Synchronizing Auxiliary Session State

Beyond the binary streaming status, the initial state fetch synchronizes additional context required to render the UI correctly. The response from `app/api/agent/[id]/route.ts` includes:

- **`thinkingLevel`**: Indicates the depth of the model's chain-of-thought reasoning, allowing the UI to display appropriate loading indicators.
- **`isCompacting`**: Signals whether a context compaction operation is currently underway, which triggers specific UI cues in the chat window.

These values are applied locally via `setThinkingLevel` and `setIsCompacting` immediately upon reconnection, ensuring the user sees the correct state of the ongoing thought process.

## Resilience Against Missed Events

While the SSE stream provides real-time updates, `useAgentSession` implements additional safeguards to handle events that might be lost during the reconnection window. The hook runs a **periodic reconciliation loop** via supplementary `GET /api/agent/[id]` calls and listens for browser **visibilitychange** and **online** events.

This dual approach ensures that if the SSE connection drops while the tab is backgrounded or during network instability, the client can recover any missed events through the state endpoint, preventing the UI from desynchronizing with the server-side agent state.

## Summary

- **State detection**: [`ChatWindow.tsx`](https://github.com/agegr/pi-web/blob/main/ChatWindow.tsx) calls `GET /api/agent/[id]` on mount to check `isStreaming` status after refresh.
- **Automatic reconnection**: When `isStreaming` is true, [`useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/useAgentSession.ts) reconnects to `GET /api/agent/[id]/events` to resume the live stream.
- **Context preservation**: Auxiliary state including `thinkingLevel` and `isCompacting` is synchronized to maintain UI consistency.
- **Event resilience**: Periodic polling and browser event listeners ensure no messages are lost during brief disconnections.

## Frequently Asked Questions

### How does Pi-Web detect if a stream is still active after a page refresh?

The `ChatWindow` component calls `GET /api/agent/[id]` immediately upon mounting, which returns the current `isStreaming` flag from the server-side session state. If this boolean is true, the application knows an agent generation is still running and triggers reconnection logic.

### What server-side endpoint maintains the streaming state during a browser refresh?

The endpoint `app/api/agent/[id]/route.ts` persists and returns the session state including `isStreaming`, `thinkingLevel`, and `isCompacting`. The companion endpoint `app/api/agent/[id]/events/route.ts` handles the actual SSE stream of agent events.

### Does Pi-Web handle events that might be missed during the reconnection window?

Yes, [`useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/useAgentSession.ts) implements a periodic reconciliation mechanism that polls the state endpoint and listens for browser visibility and online events. This ensures any events missed during brief disconnections or while the tab is backgrounded are recovered and applied to the UI.

### Which component triggers the SSE reconnection logic?

The `ChatWindow` component in [`components/ChatWindow.tsx`](https://github.com/agegr/pi-web/blob/main/components/ChatWindow.tsx) detects the active stream condition and invokes the reconnection function from [`useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/useAgentSession.ts). This separation of concerns allows the hook to manage the EventSource lifecycle while the component handles the mount-time state verification.