@ably/ai-transport 0.0.1 → 0.1.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 (110) hide show
  1. package/README.md +54 -47
  2. package/dist/ably-ai-transport.js +1006 -539
  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 +4 -0
  7. package/dist/core/codec/types.d.ts +19 -2
  8. package/dist/core/transport/decode-history.d.ts +8 -6
  9. package/dist/core/transport/headers.d.ts +4 -2
  10. package/dist/core/transport/index.d.ts +4 -1
  11. package/dist/core/transport/pipe-stream.d.ts +3 -2
  12. package/dist/core/transport/stream-router.d.ts +11 -1
  13. package/dist/core/transport/tree.d.ts +171 -0
  14. package/dist/core/transport/turn-manager.d.ts +4 -1
  15. package/dist/core/transport/types.d.ts +270 -119
  16. package/dist/core/transport/view.d.ts +166 -0
  17. package/dist/errors.d.ts +19 -2
  18. package/dist/index.d.ts +3 -1
  19. package/dist/react/ably-ai-transport-react.js +1019 -486
  20. package/dist/react/ably-ai-transport-react.js.map +1 -1
  21. package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
  22. package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
  23. package/dist/react/contexts/transport-context.d.ts +31 -0
  24. package/dist/react/contexts/transport-provider.d.ts +49 -0
  25. package/dist/react/create-transport-hooks.d.ts +124 -0
  26. package/dist/react/index.d.ts +14 -8
  27. package/dist/react/use-ably-messages.d.ts +14 -8
  28. package/dist/react/use-active-turns.d.ts +7 -3
  29. package/dist/react/use-client-transport.d.ts +78 -5
  30. package/dist/react/use-create-view.d.ts +22 -0
  31. package/dist/react/use-tree.d.ts +20 -0
  32. package/dist/react/use-view.d.ts +79 -0
  33. package/dist/vercel/ably-ai-transport-vercel.js +1478 -842
  34. package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
  35. package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
  36. package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
  37. package/dist/vercel/codec/tool-transitions.d.ts +50 -0
  38. package/dist/vercel/index.d.ts +3 -0
  39. package/dist/vercel/react/ably-ai-transport-vercel-react.js +9099 -852
  40. package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
  41. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +45 -1
  42. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
  43. package/dist/vercel/react/contexts/chat-transport-context.d.ts +32 -0
  44. package/dist/vercel/react/contexts/chat-transport-provider.d.ts +84 -0
  45. package/dist/vercel/react/index.d.ts +5 -0
  46. package/dist/vercel/react/use-chat-transport.d.ts +61 -20
  47. package/dist/vercel/react/use-message-sync.d.ts +41 -9
  48. package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +30 -0
  49. package/dist/vercel/tool-approvals.d.ts +124 -0
  50. package/dist/vercel/tool-events.d.ts +26 -0
  51. package/dist/vercel/transport/chat-transport.d.ts +33 -11
  52. package/dist/vercel/transport/index.d.ts +5 -2
  53. package/package.json +23 -17
  54. package/src/constants.ts +6 -0
  55. package/src/core/codec/encoder.ts +10 -1
  56. package/src/core/codec/types.ts +19 -3
  57. package/src/core/transport/client-transport.ts +382 -364
  58. package/src/core/transport/decode-history.ts +229 -81
  59. package/src/core/transport/headers.ts +6 -2
  60. package/src/core/transport/index.ts +13 -5
  61. package/src/core/transport/pipe-stream.ts +8 -5
  62. package/src/core/transport/server-transport.ts +212 -58
  63. package/src/core/transport/stream-router.ts +21 -3
  64. package/src/core/transport/{conversation-tree.ts → tree.ts} +192 -77
  65. package/src/core/transport/turn-manager.ts +28 -10
  66. package/src/core/transport/types.ts +318 -139
  67. package/src/core/transport/view.ts +840 -0
  68. package/src/errors.ts +21 -1
  69. package/src/index.ts +10 -5
  70. package/src/react/contexts/transport-context.ts +37 -0
  71. package/src/react/contexts/transport-provider.tsx +164 -0
  72. package/src/react/create-transport-hooks.ts +144 -0
  73. package/src/react/index.ts +15 -8
  74. package/src/react/use-ably-messages.ts +34 -16
  75. package/src/react/use-active-turns.ts +28 -17
  76. package/src/react/use-client-transport.ts +184 -24
  77. package/src/react/use-create-view.ts +68 -0
  78. package/src/react/use-tree.ts +53 -0
  79. package/src/react/use-view.ts +233 -0
  80. package/src/react/vite.config.ts +4 -1
  81. package/src/vercel/codec/accumulator.ts +64 -79
  82. package/src/vercel/codec/decoder.ts +11 -8
  83. package/src/vercel/codec/encoder.ts +68 -54
  84. package/src/vercel/codec/index.ts +0 -2
  85. package/src/vercel/codec/tool-transitions.ts +122 -0
  86. package/src/vercel/index.ts +17 -0
  87. package/src/vercel/react/contexts/chat-transport-context.ts +40 -0
  88. package/src/vercel/react/contexts/chat-transport-provider.tsx +122 -0
  89. package/src/vercel/react/index.ts +14 -0
  90. package/src/vercel/react/use-chat-transport.ts +164 -42
  91. package/src/vercel/react/use-message-sync.ts +77 -19
  92. package/src/vercel/react/use-staged-add-tool-approval-response.ts +87 -0
  93. package/src/vercel/react/vite.config.ts +4 -2
  94. package/src/vercel/tool-approvals.ts +380 -0
  95. package/src/vercel/tool-events.ts +53 -0
  96. package/src/vercel/transport/chat-transport.ts +225 -79
  97. package/src/vercel/transport/index.ts +14 -3
  98. package/dist/core/transport/conversation-tree.d.ts +0 -9
  99. package/dist/react/use-conversation-tree.d.ts +0 -20
  100. package/dist/react/use-edit.d.ts +0 -7
  101. package/dist/react/use-history.d.ts +0 -19
  102. package/dist/react/use-messages.d.ts +0 -7
  103. package/dist/react/use-regenerate.d.ts +0 -7
  104. package/dist/react/use-send.d.ts +0 -7
  105. package/src/react/use-conversation-tree.ts +0 -71
  106. package/src/react/use-edit.ts +0 -24
  107. package/src/react/use-history.ts +0 -111
  108. package/src/react/use-messages.ts +0 -32
  109. package/src/react/use-regenerate.ts +0 -24
  110. package/src/react/use-send.ts +0 -25
package/README.md CHANGED
@@ -89,14 +89,14 @@ import type { UIMessage } from 'ai';
89
89
  import { anthropic } from '@ai-sdk/anthropic';
90
90
  import Ably from 'ably';
91
91
  import { createServerTransport } from '@ably/ai-transport/vercel';
92
- import type { MessageWithHeaders } from '@ably/ai-transport';
92
+ import type { TreeNode } from '@ably/ai-transport';
93
93
 
94
94
  interface ChatRequestBody {
95
95
  turnId: string;
96
96
  clientId: string;
97
- messages: MessageWithHeaders<UIMessage>[];
98
- history?: MessageWithHeaders<UIMessage>[];
99
- id: string;
97
+ messages: TreeNode<UIMessage>[];
98
+ history?: TreeNode<UIMessage>[];
99
+ chatId: string;
100
100
  forkOf?: string;
101
101
  parent?: string | null;
102
102
  }
@@ -104,9 +104,9 @@ interface ChatRequestBody {
104
104
  const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY });
105
105
 
106
106
  export async function POST(req: Request) {
107
- const { messages, history, id, turnId, clientId, forkOf, parent } = (await req.json()) as ChatRequestBody;
107
+ const { messages, history, chatId, turnId, clientId, forkOf, parent } = (await req.json()) as ChatRequestBody;
108
108
 
109
- const channel = ably.channels.get(id);
109
+ const channel = ably.channels.get(chatId);
110
110
  const transport = createServerTransport({ channel });
111
111
  const turn = transport.newTurn({ turnId, clientId, parent, forkOf });
112
112
 
@@ -120,7 +120,7 @@ export async function POST(req: Request) {
120
120
  const newMsgs = messages.map((m) => m.message);
121
121
 
122
122
  const result = streamText({
123
- model: anthropic('claude-sonnet-4-20250514'),
123
+ model: anthropic('claude-sonnet-4-6'),
124
124
  system: 'You are a helpful assistant.',
125
125
  messages: await convertToModelMessages([...historyMsgs, ...newMsgs]),
126
126
  abortSignal: turn.abortSignal,
@@ -143,26 +143,26 @@ export async function POST(req: Request) {
143
143
  'use client';
144
144
 
145
145
  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';
146
+ import {
147
+ ChatTransportProvider,
148
+ useChatTransport,
149
+ useMessageSync,
150
+ useActiveTurns,
151
+ useView,
152
+ } from '@ably/ai-transport/vercel/react';
150
153
 
151
- function Chat({ chatId, clientId }: { chatId: string; clientId?: string }) {
152
- const { channel } = useChannel({ channelName: chatId });
153
-
154
- const transport = useClientTransport({ channel, codec: UIMessageCodec, clientId });
155
- const chatTransport = useChatTransport(transport);
154
+ function ChatInner({ chatId }: { chatId: string }) {
155
+ const { chatTransport } = useChatTransport();
156
156
 
157
157
  const { messages, setMessages, sendMessage, stop } = useChat({
158
158
  id: chatId,
159
159
  transport: chatTransport,
160
160
  });
161
161
 
162
- useMessageSync(transport, setMessages);
162
+ useMessageSync({ setMessages });
163
163
 
164
- const activeTurns = useActiveTurns(transport);
165
- const history = useHistory(transport, { limit: 30 });
164
+ const activeTurns = useActiveTurns();
165
+ useView({ limit: 30 });
166
166
 
167
167
  return (
168
168
  <div>
@@ -189,6 +189,17 @@ function Chat({ chatId, clientId }: { chatId: string; clientId?: string }) {
189
189
  </div>
190
190
  );
191
191
  }
192
+
193
+ function Chat({ chatId, clientId }: { chatId: string; clientId?: string }) {
194
+ return (
195
+ <ChatTransportProvider
196
+ channelName={chatId}
197
+ clientId={clientId}
198
+ >
199
+ <ChatInner chatId={chatId} />
200
+ </ChatTransportProvider>
201
+ );
202
+ }
192
203
  ```
193
204
 
194
205
  ### Authentication
@@ -271,8 +282,8 @@ transport.close();
271
282
 
272
283
  ## Package exports
273
284
 
274
- | Export path | Purpose | Peer dependencies |
275
- | ----------------------------------------- | ------------------------------------------- | --------------------- |
285
+ | Export path | Purpose | Peer dependencies |
286
+ | --------------------------------- | ------------------------------------------- | --------------------- |
276
287
  | `@ably/ai-transport` | Core transport, codec interfaces, utilities | `ably` |
277
288
  | `@ably/ai-transport/react` | React hooks for any codec | `ably`, `react` |
278
289
  | `@ably/ai-transport/vercel` | Vercel AI SDK codec, transport factories | `ably`, `ai` |
@@ -280,19 +291,15 @@ transport.close();
280
291
 
281
292
  ### React hooks
282
293
 
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` |
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` |
296
303
 
297
304
  ---
298
305
 
@@ -303,7 +310,7 @@ transport.close();
303
310
  Two mechanisms cover different failure modes:
304
311
 
305
312
  - **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.
313
+ - **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
314
 
308
315
  ### Cancellation
309
316
 
@@ -316,7 +323,7 @@ await transport.cancel({ turnId: 'turn-abc' });
316
323
 
317
324
  // Server: the turn's abortSignal fires automatically
318
325
  const result = streamText({
319
- model: anthropic('claude-sonnet-4-20250514'),
326
+ model: anthropic('claude-sonnet-4-6'),
320
327
  messages,
321
328
  abortSignal: turn.abortSignal, // Aborted when client cancels
322
329
  });
@@ -334,7 +341,7 @@ const turn = await transport.regenerate(assistantMessageId);
334
341
  const turn = await transport.edit(userMessageId, [newMessage]);
335
342
 
336
343
  // Navigate branches
337
- const tree = transport.getTree();
344
+ const tree = transport.tree;
338
345
  const siblings = tree.getSiblings(messageId);
339
346
  tree.select(messageId, 1); // Switch to second branch
340
347
  ```
@@ -344,22 +351,22 @@ tree.select(messageId, 1); // Switch to second branch
344
351
  Load previous conversation state when a client joins or returns to a session.
345
352
 
346
353
  ```typescript
347
- const page = await transport.history({ limit: 50 });
348
- console.log(page.items); // Decoded messages
354
+ const view = transport.view;
355
+ await view.loadOlder(50);
356
+ // view.flattenNodes() returns the messages loaded so far
349
357
 
350
- if (page.hasNext()) {
351
- const older = await page.next();
352
- }
358
+ // Load more older messages
359
+ await view.loadOlder(50);
353
360
  ```
354
361
 
355
362
  ### Events
356
363
 
357
364
  ```typescript
358
- transport.on('message', () => {
359
- console.log(transport.getMessages());
365
+ transport.view.on('update', () => {
366
+ console.log(transport.view.flattenNodes().map((n) => n.message));
360
367
  });
361
368
 
362
- transport.on('turn', (event) => {
369
+ transport.tree.on('turn', (event) => {
363
370
  console.log(event.turnId, event.type); // 'x-ably-turn-start' | 'x-ably-turn-end'
364
371
  });
365
372
 
@@ -377,7 +384,7 @@ Detailed documentation lives in the [`docs/`](./docs/) directory:
377
384
  - **[Concepts](./docs/concepts/)** - [Transport architecture](./docs/concepts/transport.md), [Turns](./docs/concepts/turns.md)
378
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)
379
386
  - **[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)
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)
381
388
  - **[Reference](./docs/reference/)** - [React hooks](./docs/reference/react-hooks.md), [Error codes](./docs/reference/error-codes.md)
382
389
  - **[Internals](./docs/internals/)** - Architecture details for contributors
383
390
 
@@ -410,7 +417,7 @@ npm run precommit # format:check + lint + typecheck
410
417
  src/
411
418
  ├── core/ # Generic transport and codec (no framework deps)
412
419
  │ ├── codec/ # Codec interfaces and core encoder/decoder
413
- │ └── transport/ # ClientTransport, ServerTransport, ConversationTree
420
+ │ └── transport/ # ClientTransport, ServerTransport, Tree
414
421
  ├── react/ # React hooks for any codec
415
422
  ├── vercel/ # Vercel AI SDK codec and transport adapters
416
423
  │ ├── codec/ # UIMessageCodec