@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,12 +1,14 @@
|
|
|
1
1
|
/** Agent (server-side) session types: options, run runtime, and the Run / AgentSession contracts. */
|
|
2
2
|
|
|
3
3
|
import type * as Ably from 'ably';
|
|
4
|
+
// Also augments RealtimeChannel with `.object` (ably/liveobjects side-effect).
|
|
5
|
+
import type * as AblyObjects from 'ably/liveobjects';
|
|
4
6
|
|
|
5
7
|
import type { Logger } from '../../../logger.js';
|
|
6
8
|
import type { Codec, CodecInputEvent, CodecOutputEvent, WriteOptions } from '../../codec/types.js';
|
|
7
9
|
import type { Invocation } from '../invocation.js';
|
|
8
10
|
import type { CancelRequest, RunEndReason } from './shared.js';
|
|
9
|
-
import type { MessageNode } from './tree.js';
|
|
11
|
+
import type { MessageNode, Tree } from './tree.js';
|
|
10
12
|
|
|
11
13
|
// ---------------------------------------------------------------------------
|
|
12
14
|
// Agent session options
|
|
@@ -43,8 +45,9 @@ export interface AgentSessionOptions<
|
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
47
|
* How long `Run.start()` will wait for the input event(s) tagged with
|
|
46
|
-
* the run's `invocationId` to arrive on the channel
|
|
47
|
-
*
|
|
48
|
+
* the run's `invocationId` to arrive on the channel — across both the
|
|
49
|
+
* post-attach live subscription and the bounded history scan — before
|
|
50
|
+
* rejecting with `InputEventNotFound`. The rejection bubbles up to the
|
|
48
51
|
* developer's HTTP handler, which should surface it as a non-2xx response
|
|
49
52
|
* so the client's pending send fails.
|
|
50
53
|
* Default: 30000 (30 seconds).
|
|
@@ -52,60 +55,41 @@ export interface AgentSessionOptions<
|
|
|
52
55
|
inputEventLookupTimeoutMs?: number;
|
|
53
56
|
|
|
54
57
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* that subsequent `start()` calls can drain them on registration.
|
|
58
|
+
* How far back in time `Run.start()` scans channel history for the
|
|
59
|
+
* triggering input event. Implements the lookback bound for the
|
|
60
|
+
* input-event scan — anything older than `Date.now() - inputEventLookbackMs`
|
|
61
|
+
* is treated as outside the lookup window.
|
|
60
62
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* — the client whose input was dropped will fail their lookup with
|
|
65
|
-
* `InputEventNotFound`. The eviction is logged at warn level so operators
|
|
66
|
-
* can correlate capacity pressure with `InputEventNotFound` errors.
|
|
63
|
+
* History is fetched with `untilAttach: true` so the scan composes
|
|
64
|
+
* with the live subscription by serial boundary — together they cover
|
|
65
|
+
* every message within `inputEventLookbackMs` of attach.
|
|
67
66
|
*
|
|
68
|
-
*
|
|
67
|
+
* Increase this for long-suspended runs whose continuation may arrive
|
|
68
|
+
* many minutes after the original publish. Decrease it for stricter
|
|
69
|
+
* recency.
|
|
70
|
+
*
|
|
71
|
+
* Default: 120000 (2 minutes).
|
|
69
72
|
*/
|
|
70
|
-
|
|
73
|
+
inputEventLookbackMs?: number;
|
|
71
74
|
|
|
72
75
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* duration strings (`"2m"`, `"30s"`) or a count of messages as a string
|
|
78
|
-
* (e.g. `"50"`). Malformed values surface as a channel attach error from
|
|
79
|
-
* Ably; the SDK does not pre-validate.
|
|
80
|
-
*
|
|
81
|
-
* A longer window improves the chances of catching an input event for an
|
|
82
|
-
* agent that takes a while to come up after the client published, but
|
|
83
|
-
* also increases the buffer pressure on `inputEventBufferLimit` because
|
|
84
|
-
* more events may be replayed on attach.
|
|
76
|
+
* Extra Ably channel modes to request on the session's channel, on top of the
|
|
77
|
+
* modes AI Transport always needs. Pass `OBJECT_MODES` (or
|
|
78
|
+
* `['OBJECT_SUBSCRIBE', 'OBJECT_PUBLISH']`) to use Ably LiveObjects via
|
|
79
|
+
* {@link AgentSession.object}. Omit to attach with the default mode set.
|
|
85
80
|
*
|
|
86
|
-
*
|
|
81
|
+
* The session requests the union of these modes with the modes it always
|
|
82
|
+
* needs, so passing extra modes never drops the SDK's required modes. The
|
|
83
|
+
* connection's token/key capability must permit the requested operations,
|
|
84
|
+
* otherwise the server grants only the permitted subset.
|
|
87
85
|
*/
|
|
88
|
-
|
|
86
|
+
channelModes?: readonly Ably.ChannelMode[];
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
// ---------------------------------------------------------------------------
|
|
92
90
|
// Run options
|
|
93
91
|
// ---------------------------------------------------------------------------
|
|
94
92
|
|
|
95
|
-
/**
|
|
96
|
-
* A batch of events targeting an existing message.
|
|
97
|
-
* Each node specifies the target message and the events to apply to it.
|
|
98
|
-
* Used for cross-run updates such as tool result delivery.
|
|
99
|
-
*/
|
|
100
|
-
export interface EventsNode<TOutput extends CodecOutputEvent> {
|
|
101
|
-
/** Discriminator — identifies this as an events node. */
|
|
102
|
-
kind: 'event';
|
|
103
|
-
/** The `codec-message-id` of the existing message to update. */
|
|
104
|
-
codecMessageId: string;
|
|
105
|
-
/** Outputs to apply to the target message. */
|
|
106
|
-
events: TOutput[];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
93
|
/**
|
|
110
94
|
* Options for `Run.pipe` — per-operation overrides for the assistant message.
|
|
111
95
|
* @template TOutput - The codec output type carried by the stream; used by the `resolveWriteOptions` hook.
|
|
@@ -206,7 +190,7 @@ export interface RunRuntime<TOutput extends CodecOutputEvent> {
|
|
|
206
190
|
* `Ably.ErrorInfo` (code `StreamError`) for standardized observability.
|
|
207
191
|
* - Failures in the `onCancel` handler.
|
|
208
192
|
*
|
|
209
|
-
* Publish failures in `start
|
|
193
|
+
* Publish failures in `start` and `end`
|
|
210
194
|
* are not delivered here — those methods reject their returned promise
|
|
211
195
|
* with an `Ably.ErrorInfo`, and the caller should handle it at the await
|
|
212
196
|
* site. Run errors never render the session unusable, but the run may
|
|
@@ -240,23 +224,47 @@ export interface RunView<TMessage> {
|
|
|
240
224
|
/** Options for {@link Run.loadConversation}. */
|
|
241
225
|
export interface LoadConversationOptions {
|
|
242
226
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
227
|
+
* Maximum number of ANCESTOR reply RunNodes to walk back through the
|
|
228
|
+
* chain. Input nodes encountered alongside don't count toward the bound,
|
|
229
|
+
* and neither does the current run's own node (it is the conversation
|
|
230
|
+
* tail, not ancestor context). Default unbounded (walks to the
|
|
231
|
+
* conversation root).
|
|
232
|
+
*
|
|
233
|
+
* Set this to bound the LLM context window — `maxRuns: 5` returns the
|
|
234
|
+
* 5 most-recent prior reply runs and their associated input nodes
|
|
235
|
+
* (each bounded run's triggering input included, so the chain never
|
|
236
|
+
* starts assistant-first), in chronological order.
|
|
252
237
|
*/
|
|
253
|
-
|
|
238
|
+
maxRuns?: number;
|
|
254
239
|
}
|
|
255
240
|
|
|
241
|
+
/**
|
|
242
|
+
* How a run terminates, passed to {@link Run.end}. Discriminated on `reason`:
|
|
243
|
+
* an `'error'` end may carry a terminal `error`; any other reason carries none.
|
|
244
|
+
*/
|
|
245
|
+
export type RunEndParams =
|
|
246
|
+
| {
|
|
247
|
+
/** Why the run ended — any terminal reason other than `'error'`. */
|
|
248
|
+
reason: Exclude<RunEndReason, 'error'>;
|
|
249
|
+
}
|
|
250
|
+
| {
|
|
251
|
+
/** The run ended in error. */
|
|
252
|
+
reason: 'error';
|
|
253
|
+
/**
|
|
254
|
+
* Optional terminal error to surface to clients. Omit to end in error
|
|
255
|
+
* without detail.
|
|
256
|
+
*/
|
|
257
|
+
error?: Ably.ErrorInfo;
|
|
258
|
+
};
|
|
259
|
+
|
|
256
260
|
/**
|
|
257
261
|
* A server-side run with explicit lifecycle methods. Generic over the codec's
|
|
258
|
-
* output, projection, and message types.
|
|
262
|
+
* output, projection, and message types. `TProjection` is retained for
|
|
263
|
+
* parameter symmetry with {@link AgentSession.createRun}; it does not
|
|
264
|
+
* appear in the Run's public surface today but keeps the type slot
|
|
265
|
+
* available for future per-Run projection accessors.
|
|
259
266
|
*/
|
|
267
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- see JSDoc
|
|
260
268
|
export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
261
269
|
/** The run's unique identifier. */
|
|
262
270
|
readonly runId: string;
|
|
@@ -295,7 +303,7 @@ export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
|
295
303
|
/**
|
|
296
304
|
* Publish the run's opening lifecycle event to the channel (run-start, or
|
|
297
305
|
* run-resume for a continuation). Must be called before any other run method
|
|
298
|
-
* (pipe,
|
|
306
|
+
* (pipe, suspend, end).
|
|
299
307
|
*/
|
|
300
308
|
start(): Promise<void>;
|
|
301
309
|
|
|
@@ -306,47 +314,26 @@ export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
|
306
314
|
*/
|
|
307
315
|
pipe(stream: ReadableStream<TOutput>, options?: PipeOptions<TOutput>): Promise<StreamResult>;
|
|
308
316
|
|
|
309
|
-
/**
|
|
310
|
-
* Publish events targeting existing messages in the tree. Each node
|
|
311
|
-
* specifies a target message (by `codecMessageId`) and the events to apply.
|
|
312
|
-
* Events are encoded and published with the target's `codec-message-id`,
|
|
313
|
-
* so receiving clients apply them to the existing node rather than
|
|
314
|
-
* creating a new one.
|
|
315
|
-
*
|
|
316
|
-
* Used for cross-run updates such as tool result delivery after
|
|
317
|
-
* approval or client-side tool execution.
|
|
318
|
-
*/
|
|
319
|
-
addEvents(nodes: EventsNode<TOutput>[]): Promise<void>;
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Fetch every channel message bound to this run and fold them through
|
|
323
|
-
* the codec into a single projection. Used by the agent to reconstruct
|
|
324
|
-
* the run's full state — including client-published tool-output amends
|
|
325
|
-
* the agent didn't observe live — when resuming a suspended run.
|
|
326
|
-
*
|
|
327
|
-
* Uses `channel.history()` (no `untilAttach`) so messages published
|
|
328
|
-
* after the channel was originally attached are still included. Each
|
|
329
|
-
* call paginates until either there are no more pages or an internal
|
|
330
|
-
* safety bound is reached.
|
|
331
|
-
* @returns The TProjection produced by folding every event for this run
|
|
332
|
-
* in serial order. The caller extracts what they need via
|
|
333
|
-
* {@link Codec.getMessages}.
|
|
334
|
-
*/
|
|
335
|
-
loadProjection(): Promise<TProjection>;
|
|
336
|
-
|
|
337
317
|
/**
|
|
338
318
|
* Reconstruct the full multi-turn conversation by walking the ancestor
|
|
339
|
-
* run chain
|
|
319
|
+
* run chain over the session's Tree, concatenating each ancestor's
|
|
320
|
+
* projection (oldest turn first) plus the current run's projection.
|
|
340
321
|
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
* @
|
|
349
|
-
*
|
|
322
|
+
* Hydrates the Tree as needed from channel history if the chain from
|
|
323
|
+
* the run's structural-parent anchor isn't already fully present;
|
|
324
|
+
* subsequent reads of {@link Run.messages} re-walk the same Tree and
|
|
325
|
+
* reflect any further folds (e.g. live arrivals from concurrent runs).
|
|
326
|
+
* No cache: every call computes a fresh snapshot from the live Tree.
|
|
327
|
+
*
|
|
328
|
+
* Walks to the conversation root by default; bound the walk via the
|
|
329
|
+
* optional {@link LoadConversationOptions.maxRuns} cap. If channel
|
|
330
|
+
* retention has expired older turns, the walk stops at what is available.
|
|
331
|
+
* @param options - Optional walk bounds.
|
|
332
|
+
* @returns The conversation messages in chronological order, ready to pass to an LLM.
|
|
333
|
+
* @throws {Ably.ErrorInfo} `HistoryFetchFailed` — or the underlying Ably
|
|
334
|
+
* code when the failure carried one — when the history fetch fails after
|
|
335
|
+
* retries (the conversation is never silently truncated on fetch
|
|
336
|
+
* failure); `InvalidArgument` when the run's signal aborts.
|
|
350
337
|
*/
|
|
351
338
|
loadConversation(options?: LoadConversationOptions): Promise<TMessage[]>;
|
|
352
339
|
|
|
@@ -362,8 +349,11 @@ export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
|
362
349
|
*/
|
|
363
350
|
suspend(): Promise<void>;
|
|
364
351
|
|
|
365
|
-
/**
|
|
366
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Publish run-end event to the channel and clean up. Terminal.
|
|
354
|
+
* @param params - How the run ended; see {@link RunEndParams}.
|
|
355
|
+
*/
|
|
356
|
+
end(params: RunEndParams): Promise<void>;
|
|
367
357
|
}
|
|
368
358
|
|
|
369
359
|
// ---------------------------------------------------------------------------
|
|
@@ -372,13 +362,50 @@ export interface Run<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
|
372
362
|
|
|
373
363
|
/** Server-side session that manages run lifecycles over an Ably channel. */
|
|
374
364
|
export interface AgentSession<TOutput extends CodecOutputEvent, TProjection, TMessage> {
|
|
365
|
+
/**
|
|
366
|
+
* The Ably presence object for this session's channel.
|
|
367
|
+
*
|
|
368
|
+
* Exposed as a convenience so the agent can track and publish presence
|
|
369
|
+
* (`enter`/`leave`/`update`/`get`/`subscribe`) — for example, to detect
|
|
370
|
+
* whether the requesting user is still connected — without obtaining the
|
|
371
|
+
* channel separately. This is the same `Ably.RealtimePresence` instance the
|
|
372
|
+
* underlying channel exposes; the session applies no additional semantics.
|
|
373
|
+
* Presence operations implicitly attach the channel and do not require
|
|
374
|
+
* {@link connect} to have been called first.
|
|
375
|
+
*/
|
|
376
|
+
readonly presence: Ably.RealtimePresence;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* The Ably LiveObjects entry point for this session's channel.
|
|
380
|
+
*
|
|
381
|
+
* Exposed as a convenience so the agent can read and mutate shared objects
|
|
382
|
+
* (LiveMap / LiveCounter) on the same channel the session uses, without
|
|
383
|
+
* obtaining the channel separately. This is the same `RealtimeObject`
|
|
384
|
+
* instance the underlying channel exposes; the session applies no additional
|
|
385
|
+
* semantics. Operating on it requires (a) the Realtime client to have been
|
|
386
|
+
* constructed with the `LiveObjects` plugin from `ably/liveobjects` and
|
|
387
|
+
* (b) the object channel modes to have been requested via
|
|
388
|
+
* {@link AgentSessionOptions.channelModes}. When either is absent the
|
|
389
|
+
* underlying SDK throws; the session does not suppress the error.
|
|
390
|
+
*/
|
|
391
|
+
readonly object: AblyObjects.RealtimeObject;
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* The session's materialisation tree. Every Ably message received on the channel
|
|
395
|
+
* (live + history) folds into this tree; consumers can introspect hydrated
|
|
396
|
+
* conversation state via {@link Tree.getNodeByCodecMessageId} /
|
|
397
|
+
* {@link Tree.getRunNode} etc. Mirrors `ClientSession.tree` so both
|
|
398
|
+
* sessions share one materialisation engine.
|
|
399
|
+
*/
|
|
400
|
+
readonly tree: Tree<TOutput, TProjection>;
|
|
401
|
+
|
|
375
402
|
/**
|
|
376
403
|
* Subscribe (unfiltered) to the shared channel and (implicitly) attach. The
|
|
377
|
-
* subscribe is deliberately unfiltered so channel-
|
|
378
|
-
* events
|
|
379
|
-
*
|
|
380
|
-
* methods (`start`, `
|
|
381
|
-
* `
|
|
404
|
+
* subscribe is deliberately unfiltered so channel-history-replayed input
|
|
405
|
+
* events reach the materialisation engine, which the input-event lookup
|
|
406
|
+
* queries via the Tree. Idempotent — subsequent calls return the same
|
|
407
|
+
* promise. All run methods (`start`, `pipe`, `loadConversation`,
|
|
408
|
+
* `suspend`, `end`) throw `InvalidArgument` until
|
|
382
409
|
* `connect()` has been *called*; once it has, they await the in-flight
|
|
383
410
|
* connect promise rather than throwing.
|
|
384
411
|
*/
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** Client session types: options, send options, the ActiveRun handle, and the ClientSession contract. */
|
|
2
2
|
|
|
3
3
|
import type * as Ably from 'ably';
|
|
4
|
+
// Also augments RealtimeChannel with `.object` (ably/liveobjects side-effect).
|
|
5
|
+
import type * as AblyObjects from 'ably/liveobjects';
|
|
4
6
|
|
|
5
7
|
import type { Logger } from '../../../logger.js';
|
|
6
8
|
import type { Codec, CodecInputEvent, CodecOutputEvent } from '../../codec/types.js';
|
|
@@ -22,6 +24,12 @@ export interface ClientSessionOptions<
|
|
|
22
24
|
/**
|
|
23
25
|
* The Ably Realtime client. The caller owns its lifecycle —
|
|
24
26
|
* `session.close()` does not close the client.
|
|
27
|
+
*
|
|
28
|
+
* The session's identity is taken from this client's `auth.clientId` (set
|
|
29
|
+
* via the Ably token or `ClientOptions.clientId`) — it is read at publish
|
|
30
|
+
* time and stamped on the wire as the run/input client id so other clients
|
|
31
|
+
* can attribute messages. A connection without a concrete clientId
|
|
32
|
+
* (anonymous, or a wildcard `*` token) publishes without one.
|
|
25
33
|
*/
|
|
26
34
|
client: Ably.Realtime;
|
|
27
35
|
|
|
@@ -35,16 +43,22 @@ export interface ClientSessionOptions<
|
|
|
35
43
|
/** The codec to use for encoding/decoding. */
|
|
36
44
|
codec: Codec<TInput, TOutput, TProjection, TMessage>;
|
|
37
45
|
|
|
38
|
-
/**
|
|
39
|
-
* The client's identity, used as the Ably publisher `clientId` on
|
|
40
|
-
* everything this session publishes. Surfaces on the wire as the
|
|
41
|
-
* run/input client id so other clients can attribute messages.
|
|
42
|
-
*/
|
|
43
|
-
clientId?: string;
|
|
44
|
-
|
|
45
46
|
/** Initial messages to seed the conversation tree with. Forms a linear chain. */
|
|
46
47
|
messages?: TMessage[];
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Extra Ably channel modes to request on the session's channel, on top of the
|
|
51
|
+
* modes AI Transport always needs. Pass `OBJECT_MODES` (or
|
|
52
|
+
* `['OBJECT_SUBSCRIBE', 'OBJECT_PUBLISH']`) to use Ably LiveObjects via
|
|
53
|
+
* {@link ClientSession.object}. Omit to attach with the default mode set.
|
|
54
|
+
*
|
|
55
|
+
* The session requests the union of these modes with the modes it always
|
|
56
|
+
* needs, so passing extra modes never drops the SDK's required modes. The
|
|
57
|
+
* connection's token/key capability must permit the requested operations,
|
|
58
|
+
* otherwise the server grants only the permitted subset.
|
|
59
|
+
*/
|
|
60
|
+
channelModes?: readonly Ably.ChannelMode[];
|
|
61
|
+
|
|
48
62
|
/** Logger instance for diagnostic output. */
|
|
49
63
|
logger?: Logger;
|
|
50
64
|
}
|
|
@@ -75,12 +89,6 @@ export interface SendOptions {
|
|
|
75
89
|
* agent, which mints a distinct `invocationId` per HTTP request.
|
|
76
90
|
*/
|
|
77
91
|
runId?: string;
|
|
78
|
-
/**
|
|
79
|
-
* Currently non-functional: the send path always mints each input's
|
|
80
|
-
* `inputEventId` with `crypto.randomUUID()` (no override is read), so a
|
|
81
|
-
* value supplied here has no effect.
|
|
82
|
-
*/
|
|
83
|
-
inputEventId?: string;
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
// ---------------------------------------------------------------------------
|
|
@@ -171,6 +179,34 @@ export interface ClientSession<
|
|
|
171
179
|
/** The default paginated, branch-aware view for rendering — events scoped to visible messages. */
|
|
172
180
|
readonly view: View<TInput, TMessage>;
|
|
173
181
|
|
|
182
|
+
/**
|
|
183
|
+
* The Ably presence object for this session's channel.
|
|
184
|
+
*
|
|
185
|
+
* Exposed as a convenience so callers can track and publish presence
|
|
186
|
+
* (`enter`/`leave`/`update`/`get`/`subscribe`) — for example, to detect
|
|
187
|
+
* whether an agent is online — without obtaining the channel separately.
|
|
188
|
+
* This is the same `Ably.RealtimePresence` instance the underlying channel
|
|
189
|
+
* exposes; the session applies no additional semantics. Presence operations
|
|
190
|
+
* implicitly attach the channel and do not require {@link connect} to have
|
|
191
|
+
* been called first.
|
|
192
|
+
*/
|
|
193
|
+
readonly presence: Ably.RealtimePresence;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The Ably LiveObjects entry point for this session's channel.
|
|
197
|
+
*
|
|
198
|
+
* Exposed as a convenience so callers can read and mutate shared objects
|
|
199
|
+
* (LiveMap / LiveCounter) on the same channel the session uses, without
|
|
200
|
+
* obtaining the channel separately. This is the same `RealtimeObject`
|
|
201
|
+
* instance the underlying channel exposes; the session applies no additional
|
|
202
|
+
* semantics. Operating on it requires (a) the Realtime client to have been
|
|
203
|
+
* constructed with the `LiveObjects` plugin from `ably/liveobjects` and
|
|
204
|
+
* (b) the object channel modes to have been requested via
|
|
205
|
+
* {@link ClientSessionOptions.channelModes}. When either is absent the
|
|
206
|
+
* underlying SDK throws; the session does not suppress the error.
|
|
207
|
+
*/
|
|
208
|
+
readonly object: AblyObjects.RealtimeObject;
|
|
209
|
+
|
|
174
210
|
/**
|
|
175
211
|
* Subscribe to the channel and (implicitly) attach. Idempotent —
|
|
176
212
|
* subsequent calls return the same promise. The View's write operations
|
|
@@ -25,6 +25,12 @@ interface RunLifecycleBase {
|
|
|
25
25
|
* Empty string if the wire didn't carry an invocation-id.
|
|
26
26
|
*/
|
|
27
27
|
invocationId: string;
|
|
28
|
+
/**
|
|
29
|
+
* Ably server timestamp (epoch ms) of the lifecycle message; absent for an
|
|
30
|
+
* optimistic local event. Advances the Tree's event-log retention clock and
|
|
31
|
+
* the target run's last-activity time.
|
|
32
|
+
*/
|
|
33
|
+
timestamp?: number;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
/**
|
|
@@ -84,12 +90,23 @@ export type RunLifecycleEvent =
|
|
|
84
90
|
* optimistic local event. The Tree reads it to set the Run's endSerial.
|
|
85
91
|
*/
|
|
86
92
|
serial: string | undefined;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
} & (
|
|
94
|
+
| {
|
|
95
|
+
/** Why the run ended — any terminal reason other than `'error'`. */
|
|
96
|
+
reason: Exclude<RunEndReason, 'error'>;
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
/** The run ended in error. */
|
|
100
|
+
reason: 'error';
|
|
101
|
+
/**
|
|
102
|
+
* Terminal error detail, reconstructed from the run-end's
|
|
103
|
+
* `error-code` / `error-message` headers (or a generic fallback
|
|
104
|
+
* when the run ended in error without detail). The Tree records it
|
|
105
|
+
* on the RunNode and exposes it via `RunInfo.error`.
|
|
106
|
+
*/
|
|
107
|
+
error: Ably.ErrorInfo;
|
|
108
|
+
}
|
|
109
|
+
));
|
|
93
110
|
|
|
94
111
|
// ---------------------------------------------------------------------------
|
|
95
112
|
// Conversation tree (branching history)
|
|
@@ -117,6 +134,27 @@ export interface MessageNode<TMessage> {
|
|
|
117
134
|
serial: string | undefined;
|
|
118
135
|
}
|
|
119
136
|
|
|
137
|
+
/**
|
|
138
|
+
* A Run's lifecycle state, modelled as one discriminated value so the terminal
|
|
139
|
+
* `error` is carried exactly when `status` is `'error'`. A RunNode is mutated
|
|
140
|
+
* in place, so status and its dependent error move together — transitions
|
|
141
|
+
* reassign `node.state` wholesale rather than setting fields individually.
|
|
142
|
+
*/
|
|
143
|
+
export type RunNodeState =
|
|
144
|
+
| {
|
|
145
|
+
/** `'active'` (streaming), `'suspended'` (paused), or a non-error terminal reason. */
|
|
146
|
+
status: 'active' | 'suspended' | Exclude<RunEndReason, 'error'>;
|
|
147
|
+
}
|
|
148
|
+
| {
|
|
149
|
+
/** Terminal error status. */
|
|
150
|
+
status: 'error';
|
|
151
|
+
/**
|
|
152
|
+
* The run-end's stamped error (or a generic fallback). Exposed to
|
|
153
|
+
* consumers via `RunInfo.error`.
|
|
154
|
+
*/
|
|
155
|
+
error: Ably.ErrorInfo;
|
|
156
|
+
};
|
|
157
|
+
|
|
120
158
|
/**
|
|
121
159
|
* A node in the conversation tree, representing a single Run.
|
|
122
160
|
*
|
|
@@ -173,13 +211,12 @@ export interface RunNode<TProjection> {
|
|
|
173
211
|
*/
|
|
174
212
|
clientId: string;
|
|
175
213
|
/**
|
|
176
|
-
* Run lifecycle
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* - {@link RunEndReason} — terminal state reflecting the run-end reason.
|
|
214
|
+
* Run lifecycle state — see {@link RunNodeState}. `'active'` until a terminal
|
|
215
|
+
* event; `'suspended'` while paused (a continuation re-activates it);
|
|
216
|
+
* otherwise the run-end reason, carrying `error` when that reason is
|
|
217
|
+
* `'error'`.
|
|
181
218
|
*/
|
|
182
|
-
|
|
219
|
+
state: RunNodeState;
|
|
183
220
|
/** Per-Run codec projection. Folded by the Tree from every event published under this run-id. */
|
|
184
221
|
projection: TProjection;
|
|
185
222
|
/**
|
|
@@ -317,6 +354,18 @@ export interface Tree<TOutput extends CodecOutputEvent, TProjection> {
|
|
|
317
354
|
*/
|
|
318
355
|
getSiblingNodes(key: string): ConversationNode<TProjection>[];
|
|
319
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Look up the raw Ably message that carried the given `event-id` header,
|
|
359
|
+
* if the Tree has observed it. Populated incrementally as messages arrive
|
|
360
|
+
* through the Tree's `ably-message` channel; not bounded except by the
|
|
361
|
+
* Tree's lifetime. Used by the agent's input-event lookup to find a
|
|
362
|
+
* triggering input message by id without scanning a separate buffer.
|
|
363
|
+
* @param eventId - The `event-id` header value to look up.
|
|
364
|
+
* @returns The matching raw Ably message, or undefined when the Tree has
|
|
365
|
+
* not observed an event with that id.
|
|
366
|
+
*/
|
|
367
|
+
findAblyMessageByEventId(eventId: string): Ably.InboundMessage | undefined;
|
|
368
|
+
|
|
320
369
|
// --- Events ---
|
|
321
370
|
|
|
322
371
|
/**
|
|
@@ -31,16 +31,8 @@ export interface LoadHistoryOptions {
|
|
|
31
31
|
// View — windowed projection over the tree
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
*
|
|
37
|
-
* Exposes the Run facts a UI consumer needs (`runId`, owner `clientId`,
|
|
38
|
-
* lifecycle `status`, `invocationId`) without leaking the codec's
|
|
39
|
-
* opaque per-Run projection or the Tree's structural fields. Callers
|
|
40
|
-
* that need the full Run record (parent / fork relationships, serials,
|
|
41
|
-
* projection) reach `session.tree.getRunNode(runId)` directly.
|
|
42
|
-
*/
|
|
43
|
-
export interface RunInfo {
|
|
34
|
+
/** Fields common to every {@link RunInfo} arm. */
|
|
35
|
+
interface RunInfoBase {
|
|
44
36
|
/** The Run's unique identifier. */
|
|
45
37
|
runId: string;
|
|
46
38
|
/**
|
|
@@ -48,14 +40,6 @@ export interface RunInfo {
|
|
|
48
40
|
* when the wire didn't carry an owner client id.
|
|
49
41
|
*/
|
|
50
42
|
clientId: string;
|
|
51
|
-
/**
|
|
52
|
-
* Run lifecycle status. `'active'` while the Run is streaming;
|
|
53
|
-
* `'suspended'` while it is paused awaiting input (still live, a
|
|
54
|
-
* continuation re-activates it); otherwise the {@link RunEndReason} the Run
|
|
55
|
-
* terminated with. Literal lifecycle vocabulary — UIs that want `'streaming'`
|
|
56
|
-
* rendering language translate at the component boundary.
|
|
57
|
-
*/
|
|
58
|
-
status: 'active' | 'suspended' | RunEndReason;
|
|
59
43
|
/**
|
|
60
44
|
* The agent-minted `invocationId` observed for this Run, adopted from the
|
|
61
45
|
* wire `ai-run-start`. Stable across the Run's lifecycle once observed.
|
|
@@ -66,6 +50,48 @@ export interface RunInfo {
|
|
|
66
50
|
invocationId: string;
|
|
67
51
|
}
|
|
68
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Projection-free, View-facing snapshot of a Run.
|
|
55
|
+
*
|
|
56
|
+
* Exposes the Run facts a UI consumer needs (`runId`, owner `clientId`,
|
|
57
|
+
* lifecycle `status`, `invocationId`, and — only when it failed — the terminal
|
|
58
|
+
* `error`) without leaking the codec's opaque per-Run projection or the Tree's
|
|
59
|
+
* structural fields. Callers that need the full Run record (parent / fork
|
|
60
|
+
* relationships, serials, projection) reach `session.tree.getRunNode(runId)`
|
|
61
|
+
* directly.
|
|
62
|
+
*
|
|
63
|
+
* Discriminated on `status`: a Run with `status: 'error'` carries the terminal
|
|
64
|
+
* `error`; every other status has no `error`. So `info.error` is defined
|
|
65
|
+
* exactly when `info.status === 'error'`.
|
|
66
|
+
*/
|
|
67
|
+
export type RunInfo =
|
|
68
|
+
| (RunInfoBase & {
|
|
69
|
+
/**
|
|
70
|
+
* Run lifecycle status. `'active'` while the Run is streaming;
|
|
71
|
+
* `'suspended'` while it is paused awaiting input (still live, a
|
|
72
|
+
* continuation re-activates it); otherwise the non-error terminal
|
|
73
|
+
* {@link RunEndReason} (`'complete'` or `'cancelled'`). Literal lifecycle
|
|
74
|
+
* vocabulary — UIs that want `'streaming'` rendering language translate
|
|
75
|
+
* at the component boundary. The `'error'` terminal status lives on the
|
|
76
|
+
* other arm of this union, where it is paired with the terminal `error`.
|
|
77
|
+
*/
|
|
78
|
+
status: 'active' | 'suspended' | Exclude<RunEndReason, 'error'>;
|
|
79
|
+
/** Never present for a non-error status. */
|
|
80
|
+
error?: never;
|
|
81
|
+
})
|
|
82
|
+
| (RunInfoBase & {
|
|
83
|
+
/** Terminal error status — the Run ended with {@link RunEndReason} `'error'`, carrying the terminal `error` below. */
|
|
84
|
+
status: 'error';
|
|
85
|
+
/**
|
|
86
|
+
* The terminal error. Carries the agent-stamped `error-code` /
|
|
87
|
+
* `error-message` detail (or a generic fallback when the run ended in
|
|
88
|
+
* error without detail), so a UI can show *why* a run failed alongside
|
|
89
|
+
* its `'error'` status. Mirrors the `Ably.ErrorInfo` delivered via
|
|
90
|
+
* `ClientSession.on('error')`.
|
|
91
|
+
*/
|
|
92
|
+
error: Ably.ErrorInfo;
|
|
93
|
+
});
|
|
94
|
+
|
|
69
95
|
/**
|
|
70
96
|
* Bundle returned by {@link View.branchSelection} describing the
|
|
71
97
|
* sibling group anchored at a given codec-message-id.
|
|
@@ -138,20 +164,23 @@ export interface View<TInput extends CodecInputEvent, TMessage> {
|
|
|
138
164
|
*/
|
|
139
165
|
runs(): RunInfo[];
|
|
140
166
|
|
|
141
|
-
/** Whether there are older
|
|
167
|
+
/** Whether there are older messages that can be loaded or revealed. */
|
|
142
168
|
hasOlder(): boolean;
|
|
143
169
|
|
|
144
170
|
/**
|
|
145
|
-
* Reveal
|
|
146
|
-
*
|
|
147
|
-
*
|
|
171
|
+
* Reveal exactly `limit` older codecMessages — fewer only when channel history
|
|
172
|
+
* is exhausted. Loads from channel history when the tree doesn't already hold
|
|
173
|
+
* `limit` hidden messages, then advances the pagination window. Emits 'update'
|
|
174
|
+
* when the visible list changes.
|
|
148
175
|
*
|
|
149
|
-
* The pagination unit is the **
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
176
|
+
* The pagination unit is the **codecMessage**. A node (a user prompt, or a
|
|
177
|
+
* reply Run) contributes 1..N messages to the flat list returned by
|
|
178
|
+
* {@link View.getMessages}; the window counts those messages, so a node
|
|
179
|
+
* straddling the boundary is **partially revealed** — only its newest messages
|
|
180
|
+
* enter the window — and the page lands exactly on `limit` rather than on a
|
|
181
|
+
* node boundary. Such a partially-revealed run still appears in
|
|
182
|
+
* {@link View.runs} and is event-scoped.
|
|
183
|
+
* @param limit - Number of older codecMessages to reveal. Defaults to 10.
|
|
155
184
|
*/
|
|
156
185
|
loadOlder(limit?: number): Promise<void>;
|
|
157
186
|
|