How Pi Web Handles SSE Reconnection on Page Refresh During Active Streaming

Pi Web restores interrupted Server-Sent Events streams by checking the isStreaming session flag on component mount and automatically reconnecting to the SSE endpoint when an active generation is detected.

When a user refreshes the browser during an active AI generation, Pi Web (the open-source interface from agegr/pi-web) must seamlessly resume the stream without losing context. The application implements a robust SSE reconnection on page refresh strategy that coordinates between React hooks, UI components, and Next.js API routes to maintain conversational continuity.

The Reconnection Architecture

Pi Web’s resilience relies on a stateless server design paired with a state-aware client. The server maintains the current generation status in the session store, while the client—specifically the ChatWindow component and useAgentSession hook—detects page reloads and restores connections accordingly.

Step-by-Step Reconnection Flow

Detecting Active Streams on Component Mount

When components/ChatWindow.tsx mounts (or remounts after a refresh), it immediately issues a GET /api/agent/[id] request to retrieve the current session state. The response includes the boolean flag isStreaming, which indicates whether an agent is actively generating a response.

If isStreaming is true, the component knows it must resume the stream rather than start fresh. The same response also carries thinkingLevel (indicating chain-of-thought depth) and isCompacting (showing if memory compaction is active).

// 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

Upon detecting an active stream, useAgentSession invokes the startSse function, which creates a new EventSource connection to GET /api/agent/[id]/events. This endpoint streams agent_message, agent_end, and compaction events.

The reconnection happens transparently to the user, appending new tokens to the chat interface exactly where the previous session left off.

// 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 State

The initial state fetch ensures UI indicators reflect the true server state immediately upon reconnection. useAgentSession applies the thinkingLevel and isCompacting values locally so that thinking animations or compaction spinners display correctly without requiring a full page reset.

Resilience Against Missed Events

Beyond the initial reconnection, useAgentSession implements defensive programming to handle edge cases. The hook runs a periodic reconciliation loop via additional calls to GET /api/agent/[id] and listens for browser visibilitychange and online events.

This ensures that events missed during brief disconnections or backgrounded tabs are recovered, preventing state drift between the client and server.

Server-Side SSE Endpoint

The server-side implementation in app/api/agent/[id]/events/route.ts maintains the SSE stream using standard Server-Sent Events protocols. It streams JSON events including agent_message for token updates and agent_end for completion signals.

The endpoint is designed to handle multiple concurrent connections idempotently, allowing the refreshed client to establish a new stream while the server continues the ongoing generation uninterrupted. The companion endpoint in app/api/agent/[id]/route.ts provides the session metadata required for the client to make intelligent reconnection decisions.

Summary

  • State Detection: ChatWindow.tsx calls GET /api/agent/[id] on mount to check isStreaming and determine if reconnection is needed.
  • Automatic Reconnection: When isStreaming is true, useAgentSession initiates a new SSE connection to /api/agent/[id]/events.
  • State Synchronization: The hook restores thinkingLevel and isCompacting flags to maintain accurate UI state.
  • Event Resilience: Periodic polling and browser event listeners recover any messages lost during disconnection.
  • Source Files: Key logic resides in components/ChatWindow.tsx, hooks/useAgentSession.ts, app/api/agent/[id]/events/route.ts, and app/api/agent/[id]/route.ts.

Frequently Asked Questions

Does Pi Web lose messages if I refresh during a stream?

No. The application detects active streams via the isStreaming flag and immediately reconnects to the SSE endpoint. Any messages generated while the page was reloading are recovered through the initial state fetch and ongoing reconciliation loops in useAgentSession.

What happens to the UI state during an SSE reconnection?

The UI state is restored using auxiliary flags returned by GET /api/agent/[id]. useAgentSession applies the thinkingLevel and isCompacting values to ensure indicators like thought depth and compaction status display correctly without requiring a full page reset.

How does the server handle multiple SSE connections from the same session?

The server-side endpoint in app/api/agent/[id]/events/route.ts is designed to stream events idempotently. If a client disconnects and reconnects (such as during a refresh), the server continues emitting the same event stream, allowing the new connection to pick up the remaining tokens without duplicating or skipping messages.

Can this reconnection logic handle browser crashes or network failures?

Yes. Beyond page refresh scenarios, useAgentSession implements periodic reconciliation and listens for browser online and visibilitychange events. This ensures the client recovers from network interruptions, backgrounded tabs, or crashed sessions by repeatedly synchronizing with the server state.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →