@ably/ai-transport 0.0.1

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 (118) hide show
  1. package/LICENSE +176 -0
  2. package/README.md +426 -0
  3. package/dist/ably-ai-transport.js +1388 -0
  4. package/dist/ably-ai-transport.js.map +1 -0
  5. package/dist/ably-ai-transport.umd.cjs +2 -0
  6. package/dist/ably-ai-transport.umd.cjs.map +1 -0
  7. package/dist/constants.d.ts +50 -0
  8. package/dist/core/codec/decoder.d.ts +62 -0
  9. package/dist/core/codec/encoder.d.ts +56 -0
  10. package/dist/core/codec/index.d.ts +8 -0
  11. package/dist/core/codec/lifecycle-tracker.d.ts +74 -0
  12. package/dist/core/codec/types.d.ts +188 -0
  13. package/dist/core/transport/client-transport.d.ts +10 -0
  14. package/dist/core/transport/conversation-tree.d.ts +9 -0
  15. package/dist/core/transport/decode-history.d.ts +41 -0
  16. package/dist/core/transport/headers.d.ts +26 -0
  17. package/dist/core/transport/index.d.ts +4 -0
  18. package/dist/core/transport/pipe-stream.d.ts +16 -0
  19. package/dist/core/transport/server-transport.d.ts +7 -0
  20. package/dist/core/transport/stream-router.d.ts +19 -0
  21. package/dist/core/transport/turn-manager.d.ts +34 -0
  22. package/dist/core/transport/types.d.ts +407 -0
  23. package/dist/errors.d.ts +46 -0
  24. package/dist/event-emitter.d.ts +65 -0
  25. package/dist/index.d.ts +11 -0
  26. package/dist/logger.d.ts +103 -0
  27. package/dist/react/ably-ai-transport-react.js +823 -0
  28. package/dist/react/ably-ai-transport-react.js.map +1 -0
  29. package/dist/react/ably-ai-transport-react.umd.cjs +2 -0
  30. package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -0
  31. package/dist/react/index.d.ts +11 -0
  32. package/dist/react/use-ably-messages.d.ts +18 -0
  33. package/dist/react/use-active-turns.d.ts +8 -0
  34. package/dist/react/use-client-transport.d.ts +7 -0
  35. package/dist/react/use-conversation-tree.d.ts +20 -0
  36. package/dist/react/use-edit.d.ts +7 -0
  37. package/dist/react/use-history.d.ts +19 -0
  38. package/dist/react/use-messages.d.ts +7 -0
  39. package/dist/react/use-regenerate.d.ts +7 -0
  40. package/dist/react/use-send.d.ts +7 -0
  41. package/dist/utils.d.ts +127 -0
  42. package/dist/vercel/ably-ai-transport-vercel.js +2331 -0
  43. package/dist/vercel/ably-ai-transport-vercel.js.map +1 -0
  44. package/dist/vercel/ably-ai-transport-vercel.umd.cjs +2 -0
  45. package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -0
  46. package/dist/vercel/codec/accumulator.d.ts +21 -0
  47. package/dist/vercel/codec/decoder.d.ts +22 -0
  48. package/dist/vercel/codec/encoder.d.ts +41 -0
  49. package/dist/vercel/codec/index.d.ts +22 -0
  50. package/dist/vercel/index.d.ts +3 -0
  51. package/dist/vercel/react/ably-ai-transport-vercel-react.js +2082 -0
  52. package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -0
  53. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +2 -0
  54. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -0
  55. package/dist/vercel/react/index.d.ts +3 -0
  56. package/dist/vercel/react/use-chat-transport.d.ts +29 -0
  57. package/dist/vercel/react/use-message-sync.d.ts +19 -0
  58. package/dist/vercel/transport/chat-transport.d.ts +118 -0
  59. package/dist/vercel/transport/index.d.ts +36 -0
  60. package/package.json +123 -0
  61. package/react/README.md +3 -0
  62. package/react/index.d.ts +1 -0
  63. package/react/index.js +1 -0
  64. package/react/index.umd.cjs +1 -0
  65. package/src/constants.ts +98 -0
  66. package/src/core/codec/decoder.ts +402 -0
  67. package/src/core/codec/encoder.ts +470 -0
  68. package/src/core/codec/index.ts +28 -0
  69. package/src/core/codec/lifecycle-tracker.ts +140 -0
  70. package/src/core/codec/types.ts +249 -0
  71. package/src/core/transport/client-transport.ts +959 -0
  72. package/src/core/transport/conversation-tree.ts +434 -0
  73. package/src/core/transport/decode-history.ts +337 -0
  74. package/src/core/transport/headers.ts +46 -0
  75. package/src/core/transport/index.ts +34 -0
  76. package/src/core/transport/pipe-stream.ts +95 -0
  77. package/src/core/transport/server-transport.ts +458 -0
  78. package/src/core/transport/stream-router.ts +118 -0
  79. package/src/core/transport/turn-manager.ts +147 -0
  80. package/src/core/transport/types.ts +533 -0
  81. package/src/errors.ts +58 -0
  82. package/src/event-emitter.ts +103 -0
  83. package/src/index.ts +89 -0
  84. package/src/logger.ts +241 -0
  85. package/src/react/index.ts +11 -0
  86. package/src/react/use-ably-messages.ts +37 -0
  87. package/src/react/use-active-turns.ts +61 -0
  88. package/src/react/use-client-transport.ts +37 -0
  89. package/src/react/use-conversation-tree.ts +71 -0
  90. package/src/react/use-edit.ts +24 -0
  91. package/src/react/use-history.ts +111 -0
  92. package/src/react/use-messages.ts +32 -0
  93. package/src/react/use-regenerate.ts +24 -0
  94. package/src/react/use-send.ts +25 -0
  95. package/src/react/vite.config.ts +32 -0
  96. package/src/tsconfig.json +25 -0
  97. package/src/utils.ts +230 -0
  98. package/src/vercel/codec/accumulator.ts +603 -0
  99. package/src/vercel/codec/decoder.ts +615 -0
  100. package/src/vercel/codec/encoder.ts +396 -0
  101. package/src/vercel/codec/index.ts +37 -0
  102. package/src/vercel/index.ts +12 -0
  103. package/src/vercel/react/index.ts +4 -0
  104. package/src/vercel/react/use-chat-transport.ts +60 -0
  105. package/src/vercel/react/use-message-sync.ts +34 -0
  106. package/src/vercel/react/vite.config.ts +33 -0
  107. package/src/vercel/transport/chat-transport.ts +278 -0
  108. package/src/vercel/transport/index.ts +56 -0
  109. package/src/vercel/vite.config.ts +33 -0
  110. package/src/vite.config.ts +31 -0
  111. package/vercel/README.md +3 -0
  112. package/vercel/index.d.ts +1 -0
  113. package/vercel/index.js +1 -0
  114. package/vercel/index.umd.cjs +1 -0
  115. package/vercel/react/README.md +3 -0
  116. package/vercel/react/index.d.ts +1 -0
  117. package/vercel/react/index.js +1 -0
  118. package/vercel/react/index.umd.cjs +1 -0
@@ -0,0 +1,407 @@
1
+ import { Logger } from '../../logger.js';
2
+ import { Codec } from '../codec/types.js';
3
+ /**
4
+ * Core transport types, parameterized by codec event and message types.
5
+ *
6
+ * These types define the contract for both client and server transport
7
+ * implementations, independent of which codec (Vercel AI SDK, etc.) is used.
8
+ */
9
+ import type * as Ably from 'ably';
10
+ /** Why a turn ended. */
11
+ export type TurnEndReason = 'complete' | 'cancelled' | 'error';
12
+ /** Filter for cancel operations. At most one field should be set. */
13
+ export interface CancelFilter {
14
+ /** Cancel a specific turn by ID. */
15
+ turnId?: string;
16
+ /** Cancel all turns belonging to the sender's clientId. */
17
+ own?: boolean;
18
+ /** Cancel all turns belonging to a specific clientId. */
19
+ clientId?: string;
20
+ /** Cancel all turns on the channel. */
21
+ all?: boolean;
22
+ }
23
+ /**
24
+ * Passed to the server's `onCancel` hook for authorization decisions.
25
+ * The hook inspects the incoming cancel message and decides whether to
26
+ * allow each matched turn to be aborted.
27
+ */
28
+ export interface CancelRequest {
29
+ /** The raw Ably message that carried the cancel signal. */
30
+ message: Ably.InboundMessage;
31
+ /** The parsed cancel scope from the message headers. */
32
+ filter: CancelFilter;
33
+ /** Which active turnIds would be cancelled if allowed. */
34
+ matchedTurnIds: string[];
35
+ /** Map of turnId to the ownerClientId for the matched turns. */
36
+ turnOwners: Map<string, string>;
37
+ }
38
+ /** A domain message paired with its Ably transport headers. Used on the read path to snapshot conversation state (e.g. for HTTP POST bodies). */
39
+ export interface MessageWithHeaders<TMessage> {
40
+ /** The domain message. */
41
+ message: TMessage;
42
+ /** Ably headers associated with this message (transport metadata, domain headers). */
43
+ headers?: Record<string, string>;
44
+ }
45
+ /** Options for creating a server transport. */
46
+ export interface ServerTransportOptions<TEvent, TMessage> {
47
+ /** The Ably channel to publish to. Must match the client's channel. */
48
+ channel: Ably.RealtimeChannel;
49
+ /** The codec to use for encoding events and messages. */
50
+ codec: Codec<TEvent, TMessage>;
51
+ /** Logger instance for diagnostic output. */
52
+ logger?: Logger;
53
+ /**
54
+ * Called with non-fatal transport-level errors not scoped to any turn.
55
+ * Examples: cancel listener subscription failure, channel attach errors.
56
+ */
57
+ onError?: (error: Ably.ErrorInfo) => void;
58
+ }
59
+ /** Options for addMessages — per-operation overrides for message identity and branching. */
60
+ export interface AddMessageOptions {
61
+ /** The user's clientId for attribution. */
62
+ clientId?: string;
63
+ /** The msg-id of the immediately preceding message in this branch. */
64
+ parent?: string | null;
65
+ /** The msg-id of the message this one replaces (creates a fork). */
66
+ forkOf?: string;
67
+ }
68
+ /** Result of publishing user messages via addMessages. */
69
+ export interface AddMessagesResult {
70
+ /** The `x-ably-msg-id` of each published message, in order. */
71
+ msgIds: string[];
72
+ }
73
+ /** Options for streamResponse — per-operation overrides for the assistant message. */
74
+ export interface StreamResponseOptions {
75
+ /** The msg-id of the immediately preceding message in this branch. */
76
+ parent?: string | null;
77
+ /** The msg-id of the message this response replaces (for regeneration). */
78
+ forkOf?: string;
79
+ }
80
+ /** The result of streaming a response through the encoder. */
81
+ export interface StreamResult {
82
+ /** Why the stream ended. */
83
+ reason: TurnEndReason;
84
+ }
85
+ /** Options passed to newTurn for configuring the turn lifecycle. */
86
+ export interface NewTurnOptions<TEvent> {
87
+ /** The turn identifier (generated by the client transport or the server). */
88
+ turnId: string;
89
+ /** The user's clientId for attribution. */
90
+ clientId?: string;
91
+ /**
92
+ * The msg-id of the immediately preceding message in this branch.
93
+ * Used as the default parent for user messages (via addMessages) and
94
+ * assistant messages (via streamResponse) when not overridden per-operation.
95
+ */
96
+ parent?: string | null;
97
+ /**
98
+ * The msg-id of the message this turn replaces (creates a fork).
99
+ * Stamped on user messages (for edits) or assistant messages
100
+ * (for regeneration).
101
+ */
102
+ forkOf?: string;
103
+ /**
104
+ * Called before each Ably message is published in this turn.
105
+ * Mutate the Ably message in place to add custom extras.headers.
106
+ */
107
+ onMessage?: (message: Ably.Message) => void;
108
+ /**
109
+ * Called when the turn's stream is aborted (by cancel or server).
110
+ * Receives a write function to publish final events before the abort finalises.
111
+ */
112
+ onAbort?: (write: (event: TEvent) => Promise<void>) => void | Promise<void>;
113
+ /**
114
+ * Called when a cancel message arrives matching this turn.
115
+ * Return true to allow cancellation (fires abortSignal, stream aborts).
116
+ * Return false to reject (cancel ignored, stream continues).
117
+ * If not provided, all cancels are accepted.
118
+ */
119
+ onCancel?: (request: CancelRequest) => Promise<boolean>;
120
+ /**
121
+ * Called with non-fatal errors scoped to this turn. Examples: turn-start
122
+ * publish failure, encoder recovery failure, stream encoding errors.
123
+ */
124
+ onError?: (error: Ably.ErrorInfo) => void;
125
+ }
126
+ /** A server-side turn with explicit lifecycle methods. */
127
+ export interface Turn<TEvent, TMessage> {
128
+ /** The turn's unique identifier. */
129
+ readonly turnId: string;
130
+ /** Abort signal scoped to this turn. Fires when a cancel event arrives for this turnId. */
131
+ readonly abortSignal: AbortSignal;
132
+ /** Publish turn-start event to the channel. Must be called before addMessages or streamResponse. */
133
+ start(): Promise<void>;
134
+ /**
135
+ * Publish user messages to the channel, scoped to this turn.
136
+ * Each message is published with its own headers (including `x-ably-msg-id`
137
+ * for optimistic reconciliation with the client's inserts). Per-message
138
+ * headers from `MessageWithHeaders` override transport-generated defaults.
139
+ * @returns The msg-ids of all published messages, in order.
140
+ */
141
+ addMessages(messages: MessageWithHeaders<TMessage>[], options?: AddMessageOptions): Promise<AddMessagesResult>;
142
+ /**
143
+ * Pipe a ReadableStream through the encoder to the channel.
144
+ * Returns when the stream completes, is cancelled, or errors.
145
+ * Does NOT call end() — the caller must call end() after streamResponse returns.
146
+ */
147
+ streamResponse(stream: ReadableStream<TEvent>, options?: StreamResponseOptions): Promise<StreamResult>;
148
+ /** Publish turn-end event to the channel and clean up. */
149
+ end(reason: TurnEndReason): Promise<void>;
150
+ }
151
+ /** Server-side transport that manages turn lifecycles over an Ably channel. */
152
+ export interface ServerTransport<TEvent, TMessage> {
153
+ /**
154
+ * Create a new turn. Synchronous — no channel activity until start() is called.
155
+ * The turn is registered for cancel routing immediately so that early cancels
156
+ * fire the abort signal.
157
+ */
158
+ newTurn(options: NewTurnOptions<TEvent>): Turn<TEvent, TMessage>;
159
+ /** Unsubscribe from cancel messages, abort all active turns, and clean up. */
160
+ close(): void;
161
+ }
162
+ /** Options for creating a client transport. */
163
+ export interface ClientTransportOptions<TEvent, TMessage> {
164
+ /** The Ably channel to receive responses on and publish cancel signals to. */
165
+ channel: Ably.RealtimeChannel;
166
+ /** The codec to use for encoding/decoding. */
167
+ codec: Codec<TEvent, TMessage>;
168
+ /** The client's identity. Sent to the server in the POST body. */
169
+ clientId?: string;
170
+ /** Server endpoint URL for the HTTP POST. Defaults to `"/api/chat"`. */
171
+ api?: string;
172
+ /** Headers for the HTTP POST. Function form for dynamic values (e.g. auth tokens). */
173
+ headers?: Record<string, string> | (() => Record<string, string>);
174
+ /** Additional body fields merged into the HTTP POST. Function form for dynamic values. */
175
+ body?: Record<string, unknown> | (() => Record<string, unknown>);
176
+ /** Fetch credentials mode for the HTTP POST. */
177
+ credentials?: RequestCredentials;
178
+ /** Custom fetch implementation. Defaults to `globalThis.fetch`. */
179
+ fetch?: typeof globalThis.fetch;
180
+ /** Initial messages to seed the conversation tree with. Forms a linear chain. */
181
+ messages?: TMessage[];
182
+ /** Logger instance for diagnostic output. */
183
+ logger?: Logger;
184
+ }
185
+ /** Per-send options for customizing the HTTP POST and branching metadata. */
186
+ export interface SendOptions {
187
+ /** Additional fields merged into the HTTP POST body. */
188
+ body?: Record<string, unknown>;
189
+ /** Additional headers for the HTTP POST. */
190
+ headers?: Record<string, string>;
191
+ /**
192
+ * The msg-id of the message this send replaces (fork).
193
+ * Set for regeneration (forkOf an assistant message) or
194
+ * edit (forkOf a user message).
195
+ */
196
+ forkOf?: string;
197
+ /**
198
+ * The msg-id of the message that precedes this one in the
199
+ * conversation thread. Null means the message is a root.
200
+ * If omitted, auto-computed from the last message in the tree.
201
+ */
202
+ parent?: string | null;
203
+ }
204
+ /** A structured event describing a turn starting or ending. */
205
+ export type TurnLifecycleEvent = {
206
+ type: 'x-ably-turn-start';
207
+ turnId: string;
208
+ clientId: string;
209
+ } | {
210
+ type: 'x-ably-turn-end';
211
+ turnId: string;
212
+ clientId: string;
213
+ reason: TurnEndReason;
214
+ };
215
+ /** A handle to an active client-side turn, returned by `send()`, `regenerate()`, and `edit()`. */
216
+ export interface ActiveTurn<TEvent> {
217
+ /** The decoded event stream for this turn. */
218
+ stream: ReadableStream<TEvent>;
219
+ /** The turn's unique identifier. */
220
+ turnId: string;
221
+ /** Cancel this specific turn. Publishes a cancel message and closes the local stream. */
222
+ cancel(): Promise<void>;
223
+ }
224
+ /** Options for closing a client transport. */
225
+ export interface CloseOptions {
226
+ /** Cancel in-progress turns before closing. Publishes a cancel message to the channel. */
227
+ cancel?: CancelFilter;
228
+ }
229
+ /** A page of decoded messages from channel history. */
230
+ export interface PaginatedMessages<TMessage> {
231
+ /** Decoded messages in chronological order (oldest first). */
232
+ items: TMessage[];
233
+ /** Headers for each item, parallel to `items`. Used by the transport to populate the tree. */
234
+ itemHeaders?: Record<string, string>[];
235
+ /** Ably serial for each item, parallel to `items`. Used by the transport for tree ordering. */
236
+ itemSerials?: string[];
237
+ /** Raw Ably messages that produced this page, in chronological order. */
238
+ rawMessages?: Ably.InboundMessage[];
239
+ /** Whether there are older pages available. */
240
+ hasNext(): boolean;
241
+ /** Fetch the next (older) page. Returns undefined if no more pages. */
242
+ next(): Promise<PaginatedMessages<TMessage> | undefined>;
243
+ }
244
+ /** Options for loading channel history. */
245
+ export interface LoadHistoryOptions {
246
+ /** Max messages per page. Default: 100. */
247
+ limit?: number;
248
+ }
249
+ /** A node in the conversation tree, representing a single domain message. */
250
+ export interface ConversationNode<TMessage> {
251
+ /** The domain message. */
252
+ message: TMessage;
253
+ /** The x-ably-msg-id of this node — primary key in the tree. */
254
+ msgId: string;
255
+ /** Parent node's msg-id (x-ably-parent), or undefined for root messages. */
256
+ parentId: string | undefined;
257
+ /** The msg-id this node forks from (x-ably-fork-of), or undefined if first version. */
258
+ forkOf: string | undefined;
259
+ /** Full Ably headers for this message. */
260
+ headers: Record<string, string>;
261
+ /**
262
+ * Ably serial for this message. Lexicographically comparable for total order.
263
+ * Used to sort siblings deterministically regardless of delivery/history order.
264
+ * Absent for optimistic messages (set when the server relay arrives).
265
+ */
266
+ serial: string | undefined;
267
+ }
268
+ /**
269
+ * Materializes a branching conversation tree from a flat oplog.
270
+ *
271
+ * Owns the conversation state — `flatten()` returns the linear message list
272
+ * for the currently selected branches. The transport's `getMessages()` delegates
273
+ * to `flatten()`.
274
+ */
275
+ export interface ConversationTree<TMessage> {
276
+ /**
277
+ * Flatten the tree along the currently selected branches into
278
+ * a linear message list. This is what getMessages() returns.
279
+ */
280
+ flatten(): TMessage[];
281
+ /**
282
+ * Get all messages that are siblings (alternatives) at a given
283
+ * fork point. Returns an array ordered chronologically by serial.
284
+ * The message identified by msgId is always included.
285
+ */
286
+ getSiblings(msgId: string): TMessage[];
287
+ /** Whether a message has sibling alternatives (i.e., show navigation arrows). */
288
+ hasSiblings(msgId: string): boolean;
289
+ /** Get the index of the currently selected sibling at a fork point. */
290
+ getSelectedIndex(msgId: string): number;
291
+ /**
292
+ * Select a sibling at a fork point by index. Updates the active branch.
293
+ * Calling flatten() after this returns the new linear thread.
294
+ * Index is clamped to `[0, siblings.length - 1]`.
295
+ */
296
+ select(msgId: string, index: number): void;
297
+ /** Get a node by msgId, or undefined if not found. */
298
+ getNode(msgId: string): ConversationNode<TMessage> | undefined;
299
+ /**
300
+ * Get a node by codec message key (e.g. UIMessage.id), or undefined if
301
+ * not found. Uses a secondary index since the tree is keyed by x-ably-msg-id.
302
+ */
303
+ getNodeByKey(key: string): ConversationNode<TMessage> | undefined;
304
+ /** Get the stored headers for a node by msgId, or undefined if not found. */
305
+ getHeaders(msgId: string): Record<string, string> | undefined;
306
+ /**
307
+ * Insert or update a message in the tree. Reads parent/forkOf from the
308
+ * provided headers. If the message already exists (by msgId), updates
309
+ * it in place. The optional serial is the Ably message serial used for
310
+ * deterministic sibling ordering.
311
+ */
312
+ upsert(msgId: string, message: TMessage, headers: Record<string, string>, serial?: string): void;
313
+ /** Remove a message from the tree. */
314
+ delete(msgId: string): void;
315
+ }
316
+ /** Entry in the StreamRouter's turn map. Not part of the public API. */
317
+ export interface TurnEntry<TEvent> {
318
+ /** The ReadableStream controller for this turn. */
319
+ controller: ReadableStreamDefaultController<TEvent>;
320
+ /** The turn's unique identifier. */
321
+ turnId: string;
322
+ }
323
+ /** Client-side transport that manages conversation state over an Ably channel. */
324
+ export interface ClientTransport<TEvent, TMessage> {
325
+ /**
326
+ * Send one or more messages and start a new turn. Returns a handle to the
327
+ * active turn with the decoded event stream and a cancel function.
328
+ *
329
+ * The HTTP POST is fire-and-forget — the returned stream is available
330
+ * immediately. If the POST fails, the error is surfaced via `on("error")`.
331
+ */
332
+ send(messages: TMessage | TMessage[], options?: SendOptions): Promise<ActiveTurn<TEvent>>;
333
+ /**
334
+ * Regenerate an assistant message. Creates a new turn that forks the
335
+ * target message with no new user messages. Automatically computes
336
+ * `forkOf`, `parent`, and truncated `history` from the tree.
337
+ *
338
+ * Pass `options.body.history` to override the default truncated history.
339
+ */
340
+ regenerate(messageId: string, options?: SendOptions): Promise<ActiveTurn<TEvent>>;
341
+ /**
342
+ * Edit a user message. Creates a new turn that forks the target message
343
+ * with replacement content. Automatically computes `forkOf`, `parent`,
344
+ * and `history` from the tree.
345
+ */
346
+ edit(messageId: string, newMessages: TMessage | TMessage[], options?: SendOptions): Promise<ActiveTurn<TEvent>>;
347
+ /**
348
+ * Access the conversation tree for branch navigation.
349
+ * The tree is updated in real-time by the transport's channel subscription.
350
+ */
351
+ getTree(): ConversationTree<TMessage>;
352
+ /** Cancel turns matching the filter. Defaults to `{ own: true }` (all own turns). */
353
+ cancel(filter?: CancelFilter): Promise<void>;
354
+ /**
355
+ * Returns a promise that resolves when all active turns matching the filter
356
+ * have completed. Resolves immediately if no matching turns are active.
357
+ * Defaults to `{ own: true }`.
358
+ */
359
+ waitForTurn(filter?: CancelFilter): Promise<void>;
360
+ /**
361
+ * Subscribe to message store changes or raw Ably message additions.
362
+ * The handler is called with no arguments — call `getMessages()` or
363
+ * `getAblyMessages()` for the current state. Returns an unsubscribe function.
364
+ */
365
+ on(event: 'message' | 'ably-message', handler: () => void): () => void;
366
+ /** Subscribe to turn lifecycle events (start, end). Returns an unsubscribe function. */
367
+ on(event: 'turn', handler: (event: TurnLifecycleEvent) => void): () => void;
368
+ /**
369
+ * Subscribe to non-fatal transport errors. These indicate something went
370
+ * wrong but the transport is still operational. Returns an unsubscribe function.
371
+ */
372
+ on(event: 'error', handler: (error: Ably.ErrorInfo) => void): () => void;
373
+ /**
374
+ * Get the accumulated raw Ably messages, in chronological order.
375
+ * Includes both live messages and history-loaded messages.
376
+ */
377
+ getAblyMessages(): Ably.InboundMessage[];
378
+ /** Get all currently active turns, keyed by clientId. */
379
+ getActiveTurnIds(): Map<string, Set<string>>;
380
+ /** Get Ably headers associated with a message via the conversation tree. */
381
+ getMessageHeaders(message: TMessage): Record<string, string> | undefined;
382
+ /** Get the current message list (follows selected branches). Updated by message lifecycle events. */
383
+ getMessages(): TMessage[];
384
+ /**
385
+ * Snapshot the current message list as message + headers pairs.
386
+ * Convenience for building the `history` body field in HTTP POSTs.
387
+ */
388
+ getMessagesWithHeaders(): MessageWithHeaders<TMessage>[];
389
+ /**
390
+ * Load a page of conversation history from the channel, decoded through
391
+ * the transport's codec. Uses `untilAttach` for gapless continuity with
392
+ * the live subscription.
393
+ *
394
+ * History messages are inserted into the conversation tree and trigger
395
+ * a notification. Returns a PaginatedMessages handle — call `next()`
396
+ * for older pages.
397
+ */
398
+ history(options?: LoadHistoryOptions): Promise<PaginatedMessages<TMessage>>;
399
+ /**
400
+ * Tear down the transport: unsubscribe from the channel, close active
401
+ * streams, clear all handlers, and prevent further operations.
402
+ *
403
+ * Pass `cancel` to publish a cancel message before closing. Without it,
404
+ * only local state is torn down (the server keeps streaming).
405
+ */
406
+ close(options?: CloseOptions): Promise<void>;
407
+ }
@@ -0,0 +1,46 @@
1
+ import * as Ably from 'ably';
2
+ /**
3
+ * Error codes for the AI Transport SDK.
4
+ */
5
+ export declare enum ErrorCode {
6
+ /**
7
+ * The request was invalid.
8
+ */
9
+ BadRequest = 40000,
10
+ /**
11
+ * Invalid argument provided.
12
+ */
13
+ InvalidArgument = 40003,
14
+ /**
15
+ * Encoder recovery failed after flush — one or more updateMessage calls
16
+ * could not recover a failed append pipeline.
17
+ */
18
+ EncoderRecoveryFailed = 104000,
19
+ /**
20
+ * A transport-level channel subscription callback threw unexpectedly.
21
+ */
22
+ TransportSubscriptionError = 104001,
23
+ /**
24
+ * Cancel listener or onCancel hook threw while processing a cancel message.
25
+ */
26
+ CancelListenerError = 104002,
27
+ /**
28
+ * A turn lifecycle event (turn-start or turn-end) failed to publish.
29
+ */
30
+ TurnLifecycleError = 104003,
31
+ /**
32
+ * An operation was attempted on a transport that has already been closed.
33
+ */
34
+ TransportClosed = 104004,
35
+ /**
36
+ * The HTTP POST to the server endpoint failed (network error or non-2xx response).
37
+ */
38
+ TransportSendFailed = 104005
39
+ }
40
+ /**
41
+ * Returns true if the {@link Ably.ErrorInfo} code matches the provided ErrorCode value.
42
+ * @param errorInfo The error info to check.
43
+ * @param error The error code to compare against.
44
+ * @returns true if the error code matches, false otherwise.
45
+ */
46
+ export declare const errorInfoIs: (errorInfo: Ably.ErrorInfo, error: ErrorCode) => boolean;
@@ -0,0 +1,65 @@
1
+ import { Logger } from './logger.js';
2
+ /**
3
+ * Type-safe EventEmitter wrapping Ably's internal EventEmitter.
4
+ *
5
+ * Takes a single `EventsMap` type parameter — an interface mapping event names
6
+ * to payload types — rather than Ably's three type parameters. Adapted from
7
+ * the ably-chat-js SDK.
8
+ *
9
+ * ```ts
10
+ * interface MyEvents {
11
+ * reaction: { emoji: string };
12
+ * status: { online: boolean };
13
+ * }
14
+ *
15
+ * const emitter = new EventEmitter<MyEvents>(logger);
16
+ * emitter.on('reaction', (event) => console.log(event.emoji));
17
+ * emitter.emit('reaction', { emoji: '👍' });
18
+ * ```
19
+ */
20
+ import * as Ably from 'ably';
21
+ /** Callback receiving a union of all possible event payloads. */
22
+ type Callback<EventsMap> = (arg: EventsMap[keyof EventsMap]) => void;
23
+ /** Callback receiving the payload for a single event type. */
24
+ type CallbackSingle<K> = (arg: K) => void;
25
+ /**
26
+ * Type-safe interface for the Ably EventEmitter, parameterized by an EventsMap
27
+ * that maps event names to their payload types.
28
+ */
29
+ interface InterfaceEventEmitter<EventsMap> extends Ably.EventEmitter<Callback<EventsMap>, void, keyof EventsMap> {
30
+ /** Emit an event with a type-safe payload. Payload is optional for `undefined`-typed events. */
31
+ emit<K extends keyof EventsMap>(event: K, ...args: EventsMap[K] extends undefined ? [EventsMap[K]?] : [EventsMap[K]]): void;
32
+ /** Subscribe to a single event with a typed callback. */
33
+ on<K extends keyof EventsMap>(event: K, callback: CallbackSingle<EventsMap[K]>): void;
34
+ /** Subscribe to two events with a union-typed callback. */
35
+ on<K1 extends keyof EventsMap, K2 extends keyof EventsMap>(events: [K1, K2], callback: CallbackSingle<EventsMap[K1] | EventsMap[K2]>): void;
36
+ /** Subscribe to three events with a union-typed callback. */
37
+ on<K1 extends keyof EventsMap, K2 extends keyof EventsMap, K3 extends keyof EventsMap>(events: [K1, K2, K3], callback: CallbackSingle<EventsMap[K1] | EventsMap[K2] | EventsMap[K3]>): void;
38
+ /** Subscribe to an array of events. */
39
+ on(events: (keyof EventsMap)[], callback: Callback<EventsMap>): void;
40
+ /** Subscribe to all events. */
41
+ on(callback: Callback<EventsMap>): void;
42
+ /** Unsubscribe a callback from a specific event. */
43
+ off<K extends keyof EventsMap>(event: K, listener: CallbackSingle<EventsMap[K]>): void;
44
+ /** Unsubscribe a callback from all events, or remove all listeners if no callback provided. */
45
+ off(listener?: Callback<EventsMap>): void;
46
+ }
47
+ declare const InternalEventEmitter: new <EventsMap>(logger: unknown) => InterfaceEventEmitter<EventsMap>;
48
+ /**
49
+ * Type-safe EventEmitter based on Ably's internal EventEmitter.
50
+ *
51
+ * Provides the same semantics as {@link Ably.EventEmitter} (error isolation
52
+ * between listeners, synchronous dispatch) but with a single `EventsMap` type
53
+ * parameter for ergonomic type safety.
54
+ *
55
+ * Requires a {@link Logger} so that listener exceptions are routed through
56
+ * the application's logging infrastructure rather than silently swallowed.
57
+ */
58
+ export declare class EventEmitter<EventsMap> extends InternalEventEmitter<EventsMap> {
59
+ /**
60
+ * Create a new EventEmitter.
61
+ * @param logger - Application logger. Listener exceptions are logged at error level.
62
+ */
63
+ constructor(logger: Logger);
64
+ }
65
+ export {};
@@ -0,0 +1,11 @@
1
+ export type { ActiveTurn, AddMessageOptions, AddMessagesResult, CancelFilter, CancelRequest, ClientTransport, ClientTransportOptions, CloseOptions, ConversationNode, ConversationTree, LoadHistoryOptions, MessageWithHeaders, NewTurnOptions, PaginatedMessages, SendOptions, ServerTransport, ServerTransportOptions, StreamResponseOptions, StreamResult, Turn, TurnEndReason, TurnLifecycleEvent, } from './core/transport/index.js';
2
+ export { buildTransportHeaders, createClientTransport, createServerTransport } from './core/transport/index.js';
3
+ export type { ChannelWriter, Codec, DecoderCore, DecoderCoreHooks, DecoderCoreOptions, DecoderOutput, DiscreteEncoder, EncoderCore, EncoderCoreOptions, EncoderOptions, Extras, LifecycleTracker, MessageAccumulator, MessagePayload, PhaseConfig, StreamDecoder, StreamEncoder, StreamPayload, StreamTrackerState, WriteOptions, } from './core/codec/index.js';
4
+ export { createDecoderCore, createEncoderCore, createLifecycleTracker, eventOutput } from './core/codec/index.js';
5
+ export { DOMAIN_HEADER_PREFIX, EVENT_ABORT, EVENT_CANCEL, EVENT_ERROR, EVENT_TURN_END, EVENT_TURN_START, HEADER_CANCEL_ALL, HEADER_CANCEL_CLIENT_ID, HEADER_CANCEL_OWN, HEADER_CANCEL_TURN_ID, HEADER_FORK_OF, HEADER_MSG_ID, HEADER_PARENT, HEADER_ROLE, HEADER_STATUS, HEADER_STREAM, HEADER_STREAM_ID, HEADER_TURN_CLIENT_ID, HEADER_TURN_ID, HEADER_TURN_REASON, } from './constants.js';
6
+ export type { DomainHeaderReader, DomainHeaderWriter, Stripped } from './utils.js';
7
+ export { getHeaders, headerReader, headerWriter, mergeHeaders, stripUndefined } from './utils.js';
8
+ export { EventEmitter } from './event-emitter.js';
9
+ export { ErrorCode, errorInfoIs } from './errors.js';
10
+ export type { LogContext, Logger, LoggerOptions, LogHandler } from './logger.js';
11
+ export { consoleLogger, LogLevel, makeLogger } from './logger.js';
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Structured logger with leveled output and hierarchical context.
3
+ * Implementations filter messages by level and delegate to a {@link LogHandler}.
4
+ */
5
+ export interface Logger {
6
+ /**
7
+ * Log a message at the trace level.
8
+ * @param message The message to log.
9
+ * @param context The context of the log message as key-value pairs.
10
+ */
11
+ trace(message: string, context?: LogContext): void;
12
+ /**
13
+ * Log a message at the debug level.
14
+ * @param message The message to log.
15
+ * @param context The context of the log message as key-value pairs.
16
+ */
17
+ debug(message: string, context?: LogContext): void;
18
+ /**
19
+ * Log a message at the info level.
20
+ * @param message The message to log.
21
+ * @param context The context of the log message as key-value pairs.
22
+ */
23
+ info(message: string, context?: LogContext): void;
24
+ /**
25
+ * Log a message at the warn level.
26
+ * @param message The message to log.
27
+ * @param context The context of the log message as key-value pairs.
28
+ */
29
+ warn(message: string, context?: LogContext): void;
30
+ /**
31
+ * Log a message at the error level.
32
+ * @param message The message to log.
33
+ * @param context The context of the log message as key-value pairs.
34
+ */
35
+ error(message: string, context?: LogContext): void;
36
+ /**
37
+ * Creates a new logger with a context that will be merged with any context provided to individual log calls.
38
+ * The context will be overridden by any matching keys in the individual log call's context.
39
+ * @param context The context to use for all log calls.
40
+ * @returns A new logger instance with the context.
41
+ */
42
+ withContext(context: LogContext): Logger;
43
+ }
44
+ /**
45
+ * Represents the different levels of logging that can be used.
46
+ */
47
+ export declare enum LogLevel {
48
+ /**
49
+ * Something routine and expected has occurred. This level will provide logs for the vast majority of operations
50
+ * and function calls.
51
+ */
52
+ Trace = "trace",
53
+ /**
54
+ * Development information, messages that are useful when trying to debug library behavior,
55
+ * but superfluous to normal operation.
56
+ */
57
+ Debug = "debug",
58
+ /**
59
+ * Informational messages. Operationally significant to the library but not out of the ordinary.
60
+ */
61
+ Info = "info",
62
+ /**
63
+ * Anything that is not immediately an error, but could cause unexpected behavior in the future. For example,
64
+ * passing an invalid value to an option. Indicates that some action should be taken to prevent future errors.
65
+ */
66
+ Warn = "warn",
67
+ /**
68
+ * A given operation has failed and cannot be automatically recovered. The error may threaten the continuity
69
+ * of operation.
70
+ */
71
+ Error = "error",
72
+ /**
73
+ * No logging will be performed.
74
+ */
75
+ Silent = "silent"
76
+ }
77
+ /**
78
+ * Represents the context of a log message.
79
+ * It is an object of key-value pairs that can be used to provide additional context to a log message.
80
+ */
81
+ export type LogContext = Record<string, any>;
82
+ /**
83
+ * A function that can be used to handle log messages.
84
+ * @param message The message to log.
85
+ * @param level The log level of the message.
86
+ * @param context The context of the log message as key-value pairs.
87
+ */
88
+ export type LogHandler = (message: string, level: LogLevel, context?: LogContext) => void;
89
+ /**
90
+ * A simple console logger that logs messages to the console.
91
+ * @param message The message to log.
92
+ * @param level The log level of the message.
93
+ * @param context - The context of the log message as key-value pairs.
94
+ */
95
+ export declare const consoleLogger: (message: string, level: LogLevel, context?: LogContext) => void;
96
+ /**
97
+ * Options for creating a logger.
98
+ */
99
+ export interface LoggerOptions {
100
+ logHandler?: LogHandler;
101
+ logLevel: LogLevel;
102
+ }
103
+ export declare const makeLogger: (options: LoggerOptions) => Logger;