# AG-UI
The AG-UI codec carries [AG-UI](https://docs.ag-ui.com) events over an Ably channel, plus a `run-input` event for the input a client sends to start a run. The agent pipes its AG-UI event stream into the adaptor, and every subscribed client decodes the same events. A client can merge them with AG-UI's own reducer, such as an `AbstractAgent` from `@ag-ui/client`.
Import `agUiAdaptor` and `agUiDecoder` from `@ably/ai-transport/ag-ui`. The agent publishes with an [agent adaptor](https://ably.com/docs/ai-transport/api/javascript/agent-adaptor.md), and the client reads with a [decoder](https://ably.com/docs/ai-transport/api/javascript/decoder.md). The codec works with version 1 of the `@ag-ui/core` package.
#### Javascript
```
import { agUiAdaptor, agUiDecoder } from '@ably/ai-transport/ag-ui';
// Agent code. channel is a channel from your Ably Pub/Sub client.
const adaptor = agUiAdaptor(channel);
// Client code.
const decoder = agUiDecoder();
await channel.subscribe(decoder.receive);
```
The codec writes each event type in one of three ways:
- Streams: `TEXT_MESSAGE_CONTENT`, `TOOL_CALL_ARGS` and `REASONING_MESSAGE_CONTENT` each stream into one message. The first delta of a stream publishes the message, later deltas append to it, and the matching `*_END` event ends the stream.
- `MESSAGES_SNAPSHOT` is not published, and an inbound one does not decode to an event.
- Every other event is a plain publish with the whole event as the message's `data` field.
- A `run-input` event is a plain publish with its `input` as the message's `data` field.
The codec does not write any headers. An agent can carry the `runId` on every message of a run by passing it in the [`pipe()`](https://ably.com/docs/ai-transport/api/javascript/agent-adaptor.md#pipe) headers.
## Events that change on the wire
Some events reach a subscriber in a different form from the one the agent piped:
- The codec does not publish `MESSAGES_SNAPSHOT`, and an inbound snapshot does not decode to an event. A snapshot holds the whole conversation, so it grows past the Ably message size limit, and AG-UI's reducer treats a snapshot as the complete message list, so it would drop every message the snapshot leaves out. Your application can load the conversation from its database instead.
- `RUN_STARTED` travels with `input.messages` empty, for the same reason. Its other input fields travel as they are.
- History and a client that joins late read one delta event per stream, with the text joined. That event carries the `timestamp` and `metadata` of the last delta only.
- The codec strips `rawEvent` from every event. The field carries the provider event that the AG-UI event was translated from, so it repeats the event's content. AG-UI's reducer does not read it.
- Every `*_CHUNK` event is its own message, and the codec does not append to it. A chunk stream does not have an end event, so Ably could deliver an appended chunk after the event that followed it. An agent that wants one message per stream can emit the START, CONTENT and END events instead.
- The codec does not check sizes, so a large `STATE_SNAPSHOT`, `ACTIVITY_SNAPSHOT`, `TOOL_CALL_RESULT` or `RUN_FINISHED` fails its publish, and `pipe()` rejects with `PipeFailed`.
## Join at a run boundary
AG-UI's reducer rejects a stream that does not start with `RUN_STARTED`, and its state deltas patch the state the client already holds. A client needs to join the channel at a run boundary, where the first event the AG-UI reducer receives is a `RUN_STARTED`. If you are not using the AG-UI reducer to create messages from the event stream then this doesn't apply.
`pipe()` resolves with the serial of the last publish of the run. If you store that serial with the run's messages and state, a client can [rewind the channel](https://ably.com/docs/ai-transport/conversation/history.md#joining) to it, and receive the next run from its `RUN_STARTED` event.
## Create an AG-UI adaptor
`agUiAdaptor(channel: ChannelWriter, options?: AgentAdaptorOptions): AgentAdaptor`
Create an [agent adaptor](https://ably.com/docs/ai-transport/api/javascript/agent-adaptor.md) that publishes AG-UI events on `channel`.
### Javascript
```
import { agUiAdaptor } from '@ably/ai-transport/ag-ui';
const adaptor = agUiAdaptor(channel);
```
### Parameters
| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| channel | required | The channel to publish on. A `RealtimeChannel` satisfies it. See [`createAgentAdaptor`](https://ably.com/docs/ai-transport/api/javascript/agent-adaptor.md#create-agent-adaptor-params). | `ChannelWriter` |
| options | optional | Default headers and a logger. See [`createAgentAdaptor`](https://ably.com/docs/ai-transport/api/javascript/agent-adaptor.md#create-agent-adaptor-params). | `AgentAdaptorOptions` |
### Returns
`AgentAdaptor`. The adaptor, for events of the following type:
| Event | Description | Type |
| --- | --- | --- |
| AG-UI events | Every event that AG-UI version 1 defines, as the `@ag-ui/core` package declares it. | `Event` |
| `run-input` | The input a client sends to start a run. AG-UI sends `RunAgentInput` as an HTTP request body, so the protocol does not define an event for it. | |
## Create an AG-UI decoder
`agUiDecoder(options?: DecoderOptions): Decoder`
Create a [decoder](https://ably.com/docs/ai-transport/api/javascript/decoder.md) that decodes what an AG-UI adaptor publishes.
### Javascript
```
import { agUiDecoder } from '@ably/ai-transport/ag-ui';
const decoder = agUiDecoder();
await channel.subscribe(decoder.receive);
```
### Parameters
| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| options | optional | A logger. See [`createDecoder`](https://ably.com/docs/ai-transport/api/javascript/decoder.md#create-decoder-params). | `DecoderOptions` |
### Returns
`Decoder`. The decoder, for the [events](#ag-ui-adaptor-returns) the adaptor publishes.
A `run-input` message whose `data` does not have `threadId` and `runId` as strings, and `messages`, `tools` and `context` as arrays, fails to decode. It reaches a subscriber with `event` set to `undefined`.
## Example
The client POSTs the user's message to the agent, which publishes it as a `run-input` event. The agent pipes its AG-UI event stream with the `runId` header, and the client appends each text delta to the message in its UI.
### Javascript
```
// Client code.
decoder.onEvent(({ event }) => {
if (event?.type === 'TEXT_MESSAGE_CONTENT') appendText(event.messageId, event.delta);
});
await channel.subscribe(decoder.receive);
const input = { threadId, runId, messages: [userMessage], tools: [], context: [] };
await fetch('/api/agent', { method: 'POST', body: JSON.stringify({ channelName: 'conversations:42', input }) });
```
### Javascript
```
// Agent code. events is your agent's AG-UI event stream, as an async iterable.
await adaptor.send({ type: 'run-input', input }, { headers: { runId } });
await adaptor.pipe(events, { signal: req.signal, headers: { runId } });
```
## Related Topics
- [Agent adaptor](https://ably.com/docs/ai-transport/api/javascript/agent-adaptor.md): API reference for the AI Transport agent adaptor: createAgentAdaptor, send, pipe, and pipeEach, and the options, results, and errors of each.
- [Decoder](https://ably.com/docs/ai-transport/api/javascript/decoder.md): API reference for the AI Transport decoder: createDecoder, receive, decode, onEvent, and onError, and the Delivery each message becomes.
- [Codec](https://ably.com/docs/ai-transport/api/javascript/codec.md): API reference for the AI Transport Codec: the encode and decode contract an adaptor and a decoder use, EncodedMessage, and how defineCodec builds a codec from a table of event rows.
- [Vercel](https://ably.com/docs/ai-transport/api/javascript/vercel.md): API reference for the AI Transport codec for the Vercel AI SDK: vercelAdaptor, vercelDecoder, and the VercelEvent union of UIMessageChunk events and the user message.
- [OpenAI](https://ably.com/docs/ai-transport/api/javascript/openai.md): API reference for the AI Transport codec for the OpenAI Responses API: openaiAdaptor, openaiDecoder, and the OpenAIEvent union of Responses stream events and client input.
## 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.