@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
@@ -0,0 +1,33 @@
1
+ import { ClientSession } from '../../../core/transport/types.js';
2
+ import { VercelInput, VercelOutput, VercelProjection } from '../../codec/index.js';
3
+ import { ChatTransport } from '../../transport/chat-transport.js';
4
+ import type * as Ably from 'ably';
5
+ import type * as AI from 'ai';
6
+ /**
7
+ * A single entry in the chat transport registry, holding both the
8
+ * underlying {@link ClientSession} and the {@link ChatTransport} wrapping it.
9
+ */
10
+ export interface ChatTransportSlot {
11
+ /** The underlying client session used to create the chat transport. */
12
+ readonly session: ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;
13
+ /** Construction error from the underlying {@link ClientSession}, or `undefined` on success. */
14
+ readonly sessionError?: Ably.ErrorInfo | undefined;
15
+ /** The chat transport adapter for use with Vercel's useChat hook. */
16
+ readonly chatTransport: ChatTransport;
17
+ }
18
+ /**
19
+ * The shape of the single {@link ChatTransportContext} value.
20
+ * Combines the nearest slot with the full registry in one context object.
21
+ */
22
+ export interface ChatTransportContextValue {
23
+ /** The slot from the nearest {@link ChatTransportProvider} in the tree. */
24
+ readonly nearest: ChatTransportSlot | undefined;
25
+ /** All registered slots, keyed by channelName. */
26
+ readonly providers: Readonly<Record<string, ChatTransportSlot>>;
27
+ }
28
+ /**
29
+ * Context that carries both the nearest {@link ChatTransportSlot} and the full registry of
30
+ * registered slots keyed by channelName. Populated by {@link ChatTransportProvider};
31
+ * read by {@link useChatTransport}.
32
+ */
33
+ export declare const ChatTransportContext: import('react').Context<ChatTransportContextValue>;
@@ -0,0 +1,96 @@
1
+ import { PropsWithChildren, ReactNode } from 'react';
2
+ import { ClientSessionProviderProps } from '../../../react/index.js';
3
+ import { VercelInput, VercelOutput, VercelProjection } from '../../codec/index.js';
4
+ import { ChatTransportOptions } from '../../transport/index.js';
5
+ /**
6
+ * ChatTransportProvider: creates a ChatTransport from a ClientSession and makes it
7
+ * available to descendants via ChatTransportContext.
8
+ *
9
+ * Wraps children with ClientSessionProvider (using UIMessageCodec). The
10
+ * surrounding `<AblyProvider>` supplies the Realtime client; the session
11
+ * resolves the channel from `channelName` itself. An inner component reads
12
+ * the ClientSession via useClientSession() and creates the ChatTransport
13
+ * via useMemo, keyed on the session and transport options.
14
+ *
15
+ * The ChatTransport is NOT closed on unmount — the underlying ClientSession
16
+ * lifecycle is managed by the wrapping ClientSessionProvider. Auto-closing would break
17
+ * React Strict Mode, and ChatTransport.close() delegates to ClientSession.close()
18
+ * which ClientSessionProvider already calls.
19
+ *
20
+ * Multiple ChatTransportProviders can be nested using distinct channelNames.
21
+ * Each provider merges its session into the parent registry, so descendants
22
+ * can access all registered sessions via useChatTransport({ channelName }).
23
+ */
24
+ import type * as AI from 'ai';
25
+ export declare const ClientSessionProvider: import('react').ComponentType<ClientSessionProviderProps<VercelInput, VercelOutput, VercelProjection, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>>>, useAblyMessages: (props?: {
26
+ session?: import('../../../react/index.js').ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>> | undefined;
27
+ skip?: boolean;
28
+ } | undefined) => import('ably').InboundMessage[], useClientSession: (props?: {
29
+ channelName?: string;
30
+ skip?: boolean;
31
+ onError?: (error: import('ably').ErrorInfo) => void;
32
+ }) => import('../../../react/use-client-session.js').ClientSessionHandle<VercelInput, VercelOutput, VercelProjection, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>>, useCreateView: (props?: {
33
+ session?: import('../../../react/index.js').ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>> | null | undefined;
34
+ limit?: number;
35
+ skip?: boolean;
36
+ } | undefined) => import('../../../react/use-view.js').ViewHandle<VercelInput, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>>, useTree: (props?: {
37
+ session?: import('../../../react/index.js').ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>> | undefined;
38
+ } | undefined) => import('../../../react/use-tree.js').TreeHandle<VercelProjection>, useView: (props?: {
39
+ session?: import('../../../react/index.js').ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>> | null | undefined;
40
+ view?: import('../../../index.js').View<VercelInput, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>> | null | undefined;
41
+ limit?: number;
42
+ skip?: boolean;
43
+ } | undefined) => import('../../../react/use-view.js').ViewHandle<VercelInput, AI.UIMessage<unknown, AI.UIDataTypes, AI.UITools>>;
44
+ type CoreClientSessionProviderProps = Omit<ClientSessionProviderProps<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>, 'codec'>;
45
+ /**
46
+ * Props for {@link ChatTransportProvider}.
47
+ *
48
+ * All {@link ClientSessionProviderProps} for Vercel types except `codec` (baked as UIMessageCodec),
49
+ * plus the transport-owned invocation POST options (`api` / `credentials` / `fetch`) and
50
+ * `chatOptions` for customizing chat request construction.
51
+ */
52
+ export interface ChatTransportProviderProps extends CoreClientSessionProviderProps {
53
+ /** Endpoint the chat transport POSTs the invocation to, to wake the agent. Default `/api/chat`. */
54
+ api?: string;
55
+ /** Fetch credentials mode for the invocation POST. */
56
+ credentials?: RequestCredentials;
57
+ /** Custom fetch implementation for the invocation POST. Defaults to `globalThis.fetch`. */
58
+ fetch?: typeof globalThis.fetch;
59
+ /**
60
+ * Optional transport options for customizing chat request construction (e.g. the `prepareSendMessagesRequest` hook).
61
+ * Must be stable across renders — wrap in `useMemo` or define outside the component.
62
+ * A new object reference triggers ChatTransport recreation.
63
+ * If this object also sets `api`/`credentials`/`fetch`, the dedicated top-level props of the same name take precedence.
64
+ */
65
+ chatOptions?: ChatTransportOptions;
66
+ }
67
+ /**
68
+ * Provide a {@link ChatTransport} and its underlying {@link ClientSession} to descendant components.
69
+ *
70
+ * Wraps children with `ClientSessionProvider` using `channelName` (the Realtime
71
+ * client is read from the surrounding `<AblyProvider>`), creates a
72
+ * {@link ClientSession} with UIMessageCodec, wraps it in a {@link ChatTransport},
73
+ * and registers the full slot in `ChatTransportContext` under `channelName`. Descendants call
74
+ * {@link useChatTransport} with the same `channelName` to access both.
75
+ *
76
+ * `useClientSession` is also available inside this provider's subtree.
77
+ *
78
+ * ```tsx
79
+ * <ChatTransportProvider channelName="ai:demo">
80
+ * <Chat />
81
+ * </ChatTransportProvider>
82
+ *
83
+ * // Inside Chat:
84
+ * const { chatTransport, session } = useChatTransport();
85
+ * const { session } = useClientSession(); // also available
86
+ * ```
87
+ * @param props - Provider configuration including `channelName`, the invocation POST options (`api` / `credentials` / `fetch`), optional `chatOptions`, and all other session options.
88
+ * @param props.api - Endpoint the chat transport POSTs the invocation to. Default `/api/chat`.
89
+ * @param props.credentials - Fetch credentials mode for the invocation POST.
90
+ * @param props.fetch - Custom fetch implementation for the invocation POST.
91
+ * @param props.chatOptions - Optional hooks for customizing chat request construction. Must be stable (memoized) — a new reference recreates the ChatTransport.
92
+ * @param props.children - Descendant components that consume the chat transport via hooks.
93
+ * @returns A React element wrapping children with ClientSessionContext and ChatTransportContext.
94
+ */
95
+ export declare const ChatTransportProvider: ({ api, credentials, fetch, chatOptions, children, ...sessionProps }: ChatTransportProviderProps & PropsWithChildren) => ReactNode;
96
+ export {};
@@ -1,3 +1,7 @@
1
1
  export type { ChatTransport } from '../transport/chat-transport.js';
2
+ export type { ChatTransportProviderProps } from './contexts/chat-transport-provider.js';
3
+ export { ChatTransportProvider, ClientSessionProvider, useAblyMessages, useClientSession, useCreateView, useTree, useView, } from './contexts/chat-transport-provider.js';
4
+ export type { ChatTransportHandle, UseChatTransportOptions } from './use-chat-transport.js';
2
5
  export { useChatTransport } from './use-chat-transport.js';
6
+ export type { UseMessageSyncOptions } from './use-message-sync.js';
3
7
  export { useMessageSync } from './use-message-sync.js';
@@ -1,29 +1,74 @@
1
- import { ClientTransport } from '../../core/transport/types.js';
2
- import { ChatTransport, ChatTransportOptions } from '../transport/chat-transport.js';
3
- import { VercelClientTransportOptions } from '../transport/index.js';
1
+ import { ClientSession } from '../../core/transport/types.js';
2
+ import { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';
3
+ import { ChatTransport } from '../transport/index.js';
4
4
  /**
5
- * useChatTransport: wraps a core ClientTransport into the ChatTransport
6
- * shape that Vercel's useChat expects.
5
+ * useChatTransport: reads a ChatTransport and its underlying ClientSession from
6
+ * the nearest ChatTransportProvider.
7
7
  *
8
- * Accepts either an existing ClientTransport or options to create one:
9
- * - From an existing ClientTransport wraps it directly
10
- * - From options creates a ClientTransport with UIMessageCodec and wraps it
8
+ * The chat transport is created by ChatTransportProvider, which wraps the subtree
9
+ * with ClientSessionProvider. The Ably Realtime client is read from the
10
+ * surrounding `<AblyProvider>`. This hook is a thin context reader it does
11
+ * not create or manage any session/transport state.
11
12
  *
12
- * Both forms accept an optional second argument for ChatTransportOptions
13
- * (e.g. prepareSendMessagesRequest for the persistence pattern).
14
- *
15
- * The hook does NOT auto-close the transport on unmount. Channel lifecycle is
16
- * managed by the Ably provider (useChannel). Auto-closing would break React
17
- * Strict Mode. Call chatTransport.close() explicitly if needed.
13
+ * Pass `channelName` to look up a specific provider by name. Omit to use the nearest
14
+ * provider in the tree. Pass `skip: true` to defer (e.g. when auth is not yet resolved)
15
+ * — returns stubs whose properties throw with a descriptive error.
18
16
  */
17
+ import * as Ably from 'ably';
19
18
  import type * as AI from 'ai';
19
+ /** Options for {@link useChatTransport}. */
20
+ export interface UseChatTransportOptions {
21
+ /** Channel name to look up; omit to use the nearest {@link ChatTransportProvider}. */
22
+ channelName?: string;
23
+ /** When `true`, return stubs that throw on any access. */
24
+ skip?: boolean;
25
+ }
26
+ /**
27
+ * The value returned by {@link useChatTransport}.
28
+ * Provides both the underlying {@link ClientSession} and the {@link ChatTransport}
29
+ * adapter for Vercel's useChat hook.
30
+ */
31
+ export interface ChatTransportHandle {
32
+ /**
33
+ * The underlying client session, also available via {@link useClientSession}.
34
+ * A throwing stub when `skip` is `true`, when no matching {@link ClientSessionProvider}
35
+ * was found in the tree, or when session construction failed. Check `sessionError` before use.
36
+ */
37
+ session: ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;
38
+ /**
39
+ * The chat transport adapter for use with Vercel's `useChat` hook.
40
+ *
41
+ * A throwing stub when `skip` is `true` or when no matching
42
+ * {@link ChatTransportProvider} was found in the tree. When a provider is found
43
+ * but the underlying {@link ClientSession} failed to construct, this is the real
44
+ * transport and `sessionError` is set instead. Check `chatTransportError` and
45
+ * `sessionError` before use.
46
+ */
47
+ chatTransport: ChatTransport;
48
+ /**
49
+ * Set when no matching {@link ClientSessionProvider} was found, when session
50
+ * construction failed, and `skip` is `false`.
51
+ * `undefined` when the session resolved successfully or when `skip` is `true`.
52
+ */
53
+ sessionError?: Ably.ErrorInfo | undefined;
54
+ /**
55
+ * Set only when no matching {@link ChatTransportProvider} was found and `skip` is
56
+ * `false`.
57
+ * `undefined` when the chat transport resolved successfully (even if session
58
+ * construction failed — see `sessionError`) or when `skip` is `true`.
59
+ */
60
+ chatTransportError?: Ably.ErrorInfo | undefined;
61
+ }
20
62
  /**
21
- * Create and memoize a {@link ChatTransport} for Vercel's useChat hook.
63
+ * Access a {@link ChatTransport} and {@link ClientSession} from the nearest {@link ChatTransportProvider}.
22
64
  *
23
- * Pass an existing `ClientTransport` to wrap it, or pass
24
- * `VercelClientTransportOptions` to create one internally with UIMessageCodec.
25
- * @param transportOrOptions - An existing ClientTransport, or options to create one.
26
- * @param chatOptions - Optional hooks for customizing request construction.
27
- * @returns A {@link ChatTransport} compatible with Vercel's useChat hook.
65
+ * When `channelName` is omitted, the innermost `ChatTransportProvider` in the tree is used.
66
+ * When `skip` is `true`, returns stubs whose every property and method throws
67
+ * an {@link Ably.ErrorInfo} safe to hold in state before conditions are ready.
68
+ * When no provider is found, returns stubs with `chatTransportError` set instead of throwing.
69
+ * @param props - Options for selecting the chat transport.
70
+ * @param props.channelName - The channel name passed to the enclosing `ChatTransportProvider`. Omit to use the nearest.
71
+ * @param props.skip - When `true`, return stubs that throw on any access instead of reading from context.
72
+ * @returns The `ChatTransportHandle` containing both the chat transport adapter and the underlying client session.
28
73
  */
29
- export declare const useChatTransport: (transportOrOptions: ClientTransport<AI.UIMessageChunk, AI.UIMessage> | VercelClientTransportOptions, chatOptions?: ChatTransportOptions) => ChatTransport;
74
+ export declare const useChatTransport: ({ channelName, skip }?: UseChatTransportOptions) => ChatTransportHandle;
@@ -1,19 +1,38 @@
1
- import { ClientTransport } from '../../core/transport/types.js';
2
1
  /**
3
- * useMessageSync: wires transport message lifecycle events into useChat's setMessages.
2
+ * useMessageSync: wire view updates into useChat's setMessages.
4
3
  *
5
- * Subscribes to the transport's 'message' event and replaces messages state
6
- * with the transport's authoritative message list. Events fire immediately
7
- * on every store update (including during active streaming), so this hook
8
- * keeps React state in sync in real time.
4
+ * During active own-run streams, setMessages is gated to avoid an
5
+ * ID-mismatch in useChat's write(). When the stream ends, the gate
6
+ * opens and the view is synced into useChat's overlay.
9
7
  *
10
- * Returns the unsubscribe function in the useEffect cleanup so handlers
11
- * are removed on unmount or when dependencies change.
8
+ * The sync is a per-message merge, not a replace: when the overlay has
9
+ * resolved a client-side tool locally (via addToolResult) but the
10
+ * tree's echo hasn't landed yet, the overlay's resolution wins.
11
+ * Without that, the gate-open sync would race the AI SDK's post-stream
12
+ * sendAutomaticallyWhen check and could clobber the resolution before
13
+ * the continuation publishes.
12
14
  */
13
15
  import type * as AI from 'ai';
16
+ /** Options for {@link useMessageSync}. */
17
+ export interface UseMessageSyncOptions {
18
+ /**
19
+ * The `setMessages` updater function from `useChat()`. Called with an
20
+ * updater that returns the next overlay.
21
+ */
22
+ setMessages: (updater: (prev: AI.UIMessage[]) => AI.UIMessage[]) => void;
23
+ /**
24
+ * Channel name of the {@link ChatTransportProvider} to observe.
25
+ * Omit to use the nearest provider.
26
+ */
27
+ channelName?: string;
28
+ /** When `true`, skip all subscriptions. */
29
+ skip?: boolean;
30
+ }
14
31
  /**
15
- * Wire transport message updates into useChat's `setMessages` updater.
16
- * @param transport - The client transport to observe, or null/undefined if not yet available.
17
- * @param setMessages - The `setMessages` updater function from useChat.
32
+ * Subscribe to view updates and sync them into `useChat()`'s overlay.
33
+ * @param options - Hook options.
34
+ * @param options.setMessages - The `setMessages` function from `useChat()`.
35
+ * @param options.channelName - Channel name of the provider to observe; defaults to the nearest.
36
+ * @param options.skip - When `true`, skip all subscriptions.
18
37
  */
19
- export declare const useMessageSync: (transport: ClientTransport<unknown, AI.UIMessage> | null | undefined, setMessages: (updater: (prev: AI.UIMessage[]) => AI.UIMessage[]) => void) => void;
38
+ export declare const useMessageSync: ({ setMessages, channelName, skip }: UseMessageSyncOptions) => void;
@@ -0,0 +1,29 @@
1
+ import { RunEndReason, StreamResult } from '../core/transport/types.js';
2
+ import type * as AI from 'ai';
3
+ /**
4
+ * Derive the outcome for a Vercel `streamText` response that was piped through
5
+ * `Run.pipe`: either a terminal {@link RunEndReason} the caller passes to
6
+ * `Run.end`, or the sentinel `'suspend'` telling the caller to call
7
+ * `Run.suspend` instead. Preserves transport-level outcomes (`'cancelled'`,
8
+ * `'error'`) from the pipe result; when the pipe completed naturally, awaits
9
+ * Vercel's `finishReason` and returns `'suspend'` for `'tool-calls'` (the LLM
10
+ * requested tools the SDK did not auto-execute, so the run should suspend
11
+ * rather than end), or `'complete'` otherwise.
12
+ *
13
+ * Tolerates `finishReason` rejection. Vercel AI SDK v6 rejects
14
+ * `streamText().finishReason` with the abort signal's reason when the stream
15
+ * is aborted before any step completes, and rejects with
16
+ * `NoOutputGeneratedError` when the model produced nothing at all. Without
17
+ * this guard the rejection would bubble out of the route handler's `after()`
18
+ * block, skip the developer's `Run.end(...)` call, and leave the run with no
19
+ * `ai-run-end` event on the channel — so observers' UIs stay stuck on
20
+ * `streaming` indefinitely.
21
+ *
22
+ * Saves callers from interpreting Vercel domain semantics inline at the end
23
+ * of every route handler.
24
+ * @param pipeResult - The result returned by `Run.pipe`.
25
+ * @param finishReason - The `finishReason` promise from a `streamText` result.
26
+ * @returns `'suspend'` when the run should suspend awaiting tool input, or the
27
+ * {@link RunEndReason} to pass to `Run.end` otherwise.
28
+ */
29
+ export declare const vercelRunOutcome: (pipeResult: StreamResult, finishReason: PromiseLike<AI.FinishReason>) => Promise<RunEndReason | "suspend">;
@@ -1,19 +1,33 @@
1
- import { ClientTransport, CloseOptions } from '../../core/transport/types.js';
1
+ import { ClientSession } from '../../core/transport/types.js';
2
+ import { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';
2
3
  /**
3
- * Vercel chat transport: wraps a core ClientTransport to satisfy the
4
+ * Vercel chat transport: wraps a core ClientSession to satisfy the
4
5
  * ChatTransport interface that useChat expects.
5
6
  *
6
- * This is a thin adapter — the real logic lives in the core transport.
7
+ * This is a thin adapter — the real logic lives in the core client session.
7
8
  * The chat transport maps Vercel's sendMessages/reconnectToStream contract
8
- * to the core transport's send/cancel methods.
9
+ * to the core session's send/cancel methods.
9
10
  *
10
11
  * useChat manages message state before calling sendMessages:
11
- * - submit-message: appends the new user message, passes the full array
12
+ * - submit-message (new): appends the new user message, passes the full array
13
+ * - submit-message (edit): truncates after the edited message, replaces it,
14
+ * passes the truncated array with messageId set
12
15
  * - regenerate-message: truncates after the target, passes the truncated array
13
16
  *
14
- * The adapter uses `trigger` to determine the history/messages split:
15
- * - submit-message: last message is new (publish to channel), rest is history
17
+ * The adapter uses `(trigger, last-message role)` to determine the
18
+ * history/messages split:
19
+ * - submit-message + last message is a user message: that last message is new
20
+ * (publish to channel), rest is history. A new submit and an edit both take
21
+ * this path — an edit just carries a messageId.
22
+ * - submit-message + last message is an assistant already in the tree
23
+ * (continuation): no new messages, entire array is history
16
24
  * - regenerate-message: no new messages, entire array is history
25
+ *
26
+ * For an edit (submit-message with messageId) and for forking off an
27
+ * unresolved tool call, the adapter computes fork metadata (forkOf/parent)
28
+ * from the conversation tree so the server can place the response on the
29
+ * correct branch. Regeneration fork metadata is NOT computed here —
30
+ * `View.regenerate` derives forkOf/parent from the tree itself.
17
31
  */
18
32
  import type * as AI from 'ai';
19
33
  /**
@@ -22,33 +36,46 @@ import type * as AI from 'ai';
22
36
  */
23
37
  export interface SendMessagesRequestContext {
24
38
  /** Chat session ID (from useChat's id). */
25
- id?: string;
39
+ chatId?: string;
26
40
  /** What triggered the request: user sent a message, or requested regeneration. */
27
41
  trigger: 'submit-message' | 'regenerate-message';
28
42
  /**
29
- * The message ID for regeneration requests. Identifies which assistant
30
- * message to regenerate. Undefined for submit-message.
43
+ * The message ID for edit or regeneration requests. For regeneration,
44
+ * identifies the assistant message to regenerate. For edits (submit-message
45
+ * with messageId), identifies the user message being replaced. Undefined
46
+ * when submitting a new message.
31
47
  */
32
48
  messageId?: string;
33
49
  /** Previous messages in the conversation (context for the LLM). */
34
50
  history: AI.UIMessage[];
35
- /** The new message(s) being sent (to publish to the channel). Empty for regeneration. */
51
+ /** The new message(s) being sent (to publish to the channel). Empty for regeneration and for continuations (an auto-submit where the last message is an already-tracked assistant). */
36
52
  messages: AI.UIMessage[];
37
- /** The msg-id of the message being forked (regenerated or edited). */
53
+ /** The codec-message-id of the message being forked the edited user message, or the preceding assistant when forking off an unresolved tool call. Undefined for regeneration (View.regenerate derives it) and fresh sends. */
38
54
  forkOf?: string;
39
- /** The msg-id of the predecessor in the conversation thread. */
40
- parent?: string | null;
55
+ /** The codec-message-id of the predecessor in the conversation thread. */
56
+ parent?: string;
41
57
  }
58
+ /** Default agent endpoint the transport POSTs invocations to — mirrors Vercel's DefaultChatTransport. */
59
+ export declare const DEFAULT_VERCEL_API = "/api/chat";
42
60
  /** Options for customizing the ChatTransport behavior. */
43
61
  export interface ChatTransportOptions {
44
62
  /**
45
- * Customize the POST body before sending. Called by sendMessages()
46
- * with the conversation context. Return the body and headers for
47
- * the HTTP POST.
48
- *
49
- * Default: sends all previous messages as `history` in the body.
63
+ * Endpoint the transport POSTs the invocation pointer to, to wake the
64
+ * agent. Mirrors useChat's request-driven contract. Default `/api/chat`.
65
+ */
66
+ api?: string;
67
+ /** Fetch credentials mode for the invocation POST (e.g. `'include'`). */
68
+ credentials?: RequestCredentials;
69
+ /** Custom fetch implementation for the invocation POST. Defaults to `globalThis.fetch`. */
70
+ fetch?: typeof globalThis.fetch;
71
+ /**
72
+ * Customize the invocation POST before sending. Called by sendMessages()
73
+ * with the conversation context; the returned `body` is merged into the
74
+ * POST body (the run's invocation identifiers always take precedence) and
75
+ * `headers` are added to the request. Use it for auth headers or extra
76
+ * agent metadata.
50
77
  * @param context - The conversation context for the current request.
51
- * @returns The body and headers to use for the HTTP POST.
78
+ * @returns The body and headers to merge into the invocation POST.
52
79
  */
53
80
  prepareSendMessagesRequest?: (context: SendMessagesRequestContext) => {
54
81
  body?: Record<string, unknown>;
@@ -75,7 +102,8 @@ interface ChatRequestOptions {
75
102
  *
76
103
  * Structurally compatible with the AI SDK's internal `ChatTransport<UIMessage>`
77
104
  * interface. Extended with `close()` for releasing the underlying Ably transport
78
- * resources.
105
+ * resources and `streaming` / `onStreamingChange` for coordinating with
106
+ * useMessageSync.
79
107
  */
80
108
  export interface ChatTransport {
81
109
  /** Send messages and return a streaming response of UIMessageChunk events. */
@@ -100,19 +128,32 @@ export interface ChatTransport {
100
128
  chatId: string;
101
129
  } & ChatRequestOptions) => Promise<ReadableStream<AI.UIMessageChunk> | null>;
102
130
  /** Close the underlying transport, releasing all resources. */
103
- close(options?: CloseOptions): Promise<void>;
131
+ close(): Promise<void>;
132
+ /** Whether an own-run stream is currently being consumed by useChat. */
133
+ readonly streaming: boolean;
134
+ /**
135
+ * Subscribe to streaming state changes. The callback fires when the
136
+ * ChatTransport transitions between streaming and idle. Used by
137
+ * useMessageSync to gate setMessages calls during active streams.
138
+ * @param callback - Called with `true` when a stream starts, `false` when it ends.
139
+ * @returns Unsubscribe function.
140
+ */
141
+ onStreamingChange(callback: (streaming: boolean) => void): () => void;
104
142
  }
105
143
  /**
106
- * Create a Vercel ChatTransport from a core ClientTransport.
144
+ * Create a Vercel ChatTransport from a core ClientSession.
145
+ *
146
+ * Exposes a `streaming` flag and `onStreamingChange` callback so that
147
+ * `useMessageSync` can gate `setMessages` calls during active own-run
148
+ * streams, preventing the push/replace ID mismatch in useChat's `write()`.
107
149
  *
108
- * Maps Vercel's useChat contract to the core transport's methods:
109
- * - trigger 'submit-message' transport.send(lastMessage) with history in body
110
- * - trigger 'regenerate-message' transport.send([]) with all messages as history
111
- * - abortSignal transport.cancel({ all: true })
112
- * - reconnectToStream null (observer mode handles in-progress streams)
113
- * @param transport - The core client transport to wrap.
150
+ * Note: concurrent `sendMessage` calls from the same user are a useChat
151
+ * limitation that cannot be fixed from the transport layer. The
152
+ * developer must respect useChat's `status` and only call `sendMessage`
153
+ * when status is `'ready'`.
154
+ * @param session - The core client session to wrap.
114
155
  * @param chatOptions - Optional hooks for customizing request construction.
115
156
  * @returns A {@link ChatTransport} compatible with Vercel's useChat hook.
116
157
  */
117
- export declare const createChatTransport: (transport: ClientTransport<AI.UIMessageChunk, AI.UIMessage>, chatOptions?: ChatTransportOptions) => ChatTransport;
158
+ export declare const createChatTransport: (session: ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>, chatOptions?: ChatTransportOptions) => ChatTransport;
118
159
  export {};
@@ -1,4 +1,5 @@
1
- import { ClientTransport, ClientTransportOptions, ServerTransport, ServerTransportOptions } from '../../core/transport/types.js';
1
+ import { AgentSession, AgentSessionOptions, ClientSession, ClientSessionOptions } from '../../core/transport/types.js';
2
+ import { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';
2
3
  /**
3
4
  * Vercel AI SDK transport wrappers that pre-bind the UIMessageCodec.
4
5
  *
@@ -6,31 +7,37 @@ import { ClientTransport, ClientTransportOptions, ServerTransport, ServerTranspo
6
7
  * explicitly when using the Vercel AI SDK integration.
7
8
  *
8
9
  * ```ts
9
- * import { createClientTransport } from '@ably/ai-transport/vercel';
10
+ * import { createClientSession } from '@ably/ai-transport/vercel';
10
11
  *
11
- * const transport = createClientTransport({ channel });
12
+ * const session = createClientSession({ client, channelName: 'ai:demo' });
13
+ * await session.connect();
12
14
  * ```
13
15
  */
14
16
  export type { ChatTransport, ChatTransportOptions, SendMessagesRequestContext } from './chat-transport.js';
15
- export { createChatTransport } from './chat-transport.js';
17
+ export { createChatTransport, DEFAULT_VERCEL_API } from './chat-transport.js';
16
18
  import type * as AI from 'ai';
17
- /** Options for creating a Vercel client transport. Same as core options but without the codec field. */
18
- export type VercelClientTransportOptions = Omit<ClientTransportOptions<AI.UIMessageChunk, AI.UIMessage>, 'codec'>;
19
- /** Options for creating a Vercel server transport. Same as core options but without the codec field. */
20
- export type VercelServerTransportOptions = Omit<ServerTransportOptions<AI.UIMessageChunk, AI.UIMessage>, 'codec'>;
19
+ /** Core client session options with Vercel AI SDK types pre-applied. */
20
+ type CoreClientOpts = ClientSessionOptions<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;
21
+ /** Options for creating a Vercel client session. Same as core options but without the codec field. */
22
+ export type VercelClientSessionOptions = Omit<CoreClientOpts, 'codec'>;
23
+ /** Options for creating a Vercel agent session. Same as core options but without the codec field. */
24
+ export type VercelAgentSessionOptions = Omit<AgentSessionOptions<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>, 'codec'>;
21
25
  /**
22
- * Create a client-side transport pre-configured with the Vercel AI SDK codec.
26
+ * Create a client-side session pre-configured with the Vercel AI SDK codec.
23
27
  *
24
- * Equivalent to calling the core `createClientTransport` with `codec: UIMessageCodec`.
25
- * @param options - Configuration for the client transport (codec is provided automatically).
26
- * @returns A new {@link ClientTransport} for Vercel AI SDK UIMessage/UIMessageChunk types.
28
+ * Equivalent to calling the core `createClientSession` with `codec: UIMessageCodec`.
29
+ * The core session is a pure Ably-channel transport it never sends HTTP.
30
+ * To wake a serverless agent over HTTP, POST `run.toInvocation().toJSON()`
31
+ * yourself, or use `createChatTransport` (which does it for useChat parity).
32
+ * @param options - Configuration for the client session (codec is provided automatically).
33
+ * @returns A new {@link ClientSession} for Vercel AI SDK UIMessage/UIMessageChunk types.
27
34
  */
28
- export declare const createClientTransport: (options: VercelClientTransportOptions) => ClientTransport<AI.UIMessageChunk, AI.UIMessage>;
35
+ export declare const createClientSession: (options: VercelClientSessionOptions) => ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;
29
36
  /**
30
- * Create a server-side transport pre-configured with the Vercel AI SDK codec.
37
+ * Create an agent (server-side) session pre-configured with the Vercel AI SDK codec.
31
38
  *
32
- * Equivalent to calling the core `createServerTransport` with `codec: UIMessageCodec`.
33
- * @param options - Configuration for the server transport (codec is provided automatically).
34
- * @returns A new {@link ServerTransport} for Vercel AI SDK UIMessage/UIMessageChunk types.
39
+ * Equivalent to calling the core `createAgentSession` with `codec: UIMessageCodec`.
40
+ * @param options - Configuration for the agent session (codec is provided automatically).
41
+ * @returns A new {@link AgentSession} for Vercel AI SDK UIMessage/UIMessageChunk types.
35
42
  */
36
- export declare const createServerTransport: (options: VercelServerTransportOptions) => ServerTransport<AI.UIMessageChunk, AI.UIMessage>;
43
+ export declare const createAgentSession: (options: VercelAgentSessionOptions) => AgentSession<VercelOutput, VercelProjection, AI.UIMessage>;
@@ -0,0 +1,56 @@
1
+ import { ClientSession } from '../../core/transport/types.js';
2
+ import { VercelInput, VercelOutput, VercelProjection } from '../codec/index.js';
3
+ /**
4
+ * Vercel-owned per-run output stream.
5
+ *
6
+ * Builds the `ReadableStream<UIMessageChunk>` that `useChat` consumes by
7
+ * subscribing to the session Tree's `output` and `run` events for a single
8
+ * run. Streaming is a useChat-integration concern, so it lives in the Vercel
9
+ * layer rather than the generic core: the core Tree is the fan-out point, and
10
+ * this projects its events into the shape `useChat` expects.
11
+ *
12
+ * Close semantics — the stream the consumer reads ends when:
13
+ * - a **terminal chunk** (`finish` / `error` / `abort`) is folded for the run.
14
+ * This is the signal `useChat`'s `sendAutomaticallyWhen` waits for, and it
15
+ * fires even when the run merely *suspends* for a tool call (a tool-calls
16
+ * `finish` ends the consumer stream while the core run stays alive in the
17
+ * Tree for the continuation); or
18
+ * - the run reaches `run-end`, which is always terminal (safety net for a run
19
+ * that ends without emitting a terminal chunk). A `run-suspend` keeps the
20
+ * core run alive and does not close the consumer stream.
21
+ *
22
+ * It errors when the session emits a non-fatal `error` (e.g. channel
23
+ * continuity loss, or an agent-reported mid-run error), so the consumer's
24
+ * reader rejects rather than hanging.
25
+ */
26
+ import * as Ably from 'ably';
27
+ import type * as AI from 'ai';
28
+ type VercelSession = ClientSession<VercelInput, VercelOutput, VercelProjection, AI.UIMessage>;
29
+ /** A consumer-facing run output stream plus the handles to settle it externally. */
30
+ export interface RunOutputStream {
31
+ /** The stream of decoded outputs for the run, as `useChat` consumes it. */
32
+ stream: ReadableStream<VercelOutput>;
33
+ /** Close the stream now (e.g. on local cancel). Idempotent. */
34
+ close: () => void;
35
+ /** Error the stream now (e.g. on a failed agent-invocation POST). Idempotent. */
36
+ error: (reason: Ably.ErrorInfo) => void;
37
+ }
38
+ /**
39
+ * Create a consumer-facing output stream for a send, sourced from the session
40
+ * Tree's events. See the module docs for close/error semantics. The returned
41
+ * `close`/`error` let the caller settle the stream for conditions the Tree
42
+ * doesn't surface (local cancel, POST failure).
43
+ *
44
+ * Outputs route PURELY by the triggering input's codec-message-id — the key the
45
+ * client owns from send time, before the agent mints the runId. The agent's
46
+ * minted runId is supplied as a promise so the run-end safety-net can still
47
+ * close the stream once it resolves.
48
+ * @param session - The Vercel client session whose Tree to observe.
49
+ * @param runId - The agent-minted runId, resolved when run-start is observed.
50
+ * Used only by the run-end safety-net; routing keys on `inputCodecMessageId`.
51
+ * @param inputCodecMessageId - The triggering input's codec-message-id. An
52
+ * output routes to this stream when it carries this id.
53
+ * @returns The stream and its external settle handles.
54
+ */
55
+ export declare const createRunOutputStream: (session: VercelSession, runId: Promise<string>, inputCodecMessageId: string) => RunOutputStream;
56
+ export {};
@@ -0,0 +1,2 @@
1
+ /** SDK version. Kept in sync with `package.json` by the `/release` workflow. */
2
+ export declare const VERSION = "0.2.0";