# Turn protocol v1

## Ordered assistant content

Use ordered assistant content when one canonical assistant message must combine streamed prose with portable application components. Create one encoder per assistant turn and yield each encoded value as a normal AgentsKit text chunk:

```ts
import { createAssistantContentEncoder } from '@agentskit/chat/protocol'

const content = createAssistantContentEncoder()
yield { type: 'text', content: content.encode({ kind: 'text', text: answerChunk }) }
yield { type: 'text', content: content.encode({ kind: 'component', frame: sourceListFrame }) }
```

Never concatenate raw model output into the envelope. `encode` validates and JSON-escapes every record. The first call writes the `agentskit.chat.content/1` prefix; later calls continue the same envelope. `decodeAssistantContent` returns completed ordered parts and marks an incomplete trailing record with `complete: false`, allowing renderers to hide partial transport bytes. Total content is limited to 256 KiB, 512 records, and 16 KiB per input text record; component frames retain their existing props and semantic-fallback limits.

Existing plain-text messages and single `ComponentRenderFrame` messages remain valid. See [ADR-0021](../architecture/adrs/0021-ordered-assistant-content-records.md).

The v1 protocol identifier is `agentskit.chat.turn`; every event carries version `1`, stable event/session/turn ids, a non-negative sequence, and an ISO timestamp.

## Events

| Event | Direction | Payload |
|---|---|---|
| `client.turn.submit` | client → application | non-blank `input` |
| `server.turn.snapshot` | application → client | canonical message wire records, AgentsKit status, token usage, optional safe diagnostic and lifecycle lineage |
| `server.turn.diagnostic` | application → client | versioned code, safe message, retryability |

Snapshots are complete projections, not deltas. Streaming sends successive snapshots with stable message ids and increasing sequence numbers.
Optional lineage records `submit`, `retry`, `edit`, or `regenerate` plus parent turn/source message identities. `createSnapshotEvent` creates the canonical projection from AgentsKit messages. `createTurnSnapshotCursor(sessionId)` applies only newer snapshots from the expected session, making reconnect, duplicate delivery, and stale delivery inert without implementing another lifecycle reducer.
Message records are validated by `validateMemoryRecord` from the published `@agentskit/core/memory-validation` subpath. The protocol package owns only the application envelope and must not reproduce AgentsKit message, content-part, tool-call, or memory schemas.

## Decode safely

```ts
import { decodeTurnEvent } from '@agentskit/chat/protocol'

const result = decodeTurnEvent(untrustedInput)
if (!result.ok) {
  // Do not mutate state.
  report(result.diagnostic)
  return
}

apply(result.event)
```

The decoder returns `PROTOCOL_UNSUPPORTED_VERSION`, `PROTOCOL_UNKNOWN_EVENT`, or `PROTOCOL_INVALID_PAYLOAD`. It never returns raw Zod issues or copies the rejected payload into the diagnostic.

## Component frames and interactions

The sibling `agentskit.chat.component` v1 envelope carries custom application render frames and semantic interaction events. A render frame contains `componentKey`, `instanceId`, unknown `props` for validation by the closed application manifest, and a required semantic fallback. A selection event preserves the component and instance identities plus the selected `choiceId`.

Envelope decoding validates structure only. `@agentskit/chat` then verifies registration and the component-specific props schema. Unknown types, versions, components, invalid props, and invalid JSON are inert typed diagnostics.

## Compatibility

- Optional additive fields are allowed within v1 and ignored by older decoders.
- Unknown event kinds and versions are inert.
- Removing or renaming required fields, changing meaning, or changing lifecycle semantics requires v2, migration fixtures, and a new ADR.
- Versions are never silently coerced.

`StreamChunk` and `AgentEvent` remain upstream AgentsKit adapter/observer contracts and are not wire events.

## Application sessions

Persistent application metadata uses the separate `agentskit.chat.session` v1 envelope. `decodeSessionSnapshot` validates current snapshots and explicitly migrates the supported v0 shape. It contains session/definition identity, definition revision, cursor, deterministic decisions, and confirmation bindings; canonical messages are excluded and remain in AgentsKit `ChatMemory`.
