Understanding the Monotonic Run ID System in Pi Web's SSE

The monotonic run ID system assigns a strictly increasing numeric identifier to each prompt execution, enabling Pi Web to discard stale Server-Sent Events, handle page reloads gracefully, and prevent race conditions when multiple browser tabs are active.

Pi Web streams language model responses to the browser via Server-Sent Events (SSE), but these streams can arrive out of sequence or persist after a user refreshes the page. To ensure the interface only reflects the current execution state, the agegr/pi-web repository implements a monotonic run ID system that tags every SSE payload with a unique, incrementing numeric identifier. This mechanism serializes prompt executions and guarantees that delayed or residual messages from previous runs never corrupt the active conversation.

Why SSE Streams Require Event Serialization

Server-Sent Events provide real-time updates, yet they introduce synchronization challenges unique to long-lived HTTP connections. When a user reloads the browser, loses network connectivity, or triggers a new prompt before the previous one completes, SSE messages can arrive out of order or after a prompt has already finished. Without a versioning mechanism, the client might display "thinking" indicators from aborted runs or append completed messages to the wrong conversation thread.

How the Monotonic Run ID System Works

At the core of this implementation is a monotonically increasing counter combined with a React ref that tracks the current valid run identifier.

Generating Incrementing Run IDs

When a user submits a message, the system increments a global counter and stores the value in promptRunIdRef.current. This happens in hooks/useAgentSession.ts before the network request is dispatched:

// Initiating a new prompt execution
const runId = ++globalPromptRunCounter;
promptRunIdRef.current = runId;
await startPrompt({ message, runId });

The runId is passed to the server and echoed back in every SSE event belonging to that specific prompt execution.

Guarding Event Handlers Against Stale Data

Every SSE handler validates the runId attached to the event against promptRunIdRef.current before processing. If the IDs differ, the event is discarded because it belongs to an older or superseded execution:

function handleSseEvent(event: AgentEvent) {
  const { runId } = event;
  // Discard events from previous runs
  if (runId !== undefined && promptRunIdRef.current !== runId) return;

  switch (event.type) {
    case "agent_end":
      // Process completion only if current
      break;
    // …other cases…
  }
}

This guard appears throughout the lifecycle management in hooks/useAgentSession.ts:

  • finishPromptWithoutStream – Early-exits when an outdated run ID is received during non-streaming finalization.
  • waitForPromptSettlement – Aborts waiting for settlement if the run IDs no longer match, preventing blocking operations on obsolete prompts.
  • Main SSE handler – Ignores agent_end events and other streaming updates whose runId differs from the current ref.
const finishPromptWithoutStream = useCallback(
  async (sid: string | null = sessionIdRef.current, runId?: number) => {
    // Abort if the run ID no longer matches the current one
    if (runId !== undefined && promptRunIdRef.current !== runId) return;
    // …finalization logic…
  },
  []
);

Key Source Files and Architecture

The monotonic run ID system spans several modules in the agegr/pi-web codebase:

File Purpose
hooks/useAgentSession.ts Core hook managing SSE connections, prompt lifecycle, and run-ID validation guards.
lib/rpc-manager.ts Creates and registers AgentSession instances; assigns run IDs when prompts are initiated.
components/ChatWindow.tsx UI component displaying streaming messages and reacting to validated SSE events.
components/ChatInput.tsx Input component that triggers new prompt executions and run ID generation.

Benefits for Real-Time Streaming

The monotonic nature of the counter—where each new run receives a greater integer—provides specific guarantees:

  1. Stale events are ignored – Any delayed SSE from a previous run cannot resurrect a completed chat bubble or reinstate a dismissed loading state.
  2. Concurrent tabs stay synchronized – When a second tab initiates a new run, its higher run ID supersedes the older one, preventing both tabs from displaying duplicate "thinking" indicators for different prompts.
  3. Reconnection after refresh works reliably – On page load, the client initializes promptRunIdRef from session state and only accepts SSE carrying that specific run ID, discarding stray messages the server may still emit from prior sessions.

Summary

  • The monotonic run ID system assigns incrementing numeric identifiers to each prompt execution in Pi Web's SSE stream.
  • Every SSE event carries a runId that must match promptRunIdRef.current in hooks/useAgentSession.ts before processing.
  • Guards in finishPromptWithoutStream and waitForPromptSettlement prevent outdated events from completing or settling prompts.
  • This mechanism ensures UI consistency across page reloads, network interruptions, and multiple browser tabs by strictly filtering events that do not belong to the current run.

Frequently Asked Questions

How does Pi Web prevent stale SSE events from displaying after a page refresh?

When the page reloads, the client initializes promptRunIdRef.current from the session state with the latest run ID. Any SSE events arriving from the server that carry a different runId—whether from the previous session or delayed network packets—are discarded by the validation check in handleSseEvent, ensuring only current execution state updates the UI.

What is the purpose of the promptRunIdRef check in useAgentSession.ts?

The promptRunIdRef check acts as a concurrency guard that compares the runId embedded in each SSE event against the React ref's current value. If the values differ, the handler returns early, preventing functions like finishPromptWithoutStream or waitForPromptSettlement from processing data belonging to an obsolete or superseded prompt execution.

How does the monotonic run ID system handle concurrent prompt executions across browser tabs?

Because the run ID is generated from a monotonically increasing global counter, each new prompt receives a strictly higher number than previous executions. When multiple tabs are active, the tab with the most recent interaction holds the highest valid run ID in its promptRunIdRef, causing older tabs to silently ignore their own stale events while the newest tab's state remains authoritative.

Why use a monotonically increasing counter instead of random UUIDs for run identification?

While UUIDs provide uniqueness, the monotonic property guarantees a temporal ordering that simplifies logic for determining which execution is "newer." This ordering allows the system to perform simple numeric comparisons (!==) rather than tracking complex histories, making the finishPromptWithoutStream and waitForPromptSettlement guards both lightweight and deterministic.

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 →