# Streaming interaction replies

The cumulative-snapshot protocol, allowed streaming blocks, draft visibility, and finalization rules.

Source: https://anywe.dev/docs/interactions/streaming

Streaming is an optional evolution of the normal reply contract: a one-shot agent that never sets these fields is unaffected. A streaming agent posts multiple replies for one delivery, each a full snapshot rather than a fragment.

**One streamed delivery:** Agent (sends partial) -> Platform (writes draft) -> Browser (gets SSE event) -> Client (fetches draft) -> Agent (sends final) -> Platform (writes message)

## Partial protocol

Every non-final post has `partial: true`, echoes the platform-issued `dispatch_seq`, and carries a monotonically increasing `seq`. Each new `blocks` array is a **full cumulative snapshot**, never a token delta, so a lost or reordered partial is recoverable: a later snapshot restates the full current view instead of requiring the client to splice fragments together.

```json
{
  "version": "1",
  "in_reply_to_delivery_id": "dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "dispatch_seq": 1,
  "partial": true,
  "seq": 3,
  "blocks": [
    { "type": "text", "text": "Checking fare rules before I answer..." }
  ]
}
```

Only five block types may appear in a partial: `text`, `thinking`, `tool_use`, `tool_result`, and `intent`. Every other type is final-only, even if it would otherwise be valid, including `intent` itself in reverse: it is valid ONLY in a partial, and a final carrying it is refused. This is an allow-list, so a newly published block type is forbidden in partials by default, not legal until it is explicitly denied.

> **Deploy platform support before a streaming agent** `additionalProperties: false` stays active. A platform that predates the streaming fields rejects an early stream with a schema violation rather than silently ignoring its partial state.

## Snapshot rules

`text` may grow from snapshot to snapshot. A completed `thinking`, `tool_use`, or `tool_result` block must instead remain field-identical in every later snapshot of the same `dispatch_seq`, correlated by `signature`, `id`, and `tool_use_id`. A later partial that mutates or drops one of these is rejected; a fresh `dispatch_seq` is a new attempt and is unconstrained by the one before it.

`thinking` and `tool_use` are accumulate-then-emit: their required signature or complete JSON input must exist before the block appears at all. The allow-list permits the completed block in a snapshot; it does not turn an incomplete SDK delta into a valid one.

## Draft delivery

Each accepted partial updates a draft. The browser receives an identifier-only `messagePartial` SSE event carrying the draft's `messageId` and `seq`, then re-fetches the caller-scoped draft; the event itself carries no block content, because access must be evaluated at read time. SSE fan-out may coalesce above roughly five notifications per second for one stream, but every accepted partial still writes the current draft, so a skipped notification just means the next one fetches a newer snapshot.

## Finalization

The final reply is authoritative and may contain any currently agent-sendable block type. `stop_reason` is optional, final-only display state for a non-clean ending; it does not change delivery retry semantics. A final event supersedes the draft with the immutable message, and clients fetch again through their normal message path.
