@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.
Files changed (167) hide show
  1. package/README.md +114 -116
  2. package/dist/ably-ai-transport.js +1743 -961
  3. package/dist/ably-ai-transport.js.map +1 -1
  4. package/dist/ably-ai-transport.umd.cjs +1 -1
  5. package/dist/ably-ai-transport.umd.cjs.map +1 -1
  6. package/dist/constants.d.ts +117 -39
  7. package/dist/core/agent.d.ts +29 -0
  8. package/dist/core/codec/decoder.d.ts +20 -23
  9. package/dist/core/codec/encoder.d.ts +11 -8
  10. package/dist/core/codec/index.d.ts +1 -2
  11. package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
  12. package/dist/core/codec/types.d.ts +410 -101
  13. package/dist/core/transport/agent-session.d.ts +10 -0
  14. package/dist/core/transport/branch-chain.d.ts +43 -0
  15. package/dist/core/transport/client-session.d.ts +13 -0
  16. package/dist/core/transport/decode-fold.d.ts +47 -0
  17. package/dist/core/transport/headers.d.ts +97 -17
  18. package/dist/core/transport/index.d.ts +5 -3
  19. package/dist/core/transport/internal/bounded-map.d.ts +20 -0
  20. package/dist/core/transport/invocation.d.ts +74 -0
  21. package/dist/core/transport/load-conversation.d.ts +128 -0
  22. package/dist/core/transport/load-history.d.ts +39 -0
  23. package/dist/core/transport/pipe-stream.d.ts +9 -8
  24. package/dist/core/transport/run-manager.d.ts +78 -0
  25. package/dist/core/transport/tree.d.ts +435 -0
  26. package/dist/core/transport/types/agent.d.ts +353 -0
  27. package/dist/core/transport/types/client.d.ts +168 -0
  28. package/dist/core/transport/types/shared.d.ts +24 -0
  29. package/dist/core/transport/types/tree.d.ts +315 -0
  30. package/dist/core/transport/types/view.d.ts +222 -0
  31. package/dist/core/transport/types.d.ts +13 -402
  32. package/dist/core/transport/view.d.ts +354 -0
  33. package/dist/errors.d.ts +37 -9
  34. package/dist/index.d.ts +6 -6
  35. package/dist/logger.d.ts +12 -0
  36. package/dist/react/ably-ai-transport-react.js +1164 -645
  37. package/dist/react/ably-ai-transport-react.js.map +1 -1
  38. package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
  39. package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
  40. package/dist/react/contexts/client-session-context.d.ts +36 -0
  41. package/dist/react/contexts/client-session-provider.d.ts +53 -0
  42. package/dist/react/create-session-hooks.d.ts +116 -0
  43. package/dist/react/index.d.ts +16 -10
  44. package/dist/react/internal/use-resolved-session.d.ts +36 -0
  45. package/dist/react/use-ably-messages.d.ts +20 -11
  46. package/dist/react/use-client-session.d.ts +81 -0
  47. package/dist/react/use-create-view.d.ts +23 -0
  48. package/dist/react/use-tree.d.ts +35 -0
  49. package/dist/react/use-view.d.ts +110 -0
  50. package/dist/utils.d.ts +32 -23
  51. package/dist/vercel/ably-ai-transport-vercel.js +2748 -1625
  52. package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
  53. package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
  54. package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
  55. package/dist/vercel/codec/decoder.d.ts +5 -18
  56. package/dist/vercel/codec/encoder.d.ts +6 -36
  57. package/dist/vercel/codec/events.d.ts +51 -0
  58. package/dist/vercel/codec/index.d.ts +24 -12
  59. package/dist/vercel/codec/reducer.d.ts +144 -0
  60. package/dist/vercel/codec/tool-transitions.d.ts +50 -0
  61. package/dist/vercel/index.d.ts +4 -2
  62. package/dist/vercel/react/ably-ai-transport-vercel-react.js +10298 -1410
  63. package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
  64. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +70 -1
  65. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
  66. package/dist/vercel/react/contexts/chat-transport-context.d.ts +33 -0
  67. package/dist/vercel/react/contexts/chat-transport-provider.d.ts +96 -0
  68. package/dist/vercel/react/index.d.ts +4 -0
  69. package/dist/vercel/react/use-chat-transport.d.ts +66 -21
  70. package/dist/vercel/react/use-message-sync.d.ts +31 -12
  71. package/dist/vercel/run-end-reason.d.ts +29 -0
  72. package/dist/vercel/transport/chat-transport.d.ts +71 -30
  73. package/dist/vercel/transport/index.d.ts +25 -18
  74. package/dist/vercel/transport/run-output-stream.d.ts +56 -0
  75. package/dist/version.d.ts +2 -0
  76. package/package.json +47 -34
  77. package/src/constants.ts +126 -47
  78. package/src/core/agent.ts +68 -0
  79. package/src/core/codec/decoder.ts +71 -98
  80. package/src/core/codec/encoder.ts +115 -58
  81. package/src/core/codec/index.ts +13 -6
  82. package/src/core/codec/lifecycle-tracker.ts +10 -9
  83. package/src/core/codec/types.ts +438 -106
  84. package/src/core/transport/agent-session.ts +1344 -0
  85. package/src/core/transport/branch-chain.ts +58 -0
  86. package/src/core/transport/client-session.ts +775 -0
  87. package/src/core/transport/decode-fold.ts +91 -0
  88. package/src/core/transport/headers.ts +182 -19
  89. package/src/core/transport/index.ts +29 -22
  90. package/src/core/transport/internal/bounded-map.ts +27 -0
  91. package/src/core/transport/invocation.ts +98 -0
  92. package/src/core/transport/load-conversation.ts +355 -0
  93. package/src/core/transport/load-history.ts +269 -0
  94. package/src/core/transport/pipe-stream.ts +58 -40
  95. package/src/core/transport/run-manager.ts +249 -0
  96. package/src/core/transport/tree.ts +1167 -0
  97. package/src/core/transport/types/agent.ts +407 -0
  98. package/src/core/transport/types/client.ts +211 -0
  99. package/src/core/transport/types/shared.ts +27 -0
  100. package/src/core/transport/types/tree.ts +344 -0
  101. package/src/core/transport/types/view.ts +259 -0
  102. package/src/core/transport/types.ts +13 -527
  103. package/src/core/transport/view.ts +1271 -0
  104. package/src/errors.ts +42 -9
  105. package/src/event-emitter.ts +3 -2
  106. package/src/index.ts +55 -39
  107. package/src/logger.ts +14 -1
  108. package/src/react/contexts/client-session-context.ts +41 -0
  109. package/src/react/contexts/client-session-provider.tsx +186 -0
  110. package/src/react/create-session-hooks.ts +141 -0
  111. package/src/react/index.ts +27 -10
  112. package/src/react/internal/use-resolved-session.ts +63 -0
  113. package/src/react/use-ably-messages.ts +47 -19
  114. package/src/react/use-client-session.ts +201 -0
  115. package/src/react/use-create-view.ts +72 -0
  116. package/src/react/use-tree.ts +84 -0
  117. package/src/react/use-view.ts +275 -0
  118. package/src/react/vite.config.ts +4 -1
  119. package/src/utils.ts +63 -45
  120. package/src/vercel/codec/decoder.ts +336 -255
  121. package/src/vercel/codec/encoder.ts +348 -196
  122. package/src/vercel/codec/events.ts +87 -0
  123. package/src/vercel/codec/index.ts +59 -14
  124. package/src/vercel/codec/reducer.ts +977 -0
  125. package/src/vercel/codec/tool-transitions.ts +122 -0
  126. package/src/vercel/index.ts +7 -3
  127. package/src/vercel/react/contexts/chat-transport-context.ts +41 -0
  128. package/src/vercel/react/contexts/chat-transport-provider.tsx +150 -0
  129. package/src/vercel/react/index.ts +13 -1
  130. package/src/vercel/react/use-chat-transport.ts +162 -42
  131. package/src/vercel/react/use-message-sync.ts +121 -22
  132. package/src/vercel/react/vite.config.ts +4 -2
  133. package/src/vercel/run-end-reason.ts +78 -0
  134. package/src/vercel/transport/chat-transport.ts +553 -113
  135. package/src/vercel/transport/index.ts +40 -28
  136. package/src/vercel/transport/run-output-stream.ts +170 -0
  137. package/src/version.ts +2 -0
  138. package/dist/core/transport/client-transport.d.ts +0 -10
  139. package/dist/core/transport/conversation-tree.d.ts +0 -9
  140. package/dist/core/transport/decode-history.d.ts +0 -41
  141. package/dist/core/transport/server-transport.d.ts +0 -7
  142. package/dist/core/transport/stream-router.d.ts +0 -19
  143. package/dist/core/transport/turn-manager.d.ts +0 -34
  144. package/dist/react/use-active-turns.d.ts +0 -8
  145. package/dist/react/use-client-transport.d.ts +0 -7
  146. package/dist/react/use-conversation-tree.d.ts +0 -20
  147. package/dist/react/use-edit.d.ts +0 -7
  148. package/dist/react/use-history.d.ts +0 -19
  149. package/dist/react/use-messages.d.ts +0 -7
  150. package/dist/react/use-regenerate.d.ts +0 -7
  151. package/dist/react/use-send.d.ts +0 -7
  152. package/dist/vercel/codec/accumulator.d.ts +0 -21
  153. package/src/core/transport/client-transport.ts +0 -959
  154. package/src/core/transport/conversation-tree.ts +0 -434
  155. package/src/core/transport/decode-history.ts +0 -337
  156. package/src/core/transport/server-transport.ts +0 -458
  157. package/src/core/transport/stream-router.ts +0 -118
  158. package/src/core/transport/turn-manager.ts +0 -147
  159. package/src/react/use-active-turns.ts +0 -61
  160. package/src/react/use-client-transport.ts +0 -37
  161. package/src/react/use-conversation-tree.ts +0 -71
  162. package/src/react/use-edit.ts +0 -24
  163. package/src/react/use-history.ts +0 -111
  164. package/src/react/use-messages.ts +0 -32
  165. package/src/react/use-regenerate.ts +0 -24
  166. package/src/react/use-send.ts +0 -25
  167. 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, turn lifecycle events, and conversation history all flow through the channel rather than depending on a single HTTP connection.
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 CT as Client Transport
16
+ participant CS as Client Session
17
17
  participant AC as Ably Channel
18
- participant ST as Server Transport
18
+ participant AS as Agent Session
19
19
  participant LLM
20
20
 
21
- U->>CT: type message
22
- CT->>ST: HTTP POST (messages)
23
- ST->>LLM: prompt
24
- LLM-->>ST: token stream
25
- ST->>AC: publish chunks
26
- AC->>CT: subscribe (decode)
27
- CT->>U: render tokens
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 turn's `abortSignal` fires automatically.
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 turns** - Multiple request-response cycles run in parallel on the same channel. Each turn has its own stream and abort signal.
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 | 20+ |
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 { createServerTransport } from '@ably/ai-transport/vercel';
92
- import type { MessageWithHeaders } from '@ably/ai-transport';
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 { messages, history, id, turnId, clientId, forkOf, parent } = (await req.json()) as ChatRequestBody;
98
+ const data = (await req.json()) as InvocationData<UIMessageChunk, UIMessage>;
99
+ const invocation = Invocation.fromJSON(data);
108
100
 
109
- const channel = ably.channels.get(id);
110
- const transport = createServerTransport({ channel });
111
- const turn = transport.newTurn({ turnId, clientId, parent, forkOf });
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 turn.start();
105
+ await run.start();
114
106
 
115
- if (messages.length > 0) {
116
- await turn.addMessages(messages, { clientId });
107
+ if (invocation.messages.length > 0) {
108
+ await run.addMessages(invocation.messages, { clientId: invocation.clientId });
117
109
  }
118
110
 
119
- const historyMsgs = (history ?? []).map((h) => h.message);
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-20250514'),
115
+ model: anthropic('claude-sonnet-4-6'),
124
116
  system: 'You are a helpful assistant.',
125
117
  messages: await convertToModelMessages([...historyMsgs, ...newMsgs]),
126
- abortSignal: turn.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 turn.streamResponse(result.toUIMessageStream());
132
- await turn.end(reason);
133
- transport.close();
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 { useChannel } from 'ably/react';
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
- const transport = useClientTransport({ channel, codec: UIMessageCodec, clientId });
155
- const chatTransport = useChatTransport(transport);
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(transport, setMessages);
148
+ useMessageSync({ setMessages });
163
149
 
164
- const activeTurns = useActiveTurns(transport);
165
- const history = useHistory(transport, { limit: 30 });
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
- {activeTurns.size > 0 ? (
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 { createClientTransport } from '@ably/ai-transport';
229
+ import { createClientSession } from '@ably/ai-transport';
233
230
  import { myCodec } from './my-codec';
234
231
 
235
- const transport = createClientTransport({
236
- channel, // Ably RealtimeChannel
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 turn = await transport.send(messages);
241
+ const run = await session.view.send(messages);
243
242
 
244
243
  // Read the stream
245
- const reader = turn.stream.getReader();
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
- ### Server
252
+ ### Agent (server-side)
254
253
 
255
254
  ```typescript
256
- import { createServerTransport } from '@ably/ai-transport';
255
+ import { createAgentSession, Invocation } from '@ably/ai-transport';
257
256
  import { myCodec } from './my-codec';
258
257
 
259
- const transport = createServerTransport({ channel, codec: myCodec });
260
- const turn = transport.newTurn({ turnId, clientId, parent, forkOf });
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 turn.start();
263
- await turn.addMessages(messages, { clientId });
262
+ await run.start();
263
+ await run.addMessages(invocation.messages, { clientId: invocation.clientId });
264
264
 
265
- const { reason } = await turn.streamResponse(aiStream);
266
- await turn.end(reason);
267
- transport.close();
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 | Purpose | Peer dependencies |
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 | Entry point | Description |
284
- | --------------------- | --------------- | --------------------------------------------------- |
285
- | `useClientTransport` | `/react` | Create and memoize a client transport instance |
286
- | `useMessages` | `/react` | Subscribe to decoded messages |
287
- | `useSend` | `/react` | Stable send callback |
288
- | `useRegenerate` | `/react` | Regenerate a message (fork the conversation) |
289
- | `useEdit` | `/react` | Edit a message and regenerate from that point |
290
- | `useActiveTurns` | `/react` | Track active turns by client ID |
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 `history()`, or from your own database.
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 your own active turns
312
- await transport.cancel();
306
+ // Client: cancel a specific run by id
307
+ await session.cancel('run-abc');
313
308
 
314
- // Cancel a specific turn
315
- await transport.cancel({ turnId: 'turn-abc' });
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
- // Server: the turn's abortSignal fires automatically
313
+ // Agent: the run's abortSignal fires automatically
318
314
  const result = streamText({
319
- model: anthropic('claude-sonnet-4-20250514'),
315
+ model: anthropic('claude-sonnet-4-6'),
320
316
  messages,
321
- abortSignal: turn.abortSignal, // Aborted when client cancels
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 turn = await transport.regenerate(assistantMessageId);
327
+ const run = await session.view.regenerate(assistantMessageId);
332
328
 
333
329
  // Edit a user message and regenerate from that point
334
- const turn = await transport.edit(userMessageId, [newMessage]);
330
+ const run = await session.view.edit(userMessageId, [newMessage]);
335
331
 
336
332
  // Navigate branches
337
- const tree = transport.getTree();
333
+ const tree = session.tree;
338
334
  const siblings = tree.getSiblings(messageId);
339
- tree.select(messageId, 1); // Switch to second branch
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 page = await transport.history({ limit: 50 });
348
- console.log(page.items); // Decoded messages
343
+ const view = session.view;
344
+ await view.loadOlder(50);
345
+ // view.getMessages() returns the flat message list loaded so far
349
346
 
350
- if (page.hasNext()) {
351
- const older = await page.next();
352
- }
347
+ // Load more older messages
348
+ await view.loadOlder(50);
353
349
  ```
354
350
 
355
351
  ### Events
356
352
 
357
353
  ```typescript
358
- transport.on('message', () => {
359
- console.log(transport.getMessages());
354
+ session.view.on('update', () => {
355
+ console.log(session.view.getMessages());
360
356
  });
361
357
 
362
- transport.on('turn', (event) => {
363
- console.log(event.turnId, event.type); // 'x-ably-turn-start' | 'x-ably-turn-end'
358
+ session.tree.on('run', (event) => {
359
+ console.log(event.runId, event.type); // 'ai-run-start' | 'ai-run-end'
364
360
  });
365
361
 
366
- transport.on('error', (error) => {
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/)** - [Transport architecture](./docs/concepts/transport.md), [Turns](./docs/concepts/turns.md)
378
- - **[Get started](./docs/get-started/)** - [Vercel AI SDK with useChat](./docs/get-started/vercel-use-chat.md), [Vercel AI SDK with useClientTransport](./docs/get-started/vercel-use-client-transport.md)
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), [Barge-in](./docs/features/barge-in.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), [Tool calls](./docs/features/tool-calls.md), [Concurrent turns](./docs/features/concurrent-turns.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-transport/`](./demo/vercel/react/use-client-transport/)** - Vercel AI SDK with direct `useClientTransport` hooks
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
- npm install
399
- npm run build # Build all entry points (ESM + UMD/CJS + .d.ts)
400
- npm run typecheck # Type check
401
- npm run lint # Lint
402
- npm test # Unit tests (mocks only)
403
- npm run test:integration # Integration tests (needs ABLY_API_KEY)
404
- npm run precommit # format:check + lint + typecheck
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/ # ClientTransport, ServerTransport, ConversationTree
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