@ably/ai-transport 0.2.0 → 0.3.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 +10 -19
- package/dist/ably-ai-transport.js +1790 -1091
- 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 +2 -2
- package/dist/core/agent.d.ts +20 -5
- package/dist/core/channel-options.d.ts +57 -0
- package/dist/core/codec/codec-event.d.ts +9 -0
- package/dist/core/codec/decoder.d.ts +4 -1
- package/dist/core/codec/define-codec.d.ts +100 -0
- package/dist/core/codec/encoder.d.ts +2 -7
- package/dist/core/codec/field-bag.d.ts +85 -0
- package/dist/core/codec/fields.d.ts +141 -0
- package/dist/core/codec/index.d.ts +8 -1
- package/dist/core/codec/input-descriptor-decoder.d.ts +19 -0
- package/dist/core/codec/input-descriptor-encoder.d.ts +22 -0
- package/dist/core/codec/input-descriptors.d.ts +281 -0
- package/dist/core/codec/output-descriptor-decoder.d.ts +29 -0
- package/dist/core/codec/output-descriptor-encoder.d.ts +31 -0
- package/dist/core/codec/output-descriptors.d.ts +237 -0
- package/dist/core/codec/types.d.ts +95 -36
- package/dist/core/codec/well-known-inputs.d.ts +52 -0
- package/dist/core/transport/agent-view.d.ts +296 -0
- package/dist/core/transport/decode-fold.d.ts +40 -32
- package/dist/core/transport/headers.d.ts +30 -1
- package/dist/core/transport/index.d.ts +1 -1
- package/dist/core/transport/invocation.d.ts +1 -1
- package/dist/core/transport/load-history-pages.d.ts +71 -0
- package/dist/core/transport/load-history.d.ts +21 -16
- package/dist/core/transport/run-manager.d.ts +9 -11
- package/dist/core/transport/session-support.d.ts +55 -0
- package/dist/core/transport/tree.d.ts +165 -15
- package/dist/core/transport/types/agent.d.ts +120 -98
- package/dist/core/transport/types/client.d.ts +45 -12
- package/dist/core/transport/types/tree.d.ts +52 -10
- package/dist/core/transport/types/view.d.ts +55 -28
- package/dist/core/transport/view.d.ts +176 -58
- package/dist/core/transport/wire-log.d.ts +102 -0
- package/dist/errors.d.ts +10 -4
- package/dist/index.d.ts +6 -5
- package/dist/react/ably-ai-transport-react.js +784 -415
- 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 +2 -1
- package/dist/react/contexts/client-session-provider.d.ts +3 -0
- package/dist/react/index.d.ts +2 -1
- package/dist/react/internal/skipped-session.d.ts +8 -0
- package/dist/react/use-view.d.ts +3 -3
- package/dist/utils.d.ts +22 -54
- package/dist/vercel/ably-ai-transport-vercel.js +2297 -2026
- 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/decode-lifecycle.d.ts +9 -0
- package/dist/vercel/codec/events.d.ts +1 -2
- package/dist/vercel/codec/fields.d.ts +44 -0
- package/dist/vercel/codec/fold-content.d.ts +16 -0
- package/dist/vercel/codec/fold-data.d.ts +16 -0
- package/dist/vercel/codec/fold-input.d.ts +67 -0
- package/dist/vercel/codec/fold-lifecycle.d.ts +16 -0
- package/dist/vercel/codec/fold-text.d.ts +16 -0
- package/dist/vercel/codec/fold-tool-input.d.ts +17 -0
- package/dist/vercel/codec/fold-tool-output.d.ts +16 -0
- package/dist/vercel/codec/index.d.ts +5 -30
- package/dist/vercel/codec/inputs.d.ts +11 -0
- package/dist/vercel/codec/outputs.d.ts +11 -0
- package/dist/vercel/codec/reducer-state.d.ts +121 -0
- package/dist/vercel/codec/reducer.d.ts +20 -102
- package/dist/vercel/codec/tool-transitions.d.ts +0 -6
- package/dist/vercel/codec/wire-data.d.ts +34 -0
- package/dist/vercel/index.d.ts +1 -0
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +2013 -9500
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +1 -70
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +2 -1
- package/dist/vercel/run-end-reason.d.ts +66 -11
- package/dist/vercel/tool-part.d.ts +21 -0
- package/dist/vercel/transport/chat-transport.d.ts +0 -2
- package/dist/vercel/transport/index.d.ts +1 -1
- package/dist/vercel/transport/run-output-stream.d.ts +6 -8
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/src/constants.ts +2 -2
- package/src/core/agent.ts +43 -19
- package/src/core/channel-options.ts +89 -0
- package/src/core/codec/codec-event.ts +27 -0
- package/src/core/codec/decoder.ts +145 -21
- package/src/core/codec/define-codec.ts +432 -0
- package/src/core/codec/encoder.ts +13 -54
- package/src/core/codec/field-bag.ts +142 -0
- package/src/core/codec/fields.ts +193 -0
- package/src/core/codec/index.ts +43 -0
- package/src/core/codec/input-descriptor-decoder.ts +97 -0
- package/src/core/codec/input-descriptor-encoder.ts +150 -0
- package/src/core/codec/input-descriptors.ts +373 -0
- package/src/core/codec/output-descriptor-decoder.ts +139 -0
- package/src/core/codec/output-descriptor-encoder.ts +101 -0
- package/src/core/codec/output-descriptors.ts +307 -0
- package/src/core/codec/types.ts +99 -36
- package/src/core/codec/well-known-inputs.ts +96 -0
- package/src/core/transport/agent-session.ts +330 -589
- package/src/core/transport/agent-view.ts +738 -0
- package/src/core/transport/client-session.ts +74 -69
- package/src/core/transport/decode-fold.ts +57 -47
- package/src/core/transport/headers.ts +57 -4
- package/src/core/transport/index.ts +2 -1
- package/src/core/transport/invocation.ts +1 -1
- package/src/core/transport/load-history-pages.ts +220 -0
- package/src/core/transport/load-history.ts +63 -61
- package/src/core/transport/pipe-stream.ts +10 -1
- package/src/core/transport/run-manager.ts +25 -31
- package/src/core/transport/session-support.ts +96 -0
- package/src/core/transport/tree.ts +414 -47
- package/src/core/transport/types/agent.ts +129 -102
- package/src/core/transport/types/client.ts +49 -13
- package/src/core/transport/types/tree.ts +61 -12
- package/src/core/transport/types/view.ts +57 -28
- package/src/core/transport/view.ts +520 -172
- package/src/core/transport/wire-log.ts +189 -0
- package/src/errors.ts +10 -3
- package/src/index.ts +44 -11
- package/src/react/contexts/client-session-context.ts +1 -1
- package/src/react/contexts/client-session-provider.tsx +38 -2
- package/src/react/index.ts +2 -1
- package/src/react/internal/skipped-session.ts +62 -0
- package/src/react/use-client-session.ts +7 -30
- package/src/react/use-view.ts +3 -3
- package/src/utils.ts +31 -97
- package/src/vercel/codec/decode-lifecycle.ts +70 -0
- package/src/vercel/codec/events.ts +1 -3
- package/src/vercel/codec/fields.ts +58 -0
- package/src/vercel/codec/fold-content.ts +54 -0
- package/src/vercel/codec/fold-data.ts +46 -0
- package/src/vercel/codec/fold-input.ts +255 -0
- package/src/vercel/codec/fold-lifecycle.ts +85 -0
- package/src/vercel/codec/fold-text.ts +55 -0
- package/src/vercel/codec/fold-tool-input.ts +86 -0
- package/src/vercel/codec/fold-tool-output.ts +79 -0
- package/src/vercel/codec/index.ts +23 -63
- package/src/vercel/codec/inputs.ts +116 -0
- package/src/vercel/codec/outputs.ts +207 -0
- package/src/vercel/codec/reducer-state.ts +169 -0
- package/src/vercel/codec/reducer.ts +52 -838
- package/src/vercel/codec/tool-transitions.ts +1 -12
- package/src/vercel/codec/wire-data.ts +64 -0
- package/src/vercel/index.ts +1 -0
- package/src/vercel/react/contexts/chat-transport-context.ts +1 -1
- package/src/vercel/react/use-chat-transport.ts +8 -28
- package/src/vercel/react/use-message-sync.ts +5 -10
- package/src/vercel/run-end-reason.ts +95 -16
- package/src/vercel/tool-part.ts +25 -0
- package/src/vercel/transport/chat-transport.ts +10 -22
- package/src/vercel/transport/index.ts +1 -1
- package/src/vercel/transport/run-output-stream.ts +7 -8
- package/src/version.ts +1 -1
- package/dist/core/transport/branch-chain.d.ts +0 -43
- package/dist/core/transport/load-conversation.d.ts +0 -128
- package/dist/vercel/codec/decoder.d.ts +0 -9
- package/dist/vercel/codec/encoder.d.ts +0 -11
- package/src/core/transport/branch-chain.ts +0 -58
- package/src/core/transport/load-conversation.ts +0 -355
- package/src/vercel/codec/decoder.ts +0 -696
- package/src/vercel/codec/encoder.ts +0 -548
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Logger } from '../../logger.js';
|
|
2
|
-
import { CodecInputEvent, CodecOutputEvent, Reducer } from '../codec/types.js';
|
|
2
|
+
import { CodecEvent, CodecInputEvent, CodecOutputEvent, Reducer } from '../codec/types.js';
|
|
3
3
|
import { ConversationNode, OutputEvent, RunLifecycleEvent, RunNode, Tree } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Tree — materializes a branching conversation as a forest of nodes. Each turn
|
|
@@ -24,6 +24,14 @@ import { ConversationNode, OutputEvent, RunLifecycleEvent, RunNode, Tree } from
|
|
|
24
24
|
* sharing the same input-node parent (no fork-of).
|
|
25
25
|
*/
|
|
26
26
|
import type * as Ably from 'ably';
|
|
27
|
+
/**
|
|
28
|
+
* How long (in ms, on the Ably message-timestamp timeline) a structurally
|
|
29
|
+
* complete run's event log is retained after the node's last observed
|
|
30
|
+
* activity. Bounds cross-publisher live delivery reorder: a wire can be
|
|
31
|
+
* delivered after a higher-serial wire by at most this window. Conservative
|
|
32
|
+
* placeholder pending confirmation of the actual cross-region bound.
|
|
33
|
+
*/
|
|
34
|
+
export declare const REORDER_WINDOW_MS = 120000;
|
|
27
35
|
/**
|
|
28
36
|
* The primary key a node is indexed under: a reply run's `runId`, or an input
|
|
29
37
|
* node's `codecMessageId` (the client owns it before the agent mints a runId).
|
|
@@ -70,24 +78,34 @@ export interface TreeInternal<TInput extends CodecInputEvent, TOutput extends Co
|
|
|
70
78
|
* @param events.outputs - Agent-published events (`ai-output` wire).
|
|
71
79
|
* @param headers - Transport headers from the inbound Ably message.
|
|
72
80
|
* @param serial - Ably channel serial; undefined for optimistic inserts.
|
|
81
|
+
* @param timestamp - Ably server timestamp (epoch ms) of the message —
|
|
82
|
+
* top-level `Message.timestamp`, the message's create time on every
|
|
83
|
+
* delivery (an append's own receive time lives in `version.timestamp`) —
|
|
84
|
+
* or undefined for optimistic inserts. Advances the Tree's event-log
|
|
85
|
+
* retention clock and the owning node's last-activity time.
|
|
86
|
+
* @param version - The delivery's `Message.version.serial`, or undefined
|
|
87
|
+
* when the delivery carried none (optimistic inserts, never-mutated
|
|
88
|
+
* deliveries from sources that omit it). Guards the node's event log
|
|
89
|
+
* against whole-wire replays: a delivery at or below the version already
|
|
90
|
+
* decoded into its log entry is dropped.
|
|
73
91
|
*/
|
|
74
92
|
applyMessage(events: {
|
|
75
93
|
inputs: TInput[];
|
|
76
94
|
outputs: TOutput[];
|
|
77
|
-
}, headers: Record<string, string>, serial?: string): void;
|
|
95
|
+
}, headers: Record<string, string>, serial?: string, timestamp?: number, version?: string): void;
|
|
78
96
|
/**
|
|
79
97
|
* Apply a run-lifecycle event.
|
|
80
98
|
*
|
|
81
99
|
* - `start`: creates the reply run (if missing) or, for an existing run,
|
|
82
|
-
* sets RunNode.
|
|
100
|
+
* sets RunNode.state to 'active', promotes startSerial, and backfills
|
|
83
101
|
* structural metadata (parent / forkOf / regenerates / invocationId).
|
|
84
|
-
* - `suspend`: sets RunNode.
|
|
102
|
+
* - `suspend`: sets RunNode.state to 'suspended' and records `endSerial`.
|
|
85
103
|
* The run stays live so a resume under the same `runId` picks up where it
|
|
86
104
|
* left off.
|
|
87
|
-
* - `resume`: re-activates an existing suspended Run (
|
|
105
|
+
* - `resume`: re-activates an existing suspended Run (state back to
|
|
88
106
|
* 'active') without touching its structure or serials — a pure re-entry
|
|
89
107
|
* signal. A no-op if the Run is not yet known.
|
|
90
|
-
* - `end`: sets RunNode.
|
|
108
|
+
* - `end`: sets RunNode.state to the terminal reason and records
|
|
91
109
|
* `endSerial`.
|
|
92
110
|
*
|
|
93
111
|
* Always emits a 'run' event to subscribers.
|
|
@@ -169,7 +187,31 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
169
187
|
*/
|
|
170
188
|
private _siblingCache;
|
|
171
189
|
private _siblingCacheVersion;
|
|
172
|
-
|
|
190
|
+
/**
|
|
191
|
+
* Index from `event-id` header to the raw Ably message that carried it.
|
|
192
|
+
* Populated incrementally as messages arrive via {@link emitAblyMessage};
|
|
193
|
+
* reads back the raw message for the agent's input-event lookup
|
|
194
|
+
* ({@link findAblyMessageByEventId}). Bounded by the Tree's lifetime — cleared
|
|
195
|
+
* when the Tree is replaced on continuity loss / session close.
|
|
196
|
+
*/
|
|
197
|
+
private readonly _eventIdIndex;
|
|
198
|
+
/**
|
|
199
|
+
* Event-log retention logical clock: the max Ably message timestamp (epoch
|
|
200
|
+
* ms) observed across every apply, 0 until the first timestamped one. Only
|
|
201
|
+
* ever advances — older-page history application carries smaller timestamps
|
|
202
|
+
* and leaves it (and therefore the sweep) untouched.
|
|
203
|
+
*/
|
|
204
|
+
private _clock;
|
|
205
|
+
/**
|
|
206
|
+
* Keys of structurally complete run nodes (run-start and run-end both
|
|
207
|
+
* observed) whose event logs await the retention window, in completion
|
|
208
|
+
* order. Drained from the front whenever {@link _clock} advances; sweeping
|
|
209
|
+
* only at clock advances keeps a history page's batch atomic — applying an
|
|
210
|
+
* older page can never advance the clock, so a node cannot be swept between
|
|
211
|
+
* its run-start and the rest of its wires in the same page.
|
|
212
|
+
*/
|
|
213
|
+
private readonly _sweepQueue;
|
|
214
|
+
constructor(codec: Reducer<CodecEvent<TInput, TOutput>, TProjection>, logger: Logger);
|
|
173
215
|
/**
|
|
174
216
|
* Compare two nodes (Run or input) for sorted list ordering.
|
|
175
217
|
* Serial-bearing nodes sort by their sort serial (`startSerial` for runs,
|
|
@@ -224,6 +266,88 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
224
266
|
* @param messageId - The reducer routing key (codec-message-id), or undefined.
|
|
225
267
|
*/
|
|
226
268
|
private _foldInto;
|
|
269
|
+
/**
|
|
270
|
+
* Record a serial-bearing wire in the node's event log and fold it. Events
|
|
271
|
+
* extending the log tail (the common case — in-order live delivery) fold
|
|
272
|
+
* incrementally onto the existing projection, identical to a bare
|
|
273
|
+
* {@link _foldInto}. Events that land earlier in the log (an earlier-serial
|
|
274
|
+
* wire delivered late — cross-publisher reorder, or a history page applying
|
|
275
|
+
* an older message after a newer one) cannot be folded incrementally without
|
|
276
|
+
* corrupting serial order, so the node is refolded from the whole log via
|
|
277
|
+
* {@link _refold}.
|
|
278
|
+
*
|
|
279
|
+
* Optimistic (serial-less) applies and empty event batches are not logged;
|
|
280
|
+
* an optimistic seed folds into the projection but never into the log, and
|
|
281
|
+
* marks the node `optimistic`. The first serial-bearing wire (the echo of
|
|
282
|
+
* the optimistic input, which re-delivers the seeded content) refolds the
|
|
283
|
+
* node from the log alone — rebuilding the projection without the seed
|
|
284
|
+
* rather than folding the echo on top of it. The codec therefore never sees
|
|
285
|
+
* the seed and its echo in one projection, and needs no seed-replacement
|
|
286
|
+
* logic. The seed must be a faithful preview of the echo, since the echo's
|
|
287
|
+
* content is what survives.
|
|
288
|
+
*
|
|
289
|
+
* Whole-wire replays are dropped at the log: each entry records the highest
|
|
290
|
+
* `Message.version.serial` decoded into it (`decodedThrough`), so a
|
|
291
|
+
* version-bearing delivery the entry has already incorporated — a second
|
|
292
|
+
* hydration over a populated Tree, a remounted View's re-fetch, an agent
|
|
293
|
+
* re-walk — records nothing and folds nothing. A newer version of a
|
|
294
|
+
* discrete wire (an edited discrete) is likewise dropped; propagating edits
|
|
295
|
+
* into projections is deliberately out of scope.
|
|
296
|
+
* @param entry - The internal node whose log and projection are updated.
|
|
297
|
+
* @param events - The decoded events to fold, in wire order.
|
|
298
|
+
* @param serial - Ably channel serial; undefined for an optimistic insert.
|
|
299
|
+
* @param messageId - The reducer routing key (codec-message-id), or undefined.
|
|
300
|
+
* @param version - The delivery's `Message.version.serial`, or undefined.
|
|
301
|
+
* @param streamed - Whether the delivery is part of a streamed wire.
|
|
302
|
+
*/
|
|
303
|
+
private _recordAndFold;
|
|
304
|
+
/**
|
|
305
|
+
* Rebuild a node's projection from its event log in canonical serial order:
|
|
306
|
+
* a fresh {@link Reducer.init} folded through every logged event, each with
|
|
307
|
+
* its own wire's serial and messageId. Used when a late, earlier-serial wire
|
|
308
|
+
* makes incremental folding unsound. Reducer purity (a fold is a function of
|
|
309
|
+
* its inputs alone) is what makes the rebuild faithful; the per-fold
|
|
310
|
+
* try/catch mirrors {@link _foldInto} so one throwing event can't abort the
|
|
311
|
+
* rebuild.
|
|
312
|
+
*
|
|
313
|
+
* Rebuilds the projection only; the surrounding apply emits its usual
|
|
314
|
+
* `output` event carrying just the triggering wire's events. Consumers read
|
|
315
|
+
* the rebuilt state from `node.projection` (the View recomputes its message
|
|
316
|
+
* list from it), so on the refold path the event's `events` payload is not a
|
|
317
|
+
* delta of the full projection change.
|
|
318
|
+
* @param entry - The internal node whose projection is rebuilt in place.
|
|
319
|
+
*/
|
|
320
|
+
private _refold;
|
|
321
|
+
/**
|
|
322
|
+
* Record activity on a node and advance the retention clock. Updates the
|
|
323
|
+
* node's `lastActivityTs` and the Tree-wide `_clock` to the given timestamp
|
|
324
|
+
* when it is newer; a clock advance drains the sweep queue. `undefined`
|
|
325
|
+
* (an optimistic local apply) advances nothing.
|
|
326
|
+
* @param entry - The node the activity belongs to.
|
|
327
|
+
* @param timestamp - Ably message timestamp (epoch ms), or undefined.
|
|
328
|
+
*/
|
|
329
|
+
private _recordActivity;
|
|
330
|
+
/**
|
|
331
|
+
* Queue a run node's event log for retention sweeping once the node is
|
|
332
|
+
* structurally complete: its run-start (serial floor — no older history page
|
|
333
|
+
* can add to it) and its run-end (no further agent output) have both been
|
|
334
|
+
* observed. The actual drop happens in {@link _drainSweepQueue} once the
|
|
335
|
+
* reorder window has also lapsed. No-op for input nodes (never swept — no
|
|
336
|
+
* floor marker, and their logs are bounded by one user message), for nodes
|
|
337
|
+
* already queued or swept, and while either marker is missing.
|
|
338
|
+
* @param entry - The node to consider for sweeping.
|
|
339
|
+
*/
|
|
340
|
+
private _maybeQueueSweep;
|
|
341
|
+
/**
|
|
342
|
+
* Drop the event logs of queued nodes whose retention window has lapsed:
|
|
343
|
+
* `lastActivityTs + REORDER_WINDOW_MS < _clock`. Drains from the front and
|
|
344
|
+
* stops at the first node still inside the window — completion order is
|
|
345
|
+
* time-ordered for live traffic, so this is amortised O(1) per apply, and
|
|
346
|
+
* stopping early only ever over-retains (memory, never correctness). Called
|
|
347
|
+
* only when the clock advances, so applying an older history page (smaller
|
|
348
|
+
* timestamps) can never sweep mid-batch. Deleted nodes are skipped.
|
|
349
|
+
*/
|
|
350
|
+
private _drainSweepQueue;
|
|
227
351
|
private _addToParentIndex;
|
|
228
352
|
private _removeFromParentIndex;
|
|
229
353
|
/**
|
|
@@ -297,7 +421,7 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
297
421
|
applyMessage(events: {
|
|
298
422
|
inputs: TInput[];
|
|
299
423
|
outputs: TOutput[];
|
|
300
|
-
}, headers: Record<string, string>, serial?: string): void;
|
|
424
|
+
}, headers: Record<string, string>, serial?: string, timestamp?: number, version?: string): void;
|
|
301
425
|
/**
|
|
302
426
|
* Apply a run-less user input wire: create (or promote the serial of) the
|
|
303
427
|
* input node keyed by its codec-message-id, fold the input events into its
|
|
@@ -306,7 +430,9 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
306
430
|
* @param codecMessageId - The input node's codec-message-id (its primary key).
|
|
307
431
|
* @param headers - Transport headers from the inbound Ably message.
|
|
308
432
|
* @param serial - Ably channel serial; undefined for an optimistic insert.
|
|
309
|
-
* @param
|
|
433
|
+
* @param timestamp - Ably server timestamp (epoch ms); undefined for an optimistic insert.
|
|
434
|
+
* @param version - The delivery's `Message.version.serial`, or undefined.
|
|
435
|
+
* @param all - The direction-tagged input events to fold, in wire order.
|
|
310
436
|
*/
|
|
311
437
|
private _applyInputMessage;
|
|
312
438
|
/**
|
|
@@ -322,6 +448,8 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
322
448
|
* @param events.outputs - Agent-published events (`ai-output` wire).
|
|
323
449
|
* @param headers - Transport headers from the inbound Ably message.
|
|
324
450
|
* @param serial - Ably channel serial; undefined for an optimistic insert.
|
|
451
|
+
* @param timestamp - Ably server timestamp (epoch ms); undefined for an optimistic insert.
|
|
452
|
+
* @param version - The delivery's `Message.version.serial`, or undefined.
|
|
325
453
|
*/
|
|
326
454
|
private _applyRunMessage;
|
|
327
455
|
/**
|
|
@@ -365,10 +493,17 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
365
493
|
*/
|
|
366
494
|
private _applyRunResume;
|
|
367
495
|
/**
|
|
368
|
-
* Apply a run-end lifecycle event: record the terminal reason
|
|
369
|
-
*
|
|
370
|
-
* structure, so this never mutates
|
|
371
|
-
* emits.
|
|
496
|
+
* Apply a run-end lifecycle event: record the terminal reason (and, for an
|
|
497
|
+
* error end, the error) as the node's state, plus the serial it ended at.
|
|
498
|
+
* State/endSerial are content, not structure, so this never mutates
|
|
499
|
+
* `_structuralVersion`; the caller owns the emits.
|
|
500
|
+
*
|
|
501
|
+
* A run-end for an unknown runId is a no-op: nothing else is known about the
|
|
502
|
+
* run yet, so there is no node to mark. When that happens during history
|
|
503
|
+
* replay (a page boundary falling just before the run-end, so the run's
|
|
504
|
+
* other wires arrive in later pages), the run is never marked terminal and
|
|
505
|
+
* its event log is retained for the Tree's lifetime — over-retention, never
|
|
506
|
+
* corruption.
|
|
372
507
|
* @param event - The run-end lifecycle event.
|
|
373
508
|
*/
|
|
374
509
|
private _applyRunEnd;
|
|
@@ -382,6 +517,17 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
382
517
|
* @returns A newly-allocated internal run node ready for insertion.
|
|
383
518
|
*/
|
|
384
519
|
private _createRunFromHeaders;
|
|
520
|
+
/**
|
|
521
|
+
* Wrap a freshly-built conversation node in its internal envelope — sort
|
|
522
|
+
* sequence, event log, and retention/promotion state. The single home for
|
|
523
|
+
* those per-node fields, so a new field is added in one place rather than at
|
|
524
|
+
* every node-construction site.
|
|
525
|
+
* @param node - The conversation node to wrap.
|
|
526
|
+
* @param runStartSeen - Whether the run's ai-run-start has been observed
|
|
527
|
+
* (run nodes only; always false for input nodes).
|
|
528
|
+
* @returns A newly-allocated internal node ready for insertion.
|
|
529
|
+
*/
|
|
530
|
+
private _wrapNode;
|
|
385
531
|
/**
|
|
386
532
|
* Allocate a RunNode from already-resolved fields. Shared by the
|
|
387
533
|
* header-driven and lifecycle-driven run creators: both build the identical
|
|
@@ -394,6 +540,7 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
394
540
|
* @param params.clientId - The publishing client's id.
|
|
395
541
|
* @param params.invocationId - The agent invocation id.
|
|
396
542
|
* @param params.startSerial - Ably channel serial; undefined for optimistic inserts.
|
|
543
|
+
* @param params.runStartSeen - Whether the run's ai-run-start has been observed (true only for lifecycle-created runs).
|
|
397
544
|
* @returns A newly-allocated internal run node ready for insertion.
|
|
398
545
|
*/
|
|
399
546
|
private _buildRunNode;
|
|
@@ -418,10 +565,13 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
418
565
|
on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;
|
|
419
566
|
on(event: 'output', handler: (event: OutputEvent<TOutput>) => void): () => void;
|
|
420
567
|
/**
|
|
421
|
-
* Forward a raw Ably message event to tree subscribers.
|
|
568
|
+
* Forward a raw Ably message event to tree subscribers. Also indexes the
|
|
569
|
+
* Ably message by `event-id` header (if present) for
|
|
570
|
+
* {@link findAblyMessageByEventId} lookups.
|
|
422
571
|
* @param msg - The raw Ably message to emit.
|
|
423
572
|
*/
|
|
424
573
|
emitAblyMessage(msg: Ably.InboundMessage): void;
|
|
574
|
+
findAblyMessageByEventId(eventId: string): Ably.InboundMessage | undefined;
|
|
425
575
|
}
|
|
426
576
|
/**
|
|
427
577
|
* Create a Tree that materializes branching conversation history from a flat
|
|
@@ -432,4 +582,4 @@ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends
|
|
|
432
582
|
* directly for internal methods (applyMessage, applyRunLifecycle,
|
|
433
583
|
* emitAblyMessage). Public consumers see the narrower {@link Tree} interface.
|
|
434
584
|
*/
|
|
435
|
-
export declare const createTree: <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(codec: Reducer<TInput
|
|
585
|
+
export declare const createTree: <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(codec: Reducer<CodecEvent<TInput, TOutput>, TProjection>, logger: Logger) => DefaultTree<TInput, TOutput, TProjection>;
|
|
@@ -2,9 +2,10 @@ import { Logger } from '../../../logger.js';
|
|
|
2
2
|
import { Codec, CodecInputEvent, CodecOutputEvent, WriteOptions } from '../../codec/types.js';
|
|
3
3
|
import { Invocation } from '../invocation.js';
|
|
4
4
|
import { CancelRequest, RunEndReason } from './shared.js';
|
|
5
|
-
import { MessageNode } from './tree.js';
|
|
5
|
+
import { MessageNode, Tree } from './tree.js';
|
|
6
6
|
/** Agent (server-side) session types: options, run runtime, and the Run / AgentSession contracts. */
|
|
7
7
|
import type * as Ably from 'ably';
|
|
8
|
+
import type * as AblyObjects from 'ably/liveobjects';
|
|
8
9
|
/** Options for creating an agent session. */
|
|
9
10
|
export interface AgentSessionOptions<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
10
11
|
/**
|
|
@@ -30,60 +31,43 @@ export interface AgentSessionOptions<TInput extends CodecInputEvent, TOutput ext
|
|
|
30
31
|
onError?: (error: Ably.ErrorInfo) => void;
|
|
31
32
|
/**
|
|
32
33
|
* How long `Run.start()` will wait for the input event(s) tagged with
|
|
33
|
-
* the run's `invocationId` to arrive on the channel
|
|
34
|
-
*
|
|
34
|
+
* the run's `invocationId` to arrive on the channel — across both the
|
|
35
|
+
* post-attach live subscription and the bounded history scan — before
|
|
36
|
+
* rejecting with `InputEventNotFound`. The rejection bubbles up to the
|
|
35
37
|
* developer's HTTP handler, which should surface it as a non-2xx response
|
|
36
38
|
* so the client's pending send fails.
|
|
37
39
|
* Default: 30000 (30 seconds).
|
|
38
40
|
*/
|
|
39
41
|
inputEventLookupTimeoutMs?: number;
|
|
40
42
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* that subsequent `start()` calls can drain them on registration.
|
|
43
|
+
* How far back in time `Run.start()` scans channel history for the
|
|
44
|
+
* triggering input event. Implements the lookback bound for the
|
|
45
|
+
* input-event scan — anything older than `Date.now() - inputEventLookbackMs`
|
|
46
|
+
* is treated as outside the lookup window.
|
|
46
47
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* — the client whose input was dropped will fail their lookup with
|
|
51
|
-
* `InputEventNotFound`. The eviction is logged at warn level so operators
|
|
52
|
-
* can correlate capacity pressure with `InputEventNotFound` errors.
|
|
48
|
+
* History is fetched with `untilAttach: true` so the scan composes
|
|
49
|
+
* with the live subscription by serial boundary — together they cover
|
|
50
|
+
* every message within `inputEventLookbackMs` of attach.
|
|
53
51
|
*
|
|
54
|
-
*
|
|
52
|
+
* Increase this for long-suspended runs whose continuation may arrive
|
|
53
|
+
* many minutes after the original publish. Decrease it for stricter
|
|
54
|
+
* recency.
|
|
55
|
+
*
|
|
56
|
+
* Default: 120000 (2 minutes).
|
|
55
57
|
*/
|
|
56
|
-
|
|
58
|
+
inputEventLookbackMs?: number;
|
|
57
59
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* duration strings (`"2m"`, `"30s"`) or a count of messages as a string
|
|
63
|
-
* (e.g. `"50"`). Malformed values surface as a channel attach error from
|
|
64
|
-
* Ably; the SDK does not pre-validate.
|
|
65
|
-
*
|
|
66
|
-
* A longer window improves the chances of catching an input event for an
|
|
67
|
-
* agent that takes a while to come up after the client published, but
|
|
68
|
-
* also increases the buffer pressure on `inputEventBufferLimit` because
|
|
69
|
-
* more events may be replayed on attach.
|
|
60
|
+
* Extra Ably channel modes to request on the session's channel, on top of the
|
|
61
|
+
* modes AI Transport always needs. Pass `OBJECT_MODES` (or
|
|
62
|
+
* `['OBJECT_SUBSCRIBE', 'OBJECT_PUBLISH']`) to use Ably LiveObjects via
|
|
63
|
+
* {@link AgentSession.object}. Omit to attach with the default mode set.
|
|
70
64
|
*
|
|
71
|
-
*
|
|
65
|
+
* The session requests the union of these modes with the modes it always
|
|
66
|
+
* needs, so passing extra modes never drops the SDK's required modes. The
|
|
67
|
+
* connection's token/key capability must permit the requested operations,
|
|
68
|
+
* otherwise the server grants only the permitted subset.
|
|
72
69
|
*/
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* A batch of events targeting an existing message.
|
|
77
|
-
* Each node specifies the target message and the events to apply to it.
|
|
78
|
-
* Used for cross-run updates such as tool result delivery.
|
|
79
|
-
*/
|
|
80
|
-
export interface EventsNode<TOutput extends CodecOutputEvent> {
|
|
81
|
-
/** Discriminator — identifies this as an events node. */
|
|
82
|
-
kind: 'event';
|
|
83
|
-
/** The `codec-message-id` of the existing message to update. */
|
|
84
|
-
codecMessageId: string;
|
|
85
|
-
/** Outputs to apply to the target message. */
|
|
86
|
-
events: TOutput[];
|
|
70
|
+
channelModes?: readonly Ably.ChannelMode[];
|
|
87
71
|
}
|
|
88
72
|
/**
|
|
89
73
|
* Options for `Run.pipe` — per-operation overrides for the assistant message.
|
|
@@ -177,7 +161,7 @@ export interface RunRuntime<TOutput extends CodecOutputEvent> {
|
|
|
177
161
|
* `Ably.ErrorInfo` (code `StreamError`) for standardized observability.
|
|
178
162
|
* - Failures in the `onCancel` handler.
|
|
179
163
|
*
|
|
180
|
-
* Publish failures in `start
|
|
164
|
+
* Publish failures in `start` and `end`
|
|
181
165
|
* are not delivered here — those methods reject their returned promise
|
|
182
166
|
* with an `Ably.ErrorInfo`, and the caller should handle it at the await
|
|
183
167
|
* site. Run errors never render the session unusable, but the run may
|
|
@@ -205,21 +189,41 @@ export interface RunView<TMessage> {
|
|
|
205
189
|
/** Options for {@link Run.loadConversation}. */
|
|
206
190
|
export interface LoadConversationOptions {
|
|
207
191
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
192
|
+
* Maximum number of ANCESTOR reply RunNodes to walk back through the
|
|
193
|
+
* chain. Input nodes encountered alongside don't count toward the bound,
|
|
194
|
+
* and neither does the current run's own node (it is the conversation
|
|
195
|
+
* tail, not ancestor context). Default unbounded (walks to the
|
|
196
|
+
* conversation root).
|
|
197
|
+
*
|
|
198
|
+
* Set this to bound the LLM context window — `maxRuns: 5` returns the
|
|
199
|
+
* 5 most-recent prior reply runs and their associated input nodes
|
|
200
|
+
* (each bounded run's triggering input included, so the chain never
|
|
201
|
+
* starts assistant-first), in chronological order.
|
|
210
202
|
*/
|
|
211
|
-
|
|
203
|
+
maxRuns?: number;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* How a run terminates, passed to {@link Run.end}. Discriminated on `reason`:
|
|
207
|
+
* an `'error'` end may carry a terminal `error`; any other reason carries none.
|
|
208
|
+
*/
|
|
209
|
+
export type RunEndParams = {
|
|
210
|
+
/** Why the run ended — any terminal reason other than `'error'`. */
|
|
211
|
+
reason: Exclude<RunEndReason, 'error'>;
|
|
212
|
+
} | {
|
|
213
|
+
/** The run ended in error. */
|
|
214
|
+
reason: 'error';
|
|
212
215
|
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* doesn't exhaust memory.
|
|
216
|
-
* Default: 2000.
|
|
216
|
+
* Optional terminal error to surface to clients. Omit to end in error
|
|
217
|
+
* without detail.
|
|
217
218
|
*/
|
|
218
|
-
|
|
219
|
-
}
|
|
219
|
+
error?: Ably.ErrorInfo;
|
|
220
|
+
};
|
|
220
221
|
/**
|
|
221
222
|
* A server-side run with explicit lifecycle methods. Generic over the codec's
|
|
222
|
-
* output, projection, and message types.
|
|
223
|
+
* output, projection, and message types. `TProjection` is retained for
|
|
224
|
+
* parameter symmetry with {@link AgentSession.createRun}; it does not
|
|
225
|
+
* appear in the Run's public surface today but keeps the type slot
|
|
226
|
+
* available for future per-Run projection accessors.
|
|
223
227
|
*/
|
|
224
228
|
export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
225
229
|
/** The run's unique identifier. */
|
|
@@ -254,7 +258,7 @@ export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
|
254
258
|
/**
|
|
255
259
|
* Publish the run's opening lifecycle event to the channel (run-start, or
|
|
256
260
|
* run-resume for a continuation). Must be called before any other run method
|
|
257
|
-
* (pipe,
|
|
261
|
+
* (pipe, suspend, end).
|
|
258
262
|
*/
|
|
259
263
|
start(): Promise<void>;
|
|
260
264
|
/**
|
|
@@ -263,45 +267,26 @@ export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
|
263
267
|
* Does NOT call end() — the caller must call end() after pipe returns.
|
|
264
268
|
*/
|
|
265
269
|
pipe(stream: ReadableStream<TOutput>, options?: PipeOptions<TOutput>): Promise<StreamResult>;
|
|
266
|
-
/**
|
|
267
|
-
* Publish events targeting existing messages in the tree. Each node
|
|
268
|
-
* specifies a target message (by `codecMessageId`) and the events to apply.
|
|
269
|
-
* Events are encoded and published with the target's `codec-message-id`,
|
|
270
|
-
* so receiving clients apply them to the existing node rather than
|
|
271
|
-
* creating a new one.
|
|
272
|
-
*
|
|
273
|
-
* Used for cross-run updates such as tool result delivery after
|
|
274
|
-
* approval or client-side tool execution.
|
|
275
|
-
*/
|
|
276
|
-
addEvents(nodes: EventsNode<TOutput>[]): Promise<void>;
|
|
277
|
-
/**
|
|
278
|
-
* Fetch every channel message bound to this run and fold them through
|
|
279
|
-
* the codec into a single projection. Used by the agent to reconstruct
|
|
280
|
-
* the run's full state — including client-published tool-output amends
|
|
281
|
-
* the agent didn't observe live — when resuming a suspended run.
|
|
282
|
-
*
|
|
283
|
-
* Uses `channel.history()` (no `untilAttach`) so messages published
|
|
284
|
-
* after the channel was originally attached are still included. Each
|
|
285
|
-
* call paginates until either there are no more pages or an internal
|
|
286
|
-
* safety bound is reached.
|
|
287
|
-
* @returns The TProjection produced by folding every event for this run
|
|
288
|
-
* in serial order. The caller extracts what they need via
|
|
289
|
-
* {@link Codec.getMessages}.
|
|
290
|
-
*/
|
|
291
|
-
loadProjection(): Promise<TProjection>;
|
|
292
270
|
/**
|
|
293
271
|
* Reconstruct the full multi-turn conversation by walking the ancestor
|
|
294
|
-
* run chain
|
|
272
|
+
* run chain over the session's Tree, concatenating each ancestor's
|
|
273
|
+
* projection (oldest turn first) plus the current run's projection.
|
|
295
274
|
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* @
|
|
304
|
-
*
|
|
275
|
+
* Hydrates the Tree as needed from channel history if the chain from
|
|
276
|
+
* the run's structural-parent anchor isn't already fully present;
|
|
277
|
+
* subsequent reads of {@link Run.messages} re-walk the same Tree and
|
|
278
|
+
* reflect any further folds (e.g. live arrivals from concurrent runs).
|
|
279
|
+
* No cache: every call computes a fresh snapshot from the live Tree.
|
|
280
|
+
*
|
|
281
|
+
* Walks to the conversation root by default; bound the walk via the
|
|
282
|
+
* optional {@link LoadConversationOptions.maxRuns} cap. If channel
|
|
283
|
+
* retention has expired older turns, the walk stops at what is available.
|
|
284
|
+
* @param options - Optional walk bounds.
|
|
285
|
+
* @returns The conversation messages in chronological order, ready to pass to an LLM.
|
|
286
|
+
* @throws {Ably.ErrorInfo} `HistoryFetchFailed` — or the underlying Ably
|
|
287
|
+
* code when the failure carried one — when the history fetch fails after
|
|
288
|
+
* retries (the conversation is never silently truncated on fetch
|
|
289
|
+
* failure); `InvalidArgument` when the run's signal aborts.
|
|
305
290
|
*/
|
|
306
291
|
loadConversation(options?: LoadConversationOptions): Promise<TMessage[]>;
|
|
307
292
|
/**
|
|
@@ -315,18 +300,55 @@ export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
|
315
300
|
* suspended.
|
|
316
301
|
*/
|
|
317
302
|
suspend(): Promise<void>;
|
|
318
|
-
/**
|
|
319
|
-
|
|
303
|
+
/**
|
|
304
|
+
* Publish run-end event to the channel and clean up. Terminal.
|
|
305
|
+
* @param params - How the run ended; see {@link RunEndParams}.
|
|
306
|
+
*/
|
|
307
|
+
end(params: RunEndParams): Promise<void>;
|
|
320
308
|
}
|
|
321
309
|
/** Server-side session that manages run lifecycles over an Ably channel. */
|
|
322
310
|
export interface AgentSession<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
311
|
+
/**
|
|
312
|
+
* The Ably presence object for this session's channel.
|
|
313
|
+
*
|
|
314
|
+
* Exposed as a convenience so the agent can track and publish presence
|
|
315
|
+
* (`enter`/`leave`/`update`/`get`/`subscribe`) — for example, to detect
|
|
316
|
+
* whether the requesting user is still connected — without obtaining the
|
|
317
|
+
* channel separately. This is the same `Ably.RealtimePresence` instance the
|
|
318
|
+
* underlying channel exposes; the session applies no additional semantics.
|
|
319
|
+
* Presence operations implicitly attach the channel and do not require
|
|
320
|
+
* {@link connect} to have been called first.
|
|
321
|
+
*/
|
|
322
|
+
readonly presence: Ably.RealtimePresence;
|
|
323
|
+
/**
|
|
324
|
+
* The Ably LiveObjects entry point for this session's channel.
|
|
325
|
+
*
|
|
326
|
+
* Exposed as a convenience so the agent can read and mutate shared objects
|
|
327
|
+
* (LiveMap / LiveCounter) on the same channel the session uses, without
|
|
328
|
+
* obtaining the channel separately. This is the same `RealtimeObject`
|
|
329
|
+
* instance the underlying channel exposes; the session applies no additional
|
|
330
|
+
* semantics. Operating on it requires (a) the Realtime client to have been
|
|
331
|
+
* constructed with the `LiveObjects` plugin from `ably/liveobjects` and
|
|
332
|
+
* (b) the object channel modes to have been requested via
|
|
333
|
+
* {@link AgentSessionOptions.channelModes}. When either is absent the
|
|
334
|
+
* underlying SDK throws; the session does not suppress the error.
|
|
335
|
+
*/
|
|
336
|
+
readonly object: AblyObjects.RealtimeObject;
|
|
337
|
+
/**
|
|
338
|
+
* The session's materialisation tree. Every Ably message received on the channel
|
|
339
|
+
* (live + history) folds into this tree; consumers can introspect hydrated
|
|
340
|
+
* conversation state via {@link Tree.getNodeByCodecMessageId} /
|
|
341
|
+
* {@link Tree.getRunNode} etc. Mirrors `ClientSession.tree` so both
|
|
342
|
+
* sessions share one materialisation engine.
|
|
343
|
+
*/
|
|
344
|
+
readonly tree: Tree<TOutput, TProjection>;
|
|
323
345
|
/**
|
|
324
346
|
* Subscribe (unfiltered) to the shared channel and (implicitly) attach. The
|
|
325
|
-
* subscribe is deliberately unfiltered so channel-
|
|
326
|
-
* events
|
|
327
|
-
*
|
|
328
|
-
* methods (`start`, `
|
|
329
|
-
* `
|
|
347
|
+
* subscribe is deliberately unfiltered so channel-history-replayed input
|
|
348
|
+
* events reach the materialisation engine, which the input-event lookup
|
|
349
|
+
* queries via the Tree. Idempotent — subsequent calls return the same
|
|
350
|
+
* promise. All run methods (`start`, `pipe`, `loadConversation`,
|
|
351
|
+
* `suspend`, `end`) throw `InvalidArgument` until
|
|
330
352
|
* `connect()` has been *called*; once it has, they await the in-flight
|
|
331
353
|
* connect promise rather than throwing.
|
|
332
354
|
*/
|