@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,249 @@
1
+ /**
2
+ * Core codec interfaces as defined in the general codec specification.
3
+ *
4
+ * These types define the contract between domain event streams and Ably's
5
+ * native message primitives (publish, append, update, delete).
6
+ */
7
+
8
+ import type * as Ably from 'ably';
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // ChannelWriter — the I/O interface encoders use
12
+ // ---------------------------------------------------------------------------
13
+
14
+ /**
15
+ * The I/O interface that encoders use to publish to a channel.
16
+ * An `Ably.RealtimeChannel` satisfies this directly, but the interface
17
+ * allows mocking, batching, logging, or any other decorator.
18
+ */
19
+ export interface ChannelWriter {
20
+ /** Publish one or more discrete messages to the channel. */
21
+ publish(message: Ably.Message | Ably.Message[], options?: Ably.PublishOptions): Promise<Ably.PublishResult>;
22
+
23
+ /** Append data to an existing message identified by its serial. */
24
+ appendMessage(
25
+ message: Ably.Message,
26
+ operation?: Ably.MessageOperation,
27
+ options?: Ably.PublishOptions,
28
+ ): Promise<Ably.UpdateDeleteResult>;
29
+
30
+ /** Replace the data of an existing message identified by its serial. */
31
+ updateMessage(
32
+ message: Ably.Message,
33
+ operation?: Ably.MessageOperation,
34
+ options?: Ably.PublishOptions,
35
+ ): Promise<Ably.UpdateDeleteResult>;
36
+ }
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // WriteOptions — per-write overrides for encoder operations
40
+ // ---------------------------------------------------------------------------
41
+
42
+ /** Shape of the extras object passed through WriteOptions and EncoderOptions. */
43
+ export interface Extras {
44
+ /** Headers to attach to the Ably message extras. */
45
+ headers?: Record<string, string>;
46
+ }
47
+
48
+ /** Per-write overrides for encoder operations. */
49
+ export interface WriteOptions {
50
+ /** Override the default clientId for this write. */
51
+ clientId?: string;
52
+ /** Override the default extras for this write. */
53
+ extras?: Extras;
54
+ /** Message identity for accumulator correlation. Stamped as `x-ably-msg-id`. */
55
+ messageId?: string;
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // MessagePayload — shared description of a message for encode and decode
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /**
63
+ * A codec-agnostic description of a discrete Ably message. Used on both sides:
64
+ * - **Encode:** the domain encoder describes what to publish; the encoder core
65
+ * handles header merging, clientId resolution, and the actual publish.
66
+ * - **Decode:** the decoder core extracts these fields from an `Ably.InboundMessage`
67
+ * before calling domain hooks, keeping hooks free of Ably SDK types.
68
+ *
69
+ * Data is `unknown` because discrete messages can carry arbitrary payloads
70
+ * (strings, objects, etc.) — Ably handles serialization natively.
71
+ */
72
+ export interface MessagePayload {
73
+ /** Ably message name (e.g. "text", "tool-input", "user-message"). */
74
+ name: string;
75
+ /** Message data. Ably handles serialization — strings, objects, and arrays are all valid. */
76
+ data: unknown;
77
+ /** Headers from the Ably message extras. */
78
+ headers?: Record<string, string>;
79
+ /** Mark this message as ephemeral (not persisted in channel history). Only meaningful on encode. */
80
+ ephemeral?: boolean;
81
+ }
82
+
83
+ /**
84
+ * Payload for streamed messages. Data must be a string because
85
+ * the message append lifecycle uses text append/accumulate semantics —
86
+ * deltas are concatenated for recovery and prefix-matching on the decoder.
87
+ */
88
+ export interface StreamPayload {
89
+ /** Ably message name (e.g. "text", "reasoning", "tool-input"). */
90
+ name: string;
91
+ /** Initial or closing data for the stream. Must be a string for append/accumulate semantics. */
92
+ data: string;
93
+ /** Headers from the Ably message extras. */
94
+ headers?: Record<string, string>;
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // StreamTrackerState — accumulated state of a streamed message
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * Running state of a streamed message tracked by the decoder core.
103
+ * Accumulates text across appends and tracks lifecycle (open/closed).
104
+ */
105
+ export interface StreamTrackerState {
106
+ /** Ably message name (e.g. "text", "reasoning", "tool-input"). */
107
+ name: string;
108
+ /** Stream identifier (e.g. chunk.id for text, toolCallId for tool-input). */
109
+ streamId: string;
110
+ /** Full accumulated text so far. */
111
+ accumulated: string;
112
+ /** Current headers for this stream. Initially set from the first publish, but may be replaced on update. */
113
+ headers: Record<string, string>;
114
+ /** Whether this stream has been closed (finished or aborted). */
115
+ closed: boolean;
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // DiscreteEncoder — stateless discrete publish operations
120
+ // ---------------------------------------------------------------------------
121
+
122
+ /**
123
+ * The subset of encoder operations that are stateless — safe for long-lived
124
+ * reuse across turns. Publishes complete messages and discrete events without
125
+ * any streaming lifecycle (no trackers, no pending appends, no close).
126
+ *
127
+ * The server transport calls `writeMessages` to publish user messages to the
128
+ * channel. All messages in a single call are published atomically and share
129
+ * a single `x-ably-msg-id`, forming one node in the conversation tree.
130
+ * `writeEvent` is a public API for consumers to publish standalone discrete
131
+ * events outside the streaming flow — it is not called by the transport internally.
132
+ */
133
+ export interface DiscreteEncoder<TEvent, TMessage> {
134
+ /**
135
+ * Encode and publish one or more domain messages atomically in a single
136
+ * channel publish. All messages share the encoder's transport headers
137
+ * (including `x-ably-msg-id`), so they form one logical unit in the
138
+ * conversation tree.
139
+ */
140
+ writeMessages(messages: TMessage[], options?: WriteOptions): Promise<Ably.PublishResult>;
141
+ /**
142
+ * Encode and publish a single domain event as a standalone discrete message.
143
+ * Available for consumers to publish events outside the streaming flow.
144
+ * Implementations should throw for event types that are only meaningful
145
+ * within a stream (e.g. text deltas).
146
+ */
147
+ writeEvent(event: TEvent, options?: WriteOptions): Promise<Ably.PublishResult>;
148
+ }
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // StreamEncoder — maps domain events to Ably channel operations
152
+ // ---------------------------------------------------------------------------
153
+
154
+ /**
155
+ * Full streaming encoder with single-turn lifecycle. Extends
156
+ * `DiscreteEncoder` with stateful streaming operations (`appendEvent` for
157
+ * content streams, `close` to flush). Used by the server transport.
158
+ */
159
+ export interface StreamEncoder<TEvent, TMessage> extends DiscreteEncoder<TEvent, TMessage> {
160
+ /** Encode and append a streaming domain event to an in-progress stream (delta semantics). */
161
+ appendEvent(event: TEvent, options?: WriteOptions): Promise<void>;
162
+ /**
163
+ * Abort all in-progress streams and publish a codec-specific abort signal.
164
+ * Called by the transport when a turn is cancelled. Idempotent — calling
165
+ * abort after all streams are already aborted is a no-op.
166
+ * @param reason - Optional reason string for the abort (e.g. 'cancelled').
167
+ */
168
+ abort(reason?: string): Promise<void>;
169
+ /** Flush all pending appends and close the encoder. */
170
+ close(): Promise<void>;
171
+ }
172
+
173
+ // ---------------------------------------------------------------------------
174
+ // DecoderOutput — unified return type from the decoder
175
+ // ---------------------------------------------------------------------------
176
+
177
+ /**
178
+ * A single output from the decoder: either a domain event or a complete domain message.
179
+ * Event outputs may carry a `messageId` read from the `x-ably-msg-id` header, which the
180
+ * accumulator uses to route events to the correct in-progress message.
181
+ */
182
+ export type DecoderOutput<TEvent, TMessage> =
183
+ | { kind: 'event'; event: TEvent; messageId?: string }
184
+ | { kind: 'message'; message: TMessage };
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // StreamDecoder — maps Ably messages to decoder outputs
188
+ // ---------------------------------------------------------------------------
189
+
190
+ /** Decodes Ably messages into domain events and messages. */
191
+ export interface StreamDecoder<TEvent, TMessage> {
192
+ /** Decode a single Ably message into zero or more domain outputs. */
193
+ decode(message: Ably.InboundMessage): DecoderOutput<TEvent, TMessage>[];
194
+ }
195
+
196
+ // ---------------------------------------------------------------------------
197
+ // MessageAccumulator — builds messages from decoder outputs
198
+ // ---------------------------------------------------------------------------
199
+
200
+ /** Accumulates decoder outputs into a list of domain messages, tracking active streams. */
201
+ export interface MessageAccumulator<TEvent, TMessage> {
202
+ /** Process a batch of decoder outputs, updating internal message state. */
203
+ processOutputs(outputs: DecoderOutput<TEvent, TMessage>[]): void;
204
+ /** Apply an external update to a message (e.g. from an update callback). */
205
+ updateMessage(message: TMessage): void;
206
+ /** All messages accumulated so far (in-progress and completed). */
207
+ readonly messages: TMessage[];
208
+ /** Only messages whose streams have finished. */
209
+ readonly completedMessages: TMessage[];
210
+ /** Whether any stream is still actively receiving data. */
211
+ readonly hasActiveStream: boolean;
212
+ }
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // Codec — composite interface for transport use
216
+ // ---------------------------------------------------------------------------
217
+
218
+ /** Options passed to a codec's `createEncoder` factory to configure default identity and message hooks. */
219
+ export interface EncoderOptions {
220
+ /** Default clientId for all writes. */
221
+ clientId?: string;
222
+ /** Default extras (e.g. headers) merged into every Ably message. */
223
+ extras?: Extras;
224
+ /** Hook called before each Ably message is published. Mutate the message in place to add transport-level headers. */
225
+ onMessage?: (message: Ably.Message) => void;
226
+ }
227
+
228
+ /**
229
+ * The complete codec contract that a core transport needs.
230
+ *
231
+ * Combines factory methods (createEncoder, createDecoder, createAccumulator)
232
+ * with protocol knowledge (isTerminal). Transport-level concerns like turn
233
+ * correlation, optimistic reconciliation, and cancel signals
234
+ * are handled by the transport layer using standard `x-ably-*` headers.
235
+ */
236
+ export interface Codec<TEvent, TMessage> {
237
+ /** Create a streaming encoder bound to the given channel. */
238
+ createEncoder(channel: ChannelWriter, options?: EncoderOptions): StreamEncoder<TEvent, TMessage>;
239
+ /** Create a decoder for converting Ably messages back into domain outputs. */
240
+ createDecoder(): StreamDecoder<TEvent, TMessage>;
241
+ /** Create an accumulator for building domain messages from decoder outputs. */
242
+ createAccumulator(): MessageAccumulator<TEvent, TMessage>;
243
+
244
+ /** Whether an event signals stream completion (finish, error, abort). */
245
+ isTerminal(event: TEvent): boolean;
246
+
247
+ /** Return a stable key for a message (used by MessageStore for upsert/delete). */
248
+ getMessageKey(message: TMessage): string;
249
+ }