@ably/ai-transport 0.1.0 → 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 +91 -100
- package/dist/ably-ai-transport.js +1553 -1238
- 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 +116 -42
- 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 +407 -115
- 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 +96 -18
- package/dist/core/transport/index.d.ts +5 -6
- 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 -9
- package/dist/core/transport/run-manager.d.ts +78 -0
- package/dist/core/transport/tree.d.ts +373 -109
- 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 -553
- package/dist/core/transport/view.d.ts +272 -84
- package/dist/errors.d.ts +21 -10
- package/dist/index.d.ts +6 -8
- package/dist/logger.d.ts +12 -0
- package/dist/react/ably-ai-transport-react.js +976 -990
- 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 +12 -12
- package/dist/react/internal/use-resolved-session.d.ts +36 -0
- package/dist/react/use-ably-messages.d.ts +17 -14
- package/dist/react/use-client-session.d.ts +81 -0
- package/dist/react/use-create-view.d.ts +14 -13
- package/dist/react/use-tree.d.ts +30 -15
- package/dist/react/use-view.d.ts +82 -51
- package/dist/utils.d.ts +32 -23
- package/dist/vercel/ably-ai-transport-vercel.js +2573 -2086
- 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 +2 -2
- package/dist/vercel/index.d.ts +4 -5
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +3907 -3266
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +33 -8
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +7 -6
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
- package/dist/vercel/react/index.d.ts +1 -2
- package/dist/vercel/react/use-chat-transport.d.ts +30 -26
- package/dist/vercel/react/use-message-sync.d.ts +17 -30
- package/dist/vercel/run-end-reason.d.ts +29 -0
- package/dist/vercel/transport/chat-transport.d.ts +43 -24
- package/dist/vercel/transport/index.d.ts +25 -21
- package/dist/vercel/transport/run-output-stream.d.ts +56 -0
- package/dist/version.d.ts +2 -0
- package/package.json +30 -23
- package/src/constants.ts +124 -51
- package/src/core/agent.ts +68 -0
- package/src/core/codec/decoder.ts +71 -98
- package/src/core/codec/encoder.ts +113 -65
- package/src/core/codec/index.ts +13 -6
- package/src/core/codec/lifecycle-tracker.ts +10 -9
- package/src/core/codec/types.ts +436 -120
- 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 +181 -22
- package/src/core/transport/index.ts +25 -26
- 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 +54 -39
- package/src/core/transport/run-manager.ts +249 -0
- package/src/core/transport/tree.ts +926 -308
- 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 -706
- package/src/core/transport/view.ts +864 -433
- package/src/errors.ts +22 -9
- package/src/event-emitter.ts +3 -2
- package/src/index.ts +52 -41
- 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 +23 -13
- package/src/react/internal/use-resolved-session.ts +63 -0
- package/src/react/use-ably-messages.ts +32 -22
- package/src/react/use-client-session.ts +201 -0
- package/src/react/use-create-view.ts +33 -29
- package/src/react/use-tree.ts +61 -30
- package/src/react/use-view.ts +139 -97
- package/src/utils.ts +63 -45
- package/src/vercel/codec/decoder.ts +336 -258
- package/src/vercel/codec/encoder.ts +343 -205
- package/src/vercel/codec/events.ts +87 -0
- package/src/vercel/codec/index.ts +60 -13
- package/src/vercel/codec/reducer.ts +977 -0
- package/src/vercel/codec/tool-transitions.ts +2 -2
- package/src/vercel/index.ts +6 -19
- package/src/vercel/react/contexts/chat-transport-context.ts +7 -6
- package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
- package/src/vercel/react/index.ts +3 -5
- package/src/vercel/react/use-chat-transport.ts +47 -49
- package/src/vercel/react/use-message-sync.ts +80 -39
- package/src/vercel/run-end-reason.ts +78 -0
- package/src/vercel/transport/chat-transport.ts +392 -98
- package/src/vercel/transport/index.ts +39 -38
- 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/decode-history.d.ts +0 -43
- package/dist/core/transport/server-transport.d.ts +0 -7
- package/dist/core/transport/stream-router.d.ts +0 -29
- package/dist/core/transport/turn-manager.d.ts +0 -37
- package/dist/react/contexts/transport-context.d.ts +0 -31
- package/dist/react/contexts/transport-provider.d.ts +0 -49
- package/dist/react/create-transport-hooks.d.ts +0 -124
- package/dist/react/use-active-turns.d.ts +0 -12
- package/dist/react/use-client-transport.d.ts +0 -80
- package/dist/vercel/codec/accumulator.d.ts +0 -21
- package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
- package/dist/vercel/tool-approvals.d.ts +0 -124
- package/dist/vercel/tool-events.d.ts +0 -26
- package/src/core/transport/client-transport.ts +0 -977
- package/src/core/transport/decode-history.ts +0 -485
- package/src/core/transport/server-transport.ts +0 -612
- package/src/core/transport/stream-router.ts +0 -136
- package/src/core/transport/turn-manager.ts +0 -165
- package/src/react/contexts/transport-context.ts +0 -37
- package/src/react/contexts/transport-provider.tsx +0 -164
- package/src/react/create-transport-hooks.ts +0 -144
- package/src/react/use-active-turns.ts +0 -72
- package/src/react/use-client-transport.ts +0 -197
- package/src/vercel/codec/accumulator.ts +0 -588
- package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
- package/src/vercel/tool-approvals.ts +0 -380
- package/src/vercel/tool-events.ts +0 -53
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: TreeNode<UIMessage>[];
|
|
98
|
-
history?: TreeNode<UIMessage>[];
|
|
99
|
-
chatId: 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
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,25 +135,19 @@ export async function POST(req: Request) {
|
|
|
143
135
|
'use client';
|
|
144
136
|
|
|
145
137
|
import { useChat } from '@ai-sdk/react';
|
|
146
|
-
import {
|
|
147
|
-
ChatTransportProvider,
|
|
148
|
-
useChatTransport,
|
|
149
|
-
useMessageSync,
|
|
150
|
-
useActiveTurns,
|
|
151
|
-
useView,
|
|
152
|
-
} from '@ably/ai-transport/vercel/react';
|
|
138
|
+
import { ChatTransportProvider, useChatTransport, useMessageSync, useView } from '@ably/ai-transport/vercel/react';
|
|
153
139
|
|
|
154
140
|
function ChatInner({ chatId }: { chatId: string }) {
|
|
155
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
148
|
useMessageSync({ setMessages });
|
|
163
149
|
|
|
164
|
-
const
|
|
150
|
+
const isStreaming = status === 'submitted' || status === 'streaming';
|
|
165
151
|
useView({ limit: 30 });
|
|
166
152
|
|
|
167
153
|
return (
|
|
@@ -175,7 +161,7 @@ function ChatInner({ chatId }: { chatId: string }) {
|
|
|
175
161
|
sendMessage({ text: 'Hello' });
|
|
176
162
|
}}
|
|
177
163
|
>
|
|
178
|
-
{
|
|
164
|
+
{isStreaming ? (
|
|
179
165
|
<button
|
|
180
166
|
type="button"
|
|
181
167
|
onClick={stop}
|
|
@@ -240,20 +226,22 @@ The core entry point is framework-agnostic. Bring your own `Codec` to map betwee
|
|
|
240
226
|
### Client
|
|
241
227
|
|
|
242
228
|
```typescript
|
|
243
|
-
import {
|
|
229
|
+
import { createClientSession } from '@ably/ai-transport';
|
|
244
230
|
import { myCodec } from './my-codec';
|
|
245
231
|
|
|
246
|
-
const
|
|
247
|
-
|
|
232
|
+
const session = createClientSession({
|
|
233
|
+
client: ably, // Ably.Realtime
|
|
234
|
+
channelName: 'ai:demo',
|
|
248
235
|
codec: myCodec,
|
|
249
236
|
clientId: 'user-123',
|
|
250
237
|
api: '/api/chat',
|
|
251
238
|
});
|
|
239
|
+
await session.connect();
|
|
252
240
|
|
|
253
|
-
const
|
|
241
|
+
const run = await session.view.send(messages);
|
|
254
242
|
|
|
255
243
|
// Read the stream
|
|
256
|
-
const reader =
|
|
244
|
+
const reader = run.stream.getReader();
|
|
257
245
|
while (true) {
|
|
258
246
|
const { done, value } = await reader.read();
|
|
259
247
|
if (done) break;
|
|
@@ -261,21 +249,22 @@ while (true) {
|
|
|
261
249
|
}
|
|
262
250
|
```
|
|
263
251
|
|
|
264
|
-
###
|
|
252
|
+
### Agent (server-side)
|
|
265
253
|
|
|
266
254
|
```typescript
|
|
267
|
-
import {
|
|
255
|
+
import { createAgentSession, Invocation } from '@ably/ai-transport';
|
|
268
256
|
import { myCodec } from './my-codec';
|
|
269
257
|
|
|
270
|
-
const
|
|
271
|
-
|
|
258
|
+
const session = createAgentSession({ client: ably, channelName: 'ai:demo', codec: myCodec });
|
|
259
|
+
await session.connect();
|
|
260
|
+
const run = session.createRun(invocation);
|
|
272
261
|
|
|
273
|
-
await
|
|
274
|
-
await
|
|
262
|
+
await run.start();
|
|
263
|
+
await run.addMessages(invocation.messages, { clientId: invocation.clientId });
|
|
275
264
|
|
|
276
|
-
const { reason } = await
|
|
277
|
-
await
|
|
278
|
-
|
|
265
|
+
const { reason } = await run.pipe(aiStream);
|
|
266
|
+
await run.end(reason);
|
|
267
|
+
session.close();
|
|
279
268
|
```
|
|
280
269
|
|
|
281
270
|
---
|
|
@@ -291,15 +280,14 @@ transport.close();
|
|
|
291
280
|
|
|
292
281
|
### React hooks
|
|
293
282
|
|
|
294
|
-
| Hook
|
|
295
|
-
|
|
|
296
|
-
| `
|
|
297
|
-
| `useView`
|
|
298
|
-
| `
|
|
299
|
-
| `
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
| `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` |
|
|
303
291
|
|
|
304
292
|
---
|
|
305
293
|
|
|
@@ -315,17 +303,18 @@ Two mechanisms cover different failure modes:
|
|
|
315
303
|
### Cancellation
|
|
316
304
|
|
|
317
305
|
```typescript
|
|
318
|
-
// Client: cancel
|
|
319
|
-
await
|
|
306
|
+
// Client: cancel a specific run by id
|
|
307
|
+
await session.cancel('run-abc');
|
|
320
308
|
|
|
321
|
-
//
|
|
322
|
-
await
|
|
309
|
+
// Or via the ActiveRun returned by send / regenerate / edit
|
|
310
|
+
const run = await view.send(codec.createUserMessage(userMsg));
|
|
311
|
+
await run.cancel();
|
|
323
312
|
|
|
324
|
-
//
|
|
313
|
+
// Agent: the run's abortSignal fires automatically
|
|
325
314
|
const result = streamText({
|
|
326
315
|
model: anthropic('claude-sonnet-4-6'),
|
|
327
316
|
messages,
|
|
328
|
-
abortSignal:
|
|
317
|
+
abortSignal: run.abortSignal, // Aborted when client cancels
|
|
329
318
|
});
|
|
330
319
|
```
|
|
331
320
|
|
|
@@ -335,15 +324,15 @@ Regenerate or edit messages to create forks in the conversation tree. The SDK tr
|
|
|
335
324
|
|
|
336
325
|
```typescript
|
|
337
326
|
// Regenerate the last assistant message
|
|
338
|
-
const
|
|
327
|
+
const run = await session.view.regenerate(assistantMessageId);
|
|
339
328
|
|
|
340
329
|
// Edit a user message and regenerate from that point
|
|
341
|
-
const
|
|
330
|
+
const run = await session.view.edit(userMessageId, [newMessage]);
|
|
342
331
|
|
|
343
332
|
// Navigate branches
|
|
344
|
-
const tree =
|
|
333
|
+
const tree = session.tree;
|
|
345
334
|
const siblings = tree.getSiblings(messageId);
|
|
346
|
-
|
|
335
|
+
session.view.select(messageId, 1); // Switch to second branch
|
|
347
336
|
```
|
|
348
337
|
|
|
349
338
|
### History and hydration
|
|
@@ -351,9 +340,9 @@ tree.select(messageId, 1); // Switch to second branch
|
|
|
351
340
|
Load previous conversation state when a client joins or returns to a session.
|
|
352
341
|
|
|
353
342
|
```typescript
|
|
354
|
-
const view =
|
|
343
|
+
const view = session.view;
|
|
355
344
|
await view.loadOlder(50);
|
|
356
|
-
// view.
|
|
345
|
+
// view.getMessages() returns the flat message list loaded so far
|
|
357
346
|
|
|
358
347
|
// Load more older messages
|
|
359
348
|
await view.loadOlder(50);
|
|
@@ -362,15 +351,15 @@ await view.loadOlder(50);
|
|
|
362
351
|
### Events
|
|
363
352
|
|
|
364
353
|
```typescript
|
|
365
|
-
|
|
366
|
-
console.log(
|
|
354
|
+
session.view.on('update', () => {
|
|
355
|
+
console.log(session.view.getMessages());
|
|
367
356
|
});
|
|
368
357
|
|
|
369
|
-
|
|
370
|
-
console.log(event.
|
|
358
|
+
session.tree.on('run', (event) => {
|
|
359
|
+
console.log(event.runId, event.type); // 'ai-run-start' | 'ai-run-end'
|
|
371
360
|
});
|
|
372
361
|
|
|
373
|
-
|
|
362
|
+
session.on('error', (error) => {
|
|
374
363
|
console.error(error.code, error.message);
|
|
375
364
|
});
|
|
376
365
|
```
|
|
@@ -381,10 +370,10 @@ transport.on('error', (error) => {
|
|
|
381
370
|
|
|
382
371
|
Detailed documentation lives in the [`docs/`](./docs/) directory:
|
|
383
372
|
|
|
384
|
-
- **[Concepts](./docs/concepts/)** - [
|
|
385
|
-
- **[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)
|
|
386
375
|
- **[Frameworks](./docs/frameworks/)** - [Vercel AI SDK](./docs/frameworks/vercel-ai-sdk.md)
|
|
387
|
-
- **[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
|
|
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)
|
|
388
377
|
- **[Reference](./docs/reference/)** - [React hooks](./docs/reference/react-hooks.md), [Error codes](./docs/reference/error-codes.md)
|
|
389
378
|
- **[Internals](./docs/internals/)** - Architecture details for contributors
|
|
390
379
|
|
|
@@ -395,20 +384,22 @@ Detailed documentation lives in the [`docs/`](./docs/) directory:
|
|
|
395
384
|
Working demo applications live in the [`demo/`](./demo/) directory:
|
|
396
385
|
|
|
397
386
|
- **[`demo/vercel/react/use-chat/`](./demo/vercel/react/use-chat/)** - Vercel AI SDK with `useChat` integration
|
|
398
|
-
- **[`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
|
|
399
388
|
|
|
400
389
|
---
|
|
401
390
|
|
|
402
391
|
## Development
|
|
403
392
|
|
|
393
|
+
This repository uses [pnpm](https://pnpm.io/). Enable Corepack once (`corepack enable`) to pick up the pinned version automatically.
|
|
394
|
+
|
|
404
395
|
```bash
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
|
412
403
|
```
|
|
413
404
|
|
|
414
405
|
### Project structure
|
|
@@ -417,7 +408,7 @@ npm run precommit # format:check + lint + typecheck
|
|
|
417
408
|
src/
|
|
418
409
|
├── core/ # Generic transport and codec (no framework deps)
|
|
419
410
|
│ ├── codec/ # Codec interfaces and core encoder/decoder
|
|
420
|
-
│ └── transport/ #
|
|
411
|
+
│ └── transport/ # ClientSession, AgentSession, Tree
|
|
421
412
|
├── react/ # React hooks for any codec
|
|
422
413
|
├── vercel/ # Vercel AI SDK codec and transport adapters
|
|
423
414
|
│ ├── codec/ # UIMessageCodec
|