ag-ui-protocol / ag-ui-protocol/ag-ui
Design: how should the reserved 'resumable' transport (stream resumption via sequence numbers) work?
- Langage dominant
- Python
- Étoiles
- 15.9k
- Forks
- 1.4k
- Merge moyen
- 1 j 17 h
- PR mergées (30 j)
- 163
Description
## Question / design discussion
`TransportCapabilities.resumable` is reserved in the spec — *"Set `true` if the agent supports resuming interrupted streams via sequence numbers."* — but I can't find the mechanism defined anywhere, and no integration implements it. I'd like to understand the intended design (and help drive it), because the primitive it needs (**per-event sequence numbers**) isn't in the schema yet.
### What `resumable` is (and isn't)
As I read it, `resumable` is **transport-level stream resumption**: if the connection drops mid-run, the client reconnects and continues the event stream from where it left off (by sequence number) instead of losing events or re-running the turn. It's distinct from two things it's easy to conflate:
| Concept | Layer | Status |
|---|---|---|
| **`resumable`** (this) | reconnect a dropped **stream**, replay missing events by sequence | reserved flag, **no mechanism** |
| **Interrupts** (`RunFinishedInterruptOutcome` / `resume: ResumeEntry[]`) | the **agent** pauses for human input | defined |
| framework execution resumption (e.g. ADK `ResumabilityConfig`) | backend **execution** state | backend-internal |
### The gap
`BaseEvent` today is just `{ type, timestamp?, rawEvent? }` — **no sequence number**. So a client has nothing to resume *from*, and a server has no key to resume *at*. The capability flag exists but the wire contract doesn't.
### Design questions
1. **Where does the sequence live?** A new `sequence` (monotonic per run) on `BaseEvent`, or a transport-layer envelope field? On `BaseEvent` is simplest and transport-agnostic (works for SSE, WebSocket, httpBinary alike).
2. **How does the client request resumption?**
- SSE already has this natively via the `id:` field + the `Last-Event-ID` request header on reconnect — should `resumable` build on that for SSE (i.e. set `id:` = sequence), and mirror it for WebSocket/httpBinary with an explicit "resume from N" frame/param?
- Or a field on `RunAgentInput` (e.g. `resumeFrom: {runId, afterSequence}`) so it's transport-uniform?
3. **Buffering semantics.** The server must retain emitted events per run to replay them. Bounded ring buffer? TTL? What's the contract when the requested sequence has been evicted — error, or fall back to a fresh run / `MESSAGES_SNAPSHOT` + `STATE_SNAPSHOT` re-sync?
4. **No re-execution / no double side-effects.** Replay should re-send *already-produced* events only; the agent must not re-run (tool calls already executed). Worth stating explicitly in the contract so implementations don't accidentally resume the run itself.
5. **Scope of "same run".** Is resumption keyed by `runId` (same run, dropped socket) only, or also across `threadId` reconnects? I'd assume per-`runId`.
6. **Capability negotiation.** Presumably the server advertises `transport.resumable: true` and the client opts in; the sequence field could be always-present or gated.
### Motivation
Long-running agents behind gateways (Envoy, Cloud Run, API GW) over flaky client connections: a dropped socket mid-run currently loses the stream and forces a full re-run. `resumable` would let the client reconnect and continue. This is the transport-resilience counterpart to the WebSocket / httpBinary transports.
### Ask
Could a maintainer clarify the intended design for `resumable` — specifically **where the sequence number should live** and **how resumption is requested** (Last-Event-ID vs a `RunAgentInput` field vs a dedicated endpoint)? Once the wire contract is settled I'm happy to implement the `ag-ui-adk` side (per-run event buffer + replay + sequence stamping), but it needs the schema decision first since it touches `BaseEvent` in core.
Guide de contribution
Ouvrir le guide de contribution
Évaluation
Cette issue n'a pas encore été évaluée.