Ask an agent a hard question from your phone, watch the answer start streaming in, then walk into an elevator. You come out to a dead spinner. The server didn't crash. It finished your answer forty seconds ago. You just never got the second half, and there's no way to ask for it — the stream has no memory of what it already sent you.
This is the resume problem nobody budgets for, because it hides behind a different one that gets all the attention.
Two resumes, opposite sides of the wire#
When people say "make the agent resumable," they almost always mean durable execution: checkpointers, Temporal, DBOS — the machinery that lets a half-finished run survive a server restart without redoing work or double-charging a card. That's real and hard and worth getting right. It is also the server's problem. It fires when the server dies.
The elevator is the mirror image. The server is healthy; your connection died. Everything the agent computed is safe. What's lost is delivery — the tokens generated while your radio was in a metal box. Durable execution has nothing to say here, because nothing on the server failed. You can run a flawless Temporal workflow and still show the user a frozen screen.
Durable execution protects the work. It does not protect the wire. Those are two different guarantees, and shipping one feels exactly like shipping both — right up until a train tunnel proves otherwise.
The fix is older than agents#
The web solved streaming reconnection years ago, in the Server-Sent Events spec, and almost no agent backend uses the part that matters.
SSE lets the server attach an id: to each event. The browser's EventSource object already auto-reconnects after a drop — roughly every three seconds by default, tunable with the stream's retry: field — and when it does, it sends back the id of the last event it received in a Last-Event-ID header, automatically. That header is the whole trick: it turns a dead stream into a cursor. The server reads it, finds every event emitted after that id, replays them, and then resumes live output. The user sees a half-second stutter instead of a lost answer.
MCP's streamable-HTTP transport codifies exactly this. On reconnect the client issues a GET with Last-Event-ID; the server replays the messages that came after it on that stream and stitches the two halves together with no loss. The only obligations are that event ids are unique and that you keep a log of what you sent.
Where it quietly falls apart#
That log is the catch. The reference implementations keep it in an in-memory event store — a buffer on the process that answered the first request. It works beautifully on your laptop and in the demo.
Then you add a second instance behind a load balancer. The client reconnects, gets routed to instance B, and asks for events that only instance A ever held. Instance B has no idea what you're talking about. Resumable streaming across replicas requires an external, shared event store — a Redis stream, say — keyed by the run or stream id rather than by whichever process happened to pick up the phone.
And here is the part that makes this a 2026 problem rather than a 2024 one. The old resumption was anchored to a session id, which is how the server knew which history to replay. The 2026-07-28 stateless spec deletes the session and the handshake so any request can land on any instance — a genuine win for scaling plain request/response on commodity infrastructure. But streaming replay is intrinsically stateful: something, somewhere, has to remember what it already emitted. Statelessness doesn't remove that requirement. It relocates it — out of a session the protocol used to keep for you, and into shared infrastructure you now have to run yourself.
So the checklist for an agent that actually survives the real world is two items, not one. Make the work durable, so a server restart doesn't redo actions or burn tokens twice. And make the stream resumable — event ids, Last-Event-ID replay, and a shared event store — so a dropped connection doesn't strand the user in front of a spinner that will never move. Solve only the first and you'll have a bulletproof backend narrating to an empty room every time someone steps into an elevator.



