# Codecs A codec translates between your AI framework's events and Ably's messages. The same connection carries a Vercel UIMessage, an OpenAI completion, or a custom domain event without changing the SDK. A codec is the translation layer between domain-specific message models and Ably's native message primitives. It defines how events in the application's domain (text deltas, tool calls, finish signals, or whatever the domain model requires) are encoded into Ably messages, and how incoming Ably messages are decoded back into domain events. The codec is an interface, not an implementation. The generic layer of the SDK knows nothing about what the events or messages look like. It operates entirely through the codec contract. A Vercel AI SDK codec translates `UIMessageChunk` events into Ably messages and reassembles them into `UIMessage` objects. A different codec does the same for a different framework, or for a custom domain model. ![Diagram showing the codec encoding and decoding domain events on both sides of the channel](https://raw.githubusercontent.com/ably/docs/main/src/images/content/diagrams/ait-concepts-codecs.png) ## Understand the codec types The codec model has two generics that name the direction of travel: - `TInput extends CodecInputEvent`: anything the client sends to the agent. User messages, regenerate requests, tool results, tool approvals. - `TOutput extends CodecOutputEvent`: anything the agent streams back. Text deltas, tool calls, finish signals, framework-specific events. Each direction has its own wire scope: inputs publish on `ai-input`, outputs publish on `ai-output`. The SDK enforces the distinction at the type level: you can't accidentally publish an output where an input is expected. The flow on each side is symmetric. The client sends a domain message; the codec wraps it as a `TInput`, encodes it onto the channel, and the agent decodes it back into the same `TInput` shape it can act on. The agent streams `TOutput`s back; the codec encodes each one onto the channel, and the client decodes them and folds them through `codec.fold` into the per-Run `TProjection` that the View renders. The codec implements the encoder and decoder. The SDK calls them. Custom domain logic (what each event *means*, how to fold them into a projection, how to derive `TMessage[]` from the projection) lives in the codec, not in the SDK. ## Well-known input variants The SDK ships a vocabulary of `TInput` variants that show up in every framework. Each variant is a tagged union identified by its `kind` field. A codec must support these so the SDK's higher-level methods (`view.send`, `view.regenerate`, `view.edit`) can call into them without a per-framework adapter: | Variant | `kind` value | What it triggers | | --- | --- | --- | | `UserMessage` | `'user-message'` | A fresh user turn. | | `Regenerate` | `'regenerate'` | Regenerate an assistant message. | | `ToolResult` | `'tool-result'` | Deliver a successful tool result back to the agent. | | `ToolResultError` | `'tool-result-error'` | Deliver a tool failure. | | `ToolApprovalResponse` | `'tool-approval-response'` | Approve or deny a pending tool call. | The tool variants carry a codec-defined `payload`; the core knows only the routing (`kind`, `codecMessageId`) and lets the codec own the shape of the payload (for example, the Vercel layer's `{ toolCallId, output }` for `tool-result`). Codecs implement `createUserMessage(message)` and `createRegenerate(target, parent)` to bridge from the SDK's higher-level methods into well-typed `TInput`s, and optionally `createToolResult`, `createToolResultError`, `createToolApprovalResponse` for the tool variants. Edits go on the wire as a fresh `UserMessage` published with `view.edit` (which routes via the `forkOf` header on the user-message path) rather than a separate variant. Custom codecs can add their own variants on top, picking any `kind` value other than the reserved ones above. ## What the codec layer requires | Property | Why it matters | | --- | --- | | Direction-explicit | Inputs and outputs live on separate wire names (`ai-input` / `ai-output`). The type system mirrors this with `TInput` / `TOutput`. Mixing the two would corrupt the stream-append pipeline and the agent's input-event lookup. | | Deterministic | Two participants running the same codec over the same channel log produce the same projection. Encoding the same event twice produces equivalent wire messages. Without determinism, multi-device sync wouldn't converge. | | Stream-aware | Many `TOutput`s are streamed (token deltas). The encoder must distinguish single-publish messages from append sequences and reconstruct streaming state from Ably's create + append + update lifecycle on decode. | | Projection-driven | The codec owns a per-Run `TProjection` (its own opaque state, typically the message list under construction). The SDK folds decoded events through `codec.fold` to keep it up to date; the View materialises rendered messages from each Run's projection via `codec.getMessages(projection)`, which returns `CodecMessage[]` (each pair carries the codec-message-id alongside the domain message). | | Header tiering | Codec-specific fields ride under `extras.ai.codec`; transport-level headers (run-id, status, role) ride under `extras.ai.transport`. The codec uses `codecHeaders` and `transportHeaders` on `MessagePayload` / `StreamPayload` to control which tier its fields land in. | ## Write a custom codec The shape of a minimal custom codec, with full bodies omitted (just the contract): ### Javascript ``` const myCodec = { init() { return []; }, fold(projection, event, meta) {/* ... */}, createEncoder(channel, options) {/* ... */}, createDecoder() {/* ... */}, getMessages(projection) { // Return [{ codecMessageId, message }] pairs so the SDK can correlate // each rendered message back to its wire id. return projection.map((entry) => ({ codecMessageId: entry.codecMessageId, message: entry.message })); }, createUserMessage(message) { return { kind: 'user-message', message }; }, createRegenerate(target, parent) { return { kind: 'regenerate', target, parent }; }, }; ``` Most users don't write a custom codec; the bundled [Vercel `UIMessageCodec`](https://ably.com/docs/ai-transport/api/javascript/vercel/codec.md) covers the Vercel AI SDK end-to-end. See the [Codec API reference](https://ably.com/docs/ai-transport/api/javascript/core/codec.md) for the full interface. ## Read next - [Codec API reference](https://ably.com/docs/ai-transport/api/javascript/core/codec.md): the full `Codec`, `Encoder`, `Decoder` contracts and well-known variant types. - [Vercel codec](https://ably.com/docs/ai-transport/api/javascript/vercel/codec.md): the shipped `UIMessageCodec` for the Vercel AI SDK. - [Codec architecture](https://ably.com/docs/ai-transport/internals/codec-architecture.md): the wire-level encoder/decoder pipeline. - [Connections](https://ably.com/docs/ai-transport/concepts/connections.md): how the codec is bound into every connection. ## Related Topics - [Overview](https://ably.com/docs/ai-transport/concepts.md): Understand the building blocks of AI Transport: sessions, connections, Runs, Invocations, codecs, the conversation tree, authentication, and infrastructure, and how they relate. - [Sessions](https://ably.com/docs/ai-transport/concepts/sessions.md): Understand sessions in AI Transport: persistent, shared conversation state that exists independently of any participant's connection. - [Connections](https://ably.com/docs/ai-transport/concepts/connections.md): Understand connections in AI Transport: ClientSession and AgentSession instances connect each participant to the shared conversation. Lifecycle, responsibilities, and how multiple participants coexist. - [Runs](https://ably.com/docs/ai-transport/concepts/runs.md): Understand Runs in AI Transport: the SDK primitive that implements a conversation turn with explicit identity, lifecycle, cancel routing, and durable execution semantics. - [Invocations](https://ably.com/docs/ai-transport/concepts/invocations.md): Understand Invocations in AI Transport: the trigger that mediates between a client publishing input and the agent creating a Run. - [Conversation tree](https://ably.com/docs/ai-transport/concepts/conversation-tree.md): Understand how AI Transport organises messages into a branching conversation tree, and how views provide each participant with their own linear perspective. - [Authentication](https://ably.com/docs/ai-transport/concepts/authentication.md): Understand how authentication works in Ably AI Transport: Ably token auth for channel access, HTTP headers for server endpoints, and cancel authorization. - [Infrastructure](https://ably.com/docs/ai-transport/concepts/infrastructure.md): How Ably's platform provides the integrity, reliability, performance, and availability that a durable AI session layer requires. ## Documentation Index To discover additional Ably documentation: 1. Fetch [llms.txt](https://ably.com/llms.txt) for the canonical list of available pages. 2. Identify relevant URLs from that index. 3. Fetch target pages as needed. Avoid using assumed or outdated documentation paths.