# How Pi-Web Manages SSE Reconnection When a Page Refresh Occurs During a Stream

> Discover how pi-web handles SSE reconnection after a page refresh. It automatically reestablishes stream connections, preserving conversational context for uninterrupted real-time updates.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: how-to-guide
- Published: 2026-07-24

---

**Pi-Web detects active agent streams by querying session state on component mount and automatically reestablishes the SSE connection to resume real-time updates without losing conversational context.**

Real-time AI interfaces require resilient event streaming that survives browser refreshes and network interruptions. The **pi-web** repository solves this challenge through a coordinated client-server handshake that restores Server-Sent Events (SSE) connections immediately after a page reload. By synchronizing session state between the React frontend and Next.js API routes, the application seamlessly continues streaming agent responses even when the user refreshes mid-generation.

## Detecting Active Streams on Component Mount

When `ChatWindow` mounts or remounts after a refresh, it immediately issues a **GET `/api/agent/[id]`** request to retrieve the current session state from `app/api/agent/[id]/route.ts`. The server responds with a `state` object containing the critical **`isStreaming`** boolean flag.

If `isStreaming` is `true`, the component knows an agent generation is already in progress and triggers the reconnection sequence. This check happens in a `useEffect` hook that runs once per session ID, ensuring the UI accurately reflects whether it needs to establish a new SSE connection or start fresh.

## Restoring the SSE Connection

Upon detecting an active stream, `ChatWindow` invokes the **`useAgentSession`** hook to initiate SSE reconnection. The hook calls `startSse(sessionId)`, which creates a new `EventSource` instance pointing to **GET `/api/agent/[id]/events`**.

This endpoint, defined in `app/api/agent/[id]/events/route.ts`, streams `agent_message`, `agent_end`, and compaction events to the client. By reconnecting to this endpoint, the UI resumes receiving the remaining tokens of the assistant's output exactly where the previous connection left off.

## Synchronizing Auxiliary State

The initial state fetch provides more than just the streaming flag. The response includes **`thinkingLevel`** (indicating chain-of-thought depth) and **`isCompacting`** (showing whether a memory compaction operation is active).

The `useAgentSession` hook applies these values to local state immediately upon reconnection:

```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…
}

```

This synchronization ensures that visual indicators for thinking depth and compaction status remain accurate even after a hard refresh.

## Handling Missed Events and Edge Cases

To prevent state drift during brief disconnections, `useAgentSession` implements a **periodic reconciliation loop** that polls `GET /api/agent/[id]` independently of the SSE stream. The hook also listens for browser `visibilitychange` and `online` events to trigger immediate state verification when the tab returns to focus or the network reconnects.

This dual-channel approach—primary SSE streaming with fallback HTTP polling—ensures that no critical agent events are lost if the EventSource connection drops or the page refresh occurs during a high-latency window.

## Implementation Details

The reconnection logic centers on two key files. In [`components/ChatWindow.tsx`](https://github.com/agegr/pi-web/blob/main/components/ChatWindow.tsx), the mount effect determines whether to reconnect:

```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]);

```

The server-side endpoints in `app/api/agent/[id]/route.ts` and `app/api/agent/[id]/events/route.ts` work together to maintain the session continuity required for this pattern.

## Summary

- **Pi-web** detects in-progress streams by checking `isStreaming` via `GET /api/agent/[id]` when `ChatWindow` mounts after a refresh.
- The **`useAgentSession`** hook automatically reconnects to `GET /api/agent/[id]/events` to resume the SSE feed without user intervention.
- Auxiliary state—including `thinkingLevel` and `isCompacting`—synchronizes immediately upon reconnection to maintain UI consistency.
- A periodic reconciliation loop and browser event listeners provide resilience against missed events during temporary disconnections.
- This architecture ensures that page refreshes never abort ongoing agent generations, preserving full conversational context.

## Frequently Asked Questions

### How does pi-web know when to reconnect the SSE stream after a refresh?

When `ChatWindow` mounts, it fetches the current session state from `GET /api/agent/[id]`. If the response contains `isStreaming: true`, the component immediately invokes `startSse()` from the `useAgentSession` hook to reestablish the connection to the events endpoint.

### What happens to the agent's response if the user refreshes mid-stream?

The server continues generating the response regardless of client connections. When the page reloads and reconnects via `GET /api/agent/[id]/events`, the client receives all remaining `agent_message` events from the point of reconnection forward, allowing the UI to append new content seamlessly.

### Does pi-web handle network interruptions differently than manual refreshes?

Yes. While both scenarios use the same `startSse` reconnection logic, network interruptions additionally trigger the reconciliation loop and browser `online` event listeners in [`useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/useAgentSession.ts) to verify state consistency before resuming the stream.

### What server-side infrastructure is required to support this reconnection pattern?

The server must maintain durable session state—including `isStreaming`, `thinkingLevel`, and `isCompacting`—accessible via `app/api/agent/[id]/route.ts`, plus a persistent SSE stream endpoint at `app/api/agent/[id]/events/route.ts` that can handle multiple sequential connections for the same session.