@ably/ai-transport 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -100
- package/dist/ably-ai-transport.js +1553 -1238
- package/dist/ably-ai-transport.js.map +1 -1
- package/dist/ably-ai-transport.umd.cjs +1 -1
- package/dist/ably-ai-transport.umd.cjs.map +1 -1
- package/dist/constants.d.ts +116 -42
- package/dist/core/agent.d.ts +29 -0
- package/dist/core/codec/decoder.d.ts +20 -23
- package/dist/core/codec/encoder.d.ts +11 -8
- package/dist/core/codec/index.d.ts +1 -2
- package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
- package/dist/core/codec/types.d.ts +407 -115
- package/dist/core/transport/agent-session.d.ts +10 -0
- package/dist/core/transport/branch-chain.d.ts +43 -0
- package/dist/core/transport/client-session.d.ts +13 -0
- package/dist/core/transport/decode-fold.d.ts +47 -0
- package/dist/core/transport/headers.d.ts +96 -18
- package/dist/core/transport/index.d.ts +5 -6
- package/dist/core/transport/internal/bounded-map.d.ts +20 -0
- package/dist/core/transport/invocation.d.ts +74 -0
- package/dist/core/transport/load-conversation.d.ts +128 -0
- package/dist/core/transport/load-history.d.ts +39 -0
- package/dist/core/transport/pipe-stream.d.ts +9 -9
- package/dist/core/transport/run-manager.d.ts +78 -0
- package/dist/core/transport/tree.d.ts +373 -109
- package/dist/core/transport/types/agent.d.ts +353 -0
- package/dist/core/transport/types/client.d.ts +168 -0
- package/dist/core/transport/types/shared.d.ts +24 -0
- package/dist/core/transport/types/tree.d.ts +315 -0
- package/dist/core/transport/types/view.d.ts +222 -0
- package/dist/core/transport/types.d.ts +13 -553
- package/dist/core/transport/view.d.ts +272 -84
- package/dist/errors.d.ts +21 -10
- package/dist/index.d.ts +6 -8
- package/dist/logger.d.ts +12 -0
- package/dist/react/ably-ai-transport-react.js +976 -990
- package/dist/react/ably-ai-transport-react.js.map +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
- package/dist/react/contexts/client-session-context.d.ts +36 -0
- package/dist/react/contexts/client-session-provider.d.ts +53 -0
- package/dist/react/create-session-hooks.d.ts +116 -0
- package/dist/react/index.d.ts +12 -12
- package/dist/react/internal/use-resolved-session.d.ts +36 -0
- package/dist/react/use-ably-messages.d.ts +17 -14
- package/dist/react/use-client-session.d.ts +81 -0
- package/dist/react/use-create-view.d.ts +14 -13
- package/dist/react/use-tree.d.ts +30 -15
- package/dist/react/use-view.d.ts +82 -51
- package/dist/utils.d.ts +32 -23
- package/dist/vercel/ably-ai-transport-vercel.js +2573 -2086
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
- package/dist/vercel/codec/decoder.d.ts +5 -18
- package/dist/vercel/codec/encoder.d.ts +6 -36
- package/dist/vercel/codec/events.d.ts +51 -0
- package/dist/vercel/codec/index.d.ts +24 -12
- package/dist/vercel/codec/reducer.d.ts +144 -0
- package/dist/vercel/codec/tool-transitions.d.ts +2 -2
- package/dist/vercel/index.d.ts +4 -5
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +3907 -3266
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +33 -8
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +7 -6
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
- package/dist/vercel/react/index.d.ts +1 -2
- package/dist/vercel/react/use-chat-transport.d.ts +30 -26
- package/dist/vercel/react/use-message-sync.d.ts +17 -30
- package/dist/vercel/run-end-reason.d.ts +29 -0
- package/dist/vercel/transport/chat-transport.d.ts +43 -24
- package/dist/vercel/transport/index.d.ts +25 -21
- package/dist/vercel/transport/run-output-stream.d.ts +56 -0
- package/dist/version.d.ts +2 -0
- package/package.json +30 -23
- package/src/constants.ts +124 -51
- package/src/core/agent.ts +68 -0
- package/src/core/codec/decoder.ts +71 -98
- package/src/core/codec/encoder.ts +113 -65
- package/src/core/codec/index.ts +13 -6
- package/src/core/codec/lifecycle-tracker.ts +10 -9
- package/src/core/codec/types.ts +436 -120
- package/src/core/transport/agent-session.ts +1344 -0
- package/src/core/transport/branch-chain.ts +58 -0
- package/src/core/transport/client-session.ts +775 -0
- package/src/core/transport/decode-fold.ts +91 -0
- package/src/core/transport/headers.ts +181 -22
- package/src/core/transport/index.ts +25 -26
- package/src/core/transport/internal/bounded-map.ts +27 -0
- package/src/core/transport/invocation.ts +98 -0
- package/src/core/transport/load-conversation.ts +355 -0
- package/src/core/transport/load-history.ts +269 -0
- package/src/core/transport/pipe-stream.ts +54 -39
- package/src/core/transport/run-manager.ts +249 -0
- package/src/core/transport/tree.ts +926 -308
- package/src/core/transport/types/agent.ts +407 -0
- package/src/core/transport/types/client.ts +211 -0
- package/src/core/transport/types/shared.ts +27 -0
- package/src/core/transport/types/tree.ts +344 -0
- package/src/core/transport/types/view.ts +259 -0
- package/src/core/transport/types.ts +13 -706
- package/src/core/transport/view.ts +864 -433
- package/src/errors.ts +22 -9
- package/src/event-emitter.ts +3 -2
- package/src/index.ts +52 -41
- package/src/logger.ts +14 -1
- package/src/react/contexts/client-session-context.ts +41 -0
- package/src/react/contexts/client-session-provider.tsx +186 -0
- package/src/react/create-session-hooks.ts +141 -0
- package/src/react/index.ts +23 -13
- package/src/react/internal/use-resolved-session.ts +63 -0
- package/src/react/use-ably-messages.ts +32 -22
- package/src/react/use-client-session.ts +201 -0
- package/src/react/use-create-view.ts +33 -29
- package/src/react/use-tree.ts +61 -30
- package/src/react/use-view.ts +139 -97
- package/src/utils.ts +63 -45
- package/src/vercel/codec/decoder.ts +336 -258
- package/src/vercel/codec/encoder.ts +343 -205
- package/src/vercel/codec/events.ts +87 -0
- package/src/vercel/codec/index.ts +60 -13
- package/src/vercel/codec/reducer.ts +977 -0
- package/src/vercel/codec/tool-transitions.ts +2 -2
- package/src/vercel/index.ts +6 -19
- package/src/vercel/react/contexts/chat-transport-context.ts +7 -6
- package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
- package/src/vercel/react/index.ts +3 -5
- package/src/vercel/react/use-chat-transport.ts +47 -49
- package/src/vercel/react/use-message-sync.ts +80 -39
- package/src/vercel/run-end-reason.ts +78 -0
- package/src/vercel/transport/chat-transport.ts +392 -98
- package/src/vercel/transport/index.ts +39 -38
- package/src/vercel/transport/run-output-stream.ts +170 -0
- package/src/version.ts +2 -0
- package/dist/core/transport/client-transport.d.ts +0 -10
- package/dist/core/transport/decode-history.d.ts +0 -43
- package/dist/core/transport/server-transport.d.ts +0 -7
- package/dist/core/transport/stream-router.d.ts +0 -29
- package/dist/core/transport/turn-manager.d.ts +0 -37
- package/dist/react/contexts/transport-context.d.ts +0 -31
- package/dist/react/contexts/transport-provider.d.ts +0 -49
- package/dist/react/create-transport-hooks.d.ts +0 -124
- package/dist/react/use-active-turns.d.ts +0 -12
- package/dist/react/use-client-transport.d.ts +0 -80
- package/dist/vercel/codec/accumulator.d.ts +0 -21
- package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
- package/dist/vercel/tool-approvals.d.ts +0 -124
- package/dist/vercel/tool-events.d.ts +0 -26
- package/src/core/transport/client-transport.ts +0 -977
- package/src/core/transport/decode-history.ts +0 -485
- package/src/core/transport/server-transport.ts +0 -612
- package/src/core/transport/stream-router.ts +0 -136
- package/src/core/transport/turn-manager.ts +0 -165
- package/src/react/contexts/transport-context.ts +0 -37
- package/src/react/contexts/transport-provider.tsx +0 -164
- package/src/react/create-transport-hooks.ts +0 -144
- package/src/react/use-active-turns.ts +0 -72
- package/src/react/use-client-transport.ts +0 -197
- package/src/vercel/codec/accumulator.ts +0 -588
- package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
- package/src/vercel/tool-approvals.ts +0 -380
- package/src/vercel/tool-events.ts +0 -53
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Encoder core — message append lifecycle machinery.
|
|
3
3
|
*
|
|
4
|
-
* Provides Ably primitives (publish, append, close,
|
|
4
|
+
* Provides Ably primitives (publish, append, close, cancel, flush) that
|
|
5
5
|
* domain-specific encoders wire their event types to.
|
|
6
6
|
*
|
|
7
7
|
* Domain encoders call `createEncoderCore(writer, options)` and use the
|
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
import * as Ably from 'ably';
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
HEADER_CODEC_MESSAGE_ID,
|
|
16
|
+
HEADER_DISCRETE,
|
|
17
|
+
HEADER_STATUS,
|
|
18
|
+
HEADER_STREAM,
|
|
19
|
+
HEADER_STREAM_ID,
|
|
20
|
+
} from '../../constants.js';
|
|
15
21
|
import { ErrorCode } from '../../errors.js';
|
|
16
22
|
import type { Logger } from '../../logger.js';
|
|
17
23
|
import { mergeHeaders } from '../../utils.js';
|
|
@@ -36,8 +42,20 @@ interface StreamState {
|
|
|
36
42
|
name: string;
|
|
37
43
|
streamId: string;
|
|
38
44
|
accumulated: string;
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
/** Transport-tier headers repeated on every append (`extras.ai.transport`). */
|
|
46
|
+
persistentTransport: Record<string, string>;
|
|
47
|
+
/** Codec-tier headers repeated on every append (`extras.ai.codec`). */
|
|
48
|
+
persistentCodec: Record<string, string>;
|
|
49
|
+
cancelled: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The SDK's `extras.ai` namespace as written to the wire: a `transport` tier
|
|
54
|
+
* (always present on SDK-published messages) and an optional `codec` tier.
|
|
55
|
+
*/
|
|
56
|
+
interface AiExtras {
|
|
57
|
+
transport: Record<string, string>;
|
|
58
|
+
codec?: Record<string, string>;
|
|
41
59
|
}
|
|
42
60
|
|
|
43
61
|
interface PendingAppend {
|
|
@@ -57,32 +75,35 @@ export interface EncoderCore {
|
|
|
57
75
|
/** Publish multiple discrete messages atomically in a single channel publish. */
|
|
58
76
|
publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult>;
|
|
59
77
|
|
|
60
|
-
/** Start a streamed message with
|
|
78
|
+
/** Start a streamed message with status:streaming. */
|
|
61
79
|
startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void>;
|
|
62
80
|
|
|
63
81
|
/**
|
|
64
82
|
* Append data to an in-flight streamed message. Fire-and-forget: errors are
|
|
65
|
-
* collected internally and surfaced by {@link closeStream}
|
|
83
|
+
* collected internally and surfaced by {@link closeStream}, {@link cancelStream},
|
|
84
|
+
* {@link cancelAllStreams} or {@link close}.
|
|
85
|
+
* @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId` or the core is closed.
|
|
66
86
|
*/
|
|
67
87
|
appendStream(streamId: string, data: string): void;
|
|
68
88
|
|
|
69
89
|
/**
|
|
70
|
-
* Close a streamed message with
|
|
90
|
+
* Close a streamed message with status:complete. Flushes all pending
|
|
71
91
|
* appends for recovery before returning. Repeats persistent and payload headers.
|
|
92
|
+
* @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId`, or the encoder has been closed; EncoderRecoveryFailed if a failed append cannot be recovered during the flush.
|
|
72
93
|
*/
|
|
73
94
|
closeStream(streamId: string, payload: StreamPayload): Promise<void>;
|
|
74
95
|
|
|
75
96
|
/**
|
|
76
|
-
*
|
|
97
|
+
* Cancel a single in-progress stream (status:cancelled) and flush all
|
|
77
98
|
* pending appends for recovery before returning.
|
|
78
99
|
*/
|
|
79
|
-
|
|
100
|
+
cancelStream(streamId: string, opts?: WriteOptions): Promise<void>;
|
|
80
101
|
|
|
81
102
|
/**
|
|
82
|
-
*
|
|
103
|
+
* Cancel all in-progress streams (status:cancelled) and flush all
|
|
83
104
|
* pending appends for recovery before returning.
|
|
84
105
|
*/
|
|
85
|
-
|
|
106
|
+
cancelAllStreams(opts?: WriteOptions): Promise<void>;
|
|
86
107
|
|
|
87
108
|
/** Flush + clear trackers. Idempotent. */
|
|
88
109
|
close(): Promise<void>;
|
|
@@ -128,14 +149,7 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
128
149
|
async publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult> {
|
|
129
150
|
this._assertNotClosed();
|
|
130
151
|
this._logger?.trace('DefaultEncoderCore.publishDiscreteBatch();', { count: payloads.length });
|
|
131
|
-
const msgs = payloads.map((p) => this._buildDiscreteMessage(p, opts));
|
|
132
|
-
// Mark batch-published payloads as discrete message parts (from writeMessages).
|
|
133
|
-
// The decoder relies on this header to distinguish message parts from lifecycle
|
|
134
|
-
// events that also happen to be discrete (x-ably-stream: false).
|
|
135
|
-
for (const msg of msgs) {
|
|
136
|
-
// CAST: extras is built by _buildDiscreteMessage with a known { headers } shape.
|
|
137
|
-
(msg.extras as { headers: Record<string, string> }).headers[HEADER_DISCRETE] = 'true';
|
|
138
|
-
}
|
|
152
|
+
const msgs = payloads.map((p) => this._buildDiscreteMessage(p, opts, true));
|
|
139
153
|
return this._writer.publish(msgs);
|
|
140
154
|
}
|
|
141
155
|
|
|
@@ -144,16 +158,17 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
144
158
|
this._assertNotClosed();
|
|
145
159
|
this._logger?.trace('DefaultEncoderCore.startStream();', { name: payload.name, streamId });
|
|
146
160
|
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
const transport = this._buildTransport(payload.transportHeaders, opts);
|
|
162
|
+
transport[HEADER_STREAM] = 'true';
|
|
163
|
+
transport[HEADER_STATUS] = 'streaming';
|
|
164
|
+
transport[HEADER_STREAM_ID] = streamId;
|
|
165
|
+
const codec = payload.codecHeaders ?? {};
|
|
151
166
|
|
|
152
167
|
const clientId = this._resolveClientId(opts);
|
|
153
168
|
const msg: Ably.Message = {
|
|
154
169
|
name: payload.name,
|
|
155
170
|
data: payload.data,
|
|
156
|
-
extras: {
|
|
171
|
+
extras: { ai: this._aiExtras(transport, codec) },
|
|
157
172
|
...(clientId ? { clientId } : {}),
|
|
158
173
|
};
|
|
159
174
|
|
|
@@ -175,8 +190,9 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
175
190
|
name: payload.name,
|
|
176
191
|
streamId,
|
|
177
192
|
accumulated: payload.data,
|
|
178
|
-
|
|
179
|
-
|
|
193
|
+
persistentTransport: transport,
|
|
194
|
+
persistentCodec: codec,
|
|
195
|
+
cancelled: false,
|
|
180
196
|
});
|
|
181
197
|
|
|
182
198
|
this._logger?.debug('DefaultEncoderCore.startStream(); stream started', {
|
|
@@ -204,7 +220,7 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
204
220
|
const appendMsg: Ably.Message = {
|
|
205
221
|
serial: tracker.serial,
|
|
206
222
|
data,
|
|
207
|
-
extras: {
|
|
223
|
+
extras: { ai: this._aiExtras({ ...tracker.persistentTransport }, { ...tracker.persistentCodec }) },
|
|
208
224
|
};
|
|
209
225
|
|
|
210
226
|
this._invokeOnMessage(appendMsg);
|
|
@@ -229,13 +245,13 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
229
245
|
// Accumulate closing data so recovery has the full content
|
|
230
246
|
tracker.accumulated += payload.data;
|
|
231
247
|
|
|
232
|
-
const
|
|
233
|
-
|
|
248
|
+
const { transport, codec } = this._buildClosing(tracker, payload);
|
|
249
|
+
transport[HEADER_STATUS] = 'complete';
|
|
234
250
|
|
|
235
251
|
const msg: Ably.Message = {
|
|
236
252
|
serial: tracker.serial,
|
|
237
253
|
data: payload.data,
|
|
238
|
-
extras: {
|
|
254
|
+
extras: { ai: this._aiExtras(transport, codec) },
|
|
239
255
|
};
|
|
240
256
|
|
|
241
257
|
this._invokeOnMessage(msg);
|
|
@@ -248,28 +264,28 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
248
264
|
}
|
|
249
265
|
|
|
250
266
|
// Spec: AIT-CD5, AIT-CD5b
|
|
251
|
-
async
|
|
267
|
+
async cancelStream(streamId: string, opts?: WriteOptions): Promise<void> {
|
|
252
268
|
this._assertNotClosed();
|
|
253
|
-
this._logger?.trace('DefaultEncoderCore.
|
|
269
|
+
this._logger?.trace('DefaultEncoderCore.cancelStream();', { streamId });
|
|
254
270
|
|
|
255
271
|
const tracker = this._trackers.get(streamId);
|
|
256
272
|
if (!tracker) {
|
|
257
273
|
throw new Ably.ErrorInfo(
|
|
258
|
-
`unable to
|
|
274
|
+
`unable to cancel stream; no active stream for streamId '${streamId}'`,
|
|
259
275
|
ErrorCode.InvalidArgument,
|
|
260
276
|
400,
|
|
261
277
|
);
|
|
262
278
|
}
|
|
263
279
|
|
|
264
|
-
tracker.
|
|
280
|
+
tracker.cancelled = true;
|
|
265
281
|
|
|
266
|
-
const
|
|
267
|
-
|
|
282
|
+
const { transport, codec } = this._buildClosing(tracker, undefined, opts);
|
|
283
|
+
transport[HEADER_STATUS] = 'cancelled';
|
|
268
284
|
|
|
269
285
|
const msg: Ably.Message = {
|
|
270
286
|
serial: tracker.serial,
|
|
271
287
|
data: '',
|
|
272
|
-
extras: {
|
|
288
|
+
extras: { ai: this._aiExtras(transport, codec) },
|
|
273
289
|
};
|
|
274
290
|
|
|
275
291
|
this._invokeOnMessage(msg);
|
|
@@ -278,24 +294,24 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
278
294
|
|
|
279
295
|
await this._flushPending();
|
|
280
296
|
|
|
281
|
-
this._logger?.debug('DefaultEncoderCore.
|
|
297
|
+
this._logger?.debug('DefaultEncoderCore.cancelStream(); stream cancelled', { streamId });
|
|
282
298
|
}
|
|
283
299
|
|
|
284
300
|
// Spec: AIT-CD5a
|
|
285
|
-
async
|
|
301
|
+
async cancelAllStreams(opts?: WriteOptions): Promise<void> {
|
|
286
302
|
this._assertNotClosed();
|
|
287
|
-
this._logger?.trace('DefaultEncoderCore.
|
|
303
|
+
this._logger?.trace('DefaultEncoderCore.cancelAllStreams();', { streamCount: this._trackers.size });
|
|
288
304
|
|
|
289
305
|
for (const tracker of this._trackers.values()) {
|
|
290
|
-
tracker.
|
|
306
|
+
tracker.cancelled = true;
|
|
291
307
|
|
|
292
|
-
const
|
|
293
|
-
|
|
308
|
+
const { transport, codec } = this._buildClosing(tracker, undefined, opts);
|
|
309
|
+
transport[HEADER_STATUS] = 'cancelled';
|
|
294
310
|
|
|
295
311
|
const msg: Ably.Message = {
|
|
296
312
|
serial: tracker.serial,
|
|
297
313
|
data: '',
|
|
298
|
-
extras: {
|
|
314
|
+
extras: { ai: this._aiExtras(transport, codec) },
|
|
299
315
|
};
|
|
300
316
|
|
|
301
317
|
this._invokeOnMessage(msg);
|
|
@@ -354,11 +370,16 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
354
370
|
const tracker = this._trackers.get(streamId);
|
|
355
371
|
if (!tracker) continue;
|
|
356
372
|
|
|
357
|
-
const recoveryStatus = tracker.
|
|
373
|
+
const recoveryStatus = tracker.cancelled ? 'cancelled' : 'complete';
|
|
358
374
|
const msg: Ably.Message = {
|
|
359
375
|
serial: tracker.serial,
|
|
360
376
|
data: tracker.accumulated,
|
|
361
|
-
extras: {
|
|
377
|
+
extras: {
|
|
378
|
+
ai: this._aiExtras(
|
|
379
|
+
{ ...tracker.persistentTransport, [HEADER_STATUS]: recoveryStatus },
|
|
380
|
+
{ ...tracker.persistentCodec },
|
|
381
|
+
),
|
|
382
|
+
},
|
|
362
383
|
};
|
|
363
384
|
|
|
364
385
|
try {
|
|
@@ -415,25 +436,53 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
415
436
|
return opts?.clientId ?? this._defaultClientId;
|
|
416
437
|
}
|
|
417
438
|
|
|
418
|
-
|
|
439
|
+
/**
|
|
440
|
+
* Build the transport-tier header record for a message: caller-configured
|
|
441
|
+
* transport headers (default extras + per-write overrides) layered with any
|
|
442
|
+
* transport headers the codec payload stamps directly, plus the message-id.
|
|
443
|
+
* @param payloadTransport - Transport headers carried on the codec payload.
|
|
444
|
+
* @param opts - Optional per-write overrides.
|
|
445
|
+
* @returns The transport-tier headers record (`extras.ai.transport`).
|
|
446
|
+
*/
|
|
447
|
+
private _buildTransport(
|
|
448
|
+
payloadTransport: Record<string, string> | undefined,
|
|
449
|
+
opts?: WriteOptions,
|
|
450
|
+
): Record<string, string> {
|
|
419
451
|
const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);
|
|
420
|
-
const
|
|
452
|
+
const transport = { ...callerHeaders, ...payloadTransport };
|
|
421
453
|
if (opts?.messageId !== undefined) {
|
|
422
|
-
|
|
454
|
+
transport[HEADER_CODEC_MESSAGE_ID] = opts.messageId;
|
|
423
455
|
}
|
|
424
|
-
return
|
|
456
|
+
return transport;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Assemble the `extras.ai` namespace from its two tiers, omitting the codec
|
|
461
|
+
* tier when empty.
|
|
462
|
+
* @param transport - Transport-tier headers (always present on SDK messages).
|
|
463
|
+
* @param codec - Codec-tier headers; omitted from the wire when empty.
|
|
464
|
+
* @returns The `extras.ai` object.
|
|
465
|
+
*/
|
|
466
|
+
private _aiExtras(transport: Record<string, string>, codec: Record<string, string>): AiExtras {
|
|
467
|
+
return Object.keys(codec).length > 0 ? { transport, codec } : { transport };
|
|
425
468
|
}
|
|
426
469
|
|
|
427
|
-
private _buildDiscreteMessage(payload: MessagePayload, opts?: WriteOptions): Ably.Message {
|
|
428
|
-
const
|
|
429
|
-
|
|
470
|
+
private _buildDiscreteMessage(payload: MessagePayload, opts?: WriteOptions, discrete = false): Ably.Message {
|
|
471
|
+
const transport = this._buildTransport(payload.transportHeaders, opts);
|
|
472
|
+
transport[HEADER_STREAM] = 'false';
|
|
473
|
+
if (discrete) {
|
|
474
|
+
// Mark batch-published payloads as discrete message parts (from writeMessages).
|
|
475
|
+
// The decoder relies on this header to distinguish message parts from lifecycle
|
|
476
|
+
// events that also happen to be discrete (stream: false).
|
|
477
|
+
transport[HEADER_DISCRETE] = 'true';
|
|
478
|
+
}
|
|
430
479
|
const clientId = this._resolveClientId(opts);
|
|
431
480
|
|
|
432
481
|
const msg: Ably.Message = {
|
|
433
482
|
name: payload.name,
|
|
434
483
|
data: payload.data,
|
|
435
484
|
extras: {
|
|
436
|
-
|
|
485
|
+
ai: this._aiExtras(transport, payload.codecHeaders ?? {}),
|
|
437
486
|
...(payload.ephemeral ? { ephemeral: true } : {}),
|
|
438
487
|
},
|
|
439
488
|
...(clientId ? { clientId } : {}),
|
|
@@ -444,24 +493,23 @@ class DefaultEncoderCore implements EncoderCore {
|
|
|
444
493
|
}
|
|
445
494
|
|
|
446
495
|
/**
|
|
447
|
-
* Build
|
|
448
|
-
* persistent headers (Ably replaces the entire extras object on append).
|
|
496
|
+
* Build both header tiers for a closing append. Closing appends must repeat
|
|
497
|
+
* ALL persistent headers (Ably replaces the entire extras object on append).
|
|
449
498
|
* Then layer caller and codec overrides.
|
|
450
499
|
* @param tracker - The stream tracker with persistent headers.
|
|
451
|
-
* @param
|
|
500
|
+
* @param payload - The closing stream payload (codec + transport headers).
|
|
452
501
|
* @param opts - Optional per-write overrides.
|
|
453
|
-
* @returns
|
|
502
|
+
* @returns The two tiers for the closing append.
|
|
454
503
|
*/
|
|
455
|
-
private
|
|
504
|
+
private _buildClosing(
|
|
456
505
|
tracker: StreamState,
|
|
457
|
-
|
|
506
|
+
payload: StreamPayload | undefined,
|
|
458
507
|
opts?: WriteOptions,
|
|
459
|
-
): Record<string, string> {
|
|
460
|
-
const h = { ...tracker.persistentHeaders };
|
|
508
|
+
): { transport: Record<string, string>; codec: Record<string, string> } {
|
|
461
509
|
const callerHeaders = mergeHeaders(this._defaultExtras?.headers, opts?.extras?.headers);
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return
|
|
510
|
+
const transport = { ...tracker.persistentTransport, ...callerHeaders, ...payload?.transportHeaders };
|
|
511
|
+
const codec = { ...tracker.persistentCodec, ...payload?.codecHeaders };
|
|
512
|
+
return { transport, codec };
|
|
465
513
|
}
|
|
466
514
|
}
|
|
467
515
|
|
package/src/core/codec/index.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
export { eventOutput } from './decoder.js';
|
|
2
1
|
export type {
|
|
3
2
|
ChannelWriter,
|
|
4
3
|
Codec,
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
CodecInputEvent,
|
|
5
|
+
CodecMessage,
|
|
6
|
+
CodecOutputEvent,
|
|
7
|
+
DecodedMessage,
|
|
8
|
+
Decoder,
|
|
9
|
+
Encoder,
|
|
7
10
|
EncoderOptions,
|
|
8
11
|
Extras,
|
|
9
|
-
MessageAccumulator,
|
|
10
12
|
MessagePayload,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
Reducer,
|
|
14
|
+
ReducerMeta,
|
|
15
|
+
Regenerate,
|
|
13
16
|
StreamPayload,
|
|
14
17
|
StreamTrackerState,
|
|
18
|
+
ToolApprovalResponse,
|
|
19
|
+
ToolResult,
|
|
20
|
+
ToolResultError,
|
|
21
|
+
UserMessage,
|
|
15
22
|
WriteOptions,
|
|
16
23
|
} from './types.js';
|
|
17
24
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic lifecycle tracker for codec decoders.
|
|
3
3
|
*
|
|
4
|
-
* Manages per-scope (typically per-
|
|
4
|
+
* Manages per-scope (typically per-run) tracking of lifecycle phases that
|
|
5
5
|
* must be emitted before content events. When a phase has not been emitted
|
|
6
6
|
* (e.g. mid-stream join), the tracker synthesizes the missing events using
|
|
7
7
|
* codec-provided build functions.
|
|
@@ -40,15 +40,16 @@ export interface PhaseConfig<TEvent> {
|
|
|
40
40
|
* Per-scope lifecycle tracker that ensures required phases are emitted
|
|
41
41
|
* before content events, synthesizing missing ones for mid-stream joins.
|
|
42
42
|
*
|
|
43
|
-
* Scoped by an arbitrary string key (typically a
|
|
43
|
+
* Scoped by an arbitrary string key (typically a run ID). Each scope
|
|
44
44
|
* tracks independently which phases have been emitted.
|
|
45
45
|
*/
|
|
46
46
|
export interface LifecycleTracker<TEvent> {
|
|
47
47
|
/**
|
|
48
48
|
* Ensure all configured phases have been emitted for the given scope.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* Synthesizes and returns the events for any phases not yet marked as
|
|
50
|
+
* emitted, marking each as emitted so it is not synthesized again.
|
|
51
|
+
* Returns an empty array if all phases are already emitted.
|
|
52
|
+
* @param scopeId - The scope to check (e.g. run ID).
|
|
52
53
|
* @param context - Key-value pairs passed through to phase build functions.
|
|
53
54
|
* @returns Synthetic events for missing phases, in configuration order.
|
|
54
55
|
*/
|
|
@@ -57,7 +58,7 @@ export interface LifecycleTracker<TEvent> {
|
|
|
57
58
|
/**
|
|
58
59
|
* Mark a phase as emitted from the wire (not synthetic). Call this
|
|
59
60
|
* when the real event arrives so the tracker does not re-synthesize it.
|
|
60
|
-
* @param scopeId - The scope (e.g.
|
|
61
|
+
* @param scopeId - The scope (e.g. run ID).
|
|
61
62
|
* @param phaseKey - The phase key to mark.
|
|
62
63
|
*/
|
|
63
64
|
markEmitted(scopeId: string, phaseKey: string): void;
|
|
@@ -66,14 +67,14 @@ export interface LifecycleTracker<TEvent> {
|
|
|
66
67
|
* Reset a phase so it will be re-synthesized on the next
|
|
67
68
|
* {@link ensurePhases} call. Used for repeating phases (e.g. "start-step"
|
|
68
69
|
* resets after "finish-step").
|
|
69
|
-
* @param scopeId - The scope (e.g.
|
|
70
|
+
* @param scopeId - The scope (e.g. run ID).
|
|
70
71
|
* @param phaseKey - The phase key to reset.
|
|
71
72
|
*/
|
|
72
73
|
resetPhase(scopeId: string, phaseKey: string): void;
|
|
73
74
|
|
|
74
75
|
/**
|
|
75
|
-
* Remove all tracking state for a scope. Call on
|
|
76
|
-
* (finish,
|
|
76
|
+
* Remove all tracking state for a scope. Call on run completion
|
|
77
|
+
* (finish, cancel) to free memory.
|
|
77
78
|
* @param scopeId - The scope to clear.
|
|
78
79
|
*/
|
|
79
80
|
clearScope(scopeId: string): void;
|