Blog Agent Event Architecture

Agent Event Architecture / Aug 30, 2026

Replayable Event Feeds Turn Live Agent Updates into History

Build reliable agent dashboards by separating current state, live signals, and durable replay. Learn a recovery handshake for reconnecting event-stream clients.

By Virillio Code Editorial
Pixelated live event tiles enter an archival reel that can replay the same ordered sequence after a broken connection.

The easiest way to make an agent feel alive is to stream updates: a thought arrives, a tool begins, a file changes, a subtask completes, a final answer appears.

The easiest way to make that interface untrustworthy is to treat the stream as history.

A live connection can drop. A browser can sleep. A process can restart. A client can reconnect after missing events, and the server may no longer retain them. Even if every update arrives, a stream of fragments may not contain the authoritative state needed to rebuild the UI or decide what to do next.

Live updates answer “what is happening now?” History answers “what happened in order?” Current state answers “what is true now?” An agent system needs all three promises explicitly.

Server-Sent Events offers useful transport mechanics for reconnecting clients, including event identifiers and the Last-Event-ID header. But a transport-level cursor is not, by itself, a product guarantee that the server can replay everything a client missed. Durable recovery needs its own data model, retention policy, and scope rules.

A proposed three-promises model

Virillio Code Editorial proposes treating agent-facing event systems as three separate contracts. This is a design model, not a transport or industry standard.

  • Promise: Current state — User question: What is true right now? — Typical source: Read model or resource fetch — What it must not claim: A complete explanation of every transition
  • Promise: Live signal — User question: What is happening while I am connected? — Typical source: WebSocket, SSE, or in-process subscription — What it must not claim: Replayability or complete history
  • Promise: Durable replay — User question: What changed since a known position? — Typical source: Append-only event history with a cursor — What it must not claim: A current snapshot without reconstruction

Each promise can be useful alone. Together, they let a client recover without guessing.

For example, an agent session interface may need:

  • a current session view: status, latest result, pending approval, and known artifacts;
  • a live signal: typing, active execution, transient connection state, or step-in-progress;
  • a durable replay feed: committed user inputs, durable tool outcomes, state transitions, and final messages after a cursor.

The error is trying to force all three through one endpoint with one guarantee.

Why one stream cannot serve every purpose

Consider three reasonable events:

  1. “The model started generating a response.”
  2. “A tool invocation committed a file change.”
  3. “The client connection was re-established.”

They are all useful to show in real time. They do not have the same durability or replay requirements.

The first may be a transient UI detail. The second may be a durable fact that a user must be able to audit later. The third describes the client's own connection, not the agent's business history.

If all three are written to a supposedly replayable log, clients may reconstruct irrelevant transport noise as if it were a user-visible history. If all three are sent only as live signals, a disconnected client can miss a material outcome and falsely assume nothing changed.

The remedy is a classification rule:

  • Event class: State transition — Example: Task accepted, approval granted, run completed — Durable replay?: Yes — Why: It changes the authoritative user-visible story
  • Event class: Material result — Example: Tool outcome, artifact created, validation failed — Durable replay?: Usually yes — Why: It may affect later decisions or audit
  • Event class: Ephemeral progress — Example: Token delta, spinner state, local worker heartbeat — Durable replay?: Usually no — Why: Useful now, but not a durable fact
  • Event class: Transport lifecycle — Example: Connected, disconnected, retrying — Durable replay?: No — Why: It describes a client path, not agent history
  • Event class: Derived display update — Example: “3 files changed” summary — Durable replay?: Depends — Why: Replay only if it is an authoritative projection, not a render cache

This table is more important than a choice of streaming technology. It is the product contract behind the transport.

Cursors are protocol state, not business data

The WHATWG SSE specification supports an event identifier and causes an EventSource client to send the last event ID when reconnecting. That is a valuable primitive. It lets a server learn the client's claimed position.

But a cursor is not a business record. It should not encode data a client needs to interpret, such as storage offsets, filter state, or implementation details. Its job is narrower: identify a safe position from which the server can resume a scoped event sequence.

Good cursor rules:

  • Treat the cursor as opaque to clients.
  • Bind it to a specific stream scope and ordering.
  • Reject or reset invalid cursors explicitly.
  • Define the behavior when retention has expired.
  • Do not let a cursor silently widen filters or cross user/session boundaries.
  • Return enough current-state information for a client that cannot resume.

This prevents a common failure mode: the client stores an implementation-specific token, then unknowingly resumes a different feed or misses an important event after a backend change.

The recovery handshake needs a consistent cut

A reconnect should be a protocol, not a hopeful retry. It also needs a race-free boundary between the snapshot, replay, and live stream. The server can provide an atomic snapshot-plus-cursor, or the client can establish a live subscription first and buffer events while it fetches and replays durable state. Without one of those guarantees, an event can land between “replay complete” and “live subscription attached.”

1. Fetch the current state

Start with an authoritative snapshot of the resource the user cares about. This may be a session, task, job, run, or artifact. The snapshot should include, or be atomically associated with, a durable cursor that identifies the consistent point it represents. The snapshot tells the client what was true at that point even if it cannot obtain every event that happened while it was away.

Current state is especially important after a long disconnect, a deployment, or a retention boundary.

2. Establish or reserve the live boundary

Before declaring the replay complete, ensure new events cannot fall into a gap. Either subscribe from the snapshot cursor with server-side buffering, or open the live stream first and buffer incoming events locally while recovery proceeds. The stream contract must define how the live boundary relates to durable ordering.

3. Request durable events after the snapshot cursor

Ask for committed events after a known position. The server should either:

  • return the ordered gap,
  • state that the cursor is invalid or too old, or
  • return an explicit resynchronization instruction.

Never return a partial gap while making it look complete. “We cannot prove what you missed; fetch current state again” is a trustworthy response.

4. Apply events idempotently

Clients should be prepared to receive an event more than once across reconnects. A replayable event needs a stable identity or sequence so the client can deduplicate it.

Apply events to the snapshot as a projection, not as raw UI commands. That keeps the user interface resilient when the rendering logic changes.

5. Drain the buffer and continue live

After the durable gap has been reconciled, deduplicate and apply any buffered live events in sequence, then continue with the live signal. If the connection drops again, repeat the handshake.

The buffer is what makes early subscription safe: the client does not render new events ahead of older durable ones, and it does not leave an uncovered interval after replay. If the server cannot offer an atomic cursor or a bufferable live boundary, the honest recovery action is to refresh authoritative state again rather than claim a complete replay.

Retention is part of the API

Durable event history cannot necessarily live forever. Storage cost, privacy policy, legal requirements, and user expectations all shape retention. Hiding the policy turns predictable expiration into an incident.

State the contract:

  • How long are replayable events available?
  • Is history pruned, compacted, or summarized?
  • What does the client receive when it asks for an expired cursor?
  • Can a user export or inspect the durable record?
  • Which event classes are intentionally never persisted?

LangGraph's persistence documentation makes checkpoint storage, thread-scoped state, and deletion explicit design concerns. It does not prescribe one universal retention period. Event-feed teams likewise need to define their own recovery window, ownership model, and deletion behavior; “durable” should not be read as “store every byte forever.”

Do not reconstruct history from transient traces

Many agent systems already collect logs, spans, and debug traces. They are invaluable for operators. They should not automatically become user-facing history.

Operational telemetry can contain:

  • high-frequency events that overwhelm a user,
  • internal implementation names,
  • sensitive metadata,
  • retries that should not look like repeated user actions,
  • partial failures that were resolved before they became a material outcome.

Instead, project durable business events intentionally. A user should be able to see that a task was accepted, a policy boundary was reached, a result was produced, or a change was verified—without parsing the runtime's internal trace.

This is the same separation that makes tool results useful: preserve the complete operational record where appropriate, but give the active client a bounded, semantic view.

A design worksheet for agent event feeds

For each event you plan to expose, write down:

  • Field: Audience — Question: Is this for the user, the agent, an operator, or all three?
  • Field: Durability — Question: Does it record a committed fact or transient activity?
  • Field: Scope — Question: Which user, task, session, or workspace may observe it?
  • Field: Ordering — Question: What sequence does it participate in?
  • Field: Cursor behavior — Question: Can a client resume after it, and for how long?
  • Field: Snapshot relation — Question: Can a client rebuild current state without this event?
  • Field: Privacy — Question: Does it contain sensitive, private, or internal-only information?

If the answers are vague, the event contract is not ready for a public feed.

What we are learning building Virillio Code

Virillio Code's runtime work is still in development, but it has made the generic distinction unavoidable: an agent's live progress, durable history, and current state must not be treated as interchangeable. A visual stream can make an interface delightful; a replayable history makes it trustworthy after the stream disappears.

The right question is not “How do we stream everything?” It is “Which promises can the system actually keep after the client is gone?”

Sources and further reading

  • WHATWG Server-Sent Events — EventSource supports event identifiers and the Last-Event-ID mechanism on re-establishment of an event stream.
  • LangGraph persistence — LangGraph documents durable checkpoints, thread-scoped state, and checkpoint deletion as explicit design concerns; product teams still choose their own persistence and retention policies.

Editorial disclosure

This article was substantially researched, drafted, and revised with AI through the Virillio Code editorial workflow. Virillio Code publishes the final text under its editorial byline, and the supporting primary sources are linked above.