@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.
Files changed (163) hide show
  1. package/README.md +91 -100
  2. package/dist/ably-ai-transport.js +1553 -1238
  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 +116 -42
  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 +407 -115
  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 +96 -18
  18. package/dist/core/transport/index.d.ts +5 -6
  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 -9
  24. package/dist/core/transport/run-manager.d.ts +78 -0
  25. package/dist/core/transport/tree.d.ts +373 -109
  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 -553
  32. package/dist/core/transport/view.d.ts +272 -84
  33. package/dist/errors.d.ts +21 -10
  34. package/dist/index.d.ts +6 -8
  35. package/dist/logger.d.ts +12 -0
  36. package/dist/react/ably-ai-transport-react.js +976 -990
  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 +12 -12
  44. package/dist/react/internal/use-resolved-session.d.ts +36 -0
  45. package/dist/react/use-ably-messages.d.ts +17 -14
  46. package/dist/react/use-client-session.d.ts +81 -0
  47. package/dist/react/use-create-view.d.ts +14 -13
  48. package/dist/react/use-tree.d.ts +30 -15
  49. package/dist/react/use-view.d.ts +82 -51
  50. package/dist/utils.d.ts +32 -23
  51. package/dist/vercel/ably-ai-transport-vercel.js +2573 -2086
  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 +2 -2
  61. package/dist/vercel/index.d.ts +4 -5
  62. package/dist/vercel/react/ably-ai-transport-vercel-react.js +3907 -3266
  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 +33 -8
  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 +7 -6
  67. package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
  68. package/dist/vercel/react/index.d.ts +1 -2
  69. package/dist/vercel/react/use-chat-transport.d.ts +30 -26
  70. package/dist/vercel/react/use-message-sync.d.ts +17 -30
  71. package/dist/vercel/run-end-reason.d.ts +29 -0
  72. package/dist/vercel/transport/chat-transport.d.ts +43 -24
  73. package/dist/vercel/transport/index.d.ts +25 -21
  74. package/dist/vercel/transport/run-output-stream.d.ts +56 -0
  75. package/dist/version.d.ts +2 -0
  76. package/package.json +30 -23
  77. package/src/constants.ts +124 -51
  78. package/src/core/agent.ts +68 -0
  79. package/src/core/codec/decoder.ts +71 -98
  80. package/src/core/codec/encoder.ts +113 -65
  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 +436 -120
  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 +181 -22
  89. package/src/core/transport/index.ts +25 -26
  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 +54 -39
  95. package/src/core/transport/run-manager.ts +249 -0
  96. package/src/core/transport/tree.ts +926 -308
  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 -706
  103. package/src/core/transport/view.ts +864 -433
  104. package/src/errors.ts +22 -9
  105. package/src/event-emitter.ts +3 -2
  106. package/src/index.ts +52 -41
  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 +23 -13
  112. package/src/react/internal/use-resolved-session.ts +63 -0
  113. package/src/react/use-ably-messages.ts +32 -22
  114. package/src/react/use-client-session.ts +201 -0
  115. package/src/react/use-create-view.ts +33 -29
  116. package/src/react/use-tree.ts +61 -30
  117. package/src/react/use-view.ts +139 -97
  118. package/src/utils.ts +63 -45
  119. package/src/vercel/codec/decoder.ts +336 -258
  120. package/src/vercel/codec/encoder.ts +343 -205
  121. package/src/vercel/codec/events.ts +87 -0
  122. package/src/vercel/codec/index.ts +60 -13
  123. package/src/vercel/codec/reducer.ts +977 -0
  124. package/src/vercel/codec/tool-transitions.ts +2 -2
  125. package/src/vercel/index.ts +6 -19
  126. package/src/vercel/react/contexts/chat-transport-context.ts +7 -6
  127. package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
  128. package/src/vercel/react/index.ts +3 -5
  129. package/src/vercel/react/use-chat-transport.ts +47 -49
  130. package/src/vercel/react/use-message-sync.ts +80 -39
  131. package/src/vercel/run-end-reason.ts +78 -0
  132. package/src/vercel/transport/chat-transport.ts +392 -98
  133. package/src/vercel/transport/index.ts +39 -38
  134. package/src/vercel/transport/run-output-stream.ts +170 -0
  135. package/src/version.ts +2 -0
  136. package/dist/core/transport/client-transport.d.ts +0 -10
  137. package/dist/core/transport/decode-history.d.ts +0 -43
  138. package/dist/core/transport/server-transport.d.ts +0 -7
  139. package/dist/core/transport/stream-router.d.ts +0 -29
  140. package/dist/core/transport/turn-manager.d.ts +0 -37
  141. package/dist/react/contexts/transport-context.d.ts +0 -31
  142. package/dist/react/contexts/transport-provider.d.ts +0 -49
  143. package/dist/react/create-transport-hooks.d.ts +0 -124
  144. package/dist/react/use-active-turns.d.ts +0 -12
  145. package/dist/react/use-client-transport.d.ts +0 -80
  146. package/dist/vercel/codec/accumulator.d.ts +0 -21
  147. package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
  148. package/dist/vercel/tool-approvals.d.ts +0 -124
  149. package/dist/vercel/tool-events.d.ts +0 -26
  150. package/src/core/transport/client-transport.ts +0 -977
  151. package/src/core/transport/decode-history.ts +0 -485
  152. package/src/core/transport/server-transport.ts +0 -612
  153. package/src/core/transport/stream-router.ts +0 -136
  154. package/src/core/transport/turn-manager.ts +0 -165
  155. package/src/react/contexts/transport-context.ts +0 -37
  156. package/src/react/contexts/transport-provider.tsx +0 -164
  157. package/src/react/create-transport-hooks.ts +0 -144
  158. package/src/react/use-active-turns.ts +0 -72
  159. package/src/react/use-client-transport.ts +0 -197
  160. package/src/vercel/codec/accumulator.ts +0 -588
  161. package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
  162. package/src/vercel/tool-approvals.ts +0 -380
  163. 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, 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 { TreeNode } from '@ably/ai-transport';
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 { messages, history, chatId, 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(chatId);
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
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,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 activeTurns = useActiveTurns();
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
- {activeTurns.size > 0 ? (
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 { createClientTransport } from '@ably/ai-transport';
229
+ import { createClientSession } from '@ably/ai-transport';
244
230
  import { myCodec } from './my-codec';
245
231
 
246
- const transport = createClientTransport({
247
- channel, // Ably RealtimeChannel
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 turn = await transport.send(messages);
241
+ const run = await session.view.send(messages);
254
242
 
255
243
  // Read the stream
256
- const reader = turn.stream.getReader();
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
- ### Server
252
+ ### Agent (server-side)
265
253
 
266
254
  ```typescript
267
- import { createServerTransport } from '@ably/ai-transport';
255
+ import { createAgentSession, Invocation } from '@ably/ai-transport';
268
256
  import { myCodec } from './my-codec';
269
257
 
270
- const transport = createServerTransport({ channel, codec: myCodec });
271
- 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);
272
261
 
273
- await turn.start();
274
- await turn.addMessages(messages, { clientId });
262
+ await run.start();
263
+ await run.addMessages(invocation.messages, { clientId: invocation.clientId });
275
264
 
276
- const { reason } = await turn.streamResponse(aiStream);
277
- await turn.end(reason);
278
- transport.close();
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 | Entry point | Description |
295
- | -------------------- | --------------- | --------------------------------------------------- |
296
- | `useClientTransport` | `/react` | Create and memoize a client transport instance |
297
- | `useView` | `/react` | Subscribe to messages with history loading |
298
- | `useActiveTurns` | `/react` | Track active turns by client ID |
299
- | `useTree` | `/react` | Navigate branches in a forked conversation |
300
- | `useAblyMessages` | `/react` | Access raw Ably messages |
301
- | `useChatTransport` | `/vercel/react` | Wrap transport for Vercel's `useChat` |
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 your own active turns
319
- await transport.cancel();
306
+ // Client: cancel a specific run by id
307
+ await session.cancel('run-abc');
320
308
 
321
- // Cancel a specific turn
322
- 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();
323
312
 
324
- // Server: the turn's abortSignal fires automatically
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: turn.abortSignal, // Aborted when client cancels
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 turn = await transport.regenerate(assistantMessageId);
327
+ const run = await session.view.regenerate(assistantMessageId);
339
328
 
340
329
  // Edit a user message and regenerate from that point
341
- const turn = await transport.edit(userMessageId, [newMessage]);
330
+ const run = await session.view.edit(userMessageId, [newMessage]);
342
331
 
343
332
  // Navigate branches
344
- const tree = transport.tree;
333
+ const tree = session.tree;
345
334
  const siblings = tree.getSiblings(messageId);
346
- tree.select(messageId, 1); // Switch to second branch
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 = transport.view;
343
+ const view = session.view;
355
344
  await view.loadOlder(50);
356
- // view.flattenNodes() returns the messages loaded so far
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
- transport.view.on('update', () => {
366
- console.log(transport.view.flattenNodes().map((n) => n.message));
354
+ session.view.on('update', () => {
355
+ console.log(session.view.getMessages());
367
356
  });
368
357
 
369
- transport.tree.on('turn', (event) => {
370
- 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'
371
360
  });
372
361
 
373
- transport.on('error', (error) => {
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/)** - [Transport architecture](./docs/concepts/transport.md), [Turns](./docs/concepts/turns.md)
385
- - **[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)
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 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)
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-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
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
- npm install
406
- npm run build # Build all entry points (ESM + UMD/CJS + .d.ts)
407
- npm run typecheck # Type check
408
- npm run lint # Lint
409
- npm test # Unit tests (mocks only)
410
- npm run test:integration # Integration tests (needs ABLY_API_KEY)
411
- 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
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/ # ClientTransport, ServerTransport, Tree
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