@ably/ai-transport 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -116
- package/dist/ably-ai-transport.js +1743 -961
- package/dist/ably-ai-transport.js.map +1 -1
- package/dist/ably-ai-transport.umd.cjs +1 -1
- package/dist/ably-ai-transport.umd.cjs.map +1 -1
- package/dist/constants.d.ts +117 -39
- package/dist/core/agent.d.ts +29 -0
- package/dist/core/codec/decoder.d.ts +20 -23
- package/dist/core/codec/encoder.d.ts +11 -8
- package/dist/core/codec/index.d.ts +1 -2
- package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
- package/dist/core/codec/types.d.ts +410 -101
- package/dist/core/transport/agent-session.d.ts +10 -0
- package/dist/core/transport/branch-chain.d.ts +43 -0
- package/dist/core/transport/client-session.d.ts +13 -0
- package/dist/core/transport/decode-fold.d.ts +47 -0
- package/dist/core/transport/headers.d.ts +97 -17
- package/dist/core/transport/index.d.ts +5 -3
- package/dist/core/transport/internal/bounded-map.d.ts +20 -0
- package/dist/core/transport/invocation.d.ts +74 -0
- package/dist/core/transport/load-conversation.d.ts +128 -0
- package/dist/core/transport/load-history.d.ts +39 -0
- package/dist/core/transport/pipe-stream.d.ts +9 -8
- package/dist/core/transport/run-manager.d.ts +78 -0
- package/dist/core/transport/tree.d.ts +435 -0
- package/dist/core/transport/types/agent.d.ts +353 -0
- package/dist/core/transport/types/client.d.ts +168 -0
- package/dist/core/transport/types/shared.d.ts +24 -0
- package/dist/core/transport/types/tree.d.ts +315 -0
- package/dist/core/transport/types/view.d.ts +222 -0
- package/dist/core/transport/types.d.ts +13 -402
- package/dist/core/transport/view.d.ts +354 -0
- package/dist/errors.d.ts +37 -9
- package/dist/index.d.ts +6 -6
- package/dist/logger.d.ts +12 -0
- package/dist/react/ably-ai-transport-react.js +1164 -645
- package/dist/react/ably-ai-transport-react.js.map +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
- package/dist/react/contexts/client-session-context.d.ts +36 -0
- package/dist/react/contexts/client-session-provider.d.ts +53 -0
- package/dist/react/create-session-hooks.d.ts +116 -0
- package/dist/react/index.d.ts +16 -10
- package/dist/react/internal/use-resolved-session.d.ts +36 -0
- package/dist/react/use-ably-messages.d.ts +20 -11
- package/dist/react/use-client-session.d.ts +81 -0
- package/dist/react/use-create-view.d.ts +23 -0
- package/dist/react/use-tree.d.ts +35 -0
- package/dist/react/use-view.d.ts +110 -0
- package/dist/utils.d.ts +32 -23
- package/dist/vercel/ably-ai-transport-vercel.js +2748 -1625
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
- package/dist/vercel/codec/decoder.d.ts +5 -18
- package/dist/vercel/codec/encoder.d.ts +6 -36
- package/dist/vercel/codec/events.d.ts +51 -0
- package/dist/vercel/codec/index.d.ts +24 -12
- package/dist/vercel/codec/reducer.d.ts +144 -0
- package/dist/vercel/codec/tool-transitions.d.ts +50 -0
- package/dist/vercel/index.d.ts +4 -2
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +10298 -1410
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +70 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +33 -0
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +96 -0
- package/dist/vercel/react/index.d.ts +4 -0
- package/dist/vercel/react/use-chat-transport.d.ts +66 -21
- package/dist/vercel/react/use-message-sync.d.ts +31 -12
- package/dist/vercel/run-end-reason.d.ts +29 -0
- package/dist/vercel/transport/chat-transport.d.ts +71 -30
- package/dist/vercel/transport/index.d.ts +25 -18
- package/dist/vercel/transport/run-output-stream.d.ts +56 -0
- package/dist/version.d.ts +2 -0
- package/package.json +47 -34
- package/src/constants.ts +126 -47
- package/src/core/agent.ts +68 -0
- package/src/core/codec/decoder.ts +71 -98
- package/src/core/codec/encoder.ts +115 -58
- package/src/core/codec/index.ts +13 -6
- package/src/core/codec/lifecycle-tracker.ts +10 -9
- package/src/core/codec/types.ts +438 -106
- package/src/core/transport/agent-session.ts +1344 -0
- package/src/core/transport/branch-chain.ts +58 -0
- package/src/core/transport/client-session.ts +775 -0
- package/src/core/transport/decode-fold.ts +91 -0
- package/src/core/transport/headers.ts +182 -19
- package/src/core/transport/index.ts +29 -22
- package/src/core/transport/internal/bounded-map.ts +27 -0
- package/src/core/transport/invocation.ts +98 -0
- package/src/core/transport/load-conversation.ts +355 -0
- package/src/core/transport/load-history.ts +269 -0
- package/src/core/transport/pipe-stream.ts +58 -40
- package/src/core/transport/run-manager.ts +249 -0
- package/src/core/transport/tree.ts +1167 -0
- package/src/core/transport/types/agent.ts +407 -0
- package/src/core/transport/types/client.ts +211 -0
- package/src/core/transport/types/shared.ts +27 -0
- package/src/core/transport/types/tree.ts +344 -0
- package/src/core/transport/types/view.ts +259 -0
- package/src/core/transport/types.ts +13 -527
- package/src/core/transport/view.ts +1271 -0
- package/src/errors.ts +42 -9
- package/src/event-emitter.ts +3 -2
- package/src/index.ts +55 -39
- package/src/logger.ts +14 -1
- package/src/react/contexts/client-session-context.ts +41 -0
- package/src/react/contexts/client-session-provider.tsx +186 -0
- package/src/react/create-session-hooks.ts +141 -0
- package/src/react/index.ts +27 -10
- package/src/react/internal/use-resolved-session.ts +63 -0
- package/src/react/use-ably-messages.ts +47 -19
- package/src/react/use-client-session.ts +201 -0
- package/src/react/use-create-view.ts +72 -0
- package/src/react/use-tree.ts +84 -0
- package/src/react/use-view.ts +275 -0
- package/src/react/vite.config.ts +4 -1
- package/src/utils.ts +63 -45
- package/src/vercel/codec/decoder.ts +336 -255
- package/src/vercel/codec/encoder.ts +348 -196
- package/src/vercel/codec/events.ts +87 -0
- package/src/vercel/codec/index.ts +59 -14
- package/src/vercel/codec/reducer.ts +977 -0
- package/src/vercel/codec/tool-transitions.ts +122 -0
- package/src/vercel/index.ts +7 -3
- package/src/vercel/react/contexts/chat-transport-context.ts +41 -0
- package/src/vercel/react/contexts/chat-transport-provider.tsx +150 -0
- package/src/vercel/react/index.ts +13 -1
- package/src/vercel/react/use-chat-transport.ts +162 -42
- package/src/vercel/react/use-message-sync.ts +121 -22
- package/src/vercel/react/vite.config.ts +4 -2
- package/src/vercel/run-end-reason.ts +78 -0
- package/src/vercel/transport/chat-transport.ts +553 -113
- package/src/vercel/transport/index.ts +40 -28
- package/src/vercel/transport/run-output-stream.ts +170 -0
- package/src/version.ts +2 -0
- package/dist/core/transport/client-transport.d.ts +0 -10
- package/dist/core/transport/conversation-tree.d.ts +0 -9
- package/dist/core/transport/decode-history.d.ts +0 -41
- package/dist/core/transport/server-transport.d.ts +0 -7
- package/dist/core/transport/stream-router.d.ts +0 -19
- package/dist/core/transport/turn-manager.d.ts +0 -34
- package/dist/react/use-active-turns.d.ts +0 -8
- package/dist/react/use-client-transport.d.ts +0 -7
- package/dist/react/use-conversation-tree.d.ts +0 -20
- package/dist/react/use-edit.d.ts +0 -7
- package/dist/react/use-history.d.ts +0 -19
- package/dist/react/use-messages.d.ts +0 -7
- package/dist/react/use-regenerate.d.ts +0 -7
- package/dist/react/use-send.d.ts +0 -7
- package/dist/vercel/codec/accumulator.d.ts +0 -21
- package/src/core/transport/client-transport.ts +0 -959
- package/src/core/transport/conversation-tree.ts +0 -434
- package/src/core/transport/decode-history.ts +0 -337
- package/src/core/transport/server-transport.ts +0 -458
- package/src/core/transport/stream-router.ts +0 -118
- package/src/core/transport/turn-manager.ts +0 -147
- package/src/react/use-active-turns.ts +0 -61
- package/src/react/use-client-transport.ts +0 -37
- package/src/react/use-conversation-tree.ts +0 -71
- package/src/react/use-edit.ts +0 -24
- package/src/react/use-history.ts +0 -111
- package/src/react/use-messages.ts +0 -32
- package/src/react/use-regenerate.ts +0 -24
- package/src/react/use-send.ts +0 -25
- package/src/vercel/codec/accumulator.ts +0 -603
package/README.md
CHANGED
|
@@ -8,23 +8,23 @@ A durable transport layer between AI agents and users. Streams AI responses over
|
|
|
8
8
|
|
|
9
9
|
Most AI frameworks stream tokens over HTTP response bodies or SSE. That works until it doesn't: connections drop through corporate proxies, responses vanish on page refresh, and sessions are stuck on a single device or tab. Once an agent starts a long-running task, the user has no way to interrupt it, check if it's still running, or continue the conversation from another device. If a human needs to take over from the agent, the session context is lost.
|
|
10
10
|
|
|
11
|
-
Ably AI Transport replaces the HTTP stream with an Ably channel. The server publishes tokens to the channel as they arrive from the LLM; the response accumulates on the channel and persists, so partial responses survive disconnection. Any client can subscribe to the same channel from any device. Cancel signals,
|
|
11
|
+
Ably AI Transport replaces the HTTP stream with an Ably channel. The server publishes tokens to the channel as they arrive from the LLM; the response accumulates on the channel and persists, so partial responses survive disconnection. Any client can subscribe to the same channel from any device. Cancel signals, run lifecycle events, and conversation history all flow through the channel rather than depending on a single HTTP connection.
|
|
12
12
|
|
|
13
13
|
```mermaid
|
|
14
14
|
sequenceDiagram
|
|
15
15
|
participant U as User
|
|
16
|
-
participant
|
|
16
|
+
participant CS as Client Session
|
|
17
17
|
participant AC as Ably Channel
|
|
18
|
-
participant
|
|
18
|
+
participant AS as Agent Session
|
|
19
19
|
participant LLM
|
|
20
20
|
|
|
21
|
-
U->>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
LLM-->>
|
|
25
|
-
|
|
26
|
-
AC->>
|
|
27
|
-
|
|
21
|
+
U->>CS: type message
|
|
22
|
+
CS->>AS: HTTP POST (messages)
|
|
23
|
+
AS->>LLM: prompt
|
|
24
|
+
LLM-->>AS: token stream
|
|
25
|
+
AS->>AC: publish chunks
|
|
26
|
+
AC->>CS: subscribe (decode)
|
|
27
|
+
CS->>U: render tokens
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Ably AI Transport SDK is not an agent framework or orchestration layer - it works alongside whatever agent framework/model provider you choose, through a pluggable codec architecture (Vercel AI SDK supported now, more frameworks and models coming soon). It can be used in a serverless architecture (e.g. Next.js), with a durable execution framework (e.g. Temporal, Vercel Workflow DevKit) or in a traditional client-server architecture.
|
|
@@ -34,9 +34,9 @@ Ably AI Transport SDK is not an agent framework or orchestration layer - it work
|
|
|
34
34
|
- **Resumable streaming** - If a connection drops mid-response, client reconnects and picks up where it left off. The response persists on the channel, so nothing is lost.
|
|
35
35
|
- **Session continuity across surfaces** - The session belongs to the channel, not the connection. A user can change tab or device and pick up at the same point.
|
|
36
36
|
- **Multi-client sync** - Multiple users, agents, or operators subscribe to the same channel. Human-AI handover is a channel operation, not a session migration.
|
|
37
|
-
- **Cancellation** - Cancel signals travel over the Ably channel, not the HTTP connection, and the server
|
|
37
|
+
- **Cancellation** - Cancel signals travel over the Ably channel, not the HTTP connection, and the server run's `abortSignal` fires automatically.
|
|
38
38
|
- **Interruption** - Users send new messages while the AI is still responding, with composable primitives for cancel-and-resend or queue-until-complete.
|
|
39
|
-
- **Concurrent
|
|
39
|
+
- **Concurrent runs** - Multiple request-response cycles run in parallel on the same channel. Each run has its own stream and abort signal.
|
|
40
40
|
- **History** - The Ably channel is the conversation record. Clients hydrate from channel history on load - no separate database query needed.
|
|
41
41
|
- **Branching** - Regenerate or edit messages to fork the conversation. The SDK tracks parent/child relationships and exposes a navigable tree.
|
|
42
42
|
- **Framework-agnostic** - A codec interface decouples transport from the AI framework. Ships with a Vercel AI SDK codec; bring your own for any other stack.
|
|
@@ -68,7 +68,7 @@ npm install @ably/ai-transport ably ai
|
|
|
68
68
|
|
|
69
69
|
| Platform | Support |
|
|
70
70
|
| ------------- | -------------------------------------------------- |
|
|
71
|
-
| Node.js |
|
|
71
|
+
| Node.js | 22+ |
|
|
72
72
|
| Browsers | All major browsers (Chrome, Firefox, Edge, Safari) |
|
|
73
73
|
| TypeScript | Written in TypeScript, ships with types |
|
|
74
74
|
| React | 18+ and 19+ via dedicated hooks |
|
|
@@ -88,49 +88,41 @@ import { streamText, convertToModelMessages } from 'ai';
|
|
|
88
88
|
import type { UIMessage } from 'ai';
|
|
89
89
|
import { anthropic } from '@ai-sdk/anthropic';
|
|
90
90
|
import Ably from 'ably';
|
|
91
|
-
import {
|
|
92
|
-
import type
|
|
93
|
-
|
|
94
|
-
interface ChatRequestBody {
|
|
95
|
-
turnId: string;
|
|
96
|
-
clientId: string;
|
|
97
|
-
messages: MessageWithHeaders<UIMessage>[];
|
|
98
|
-
history?: MessageWithHeaders<UIMessage>[];
|
|
99
|
-
id: string;
|
|
100
|
-
forkOf?: string;
|
|
101
|
-
parent?: string | null;
|
|
102
|
-
}
|
|
91
|
+
import { createAgentSession } from '@ably/ai-transport/vercel';
|
|
92
|
+
import { Invocation, type InvocationData } from '@ably/ai-transport';
|
|
93
|
+
import type { UIMessageChunk } from 'ai';
|
|
103
94
|
|
|
104
95
|
const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY });
|
|
105
96
|
|
|
106
97
|
export async function POST(req: Request) {
|
|
107
|
-
const
|
|
98
|
+
const data = (await req.json()) as InvocationData<UIMessageChunk, UIMessage>;
|
|
99
|
+
const invocation = Invocation.fromJSON(data);
|
|
108
100
|
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
const
|
|
101
|
+
const session = createAgentSession({ client: ably, channelName: invocation.sessionName });
|
|
102
|
+
await session.connect();
|
|
103
|
+
const run = session.createRun(invocation, { signal: req.signal });
|
|
112
104
|
|
|
113
|
-
await
|
|
105
|
+
await run.start();
|
|
114
106
|
|
|
115
|
-
if (messages.length > 0) {
|
|
116
|
-
await
|
|
107
|
+
if (invocation.messages.length > 0) {
|
|
108
|
+
await run.addMessages(invocation.messages, { clientId: invocation.clientId });
|
|
117
109
|
}
|
|
118
110
|
|
|
119
|
-
const historyMsgs =
|
|
120
|
-
const newMsgs = messages.map((m) => m.message);
|
|
111
|
+
const historyMsgs = invocation.history.map((h) => h.message);
|
|
112
|
+
const newMsgs = invocation.messages.map((m) => m.message);
|
|
121
113
|
|
|
122
114
|
const result = streamText({
|
|
123
|
-
model: anthropic('claude-sonnet-4-
|
|
115
|
+
model: anthropic('claude-sonnet-4-6'),
|
|
124
116
|
system: 'You are a helpful assistant.',
|
|
125
117
|
messages: await convertToModelMessages([...historyMsgs, ...newMsgs]),
|
|
126
|
-
abortSignal:
|
|
118
|
+
abortSignal: run.abortSignal,
|
|
127
119
|
});
|
|
128
120
|
|
|
129
121
|
// Stream the response over Ably in the background
|
|
130
122
|
after(async () => {
|
|
131
|
-
const { reason } = await
|
|
132
|
-
await
|
|
133
|
-
|
|
123
|
+
const { reason } = await run.pipe(result.toUIMessageStream());
|
|
124
|
+
await run.end(reason);
|
|
125
|
+
session.close();
|
|
134
126
|
});
|
|
135
127
|
|
|
136
128
|
return new Response(null, { status: 200 });
|
|
@@ -143,26 +135,20 @@ export async function POST(req: Request) {
|
|
|
143
135
|
'use client';
|
|
144
136
|
|
|
145
137
|
import { useChat } from '@ai-sdk/react';
|
|
146
|
-
import {
|
|
147
|
-
import { useClientTransport, useActiveTurns, useHistory } from '@ably/ai-transport/react';
|
|
148
|
-
import { useChatTransport, useMessageSync } from '@ably/ai-transport/vercel/react';
|
|
149
|
-
import { UIMessageCodec } from '@ably/ai-transport/vercel';
|
|
150
|
-
|
|
151
|
-
function Chat({ chatId, clientId }: { chatId: string; clientId?: string }) {
|
|
152
|
-
const { channel } = useChannel({ channelName: chatId });
|
|
138
|
+
import { ChatTransportProvider, useChatTransport, useMessageSync, useView } from '@ably/ai-transport/vercel/react';
|
|
153
139
|
|
|
154
|
-
|
|
155
|
-
const chatTransport = useChatTransport(
|
|
140
|
+
function ChatInner({ chatId }: { chatId: string }) {
|
|
141
|
+
const { chatTransport } = useChatTransport();
|
|
156
142
|
|
|
157
|
-
const { messages, setMessages, sendMessage, stop } = useChat({
|
|
143
|
+
const { messages, setMessages, sendMessage, stop, status } = useChat({
|
|
158
144
|
id: chatId,
|
|
159
145
|
transport: chatTransport,
|
|
160
146
|
});
|
|
161
147
|
|
|
162
|
-
useMessageSync(
|
|
148
|
+
useMessageSync({ setMessages });
|
|
163
149
|
|
|
164
|
-
const
|
|
165
|
-
|
|
150
|
+
const isStreaming = status === 'submitted' || status === 'streaming';
|
|
151
|
+
useView({ limit: 30 });
|
|
166
152
|
|
|
167
153
|
return (
|
|
168
154
|
<div>
|
|
@@ -175,7 +161,7 @@ function Chat({ chatId, clientId }: { chatId: string; clientId?: string }) {
|
|
|
175
161
|
sendMessage({ text: 'Hello' });
|
|
176
162
|
}}
|
|
177
163
|
>
|
|
178
|
-
{
|
|
164
|
+
{isStreaming ? (
|
|
179
165
|
<button
|
|
180
166
|
type="button"
|
|
181
167
|
onClick={stop}
|
|
@@ -189,6 +175,17 @@ function Chat({ chatId, clientId }: { chatId: string; clientId?: string }) {
|
|
|
189
175
|
</div>
|
|
190
176
|
);
|
|
191
177
|
}
|
|
178
|
+
|
|
179
|
+
function Chat({ chatId, clientId }: { chatId: string; clientId?: string }) {
|
|
180
|
+
return (
|
|
181
|
+
<ChatTransportProvider
|
|
182
|
+
channelName={chatId}
|
|
183
|
+
clientId={clientId}
|
|
184
|
+
>
|
|
185
|
+
<ChatInner chatId={chatId} />
|
|
186
|
+
</ChatTransportProvider>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
192
189
|
```
|
|
193
190
|
|
|
194
191
|
### Authentication
|
|
@@ -229,20 +226,22 @@ The core entry point is framework-agnostic. Bring your own `Codec` to map betwee
|
|
|
229
226
|
### Client
|
|
230
227
|
|
|
231
228
|
```typescript
|
|
232
|
-
import {
|
|
229
|
+
import { createClientSession } from '@ably/ai-transport';
|
|
233
230
|
import { myCodec } from './my-codec';
|
|
234
231
|
|
|
235
|
-
const
|
|
236
|
-
|
|
232
|
+
const session = createClientSession({
|
|
233
|
+
client: ably, // Ably.Realtime
|
|
234
|
+
channelName: 'ai:demo',
|
|
237
235
|
codec: myCodec,
|
|
238
236
|
clientId: 'user-123',
|
|
239
237
|
api: '/api/chat',
|
|
240
238
|
});
|
|
239
|
+
await session.connect();
|
|
241
240
|
|
|
242
|
-
const
|
|
241
|
+
const run = await session.view.send(messages);
|
|
243
242
|
|
|
244
243
|
// Read the stream
|
|
245
|
-
const reader =
|
|
244
|
+
const reader = run.stream.getReader();
|
|
246
245
|
while (true) {
|
|
247
246
|
const { done, value } = await reader.read();
|
|
248
247
|
if (done) break;
|
|
@@ -250,29 +249,30 @@ while (true) {
|
|
|
250
249
|
}
|
|
251
250
|
```
|
|
252
251
|
|
|
253
|
-
###
|
|
252
|
+
### Agent (server-side)
|
|
254
253
|
|
|
255
254
|
```typescript
|
|
256
|
-
import {
|
|
255
|
+
import { createAgentSession, Invocation } from '@ably/ai-transport';
|
|
257
256
|
import { myCodec } from './my-codec';
|
|
258
257
|
|
|
259
|
-
const
|
|
260
|
-
|
|
258
|
+
const session = createAgentSession({ client: ably, channelName: 'ai:demo', codec: myCodec });
|
|
259
|
+
await session.connect();
|
|
260
|
+
const run = session.createRun(invocation);
|
|
261
261
|
|
|
262
|
-
await
|
|
263
|
-
await
|
|
262
|
+
await run.start();
|
|
263
|
+
await run.addMessages(invocation.messages, { clientId: invocation.clientId });
|
|
264
264
|
|
|
265
|
-
const { reason } = await
|
|
266
|
-
await
|
|
267
|
-
|
|
265
|
+
const { reason } = await run.pipe(aiStream);
|
|
266
|
+
await run.end(reason);
|
|
267
|
+
session.close();
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
---
|
|
271
271
|
|
|
272
272
|
## Package exports
|
|
273
273
|
|
|
274
|
-
| Export path
|
|
275
|
-
|
|
|
274
|
+
| Export path | Purpose | Peer dependencies |
|
|
275
|
+
| --------------------------------- | ------------------------------------------- | --------------------- |
|
|
276
276
|
| `@ably/ai-transport` | Core transport, codec interfaces, utilities | `ably` |
|
|
277
277
|
| `@ably/ai-transport/react` | React hooks for any codec | `ably`, `react` |
|
|
278
278
|
| `@ably/ai-transport/vercel` | Vercel AI SDK codec, transport factories | `ably`, `ai` |
|
|
@@ -280,19 +280,14 @@ transport.close();
|
|
|
280
280
|
|
|
281
281
|
### React hooks
|
|
282
282
|
|
|
283
|
-
| Hook
|
|
284
|
-
|
|
|
285
|
-
| `
|
|
286
|
-
| `
|
|
287
|
-
| `
|
|
288
|
-
| `
|
|
289
|
-
| `
|
|
290
|
-
| `
|
|
291
|
-
| `useHistory` | `/react` | Paginate through conversation history |
|
|
292
|
-
| `useConversationTree` | `/react` | Navigate branches in a forked conversation |
|
|
293
|
-
| `useAblyMessages` | `/react` | Access raw Ably messages |
|
|
294
|
-
| `useChatTransport` | `/vercel/react` | Wrap transport for Vercel's `useChat` |
|
|
295
|
-
| `useMessageSync` | `/vercel/react` | Sync transport state with `useChat`'s `setMessages` |
|
|
283
|
+
| Hook | Entry point | Description |
|
|
284
|
+
| ------------------ | --------------- | ------------------------------------------------- |
|
|
285
|
+
| `useClientSession` | `/react` | Read a client session from the nearest provider |
|
|
286
|
+
| `useView` | `/react` | Subscribe to messages with history loading |
|
|
287
|
+
| `useTree` | `/react` | Navigate branches in a forked conversation |
|
|
288
|
+
| `useAblyMessages` | `/react` | Access raw Ably messages |
|
|
289
|
+
| `useChatTransport` | `/vercel/react` | Wrap session for Vercel's `useChat` |
|
|
290
|
+
| `useMessageSync` | `/vercel/react` | Sync session state with `useChat`'s `setMessages` |
|
|
296
291
|
|
|
297
292
|
---
|
|
298
293
|
|
|
@@ -303,22 +298,23 @@ transport.close();
|
|
|
303
298
|
Two mechanisms cover different failure modes:
|
|
304
299
|
|
|
305
300
|
- **Network blips** - Ably's connection protocol automatically reconnects and delivers any messages published while the client was disconnected. No application code required.
|
|
306
|
-
- **Resumable streams** - A client that joins or rejoins a channel mid-response (after a page refresh, on a new device, or as a second participant) receives the in-progress stream immediately on subscribing. Load previous conversation history from the channel via `
|
|
301
|
+
- **Resumable streams** - A client that joins or rejoins a channel mid-response (after a page refresh, on a new device, or as a second participant) receives the in-progress stream immediately on subscribing. Load previous conversation history from the channel via `view.loadOlder()`, or from your own database.
|
|
307
302
|
|
|
308
303
|
### Cancellation
|
|
309
304
|
|
|
310
305
|
```typescript
|
|
311
|
-
// Client: cancel
|
|
312
|
-
await
|
|
306
|
+
// Client: cancel a specific run by id
|
|
307
|
+
await session.cancel('run-abc');
|
|
313
308
|
|
|
314
|
-
//
|
|
315
|
-
await
|
|
309
|
+
// Or via the ActiveRun returned by send / regenerate / edit
|
|
310
|
+
const run = await view.send(codec.createUserMessage(userMsg));
|
|
311
|
+
await run.cancel();
|
|
316
312
|
|
|
317
|
-
//
|
|
313
|
+
// Agent: the run's abortSignal fires automatically
|
|
318
314
|
const result = streamText({
|
|
319
|
-
model: anthropic('claude-sonnet-4-
|
|
315
|
+
model: anthropic('claude-sonnet-4-6'),
|
|
320
316
|
messages,
|
|
321
|
-
abortSignal:
|
|
317
|
+
abortSignal: run.abortSignal, // Aborted when client cancels
|
|
322
318
|
});
|
|
323
319
|
```
|
|
324
320
|
|
|
@@ -328,15 +324,15 @@ Regenerate or edit messages to create forks in the conversation tree. The SDK tr
|
|
|
328
324
|
|
|
329
325
|
```typescript
|
|
330
326
|
// Regenerate the last assistant message
|
|
331
|
-
const
|
|
327
|
+
const run = await session.view.regenerate(assistantMessageId);
|
|
332
328
|
|
|
333
329
|
// Edit a user message and regenerate from that point
|
|
334
|
-
const
|
|
330
|
+
const run = await session.view.edit(userMessageId, [newMessage]);
|
|
335
331
|
|
|
336
332
|
// Navigate branches
|
|
337
|
-
const tree =
|
|
333
|
+
const tree = session.tree;
|
|
338
334
|
const siblings = tree.getSiblings(messageId);
|
|
339
|
-
|
|
335
|
+
session.view.select(messageId, 1); // Switch to second branch
|
|
340
336
|
```
|
|
341
337
|
|
|
342
338
|
### History and hydration
|
|
@@ -344,26 +340,26 @@ tree.select(messageId, 1); // Switch to second branch
|
|
|
344
340
|
Load previous conversation state when a client joins or returns to a session.
|
|
345
341
|
|
|
346
342
|
```typescript
|
|
347
|
-
const
|
|
348
|
-
|
|
343
|
+
const view = session.view;
|
|
344
|
+
await view.loadOlder(50);
|
|
345
|
+
// view.getMessages() returns the flat message list loaded so far
|
|
349
346
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
347
|
+
// Load more older messages
|
|
348
|
+
await view.loadOlder(50);
|
|
353
349
|
```
|
|
354
350
|
|
|
355
351
|
### Events
|
|
356
352
|
|
|
357
353
|
```typescript
|
|
358
|
-
|
|
359
|
-
console.log(
|
|
354
|
+
session.view.on('update', () => {
|
|
355
|
+
console.log(session.view.getMessages());
|
|
360
356
|
});
|
|
361
357
|
|
|
362
|
-
|
|
363
|
-
console.log(event.
|
|
358
|
+
session.tree.on('run', (event) => {
|
|
359
|
+
console.log(event.runId, event.type); // 'ai-run-start' | 'ai-run-end'
|
|
364
360
|
});
|
|
365
361
|
|
|
366
|
-
|
|
362
|
+
session.on('error', (error) => {
|
|
367
363
|
console.error(error.code, error.message);
|
|
368
364
|
});
|
|
369
365
|
```
|
|
@@ -374,10 +370,10 @@ transport.on('error', (error) => {
|
|
|
374
370
|
|
|
375
371
|
Detailed documentation lives in the [`docs/`](./docs/) directory:
|
|
376
372
|
|
|
377
|
-
- **[Concepts](./docs/concepts/)** - [
|
|
378
|
-
- **[Get started](./docs/get-started/)** - [Vercel AI SDK with useChat](./docs/get-started/vercel-use-chat.md), [Vercel AI SDK with
|
|
373
|
+
- **[Concepts](./docs/concepts/)** - [Sessions](./docs/concepts/sessions.md), [Runs](./docs/concepts/runs.md)
|
|
374
|
+
- **[Get started](./docs/get-started/)** - [Vercel AI SDK with useChat](./docs/get-started/vercel-use-chat.md), [Vercel AI SDK with useClientSession](./docs/get-started/vercel-use-client-session.md)
|
|
379
375
|
- **[Frameworks](./docs/frameworks/)** - [Vercel AI SDK](./docs/frameworks/vercel-ai-sdk.md)
|
|
380
|
-
- **[Features](./docs/features/)** - [Streaming](./docs/features/streaming.md), [Cancellation](./docs/features/cancel.md), [
|
|
376
|
+
- **[Features](./docs/features/)** - [Streaming](./docs/features/streaming.md), [Cancellation](./docs/features/cancel.md), [Interruption](./docs/features/interruption.md), [Optimistic updates](./docs/features/optimistic-updates.md), [History](./docs/features/history.md), [Branching](./docs/features/branching.md), [Multi-client sync](./docs/features/multi-client.md), [Concurrent runs](./docs/features/concurrent-runs.md)
|
|
381
377
|
- **[Reference](./docs/reference/)** - [React hooks](./docs/reference/react-hooks.md), [Error codes](./docs/reference/error-codes.md)
|
|
382
378
|
- **[Internals](./docs/internals/)** - Architecture details for contributors
|
|
383
379
|
|
|
@@ -388,20 +384,22 @@ Detailed documentation lives in the [`docs/`](./docs/) directory:
|
|
|
388
384
|
Working demo applications live in the [`demo/`](./demo/) directory:
|
|
389
385
|
|
|
390
386
|
- **[`demo/vercel/react/use-chat/`](./demo/vercel/react/use-chat/)** - Vercel AI SDK with `useChat` integration
|
|
391
|
-
- **[`demo/vercel/react/use-client-
|
|
387
|
+
- **[`demo/vercel/react/use-client-session/`](./demo/vercel/react/use-client-session/)** - Vercel AI SDK with direct `useClientSession` hooks
|
|
392
388
|
|
|
393
389
|
---
|
|
394
390
|
|
|
395
391
|
## Development
|
|
396
392
|
|
|
393
|
+
This repository uses [pnpm](https://pnpm.io/). Enable Corepack once (`corepack enable`) to pick up the pinned version automatically.
|
|
394
|
+
|
|
397
395
|
```bash
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
396
|
+
pnpm install
|
|
397
|
+
pnpm run build # Build all entry points (ESM + UMD/CJS + .d.ts)
|
|
398
|
+
pnpm run typecheck # Type check
|
|
399
|
+
pnpm run lint # Lint
|
|
400
|
+
pnpm test # Unit tests (mocks only)
|
|
401
|
+
pnpm run test:integration # Integration tests (needs ABLY_API_KEY)
|
|
402
|
+
pnpm run precommit # format:check + lint + typecheck
|
|
405
403
|
```
|
|
406
404
|
|
|
407
405
|
### Project structure
|
|
@@ -410,7 +408,7 @@ npm run precommit # format:check + lint + typecheck
|
|
|
410
408
|
src/
|
|
411
409
|
├── core/ # Generic transport and codec (no framework deps)
|
|
412
410
|
│ ├── codec/ # Codec interfaces and core encoder/decoder
|
|
413
|
-
│ └── transport/ #
|
|
411
|
+
│ └── transport/ # ClientSession, AgentSession, Tree
|
|
414
412
|
├── react/ # React hooks for any codec
|
|
415
413
|
├── vercel/ # Vercel AI SDK codec and transport adapters
|
|
416
414
|
│ ├── codec/ # UIMessageCodec
|