@ably/ai-transport 0.0.1 → 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.
Files changed (167) hide show
  1. package/README.md +114 -116
  2. package/dist/ably-ai-transport.js +1743 -961
  3. package/dist/ably-ai-transport.js.map +1 -1
  4. package/dist/ably-ai-transport.umd.cjs +1 -1
  5. package/dist/ably-ai-transport.umd.cjs.map +1 -1
  6. package/dist/constants.d.ts +117 -39
  7. package/dist/core/agent.d.ts +29 -0
  8. package/dist/core/codec/decoder.d.ts +20 -23
  9. package/dist/core/codec/encoder.d.ts +11 -8
  10. package/dist/core/codec/index.d.ts +1 -2
  11. package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
  12. package/dist/core/codec/types.d.ts +410 -101
  13. package/dist/core/transport/agent-session.d.ts +10 -0
  14. package/dist/core/transport/branch-chain.d.ts +43 -0
  15. package/dist/core/transport/client-session.d.ts +13 -0
  16. package/dist/core/transport/decode-fold.d.ts +47 -0
  17. package/dist/core/transport/headers.d.ts +97 -17
  18. package/dist/core/transport/index.d.ts +5 -3
  19. package/dist/core/transport/internal/bounded-map.d.ts +20 -0
  20. package/dist/core/transport/invocation.d.ts +74 -0
  21. package/dist/core/transport/load-conversation.d.ts +128 -0
  22. package/dist/core/transport/load-history.d.ts +39 -0
  23. package/dist/core/transport/pipe-stream.d.ts +9 -8
  24. package/dist/core/transport/run-manager.d.ts +78 -0
  25. package/dist/core/transport/tree.d.ts +435 -0
  26. package/dist/core/transport/types/agent.d.ts +353 -0
  27. package/dist/core/transport/types/client.d.ts +168 -0
  28. package/dist/core/transport/types/shared.d.ts +24 -0
  29. package/dist/core/transport/types/tree.d.ts +315 -0
  30. package/dist/core/transport/types/view.d.ts +222 -0
  31. package/dist/core/transport/types.d.ts +13 -402
  32. package/dist/core/transport/view.d.ts +354 -0
  33. package/dist/errors.d.ts +37 -9
  34. package/dist/index.d.ts +6 -6
  35. package/dist/logger.d.ts +12 -0
  36. package/dist/react/ably-ai-transport-react.js +1164 -645
  37. package/dist/react/ably-ai-transport-react.js.map +1 -1
  38. package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
  39. package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
  40. package/dist/react/contexts/client-session-context.d.ts +36 -0
  41. package/dist/react/contexts/client-session-provider.d.ts +53 -0
  42. package/dist/react/create-session-hooks.d.ts +116 -0
  43. package/dist/react/index.d.ts +16 -10
  44. package/dist/react/internal/use-resolved-session.d.ts +36 -0
  45. package/dist/react/use-ably-messages.d.ts +20 -11
  46. package/dist/react/use-client-session.d.ts +81 -0
  47. package/dist/react/use-create-view.d.ts +23 -0
  48. package/dist/react/use-tree.d.ts +35 -0
  49. package/dist/react/use-view.d.ts +110 -0
  50. package/dist/utils.d.ts +32 -23
  51. package/dist/vercel/ably-ai-transport-vercel.js +2748 -1625
  52. package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
  53. package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
  54. package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
  55. package/dist/vercel/codec/decoder.d.ts +5 -18
  56. package/dist/vercel/codec/encoder.d.ts +6 -36
  57. package/dist/vercel/codec/events.d.ts +51 -0
  58. package/dist/vercel/codec/index.d.ts +24 -12
  59. package/dist/vercel/codec/reducer.d.ts +144 -0
  60. package/dist/vercel/codec/tool-transitions.d.ts +50 -0
  61. package/dist/vercel/index.d.ts +4 -2
  62. package/dist/vercel/react/ably-ai-transport-vercel-react.js +10298 -1410
  63. package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
  64. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +70 -1
  65. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
  66. package/dist/vercel/react/contexts/chat-transport-context.d.ts +33 -0
  67. package/dist/vercel/react/contexts/chat-transport-provider.d.ts +96 -0
  68. package/dist/vercel/react/index.d.ts +4 -0
  69. package/dist/vercel/react/use-chat-transport.d.ts +66 -21
  70. package/dist/vercel/react/use-message-sync.d.ts +31 -12
  71. package/dist/vercel/run-end-reason.d.ts +29 -0
  72. package/dist/vercel/transport/chat-transport.d.ts +71 -30
  73. package/dist/vercel/transport/index.d.ts +25 -18
  74. package/dist/vercel/transport/run-output-stream.d.ts +56 -0
  75. package/dist/version.d.ts +2 -0
  76. package/package.json +47 -34
  77. package/src/constants.ts +126 -47
  78. package/src/core/agent.ts +68 -0
  79. package/src/core/codec/decoder.ts +71 -98
  80. package/src/core/codec/encoder.ts +115 -58
  81. package/src/core/codec/index.ts +13 -6
  82. package/src/core/codec/lifecycle-tracker.ts +10 -9
  83. package/src/core/codec/types.ts +438 -106
  84. package/src/core/transport/agent-session.ts +1344 -0
  85. package/src/core/transport/branch-chain.ts +58 -0
  86. package/src/core/transport/client-session.ts +775 -0
  87. package/src/core/transport/decode-fold.ts +91 -0
  88. package/src/core/transport/headers.ts +182 -19
  89. package/src/core/transport/index.ts +29 -22
  90. package/src/core/transport/internal/bounded-map.ts +27 -0
  91. package/src/core/transport/invocation.ts +98 -0
  92. package/src/core/transport/load-conversation.ts +355 -0
  93. package/src/core/transport/load-history.ts +269 -0
  94. package/src/core/transport/pipe-stream.ts +58 -40
  95. package/src/core/transport/run-manager.ts +249 -0
  96. package/src/core/transport/tree.ts +1167 -0
  97. package/src/core/transport/types/agent.ts +407 -0
  98. package/src/core/transport/types/client.ts +211 -0
  99. package/src/core/transport/types/shared.ts +27 -0
  100. package/src/core/transport/types/tree.ts +344 -0
  101. package/src/core/transport/types/view.ts +259 -0
  102. package/src/core/transport/types.ts +13 -527
  103. package/src/core/transport/view.ts +1271 -0
  104. package/src/errors.ts +42 -9
  105. package/src/event-emitter.ts +3 -2
  106. package/src/index.ts +55 -39
  107. package/src/logger.ts +14 -1
  108. package/src/react/contexts/client-session-context.ts +41 -0
  109. package/src/react/contexts/client-session-provider.tsx +186 -0
  110. package/src/react/create-session-hooks.ts +141 -0
  111. package/src/react/index.ts +27 -10
  112. package/src/react/internal/use-resolved-session.ts +63 -0
  113. package/src/react/use-ably-messages.ts +47 -19
  114. package/src/react/use-client-session.ts +201 -0
  115. package/src/react/use-create-view.ts +72 -0
  116. package/src/react/use-tree.ts +84 -0
  117. package/src/react/use-view.ts +275 -0
  118. package/src/react/vite.config.ts +4 -1
  119. package/src/utils.ts +63 -45
  120. package/src/vercel/codec/decoder.ts +336 -255
  121. package/src/vercel/codec/encoder.ts +348 -196
  122. package/src/vercel/codec/events.ts +87 -0
  123. package/src/vercel/codec/index.ts +59 -14
  124. package/src/vercel/codec/reducer.ts +977 -0
  125. package/src/vercel/codec/tool-transitions.ts +122 -0
  126. package/src/vercel/index.ts +7 -3
  127. package/src/vercel/react/contexts/chat-transport-context.ts +41 -0
  128. package/src/vercel/react/contexts/chat-transport-provider.tsx +150 -0
  129. package/src/vercel/react/index.ts +13 -1
  130. package/src/vercel/react/use-chat-transport.ts +162 -42
  131. package/src/vercel/react/use-message-sync.ts +121 -22
  132. package/src/vercel/react/vite.config.ts +4 -2
  133. package/src/vercel/run-end-reason.ts +78 -0
  134. package/src/vercel/transport/chat-transport.ts +553 -113
  135. package/src/vercel/transport/index.ts +40 -28
  136. package/src/vercel/transport/run-output-stream.ts +170 -0
  137. package/src/version.ts +2 -0
  138. package/dist/core/transport/client-transport.d.ts +0 -10
  139. package/dist/core/transport/conversation-tree.d.ts +0 -9
  140. package/dist/core/transport/decode-history.d.ts +0 -41
  141. package/dist/core/transport/server-transport.d.ts +0 -7
  142. package/dist/core/transport/stream-router.d.ts +0 -19
  143. package/dist/core/transport/turn-manager.d.ts +0 -34
  144. package/dist/react/use-active-turns.d.ts +0 -8
  145. package/dist/react/use-client-transport.d.ts +0 -7
  146. package/dist/react/use-conversation-tree.d.ts +0 -20
  147. package/dist/react/use-edit.d.ts +0 -7
  148. package/dist/react/use-history.d.ts +0 -19
  149. package/dist/react/use-messages.d.ts +0 -7
  150. package/dist/react/use-regenerate.d.ts +0 -7
  151. package/dist/react/use-send.d.ts +0 -7
  152. package/dist/vercel/codec/accumulator.d.ts +0 -21
  153. package/src/core/transport/client-transport.ts +0 -959
  154. package/src/core/transport/conversation-tree.ts +0 -434
  155. package/src/core/transport/decode-history.ts +0 -337
  156. package/src/core/transport/server-transport.ts +0 -458
  157. package/src/core/transport/stream-router.ts +0 -118
  158. package/src/core/transport/turn-manager.ts +0 -147
  159. package/src/react/use-active-turns.ts +0 -61
  160. package/src/react/use-client-transport.ts +0 -37
  161. package/src/react/use-conversation-tree.ts +0 -71
  162. package/src/react/use-edit.ts +0 -24
  163. package/src/react/use-history.ts +0 -111
  164. package/src/react/use-messages.ts +0 -32
  165. package/src/react/use-regenerate.ts +0 -24
  166. package/src/react/use-send.ts +0 -25
  167. package/src/vercel/codec/accumulator.ts +0 -603
@@ -0,0 +1,315 @@
1
+ import { CodecOutputEvent } from '../../codec/types.js';
2
+ import { RunEndReason } from './shared.js';
3
+ /** Conversation-tree types: nodes, the run-lifecycle event, output events, and the Tree contract. */
4
+ import type * as Ably from 'ably';
5
+ /**
6
+ * Fields common to every {@link RunLifecycleEvent} arm.
7
+ */
8
+ interface RunLifecycleBase {
9
+ /** The run-id this lifecycle event concerns. */
10
+ runId: string;
11
+ /** The owning client's identity (Ably publisher `clientId`). */
12
+ clientId: string;
13
+ /**
14
+ * The invocation-id this lifecycle event was published under (wire
15
+ * `invocation-id`). Lets consumers correlate the run's lifecycle back to the
16
+ * invocation that drove it; on a run-start the Tree records it on the RunNode
17
+ * at first creation so an optimistic Run exposes the invocation synchronously.
18
+ * Empty string if the wire didn't carry an invocation-id.
19
+ */
20
+ invocationId: string;
21
+ }
22
+ /**
23
+ * A structured event describing a run starting, suspending, resuming, or
24
+ * ending. The `type` discriminator (`start` / `suspend` / `resume` / `end`) is
25
+ * the in-memory domain vocabulary and is intentionally distinct from the wire
26
+ * message names (`ai-run-start` / `ai-run-suspend` / `ai-run-resume` /
27
+ * `ai-run-end`) those events are decoded from.
28
+ */
29
+ export type RunLifecycleEvent = (RunLifecycleBase & {
30
+ type: 'start';
31
+ /**
32
+ * Ably channel serial of the run-start message, or `undefined` for an
33
+ * optimistic local event (no serial assigned yet). The Tree reads it to
34
+ * promote the Run's startSerial.
35
+ */
36
+ serial: string | undefined;
37
+ /** The codec-message-id of the parent message, if known. Omitted for root runs. */
38
+ parent?: string;
39
+ /**
40
+ * The codec-message-id of the user prompt being forked, when the run is an
41
+ * edit. Carried verbatim from the `fork-of` wire header.
42
+ */
43
+ forkOf?: string;
44
+ /**
45
+ * The codec-message-id of the assistant message this run regenerates, when
46
+ * the run is a regenerate continuation. Carried verbatim from the
47
+ * `msg-regenerate` wire header. The Tree treats regenerates
48
+ * as continuations (no `forkOf` at the Run level) — the View
49
+ * realises the replacement when materialising messages.
50
+ */
51
+ regenerates?: string;
52
+ }) | (RunLifecycleBase & {
53
+ type: 'suspend';
54
+ /**
55
+ * Ably channel serial of the run-suspend message, or `undefined` for an
56
+ * optimistic local event. The Tree reads it to set the Run's endSerial
57
+ * (a suspended run carries the serial at which it paused).
58
+ */
59
+ serial: string | undefined;
60
+ }) | (RunLifecycleBase & {
61
+ type: 'resume';
62
+ /**
63
+ * Ably channel serial of the run-resume message, or `undefined` for an
64
+ * optimistic local event. A resume re-enters an existing run; it does not
65
+ * promote the Run's startSerial (the original run-start owns that).
66
+ */
67
+ serial: string | undefined;
68
+ }) | (RunLifecycleBase & {
69
+ type: 'end';
70
+ /**
71
+ * Ably channel serial of the run-end message, or `undefined` for an
72
+ * optimistic local event. The Tree reads it to set the Run's endSerial.
73
+ */
74
+ serial: string | undefined;
75
+ /**
76
+ * Why the run ended — the terminal reason the Tree records as the
77
+ * RunNode's status: `complete`, `cancelled`, or `error`.
78
+ */
79
+ reason: RunEndReason;
80
+ });
81
+ /** A node in the conversation tree, representing a single domain message. */
82
+ export interface MessageNode<TMessage> {
83
+ /** Discriminator — identifies this as a message node. */
84
+ kind: 'message';
85
+ /** The domain message. */
86
+ message: TMessage;
87
+ /** The codec-message-id of this node — primary key in the tree. */
88
+ codecMessageId: string;
89
+ /** Parent node's codec-message-id (parent), or undefined for root messages. */
90
+ parentId: string | undefined;
91
+ /** The codec-message-id this node forks from (fork-of), or undefined if first version. */
92
+ forkOf: string | undefined;
93
+ /** The transport-tier headers (`extras.ai.transport`) for this message: the run/stream/identity/branching headers set and read by the transport layer. Codec-tier headers (`extras.ai.codec`) are not included. */
94
+ headers: Record<string, string>;
95
+ /**
96
+ * Ably serial for this message. Lexicographically comparable for total order.
97
+ * Used to sort siblings deterministically regardless of delivery/history order.
98
+ * Absent for optimistic messages (set when the server relay arrives).
99
+ */
100
+ serial: string | undefined;
101
+ }
102
+ /**
103
+ * A node in the conversation tree, representing a single Run.
104
+ *
105
+ * A RunNode is keyed by its agent-minted `runId`. Each RunNode owns a per-Run
106
+ * codec {@link TProjection} folded from every event published under that
107
+ * run-id; the SDK extracts the per-message list via {@link Codec.getMessages}
108
+ * when it needs to render messages for that Run.
109
+ *
110
+ * A regenerate is a sibling reply run: it shares its input-node parent
111
+ * ({@link parentCodecMessageId}) with the original reply, so same-parent reply
112
+ * runs form the regenerate group with no `forkOf` involved. (Editing a prompt
113
+ * instead produces a sibling {@link InputNode} via that node's `forkOf`.)
114
+ */
115
+ export interface RunNode<TProjection> {
116
+ /** Discriminator — identifies this as a reply-run node within {@link ConversationNode}. */
117
+ kind: 'run';
118
+ /** The run-id of this Run — primary key in the tree. */
119
+ runId: string;
120
+ /**
121
+ * The codec-message-id this Run is rooted at — the `parent` header of the
122
+ * first observed message (or the run-start lifecycle event's `parent`
123
+ * field). This is the run's input node's codec-message-id: the user prompt
124
+ * the agent replied to. The Tree uses it for kind-blind reachability and to
125
+ * build the input→reply edge. `undefined` for the root Run.
126
+ */
127
+ parentCodecMessageId: string | undefined;
128
+ /**
129
+ * The node key of the node this Run replaces, or `undefined` if this Run is
130
+ * not a fork. Populated when the wire's `fork-of` header points at a
131
+ * codec-message-id that has been observed; the Tree resolves it through the
132
+ * codec-message-id → node-key index. Reply-run regenerate siblings do not
133
+ * use this (they group by shared parent) — it carries an explicit fork link
134
+ * when the wire stamps one.
135
+ */
136
+ forkOf: string | undefined;
137
+ /**
138
+ * The codec-message-id this Run regenerates, or `undefined` for non-regenerate
139
+ * Runs. Populated from the wire's `msg-regenerate` header (and the lifecycle
140
+ * event's `regenerates` field) verbatim — the Tree does not resolve it to a
141
+ * node key because the anchor is a message position, not a node.
142
+ *
143
+ * A regenerate run parents at the SAME input node as the reply it
144
+ * regenerates, so it joins that input's reply runs as a same-parent sibling;
145
+ * the message named by `regeneratesCodecMessageId` is replaced by this Run's
146
+ * content when the View materialises the chain into messages (Spec: AIT-CT13d).
147
+ */
148
+ regeneratesCodecMessageId: string | undefined;
149
+ /**
150
+ * Identity of the Ably client that started this Run, sourced from the
151
+ * `run-client-id` wire header (or the run-start lifecycle event's
152
+ * `clientId` field). Set once at Run creation and never updated; persists
153
+ * through the Run's lifecycle, including after `run-end`. Empty string if
154
+ * the wire didn't carry a client id.
155
+ */
156
+ clientId: string;
157
+ /**
158
+ * Run lifecycle status.
159
+ * - `'active'` — run-start observed, no terminal event yet.
160
+ * - `'suspended'` — run-suspend observed; the run is paused awaiting input
161
+ * and stays live (a continuation re-activates it). Not terminal.
162
+ * - {@link RunEndReason} — terminal state reflecting the run-end reason.
163
+ */
164
+ status: 'active' | 'suspended' | RunEndReason;
165
+ /** Per-Run codec projection. Folded by the Tree from every event published under this run-id. */
166
+ projection: TProjection;
167
+ /**
168
+ * The agent-minted invocationId observed for this Run (wire `invocation-id`).
169
+ * The agent mints it, so an optimistic Run starts with an empty id; it is
170
+ * adopted from the agent's `ai-run-start` (or set at creation when the Run is
171
+ * first seen from a wire event carrying one) and never reassigned thereafter.
172
+ * Empty string until run-start arrives, or if the wire didn't carry an
173
+ * invocation-id.
174
+ */
175
+ invocationId: string;
176
+ /** Ably serial of the first observed message tagged with this run-id. Absent for optimistic Runs. */
177
+ startSerial: string | undefined;
178
+ /** Ably serial of the run-end lifecycle event, if observed. */
179
+ endSerial: string | undefined;
180
+ }
181
+ /**
182
+ * A node in the conversation tree, representing a single user input (prompt).
183
+ *
184
+ * An input node owns the user's prompt for one turn. It is keyed by the
185
+ * client-owned `codec-message-id` and never carries a run-id — the agent mints
186
+ * the run-id for the reply, which becomes a separate {@link RunNode} parented to
187
+ * this input node. An edit of a prompt is a sibling input node (via `forkOf`).
188
+ *
189
+ * Like a {@link RunNode}, it carries its own per-input codec {@link TProjection}
190
+ * folded from the input event(s) published under its codec-message-id; the SDK
191
+ * extracts the per-message list via {@link Codec.getMessages} when rendering.
192
+ */
193
+ export interface InputNode<TProjection> {
194
+ /** Discriminator — identifies this as an input node within {@link ConversationNode}. */
195
+ kind: 'input';
196
+ /** The codec-message-id of this input — primary key in the tree. */
197
+ codecMessageId: string;
198
+ /**
199
+ * The codec-message-id of the node this input hangs off (its structural
200
+ * parent — the immediately preceding reply run on this chain), or `undefined`
201
+ * for the first input in a conversation. Used for kind-blind tree
202
+ * reachability alongside {@link RunNode.parentCodecMessageId}.
203
+ */
204
+ parentCodecMessageId: string | undefined;
205
+ /**
206
+ * The codec-message-id this input forks from when it is an edit of an earlier
207
+ * prompt, or `undefined` if it is the first version. Sibling input nodes
208
+ * (alternate prompts) share the same `forkOf` anchor.
209
+ */
210
+ forkOf: string | undefined;
211
+ /** Per-input codec projection. Folded by the Tree from every input event published under this codec-message-id. */
212
+ projection: TProjection;
213
+ /** Ably serial of the first observed message for this input. Absent for optimistic (locally-created) inputs. */
214
+ serial: string | undefined;
215
+ }
216
+ /**
217
+ * A node in the conversation tree: either a user {@link InputNode} or an agent
218
+ * {@link RunNode}. Narrow on `kind` (`'input'` vs `'run'`) before reading
219
+ * kind-specific fields.
220
+ */
221
+ export type ConversationNode<TProjection> = InputNode<TProjection> | RunNode<TProjection>;
222
+ /**
223
+ * Payload of the Tree's `output` event: the decoded agent outputs folded
224
+ * for a Run from a single inbound message, carrying the routing metadata a
225
+ * consumer needs to attribute or stream them.
226
+ */
227
+ export interface OutputEvent<TOutput extends CodecOutputEvent> {
228
+ /**
229
+ * The runId the outputs were folded into, or `undefined` when the fold was
230
+ * into a user input node (which carries no run-id — the agent mints run-ids).
231
+ * An input fold always has empty {@link events}; consumers route by
232
+ * {@link inputCodecMessageId}, not this.
233
+ */
234
+ runId: string | undefined;
235
+ /**
236
+ * The codec-message-id of the input event that triggered this run — the
237
+ * agent's `input-codec-message-id` header. This is the stable key the client
238
+ * owns from send time (before the agent mints the runId), so the output
239
+ * stream can attribute outputs to the request that produced them. Distinct
240
+ * from {@link runId}: causal (which input produced these outputs) rather than
241
+ * the run's own identity. `undefined` when the carrying message had no such
242
+ * header — e.g. a purely-optimistic local fold with no wire echo yet.
243
+ */
244
+ inputCodecMessageId: string | undefined;
245
+ /**
246
+ * The `codec-message-id` the outputs were published under, or `undefined`
247
+ * when the message carried none.
248
+ */
249
+ codecMessageId: string | undefined;
250
+ /**
251
+ * Ably channel serial of the message that carried the outputs, or
252
+ * `undefined` for an optimistic local fold (no serial assigned yet).
253
+ */
254
+ serial: string | undefined;
255
+ /**
256
+ * The decoded agent outputs from this message, in wire order. Empty when
257
+ * the folded message carried only inputs (e.g. an optimistic user
258
+ * message); the event still fires so consumers can observe that the Run's
259
+ * projection changed.
260
+ */
261
+ events: TOutput[];
262
+ }
263
+ /**
264
+ * Materializes a branching conversation tree from a flat oplog of Ably
265
+ * messages. Each turn is two nodes: a user {@link InputNode} keyed by its
266
+ * client-owned codec-message-id and an agent {@link RunNode} keyed by the
267
+ * agent-minted run-id, parented to the input node.
268
+ *
269
+ * The Tree owns the complete conversation state across every observed node.
270
+ * Each node holds a per-node codec {@link TProjection} which the Tree folds
271
+ * from inbound events. The View walks the parent chain to extract a flat
272
+ * message list for rendering.
273
+ */
274
+ export interface Tree<TOutput extends CodecOutputEvent, TProjection> {
275
+ /** Get a Run by runId, or undefined if not found. */
276
+ getRunNode(runId: string): RunNode<TProjection> | undefined;
277
+ /**
278
+ * Get the node that owns a given codec-message-id (via the Tree's
279
+ * codecMessageId index), or undefined if the codec-message-id hasn't been
280
+ * observed. The result is a {@link ConversationNode} union: narrow on `kind`
281
+ * (`'input'` vs `'run'`) before reading kind-specific fields.
282
+ */
283
+ getNodeByCodecMessageId(codecMessageId: string): ConversationNode<TProjection> | undefined;
284
+ /**
285
+ * Get the sibling group (both kinds) the node keyed by `key` belongs to:
286
+ * edit versions for an input node (forkOf-linked, same parent), regenerate
287
+ * runs for a reply run (same input-node parent). Ordered oldest-first by
288
+ * serial; a single-element array when the node has no siblings. Empty when
289
+ * `key` is unknown. Narrow each node on `kind` before reading kind-specific
290
+ * fields.
291
+ * @param key - The node key ({@link RunNode.runId} or {@link InputNode.codecMessageId}).
292
+ * @returns The ordered sibling nodes.
293
+ */
294
+ getSiblingNodes(key: string): ConversationNode<TProjection>[];
295
+ /**
296
+ * Subscribe to tree structural changes (Run insert, delete, sort-reorder,
297
+ * startSerial promotion, run-start metadata backfill). Does NOT fire on
298
+ * content-only folds (streaming chunks) or on run-end status changes —
299
+ * those flow through `output` and `run` respectively.
300
+ */
301
+ on(event: 'update', handler: () => void): () => void;
302
+ /** Subscribe to raw Ably messages arriving on the channel. */
303
+ on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;
304
+ /** Subscribe to run lifecycle events (start, suspend, resume, and end). */
305
+ on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;
306
+ /**
307
+ * Subscribe to decoded agent outputs as they are folded into a Run.
308
+ * Fires once per inbound message after its fold, carrying the message's
309
+ * output events plus routing metadata (runId, codec-message-id, serial).
310
+ * Fires with an empty `events` array for inputs-only folds so it can also
311
+ * serve as a projection-changed signal.
312
+ */
313
+ on(event: 'output', handler: (event: OutputEvent<TOutput>) => void): () => void;
314
+ }
315
+ export {};
@@ -0,0 +1,222 @@
1
+ import { CodecInputEvent, CodecMessage } from '../../codec/types.js';
2
+ import { ActiveRun, SendOptions } from './client.js';
3
+ import { RunEndReason } from './shared.js';
4
+ import { RunLifecycleEvent } from './tree.js';
5
+ /** View types: history pagination, branch selection, run info, and the windowed View contract. */
6
+ import type * as Ably from 'ably';
7
+ /** A page of raw history wires from the channel. Internal to View/decodeHistory. */
8
+ export interface HistoryPage {
9
+ /** Raw Ably messages that produced this page, in chronological order (oldest first). */
10
+ rawMessages: Ably.InboundMessage[];
11
+ /** Whether there are older pages available. */
12
+ hasNext(): boolean;
13
+ /** Fetch the next (older) page. Returns undefined if no more pages. */
14
+ next(): Promise<HistoryPage | undefined>;
15
+ }
16
+ /** Options for loading channel history. */
17
+ export interface LoadHistoryOptions {
18
+ /** Max messages per page. Default: 100. */
19
+ limit?: number;
20
+ }
21
+ /**
22
+ * Projection-free, View-facing snapshot of a Run.
23
+ *
24
+ * Exposes the Run facts a UI consumer needs (`runId`, owner `clientId`,
25
+ * lifecycle `status`, `invocationId`) without leaking the codec's
26
+ * opaque per-Run projection or the Tree's structural fields. Callers
27
+ * that need the full Run record (parent / fork relationships, serials,
28
+ * projection) reach `session.tree.getRunNode(runId)` directly.
29
+ */
30
+ export interface RunInfo {
31
+ /** The Run's unique identifier. */
32
+ runId: string;
33
+ /**
34
+ * Identity of the Ably client that started this Run. Empty string
35
+ * when the wire didn't carry an owner client id.
36
+ */
37
+ clientId: string;
38
+ /**
39
+ * Run lifecycle status. `'active'` while the Run is streaming;
40
+ * `'suspended'` while it is paused awaiting input (still live, a
41
+ * continuation re-activates it); otherwise the {@link RunEndReason} the Run
42
+ * terminated with. Literal lifecycle vocabulary — UIs that want `'streaming'`
43
+ * rendering language translate at the component boundary.
44
+ */
45
+ status: 'active' | 'suspended' | RunEndReason;
46
+ /**
47
+ * The agent-minted `invocationId` observed for this Run, adopted from the
48
+ * wire `ai-run-start`. Stable across the Run's lifecycle once observed.
49
+ * Empty string until run-start arrives (the agent mints it, so an
50
+ * optimistic Run carries none) or when the wire didn't carry an
51
+ * invocation-id.
52
+ */
53
+ invocationId: string;
54
+ }
55
+ /**
56
+ * Bundle returned by {@link View.branchSelection} describing the
57
+ * sibling group anchored at a given codec-message-id.
58
+ *
59
+ * Total / always-defined — `view.branchSelection(id)` is safe to call
60
+ * for any message:
61
+ *
62
+ * - **Branch anchor (N ≥ 2 siblings)**: `siblings` carries every
63
+ * sibling Run's view of the anchor slot, `index` is the selected
64
+ * sibling's position, `selected === siblings[index]`,
65
+ * `hasSiblings: true`.
66
+ * - **Known non-anchor message**: `siblings = [thisMessage]`,
67
+ * `index: 0`, `selected: thisMessage`, `hasSiblings: false`.
68
+ * - **Unknown codec-message-id**: `siblings: []`, `index: 0`,
69
+ * `selected: undefined`, `hasSiblings: false`.
70
+ *
71
+ * Because `siblings` always contains the currently rendered message
72
+ * (for known ids), `siblings.length` is `1` for a plain bubble (not
73
+ * `0`) and the indexing space matches between read and write —
74
+ * passing `branch.index` back into {@link View.selectSibling} is a
75
+ * round-trip no-op.
76
+ */
77
+ export interface BranchSelection<TMessage> {
78
+ /** True when the codec-message-id is a branch anchor with more than one sibling. Equivalent to `siblings.length > 1`. */
79
+ hasSiblings: boolean;
80
+ /**
81
+ * The selected sibling and any alternatives, in tree-order (oldest
82
+ * first). Always contains the currently rendered message itself for
83
+ * known codec-message-ids; empty only when the id is unknown to the
84
+ * view.
85
+ */
86
+ siblings: TMessage[];
87
+ /** Index of the selected sibling within `siblings`. `0` when there is no real branching or the id is unknown. */
88
+ index: number;
89
+ /** Convenience reference to `siblings[index]`. `undefined` only when `siblings` is empty. */
90
+ selected: TMessage | undefined;
91
+ }
92
+ /**
93
+ * A paginated, branch-aware projection of the conversation tree.
94
+ *
95
+ * Returns only the visible portion of the selected branch. New live messages
96
+ * appear immediately; older messages are revealed progressively via
97
+ * `loadOlder()`. Events are scoped to the visible window — subscribers
98
+ * are only notified when the visible output changes.
99
+ */
100
+ export interface View<TInput extends CodecInputEvent, TMessage> {
101
+ /**
102
+ * The visible messages along the selected branch, each paired with its
103
+ * codec-message-id (see {@link CodecMessage}). Computed by walking the
104
+ * visible Run chain (newest to root) and concatenating each Run's
105
+ * `codec.getMessages(projection)` in chronological order.
106
+ *
107
+ * Correlate a message back to the transport — routing a continuation
108
+ * input, resolving a regenerate/edit target, looking up the owning Run —
109
+ * via its `codecMessageId`, which the SDK assigns and tracks
110
+ * independently of any identity the domain `message` may carry. Read the
111
+ * domain object from each entry's `message` field.
112
+ */
113
+ getMessages(): CodecMessage<TMessage>[];
114
+ /**
115
+ * Snapshot of the visible Runs along the selected branch, in
116
+ * chronological order — already filtered by this view's pagination
117
+ * window, branch selection, and regenerate substitution. The
118
+ * companion to {@link getMessages}: same scope, exposed as
119
+ * projection-free {@link RunInfo} so consumers can iterate Run
120
+ * identity (runId, clientId, status, invocationId) without touching
121
+ * the Tree.
122
+ */
123
+ runs(): RunInfo[];
124
+ /** Whether there are older Runs that can be loaded or revealed. */
125
+ hasOlder(): boolean;
126
+ /**
127
+ * Reveal older Runs. Loads from channel history if the tree doesn't have
128
+ * enough, then advances the pagination window by up to `limit` Runs.
129
+ * Emits 'update' when the visible list changes.
130
+ *
131
+ * The pagination unit is the **Run**, not the message. A single Run
132
+ * typically contributes more than one message to the flat list returned
133
+ * by {@link View.getMessages} (e.g. a user prompt + assistant reply
134
+ * pair). Revealing `limit` Runs may add 1..N messages each to the
135
+ * visible window.
136
+ * @param limit - Maximum number of older Runs to reveal. Defaults to 100.
137
+ */
138
+ loadOlder(limit?: number): Promise<void>;
139
+ /**
140
+ * Look up the {@link RunInfo} for the Run that owns `codecMessageId`.
141
+ * For a user input node's codec-message-id, resolves to its
142
+ * currently-selected reply run. Returns `undefined` when the
143
+ * codec-message-id hasn't been observed by the view, or when it names
144
+ * an input node that has no reply run yet.
145
+ * @param codecMessageId - The codec-message-id to look up.
146
+ */
147
+ runOf(codecMessageId: string): RunInfo | undefined;
148
+ /**
149
+ * Direct lookup by Run id. Kept for symmetry with {@link runOf} so
150
+ * callers that hold a `runId` (e.g. cancel handlers) get a one-step
151
+ * lookup. Returns `undefined` when the Run hasn't been observed.
152
+ * @param runId - The Run id to look up.
153
+ */
154
+ run(runId: string): RunInfo | undefined;
155
+ /**
156
+ * Resolve the {@link BranchSelection} bundle anchored at
157
+ * `codecMessageId`. Always returns a safe object — see
158
+ * {@link BranchSelection} for the per-case shape.
159
+ *
160
+ * Per AITRFC-014, branch points are message-anchored: edit forks
161
+ * point at the user prompt's codec-message-id, regenerate forks
162
+ * point at the assistant message's codec-message-id.
163
+ * @param codecMessageId - The codec-message-id of the bubble being rendered.
164
+ */
165
+ branchSelection(codecMessageId: string): BranchSelection<TMessage>;
166
+ /**
167
+ * Select a sibling at the branch point anchored at
168
+ * `codecMessageId`. `index` is clamped to
169
+ * `[0, siblings.length - 1]`. Silent no-op when `codecMessageId`
170
+ * is not a branch anchor. Emits 'update' when the visible output
171
+ * changes.
172
+ * @param codecMessageId - The codec-message-id of the bubble being rendered.
173
+ * @param index - The index of the sibling to select.
174
+ */
175
+ selectSibling(codecMessageId: string, index: number): void;
176
+ /**
177
+ * Send one or more TInputs on the channel and fire a POST. Each TInput
178
+ * carries its own routing metadata (`parent` / `target` / `codecMessageId`)
179
+ * via the {@link CodecInputEvent} base; the SDK reads those fields
180
+ * directly without runtime classification.
181
+ *
182
+ * To send a fresh user message, wrap the domain message with
183
+ * {@link Codec.createUserMessage} and pass the result here, e.g.
184
+ * `view.send(codec.createUserMessage(message))`.
185
+ *
186
+ * Convention: a send containing at least one `UserMessage` is a
187
+ * fresh send (mints a new `runId`). A send containing only
188
+ * tool-resolution inputs is a continuation — pair with
189
+ * `options.runId` to extend a suspended run.
190
+ *
191
+ * The parent is auto-computed from this view's selected branch unless
192
+ * overridden. The HTTP POST is fire-and-forget — the returned stream is
193
+ * available immediately. If the POST fails, the error is surfaced via
194
+ * the session's `on("error")` and the stream is errored.
195
+ */
196
+ send(events: TInput | TInput[], options?: SendOptions): Promise<ActiveRun>;
197
+ /**
198
+ * Regenerate an assistant message. Mints a codec `Regenerate` input
199
+ * carrying `target` (the assistant codec-message-id being regenerated)
200
+ * and `parent` (the preceding user prompt's codec-message-id), both
201
+ * auto-computed from this view's branch — there are no new user inputs.
202
+ * The new reply run is not a `forkOf` fork; it continues the
203
+ * regenerated message's run, and the message-level replacement (the new
204
+ * assistant superseding the original) happens at projection-extraction
205
+ * time.
206
+ */
207
+ regenerate(messageId: string, options?: SendOptions): Promise<ActiveRun>;
208
+ /**
209
+ * Edit a user message. Creates a new run that forks the target message
210
+ * with replacement content. Automatically computes `forkOf` (the edited
211
+ * message) and `parent` from this view's branch.
212
+ */
213
+ edit(messageId: string, inputs: TInput | TInput[], options?: SendOptions): Promise<ActiveRun>;
214
+ /** The visible message list changed (new visible node, branch switch, window shift). */
215
+ on(event: 'update', handler: () => void): () => void;
216
+ /** A raw Ably message arrived that corresponds to a visible node. */
217
+ on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;
218
+ /** A run event occurred for a run with visible messages in the window. */
219
+ on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;
220
+ /** Tear down the view — unsubscribe from tree events and clear internal state. */
221
+ close(): void;
222
+ }