Why architecture matters here

The reason to care starts with the cost of lost work and ends with the shape of the runner fleet. Everything an agent does in a session has a price: tokens spent on model calls, latency the user waited through, side effects tools performed, context the user built up turn by turn. When an interruption discards a session held only in memory, all of that is lost — the user re-explains, the tokens are re-spent, the expensive research runs again. For a short single-turn chat that is annoying; for a long, stateful, tool-heavy agent run it is unacceptable. Resumption exists because the value accumulated in a session is too high to throw away on every transient failure.

The second reason is operational: without durable sessions, a runner process becomes a stateful pet you cannot restart. If a session's only copy lives in one process's memory, then that process cannot be redeployed, scaled down, or moved without destroying the session — which means deploys must drain, autoscaling must be gentle, and a crash is a user-visible data loss. That is a brittle way to run a fleet. When sessions are durable and any runner can rehydrate any session from the store, the runners become interchangeable stateless workers: deploy whenever, scale freely, and a crashed instance's sessions simply resume on another. The durability of the session store is what buys the elasticity of the compute tier.

The third reason is that long-running work is the whole point of agents, and long-running work outlives connections and processes by nature. An agent that calls a model for thirty seconds, then a tool that takes a minute, then another model call, is a process whose lifetime routinely exceeds the mean time between the disruptions listed above. If the architecture cannot survive an interruption in the middle of that chain, it cannot reliably do the multi-step work agents are built for. Resumption is therefore not a nice-to-have for edge cases; it is a precondition for agents doing anything that takes real time.

Understanding the mechanism changes how you think about session state: it must be treated as durable, evolving data with a well-defined commit point, not as incidental process memory. The design questions become 'what is the unit of progress I persist, and when is it committed?' and 'given the last committed state, can I deterministically reconstruct where the agent was and continue?' Answering those turns resumption from a hope into a guarantee — the agent can always be rebuilt from the store, because the store, not the process, is the source of truth for the session.

There is a subtler payoff too: durable, event-logged sessions are inspectable and auditable in a way in-memory sessions never are. Because every step appends to the log, you can replay a session to understand exactly what the agent did, debug a bad run by reading its history, or attach evaluation to real traces. The same event log that enables resumption doubles as the audit trail and the debugging surface — so the durability you add for reliability pays back as observability, which is often the harder property to bolt on later. Designing the session as an append-only history from the start gives you all three at once.

Advertisement

The architecture: every piece explained

Top row: the reconnect path. A client that was disconnected returns and presents its session id. The runner — the ADK component that drives the agent loop — uses that id to load the session, replaying its history to reconstruct context. The session store is the durable home of that history: it holds the sequence of events and the committed state, backed by a real database rather than process memory. Within it, the event log is an append-only record of everything that happened in the session — user messages, model responses, tool calls and results, state mutations — in order. That log, not any process's RAM, is the authoritative account of the session.

Middle row: what marks the agent's position and drives the next step. The state snapshot is the last committed value of the session's working state — the accumulated variables, plan progress, and context the agent needs to act. The resume token captures where in the run the agent is: which step it had reached, so that after a load the runner knows what to do next rather than redoing prior steps. From that recovered position the runner computes the next agent step — the next action the agent should take given its state — which typically means dispatching a tool or model call, and those calls may be long-running, which is precisely why an interruption can catch the agent mid-call and why resumption must reason about in-flight work.

Bottom rows: the two operations that make it all durable, and the ops surface. On every event, the runner checkpoints — it appends the event to the log and commits the updated state, so the durable record always reflects the agent's latest completed step. On reconnect it recovers — loads the session, rebuilds the in-memory context from the persisted history, and continues. The ops strip names the hard parts: idempotent replay (re-loading a session must not re-execute side effects already performed), snapshot cadence (how often state is committed, trading write cost against how much can be lost), store durability (the session store is now critical infrastructure whose availability bounds the agent's), and in-flight tool calls (the genuinely hard case of an interruption during a long-running call).

A design nuance ties these together: the commit point defines what 'resumable' means. If state is committed after every event, an interruption loses at most the event in progress; if it is committed lazily, more can be lost. Event-sourced sessions — where the log is the source of truth and state is a fold over the log — make this clean: appending an event is the commit, and state is reconstructed by replaying the log, so there is no separate 'save state' step to get out of sync with history. Whether ADK persists state snapshots, an event log, or both, the invariant is the same: after any completed event, the store holds enough to reconstruct the session, and a resume starts from the last such point.

ADK session resumption — pick up a long agent run exactly where it stoppeddurable state survives crash and disconnectClientreconnects with session idRunnerloads session, replaysSession storeevents + state, durableEvent logappend-only historyState snapshotlast committed stateResume tokenposition in the runAgent stepnext action from stateTool / model callmay be long-runningCheckpoint on each eventappend + commit stateRecover on reconnectrebuild context, continueOps — idempotent replay, snapshot cadence, store durability, in-flight tool callsresumeloadappendpersistsnapshottokendispatchoperateoperate
ADK session resumption: every event appends to a durable session store with a committed state snapshot; on reconnect the runner loads the session, rebuilds context from the event log, and continues the agent from its last committed step.
Advertisement

End-to-end flow

Trace a healthy long session first. A client starts a session; the runner creates it in the session store and begins the agent loop. The user sends a message — appended to the event log, state committed. The agent decides to call a tool — the call and its result are appended, state updated. Another model call, another append. Each step advances the durable record, so at every moment the session store holds a complete, ordered history and the latest committed state. The user sees a normal, flowing conversation; underneath, every step is being checkpointed so that the session could be reconstructed from the store at any point.

Now the interruption. Mid-session, the runner process is redeployed — the connection drops and the in-memory session is gone. From the client's side, the connection simply broke. It reconnects, presenting the same session id. A runner (possibly a different instance entirely) receives the reconnect, loads the session from the store, and replays the event log to rebuild the exact context the agent had: the full conversation history, the accumulated state, the position in the run. The agent continues from its last committed step — the user's next message is handled with all prior context intact. To the user, the redeploy was invisible; to the system, a stateless runner rehydrated a durable session and carried on.

The subtle case is an interruption during a step, and this is where the design earns its keep. Suppose the agent had just committed the decision to call a tool, dispatched the call, and then the runner crashed before the result came back. On resume, the log shows 'tool call issued' but no result. What should happen? If the tool call was idempotent or read-only, the runner can safely re-issue it. If it had a side effect — charged a card, sent a message — re-issuing it would double the effect, which is unacceptable. This is why resumption and idempotency are inseparable: the safe way to resume across a side-effecting call is to make the call idempotent (via an idempotency key recorded before dispatch) so that re-issuing it on resume is a no-op if it already succeeded. The event log records intent before action, and idempotency makes replaying that intent safe.

The performance and correctness character follows from the commit discipline. Because state is committed on each event, the amount of work at risk from an interruption is at most one step — the agent never loses more than the in-flight action. Recovery cost is the cost of loading and replaying the log, which for a long session can be non-trivial, so systems periodically fold the log into a state snapshot and replay only events after the snapshot, bounding recovery time. The trade-off is write amplification: committing on every event costs a durable write per step, which is the price of losing at most one step. Tuning snapshot cadence balances recovery speed against write cost, but the invariant holds — the session is always reconstructable, and resume never loses more than the last uncommitted action.

Consider the stress case that exposes the hardest edge: a long-running tool call that outlives the interruption itself. Imagine the agent dispatched a tool that takes two minutes, and the runner crashed thirty seconds in. The tool may still be running somewhere, oblivious to the runner's death; when a new runner resumes the session, it sees an issued-but-unfinished call. Blindly re-issuing would run the tool twice; blindly waiting could hang forever if the original call died with the runner. The robust pattern is to make long-running tools themselves resumable — the call registers a durable operation id, and on resume the runner reattaches to that operation and polls for its result rather than re-issuing it. This turns the tool call into another piece of durable state the session can recover, so even an interruption in the middle of the slowest possible step resolves correctly instead of duplicating or abandoning the work.