@ably/ai-transport 0.1.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.
Files changed (221) hide show
  1. package/README.md +93 -111
  2. package/dist/ably-ai-transport.js +2401 -1387
  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 +116 -42
  7. package/dist/core/agent.d.ts +44 -0
  8. package/dist/core/channel-options.d.ts +57 -0
  9. package/dist/core/codec/codec-event.d.ts +9 -0
  10. package/dist/core/codec/decoder.d.ts +24 -24
  11. package/dist/core/codec/define-codec.d.ts +100 -0
  12. package/dist/core/codec/encoder.d.ts +10 -12
  13. package/dist/core/codec/field-bag.d.ts +85 -0
  14. package/dist/core/codec/fields.d.ts +141 -0
  15. package/dist/core/codec/index.d.ts +8 -2
  16. package/dist/core/codec/input-descriptor-decoder.d.ts +19 -0
  17. package/dist/core/codec/input-descriptor-encoder.d.ts +22 -0
  18. package/dist/core/codec/input-descriptors.d.ts +281 -0
  19. package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
  20. package/dist/core/codec/output-descriptor-decoder.d.ts +29 -0
  21. package/dist/core/codec/output-descriptor-encoder.d.ts +31 -0
  22. package/dist/core/codec/output-descriptors.d.ts +237 -0
  23. package/dist/core/codec/types.d.ts +470 -119
  24. package/dist/core/codec/well-known-inputs.d.ts +52 -0
  25. package/dist/core/transport/agent-session.d.ts +10 -0
  26. package/dist/core/transport/agent-view.d.ts +296 -0
  27. package/dist/core/transport/client-session.d.ts +13 -0
  28. package/dist/core/transport/decode-fold.d.ts +55 -0
  29. package/dist/core/transport/headers.d.ts +121 -14
  30. package/dist/core/transport/index.d.ts +5 -6
  31. package/dist/core/transport/internal/bounded-map.d.ts +20 -0
  32. package/dist/core/transport/invocation.d.ts +74 -0
  33. package/dist/core/transport/load-history-pages.d.ts +71 -0
  34. package/dist/core/transport/load-history.d.ts +44 -0
  35. package/dist/core/transport/pipe-stream.d.ts +9 -9
  36. package/dist/core/transport/run-manager.d.ts +76 -0
  37. package/dist/core/transport/session-support.d.ts +55 -0
  38. package/dist/core/transport/tree.d.ts +523 -109
  39. package/dist/core/transport/types/agent.d.ts +375 -0
  40. package/dist/core/transport/types/client.d.ts +201 -0
  41. package/dist/core/transport/types/shared.d.ts +24 -0
  42. package/dist/core/transport/types/tree.d.ts +357 -0
  43. package/dist/core/transport/types/view.d.ts +249 -0
  44. package/dist/core/transport/types.d.ts +13 -553
  45. package/dist/core/transport/view.d.ts +390 -84
  46. package/dist/core/transport/wire-log.d.ts +102 -0
  47. package/dist/errors.d.ts +27 -10
  48. package/dist/index.d.ts +8 -9
  49. package/dist/logger.d.ts +12 -0
  50. package/dist/react/ably-ai-transport-react.js +1365 -1010
  51. package/dist/react/ably-ai-transport-react.js.map +1 -1
  52. package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
  53. package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
  54. package/dist/react/contexts/client-session-context.d.ts +37 -0
  55. package/dist/react/contexts/client-session-provider.d.ts +56 -0
  56. package/dist/react/create-session-hooks.d.ts +116 -0
  57. package/dist/react/index.d.ts +13 -12
  58. package/dist/react/internal/skipped-session.d.ts +8 -0
  59. package/dist/react/internal/use-resolved-session.d.ts +36 -0
  60. package/dist/react/use-ably-messages.d.ts +17 -14
  61. package/dist/react/use-client-session.d.ts +81 -0
  62. package/dist/react/use-create-view.d.ts +14 -13
  63. package/dist/react/use-tree.d.ts +30 -15
  64. package/dist/react/use-view.d.ts +81 -50
  65. package/dist/utils.d.ts +48 -71
  66. package/dist/vercel/ably-ai-transport-vercel.js +3257 -2499
  67. package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
  68. package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
  69. package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
  70. package/dist/vercel/codec/decode-lifecycle.d.ts +9 -0
  71. package/dist/vercel/codec/events.d.ts +50 -0
  72. package/dist/vercel/codec/fields.d.ts +44 -0
  73. package/dist/vercel/codec/fold-content.d.ts +16 -0
  74. package/dist/vercel/codec/fold-data.d.ts +16 -0
  75. package/dist/vercel/codec/fold-input.d.ts +67 -0
  76. package/dist/vercel/codec/fold-lifecycle.d.ts +16 -0
  77. package/dist/vercel/codec/fold-text.d.ts +16 -0
  78. package/dist/vercel/codec/fold-tool-input.d.ts +17 -0
  79. package/dist/vercel/codec/fold-tool-output.d.ts +16 -0
  80. package/dist/vercel/codec/index.d.ts +7 -20
  81. package/dist/vercel/codec/inputs.d.ts +11 -0
  82. package/dist/vercel/codec/outputs.d.ts +11 -0
  83. package/dist/vercel/codec/reducer-state.d.ts +121 -0
  84. package/dist/vercel/codec/reducer.d.ts +62 -0
  85. package/dist/vercel/codec/tool-transitions.d.ts +2 -8
  86. package/dist/vercel/codec/wire-data.d.ts +34 -0
  87. package/dist/vercel/index.d.ts +5 -5
  88. package/dist/vercel/react/ably-ai-transport-vercel-react.js +2859 -9705
  89. package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
  90. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +1 -45
  91. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
  92. package/dist/vercel/react/contexts/chat-transport-context.d.ts +9 -7
  93. package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
  94. package/dist/vercel/react/index.d.ts +1 -2
  95. package/dist/vercel/react/use-chat-transport.d.ts +30 -26
  96. package/dist/vercel/react/use-message-sync.d.ts +17 -30
  97. package/dist/vercel/run-end-reason.d.ts +84 -0
  98. package/dist/vercel/tool-part.d.ts +21 -0
  99. package/dist/vercel/transport/chat-transport.d.ts +41 -24
  100. package/dist/vercel/transport/index.d.ts +24 -20
  101. package/dist/vercel/transport/run-output-stream.d.ts +54 -0
  102. package/dist/version.d.ts +2 -0
  103. package/package.json +31 -24
  104. package/src/constants.ts +124 -51
  105. package/src/core/agent.ts +92 -0
  106. package/src/core/channel-options.ts +89 -0
  107. package/src/core/codec/codec-event.ts +27 -0
  108. package/src/core/codec/decoder.ts +202 -105
  109. package/src/core/codec/define-codec.ts +432 -0
  110. package/src/core/codec/encoder.ts +114 -107
  111. package/src/core/codec/field-bag.ts +142 -0
  112. package/src/core/codec/fields.ts +193 -0
  113. package/src/core/codec/index.ts +56 -6
  114. package/src/core/codec/input-descriptor-decoder.ts +97 -0
  115. package/src/core/codec/input-descriptor-encoder.ts +150 -0
  116. package/src/core/codec/input-descriptors.ts +373 -0
  117. package/src/core/codec/lifecycle-tracker.ts +10 -9
  118. package/src/core/codec/output-descriptor-decoder.ts +139 -0
  119. package/src/core/codec/output-descriptor-encoder.ts +101 -0
  120. package/src/core/codec/output-descriptors.ts +307 -0
  121. package/src/core/codec/types.ts +505 -126
  122. package/src/core/codec/well-known-inputs.ts +96 -0
  123. package/src/core/transport/agent-session.ts +1085 -0
  124. package/src/core/transport/agent-view.ts +738 -0
  125. package/src/core/transport/client-session.ts +780 -0
  126. package/src/core/transport/decode-fold.ts +101 -0
  127. package/src/core/transport/headers.ts +234 -22
  128. package/src/core/transport/index.ts +27 -27
  129. package/src/core/transport/internal/bounded-map.ts +27 -0
  130. package/src/core/transport/invocation.ts +98 -0
  131. package/src/core/transport/load-history-pages.ts +220 -0
  132. package/src/core/transport/load-history.ts +271 -0
  133. package/src/core/transport/pipe-stream.ts +63 -39
  134. package/src/core/transport/run-manager.ts +243 -0
  135. package/src/core/transport/session-support.ts +96 -0
  136. package/src/core/transport/tree.ts +1293 -308
  137. package/src/core/transport/types/agent.ts +434 -0
  138. package/src/core/transport/types/client.ts +247 -0
  139. package/src/core/transport/types/shared.ts +27 -0
  140. package/src/core/transport/types/tree.ts +393 -0
  141. package/src/core/transport/types/view.ts +288 -0
  142. package/src/core/transport/types.ts +13 -706
  143. package/src/core/transport/view.ts +1229 -450
  144. package/src/core/transport/wire-log.ts +189 -0
  145. package/src/errors.ts +29 -9
  146. package/src/event-emitter.ts +3 -2
  147. package/src/index.ts +86 -42
  148. package/src/logger.ts +14 -1
  149. package/src/react/contexts/client-session-context.ts +41 -0
  150. package/src/react/contexts/client-session-provider.tsx +222 -0
  151. package/src/react/create-session-hooks.ts +141 -0
  152. package/src/react/index.ts +24 -13
  153. package/src/react/internal/skipped-session.ts +62 -0
  154. package/src/react/internal/use-resolved-session.ts +63 -0
  155. package/src/react/use-ably-messages.ts +32 -22
  156. package/src/react/use-client-session.ts +178 -0
  157. package/src/react/use-create-view.ts +33 -29
  158. package/src/react/use-tree.ts +61 -30
  159. package/src/react/use-view.ts +138 -96
  160. package/src/utils.ts +83 -131
  161. package/src/vercel/codec/decode-lifecycle.ts +70 -0
  162. package/src/vercel/codec/events.ts +85 -0
  163. package/src/vercel/codec/fields.ts +58 -0
  164. package/src/vercel/codec/fold-content.ts +54 -0
  165. package/src/vercel/codec/fold-data.ts +46 -0
  166. package/src/vercel/codec/fold-input.ts +255 -0
  167. package/src/vercel/codec/fold-lifecycle.ts +85 -0
  168. package/src/vercel/codec/fold-text.ts +55 -0
  169. package/src/vercel/codec/fold-tool-input.ts +86 -0
  170. package/src/vercel/codec/fold-tool-output.ts +79 -0
  171. package/src/vercel/codec/index.ts +28 -21
  172. package/src/vercel/codec/inputs.ts +116 -0
  173. package/src/vercel/codec/outputs.ts +207 -0
  174. package/src/vercel/codec/reducer-state.ts +169 -0
  175. package/src/vercel/codec/reducer.ts +191 -0
  176. package/src/vercel/codec/tool-transitions.ts +3 -14
  177. package/src/vercel/codec/wire-data.ts +64 -0
  178. package/src/vercel/index.ts +7 -19
  179. package/src/vercel/react/contexts/chat-transport-context.ts +8 -7
  180. package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
  181. package/src/vercel/react/index.ts +3 -5
  182. package/src/vercel/react/use-chat-transport.ts +44 -66
  183. package/src/vercel/react/use-message-sync.ts +75 -39
  184. package/src/vercel/run-end-reason.ts +157 -0
  185. package/src/vercel/tool-part.ts +25 -0
  186. package/src/vercel/transport/chat-transport.ts +380 -98
  187. package/src/vercel/transport/index.ts +38 -37
  188. package/src/vercel/transport/run-output-stream.ts +169 -0
  189. package/src/version.ts +2 -0
  190. package/dist/core/transport/client-transport.d.ts +0 -10
  191. package/dist/core/transport/decode-history.d.ts +0 -43
  192. package/dist/core/transport/server-transport.d.ts +0 -7
  193. package/dist/core/transport/stream-router.d.ts +0 -29
  194. package/dist/core/transport/turn-manager.d.ts +0 -37
  195. package/dist/react/contexts/transport-context.d.ts +0 -31
  196. package/dist/react/contexts/transport-provider.d.ts +0 -49
  197. package/dist/react/create-transport-hooks.d.ts +0 -124
  198. package/dist/react/use-active-turns.d.ts +0 -12
  199. package/dist/react/use-client-transport.d.ts +0 -80
  200. package/dist/vercel/codec/accumulator.d.ts +0 -21
  201. package/dist/vercel/codec/decoder.d.ts +0 -22
  202. package/dist/vercel/codec/encoder.d.ts +0 -41
  203. package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
  204. package/dist/vercel/tool-approvals.d.ts +0 -124
  205. package/dist/vercel/tool-events.d.ts +0 -26
  206. package/src/core/transport/client-transport.ts +0 -977
  207. package/src/core/transport/decode-history.ts +0 -485
  208. package/src/core/transport/server-transport.ts +0 -612
  209. package/src/core/transport/stream-router.ts +0 -136
  210. package/src/core/transport/turn-manager.ts +0 -165
  211. package/src/react/contexts/transport-context.ts +0 -37
  212. package/src/react/contexts/transport-provider.tsx +0 -164
  213. package/src/react/create-transport-hooks.ts +0 -144
  214. package/src/react/use-active-turns.ts +0 -72
  215. package/src/react/use-client-transport.ts +0 -197
  216. package/src/vercel/codec/accumulator.ts +0 -588
  217. package/src/vercel/codec/decoder.ts +0 -618
  218. package/src/vercel/codec/encoder.ts +0 -410
  219. package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
  220. package/src/vercel/tool-approvals.ts +0 -380
  221. package/src/vercel/tool-events.ts +0 -53
@@ -1,171 +1,585 @@
1
1
  import { Logger } from '../../logger.js';
2
- import { MessageNode, Tree, TurnLifecycleEvent } from './types.js';
2
+ import { CodecEvent, CodecInputEvent, CodecOutputEvent, Reducer } from '../codec/types.js';
3
+ import { ConversationNode, OutputEvent, RunLifecycleEvent, RunNode, Tree } from './types.js';
3
4
  /**
4
- * Tree — materializes a branching conversation from a flat
5
- * oplog of Ably messages using serial-first ordering.
5
+ * Tree — materializes a branching conversation as a forest of nodes. Each turn
6
+ * is two nodes: a user {@link InputNode} keyed by its client-owned
7
+ * codec-message-id and an agent {@link RunNode} keyed by the agent-minted
8
+ * run-id, parented to the input node.
6
9
  *
7
- * Serial order (the total order assigned by Ably) is the primary mechanism
8
- * for linear message sequences. `x-ably-parent` and `x-ably-fork-of` headers
9
- * are only structurally meaningful at branch points where the user is
10
- * interacting with a visible message and the client always has it loaded.
10
+ * Each node holds a per-node codec {@link TProjection} which the Tree folds
11
+ * from inbound events. The Tree owns the complete conversation state across
12
+ * every observed node. The {@link View} walks the parent chain to extract a
13
+ * flat message list for rendering.
11
14
  *
12
- * `upsert()` is the sole mutation method. Messages can arrive in any order
13
- * (live subscription, history pages, seed data) and the tree produces the
14
- * correct `flattenNodes()` output once all messages are present.
15
+ * `applyMessage()` is the entry point for inbound channel messages it
16
+ * classifies a run-less user input into an input node (keyed by
17
+ * codec-message-id) or routes a run-bearing wire to its reply run (keyed by
18
+ * run-id), folds events into that node's projection, and maintains a secondary
19
+ * `codecMessageId -> nodeKey` index. `applyRunLifecycle()` handles run-start /
20
+ * run-suspend / run-resume / run-end events.
15
21
  *
16
- * The tree owns conversation state. `flattenNodes()` returns the linear node
17
- * list for the currently selected branches this is what the transport's
18
- * `getMessages()` delegates to.
22
+ * Sibling structure: editing a prompt produces a sibling input node linked by
23
+ * {@link InputNode.forkOf}; regenerating a reply produces a sibling reply run
24
+ * sharing the same input-node parent (no fork-of).
19
25
  */
20
26
  import type * as Ably from 'ably';
21
- /** Internal tree surface used by View — not part of the public Tree API. */
22
- export interface TreeInternal<TMessage> extends Tree<TMessage> {
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;
35
+ /**
36
+ * The primary key a node is indexed under: a reply run's `runId`, or an input
37
+ * node's `codecMessageId` (the client owns it before the agent mints a runId).
38
+ * @param node - The node to key.
39
+ * @returns The node's primary key.
40
+ */
41
+ export declare const nodeKey: <TProjection>(node: ConversationNode<TProjection>) => string;
42
+ /** Internal tree surface used by View and ClientSession — not part of the public Tree API. */
43
+ export interface TreeInternal<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection> extends Tree<TOutput, TProjection> {
44
+ /**
45
+ * Walk the visible node chain (both input nodes and reply runs) along the
46
+ * selected branches, in chronological order. The View renders from this.
47
+ * @param selections - Per-group selected member key, keyed by group root.
48
+ * @returns The visible nodes in chronological order.
49
+ */
50
+ visibleNodes(selections?: Map<string, string>): ConversationNode<TProjection>[];
23
51
  /**
24
- * Monotonic counter that increments on structural changes (node insert,
25
- * delete, serial promotion/reorder) but NOT on content-only updates
26
- * (existing node's message replaced). Allows the View to skip full
27
- * tree walks when only message content changed.
52
+ * Get the "group root" key for a sibling group — the stable key the
53
+ * selection map is keyed by (the earliest edit version for input nodes, the
54
+ * original reply for a regenerate group).
55
+ */
56
+ getGroupRoot(key: string): string;
57
+ /**
58
+ * The reply runs parented at an input node (its codec-message-id), in
59
+ * iteration order. Empty when none have been observed. Used to resolve a
60
+ * user prompt to its reply run(s).
61
+ * @param inputCodecMessageId - The input node's codec-message-id.
62
+ * @returns The reply runs parented at that input.
63
+ */
64
+ getReplyRuns(inputCodecMessageId: string): RunNode<TProjection>[];
65
+ /**
66
+ * Apply an inbound channel message to the tree.
67
+ *
68
+ * Classifies the message and routes it to the owning node:
69
+ * 1. Run-less user input (no run-id, a `user`-role message carrying a
70
+ * codec-message-id and input events): creates or promotes the input node
71
+ * keyed by that codec-message-id, folds the input events.
72
+ * 2. Run-bearing wire (assistant output, continuation tool-resolution, or a
73
+ * fresh agent-minted run): routes to the reply run by run-id (reconciling
74
+ * an optimistic insert by codec-message-id), folds events.
75
+ * @param events - Decoded codec events, split by wire direction. Both are
76
+ * folded into the node's projection, inputs first.
77
+ * @param events.inputs - Client-published events (`ai-input` wire).
78
+ * @param events.outputs - Agent-published events (`ai-output` wire).
79
+ * @param headers - Transport headers from the inbound Ably message.
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.
28
91
  */
29
- readonly structuralVersion: number;
92
+ applyMessage(events: {
93
+ inputs: TInput[];
94
+ outputs: TOutput[];
95
+ }, headers: Record<string, string>, serial?: string, timestamp?: number, version?: string): void;
30
96
  /**
31
- * Flatten the tree along selected branches into a linear node list.
32
- * The `selections` map provides the selected sibling's msgId at each
33
- * fork point, keyed by group root msgId. Fork points not present in
34
- * the map default to the latest sibling. If a selectedMsgId is not
35
- * found in the sibling group (stale/deleted), falls back to latest.
97
+ * Apply a run-lifecycle event.
98
+ *
99
+ * - `start`: creates the reply run (if missing) or, for an existing run,
100
+ * sets RunNode.state to 'active', promotes startSerial, and backfills
101
+ * structural metadata (parent / forkOf / regenerates / invocationId).
102
+ * - `suspend`: sets RunNode.state to 'suspended' and records `endSerial`.
103
+ * The run stays live so a resume under the same `runId` picks up where it
104
+ * left off.
105
+ * - `resume`: re-activates an existing suspended Run (state back to
106
+ * 'active') without touching its structure or serials — a pure re-entry
107
+ * signal. A no-op if the Run is not yet known.
108
+ * - `end`: sets RunNode.state to the terminal reason and records
109
+ * `endSerial`.
110
+ *
111
+ * Always emits a 'run' event to subscribers.
112
+ * @param event - Lifecycle event payload, including the channel serial.
36
113
  */
37
- flattenNodes(selections: Map<string, string>): MessageNode<TMessage>[];
114
+ applyRunLifecycle(event: RunLifecycleEvent): void;
38
115
  /**
39
- * Get the "group root" msgId for a sibling group the original message
40
- * that all forks in the group trace back to.
116
+ * Get the node keyed by `key`, or undefined if `key` names no node. The
117
+ * key is a {@link nodeKey} a runId (reply run) or an input node's
118
+ * codec-message-id — so the result is a {@link ConversationNode} union:
119
+ * narrow on `kind` before reading kind-specific fields. Pairs with
120
+ * {@link getNodeByCodecMessageId}, which resolves an arbitrary owned
121
+ * codec-message-id (including an assistant message's) to its node.
122
+ * @param key - The node key to look up.
123
+ * @returns The node, or undefined if not found.
41
124
  */
42
- getGroupRoot(msgId: string): string;
125
+ getNode(key: string): ConversationNode<TProjection> | undefined;
43
126
  /**
44
- * Get the sibling group that `msgId` belongs to, as full MessageNode objects.
45
- * Allows callers to resolve index msgId without losing identity.
127
+ * Remove a node from the tree by its key ({@link nodeKey} a runId or an
128
+ * input node's codec-message-id). Children become unreachable because their
129
+ * parent is no longer on the active path.
130
+ * @param key - The node key to remove.
46
131
  */
47
- getSiblingNodes(msgId: string): MessageNode<TMessage>[];
132
+ delete(key: string): void;
48
133
  /** Forward a raw Ably message event to tree subscribers. */
49
134
  emitAblyMessage(msg: Ably.InboundMessage): void;
50
- /** Forward a turn lifecycle event to tree subscribers. */
51
- emitTurn(event: TurnLifecycleEvent): void;
52
- /** Register an active turn. */
53
- trackTurn(turnId: string, clientId: string): void;
54
- /** Unregister an active turn. */
55
- untrackTurn(turnId: string): void;
56
135
  }
57
- export declare class DefaultTree<TMessage> implements TreeInternal<TMessage> {
58
- /** All nodes indexed by msgId (x-ably-msg-id). */
136
+ export declare class DefaultTree<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection> implements TreeInternal<TInput, TOutput, TProjection> {
137
+ private readonly _codec;
138
+ private readonly _logger;
139
+ private readonly _emitter;
140
+ /**
141
+ * All nodes indexed by their primary key ({@link nodeKey}): a reply run's
142
+ * runId, or an input node's codec-message-id.
143
+ */
59
144
  private readonly _nodeIndex;
60
145
  /**
61
- * All nodes sorted by serial (lexicographic). Null-serial messages
62
- * (optimistic inserts, seed data) sort after all serial-bearing messages,
63
- * ordered among themselves by insertion sequence.
146
+ * Maps every observed `codec-message-id` to its owning node's key
147
+ * ({@link nodeKey}). For a reply run that is the runId of every message the
148
+ * run published; for an input node it is the input's own codec-message-id.
149
+ * Resolves fork-of / parent codec-message-ids to node keys, routes
150
+ * continuation amend wires to existing nodes, and backs UI lookups that hold
151
+ * a codec-message-id.
64
152
  */
65
- private readonly _sortedList;
153
+ private readonly _codecMessageIdToNodeKey;
66
154
  /**
67
- * Parent index: parentId to set of child msgIds.
68
- * Nodes with no parent are indexed under the key `null`.
155
+ * All nodes sorted by their sort serial ({@link sortSerial}: `startSerial`
156
+ * for runs, `serial` for input nodes), lexicographically. Nodes with no sort
157
+ * serial (optimistic) sort after all serial-bearing nodes, ordered among
158
+ * themselves by insertion sequence.
159
+ */
160
+ private readonly _sortedNodes;
161
+ /**
162
+ * Parent index: parent node key (the key its children's
163
+ * `parentCodecMessageId` resolves to) to the set of child node keys. Root
164
+ * nodes (no parent) are indexed under the key `undefined`. Kind-blind — a
165
+ * reply run and an input node parent off each other through the same index.
69
166
  */
70
167
  private readonly _parentIndex;
71
- private readonly _emitter;
72
- private readonly _logger;
73
- /** Active turns: turnId clientId. */
74
- private readonly _turnClientIds;
168
+ /**
169
+ * Reverse edge: an input node's codec-message-id to the set of reply-run ids
170
+ * parented at it. Lets the View resolve a user prompt to its (selected) reply
171
+ * run, and groups regenerate siblings (which all parent at the same input
172
+ * node).
173
+ */
174
+ private readonly _replyRunsByInput;
75
175
  /** Monotonically increasing counter for insertion sequence. */
76
176
  private _seqCounter;
77
- /** Incremented on structural changes; unchanged on content-only updates. */
177
+ /** Incremented on structural changes; unchanged on projection-only updates. */
78
178
  private _structuralVersion;
79
- get structuralVersion(): number;
80
- constructor(logger: Logger);
81
179
  /**
82
- * Compare two nodes for sorted list ordering.
83
- * Serial-bearing nodes sort by serial (lexicographic).
84
- * Null-serial nodes sort after all serial-bearing nodes.
85
- * Among null-serial nodes, sort by insertion sequence.
180
+ * Cached sibling-group lookups keyed by node key. The walk over forkOf
181
+ * chains and the per-parent fan-out are pure functions of the node
182
+ * graph, so the cache is keyed against {@link _structuralVersion}:
183
+ * any topology mutation drops the cache and the next lookup
184
+ * recomputes. Hits matter most during a single render pass where
185
+ * the View calls `getSiblingNodes` once per visible node plus extra
186
+ * per-message branch-anchor probes from React components.
187
+ */
188
+ private _siblingCache;
189
+ private _siblingCacheVersion;
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);
215
+ /**
216
+ * Compare two nodes (Run or input) for sorted list ordering.
217
+ * Serial-bearing nodes sort by their sort serial (`startSerial` for runs,
218
+ * `serial` for input nodes), lexicographically.
219
+ * Nodes with no sort serial sort after all serial-bearing nodes.
220
+ * Among them, sort by insertion sequence.
221
+ *
222
+ * Optimistic (null-serial) nodes intentionally tail-sort so they reorder
223
+ * into place when the server relay arrives and `applyMessage` promotes
224
+ * startSerial — see {@link applyMessage}'s `_removeSortedNode` /
225
+ * `_insertSortedNode` pair on the promotion path.
86
226
  * @param a - First node to compare.
87
227
  * @param b - Second node to compare.
88
228
  * @returns Negative if a sorts before b, positive if after, zero if equal.
89
229
  */
90
230
  private _compareNodes;
91
231
  /**
92
- * Insert a node into sortedList at the correct position via binary search.
232
+ * Insert a node into the sorted list at the correct position via binary search.
93
233
  * @param internal - The node to insert.
94
234
  */
95
- private _insertSorted;
235
+ private _insertSortedNode;
96
236
  /**
97
- * Remove a node from sortedList.
237
+ * Remove a node from the sorted list.
98
238
  * @param internal - The node to remove.
99
239
  */
100
- private _removeSorted;
240
+ private _removeSortedNode;
241
+ /**
242
+ * Insert a freshly-created node into the primary store, the parent index, and
243
+ * the sorted list, then bump the structural version. Kind-specific secondary
244
+ * indexing — the codec-message-id map for input nodes, the reply→input edge
245
+ * for reply runs — is the caller's responsibility.
246
+ * @param key - The node's primary key ({@link nodeKey}).
247
+ * @param entry - The internal node to insert.
248
+ * @param parentCodecMessageId - The node's structural parent, or undefined for a root.
249
+ */
250
+ private _insertNode;
251
+ /**
252
+ * Re-sort a node whose sort key just changed and bump the structural version.
253
+ * The caller mutates the serial field (`serial` for input nodes, `startSerial`
254
+ * for runs); this keeps the sorted list and version in step. Used on the
255
+ * optimistic-serial promotion paths when the server relay/echo arrives.
256
+ * @param entry - The internal node whose serial was just promoted.
257
+ */
258
+ private _promoteSerial;
259
+ /**
260
+ * Fold a batch of events into a node's projection in place, isolating each
261
+ * fold in a try/catch so a throwing reducer can't abort the rest of the batch
262
+ * or the surrounding apply.
263
+ * @param entry - The internal node whose projection is folded in place.
264
+ * @param events - The decoded events to fold, in wire order.
265
+ * @param serial - Ably channel serial; coerced to '' for an optimistic insert.
266
+ * @param messageId - The reducer routing key (codec-message-id), or undefined.
267
+ */
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;
101
351
  private _addToParentIndex;
102
352
  private _removeFromParentIndex;
103
353
  /**
104
- * Get the sibling group that `msgId` belongs to.
354
+ * Resolve a node's structural parent to the parent node's key
355
+ * ({@link nodeKey}), or undefined for a root. The parent is named by a
356
+ * codec-message-id (`parentCodecMessageId`); this maps it through the
357
+ * codec-message-id index to the owning node's key (a runId for a reply run,
358
+ * a codec-message-id for an input node). Returns undefined when the parent
359
+ * hasn't been observed yet (the node is treated as a root until it arrives).
360
+ * @param node - The node whose parent to resolve.
361
+ * @returns The parent node's key, or undefined.
362
+ */
363
+ private _parentKeyOf;
364
+ /**
365
+ * Walk an input node's `forkOf` chain to the group root — the earliest edit
366
+ * version sharing the same structural parent. Stops at a missing target, a
367
+ * non-input target, a parent mismatch, or a cycle.
368
+ * @param node - The input node to walk from.
369
+ * @returns The group-root input node (the node itself when it is the root).
370
+ */
371
+ private _inputGroupRoot;
372
+ /**
373
+ * Get the sibling group that the node keyed by `key` belongs to. Kind-split:
374
+ *
375
+ * - **Reply runs** — every reply run sharing the same input-node parent is a
376
+ * sibling (the original reply + its regenerators all parent at the same
377
+ * input node M_user). No fork-of involved.
378
+ * - **Input nodes** — edit versions: nodes sharing a parent AND linked by a
379
+ * `forkOf` chain to the group root.
105
380
  *
106
- * A sibling group is: the original message + all messages whose `forkOf`
107
- * points to the original (or transitively to a sibling). We find the
108
- * group root by following `forkOf` chains to the earliest ancestor that
109
- * has no `forkOf` (or whose `forkOf` target doesn't share the same parent).
110
- * @param msgId - The msg-id to look up the sibling group for.
381
+ * Returned ordered by startSerial (original/oldest first). A group of one is
382
+ * returned as a single-element array (no branching).
383
+ * @param key - The node key ({@link nodeKey}) to look up the group for.
111
384
  * @returns The ordered list of sibling nodes.
112
385
  */
113
386
  private _getSiblingGroup;
114
387
  /**
115
- * Check if `node` belongs to the sibling group rooted at `originalId`.
116
- * A node is a sibling if it IS the original or its forkOf chain leads
117
- * to the original (with the same parentId).
118
- * @param node - The node to check.
119
- * @param originalId - The group root to match against.
120
- * @returns True if the node belongs to the sibling group.
388
+ * Whether `node` belongs to the sibling group anchored at `original`.
389
+ * Requires the same kind and the same structural parent; reply runs need
390
+ * nothing more (same-parent runs are regenerate siblings), input nodes must
391
+ * additionally be forkOf-linked to the original (edit versions).
392
+ * @param node - The candidate node.
393
+ * @param original - The group's anchor node.
394
+ * @returns True if `node` is a sibling of `original`.
121
395
  */
122
396
  private _isSiblingOf;
123
397
  /**
124
- * Get the "group root" msgId for a sibling group — the original message
125
- * that all forks trace back to.
126
- * @param msgId - Any msg-id in the sibling group.
127
- * @returns The msg-id of the group root.
128
- */
129
- getGroupRoot(msgId: string): string;
130
- flattenNodes(selections: Map<string, string>): MessageNode<TMessage>[];
131
- getSiblings(msgId: string): TMessage[];
132
- getSiblingNodes(msgId: string): MessageNode<TMessage>[];
133
- hasSiblings(msgId: string): boolean;
134
- getNode(msgId: string): MessageNode<TMessage> | undefined;
135
- getHeaders(msgId: string): Record<string, string> | undefined;
136
- upsert(msgId: string, message: TMessage, headers: Record<string, string>, serial?: string): void;
137
- delete(msgId: string): void;
138
- getActiveTurnIds(): Map<string, Set<string>>;
139
- on(event: 'update', handler: () => void): () => void;
140
- on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;
141
- on(event: 'turn', handler: (event: TurnLifecycleEvent) => void): () => void;
398
+ * Get the "group root" key for a sibling group — the stable key the
399
+ * selection map is keyed by. For an input node (edit versions) that is the
400
+ * earliest fork-of ancestor; for a reply run (regenerate group) it is the
401
+ * oldest same-parent run (the original reply).
402
+ * @param key - Any node key in the sibling group.
403
+ * @returns The group root's key.
404
+ */
405
+ getGroupRoot(key: string): string;
142
406
  /**
143
- * Forward a raw Ably message event to tree subscribers.
144
- * @param msg - The raw Ably message to emit.
407
+ * Walk the visible node chain along the selected branches, kind-blind. An
408
+ * input node and a reply run reach each other through the same
409
+ * parent-membership check, so seed-only user→user chains and the
410
+ * input→reply→input weave both resolve here. Sibling groups (edit versions /
411
+ * regenerate runs) collapse to the selected member.
412
+ * @param selections - Per-group selected member key, keyed by group root.
413
+ * @returns The visible nodes (both kinds) in chronological order.
145
414
  */
146
- emitAblyMessage(msg: Ably.InboundMessage): void;
415
+ visibleNodes(selections?: Map<string, string>): ConversationNode<TProjection>[];
416
+ getRunNode(runId: string): RunNode<TProjection> | undefined;
417
+ getNode(key: string): ConversationNode<TProjection> | undefined;
418
+ getNodeByCodecMessageId(codecMessageId: string): ConversationNode<TProjection> | undefined;
419
+ getReplyRuns(inputCodecMessageId: string): RunNode<TProjection>[];
420
+ getSiblingNodes(key: string): ConversationNode<TProjection>[];
421
+ applyMessage(events: {
422
+ inputs: TInput[];
423
+ outputs: TOutput[];
424
+ }, headers: Record<string, string>, serial?: string, timestamp?: number, version?: string): void;
425
+ /**
426
+ * Apply a run-less user input wire: create (or promote the serial of) the
427
+ * input node keyed by its codec-message-id, fold the input events into its
428
+ * own projection, and emit an `output` event (with empty outputs — input
429
+ * folds carry none) so the View observes the optimistic insert.
430
+ * @param codecMessageId - The input node's codec-message-id (its primary key).
431
+ * @param headers - Transport headers from the inbound Ably message.
432
+ * @param serial - Ably channel serial; undefined for an optimistic insert.
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.
436
+ */
437
+ private _applyInputMessage;
438
+ /**
439
+ * Apply a reply-run wire (assistant output, continuation tool-resolution, or
440
+ * a fresh run keyed by the agent-minted run-id): create or reconcile the run
441
+ * node, fold its events, maintain the codec-message-id and reply→input
442
+ * indices, and emit the `output` event. Derives the codec-message-id,
443
+ * triggering-input id, fold list, and outputs from `events`/`headers`,
444
+ * mirroring `applyMessage`.
445
+ * @param wireRunId - The run-id from the inbound wire (the node's primary key).
446
+ * @param events - The decoded inputs and outputs from the wire.
447
+ * @param events.inputs - Client-published events (`ai-input` wire).
448
+ * @param events.outputs - Agent-published events (`ai-output` wire).
449
+ * @param headers - Transport headers from the inbound Ably message.
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.
453
+ */
454
+ private _applyRunMessage;
147
455
  /**
148
- * Forward a turn lifecycle event to tree subscribers.
149
- * @param event - The turn lifecycle event to emit.
456
+ * Record a reply run against its input-node parent (the reverse edge powering
457
+ * `getReplyRuns` and regenerate sibling grouping). A reply run's
458
+ * `parentCodecMessageId` is its input node's codec-message-id (the master
459
+ * invariant), so no resolution is needed.
460
+ * @param node - The reply run node.
461
+ * @param runId - The run's id.
150
462
  */
151
- emitTurn(event: TurnLifecycleEvent): void;
463
+ private _indexReplyRun;
464
+ applyRunLifecycle(event: RunLifecycleEvent): void;
152
465
  /**
153
- * Register an active turn.
154
- * @param turnId - The turn's unique identifier.
155
- * @param clientId - The client that owns the turn.
466
+ * Apply a run-start lifecycle event's structural effect: create the reply
467
+ * run if it doesn't exist yet, or backfill an optimistic / wire-created
468
+ * node's structure and metadata from the canonical run-start. Mutates
469
+ * `_structuralVersion` when the tree shape changes; the caller owns the
470
+ * `run`/`update` emits.
471
+ * @param event - The run-start lifecycle event.
156
472
  */
157
- trackTurn(turnId: string, clientId: string): void;
473
+ private _applyRunStart;
158
474
  /**
159
- * Unregister an active turn.
160
- * @param turnId - The turn to untrack.
475
+ * Apply a run-suspend lifecycle event: pause the run without ending it —
476
+ * mark the node 'suspended' and record the serial it paused at, but keep the
477
+ * Run live so a resume under the same runId resumes it. Status/endSerial are
478
+ * content, not structure, so this never mutates `_structuralVersion`; the
479
+ * caller owns the emits.
480
+ * @param event - The run-suspend lifecycle event.
161
481
  */
162
- untrackTurn(turnId: string): void;
482
+ private _applyRunSuspend;
483
+ /**
484
+ * Apply a run-resume lifecycle event: re-enter an already-started run by
485
+ * flipping a suspended run back to 'active'. Pure re-entry — it carries no
486
+ * parent/forkOf and does not promote startSerial (the original run-start owns
487
+ * the run's structure). Only a suspended run resumes: a no-op when the run
488
+ * isn't known (e.g. a resume replayed from a newer history page before its
489
+ * run-start) and a no-op for an already-active or terminal
490
+ * (complete/cancelled/error) run — a stray resume must never resurrect a run
491
+ * that has ended. The caller owns the emits.
492
+ * @param event - The run-resume lifecycle event.
493
+ */
494
+ private _applyRunResume;
495
+ /**
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.
507
+ * @param event - The run-end lifecycle event.
508
+ */
509
+ private _applyRunEnd;
510
+ delete(key: string): void;
511
+ /**
512
+ * Build a fresh RunNode from a wire message's headers. Used when an
513
+ * inbound message arrives before any run-start event for its runId.
514
+ * @param runId - The run-id from the inbound wire.
515
+ * @param headers - Transport headers from the inbound Ably message.
516
+ * @param serial - Ably channel serial; undefined for optimistic inserts.
517
+ * @returns A newly-allocated internal run node ready for insertion.
518
+ */
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;
531
+ /**
532
+ * Allocate a RunNode from already-resolved fields. Shared by the
533
+ * header-driven and lifecycle-driven run creators: both build the identical
534
+ * RunNode literal and stamp an insert sequence.
535
+ * @param params - The resolved run fields.
536
+ * @param params.runId - The run's id (its primary key).
537
+ * @param params.parentCodecMessageId - Structural parent codec-message-id, or undefined for a root.
538
+ * @param params.forkOf - The resolved fork target's node key (already mapped through the codec-message-id index), or undefined.
539
+ * @param params.regeneratesCodecMessageId - The codec-message-id this run regenerates, or undefined.
540
+ * @param params.clientId - The publishing client's id.
541
+ * @param params.invocationId - The agent invocation id.
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).
544
+ * @returns A newly-allocated internal run node ready for insertion.
545
+ */
546
+ private _buildRunNode;
547
+ /**
548
+ * Build a fresh InputNode from a run-less user input wire's headers.
549
+ * @param codecMessageId - The input's codec-message-id (its primary key).
550
+ * @param headers - Transport headers from the inbound Ably message.
551
+ * @param serial - Ably channel serial; undefined for optimistic inserts.
552
+ * @returns A newly-allocated internal input node ready for insertion.
553
+ */
554
+ private _createInputNodeFromHeaders;
555
+ /**
556
+ * Build a fresh RunNode from a run-start lifecycle event. Used when a
557
+ * run-start event arrives before any message for its runId.
558
+ * @param event - The run-start lifecycle event from the agent, including
559
+ * its channel serial.
560
+ * @returns A newly-allocated internal run node ready for insertion.
561
+ */
562
+ private _createRunFromLifecycle;
563
+ on(event: 'update', handler: () => void): () => void;
564
+ on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;
565
+ on(event: 'run', handler: (event: RunLifecycleEvent) => void): () => void;
566
+ on(event: 'output', handler: (event: OutputEvent<TOutput>) => void): () => void;
567
+ /**
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.
571
+ * @param msg - The raw Ably message to emit.
572
+ */
573
+ emitAblyMessage(msg: Ably.InboundMessage): void;
574
+ findAblyMessageByEventId(eventId: string): Ably.InboundMessage | undefined;
163
575
  }
164
576
  /**
165
- * Create a Tree that materializes branching history from a flat oplog.
577
+ * Create a Tree that materializes branching conversation history from a flat
578
+ * oplog of Ably messages as a two-node-per-turn forest (input node + reply run).
579
+ * @param codec - Codec used to fold inbound events into per-Run projections.
166
580
  * @param logger - Logger for diagnostic output.
167
- * @returns A new {@link DefaultTree} instance. The transport uses DefaultTree
168
- * directly for internal methods (emitAblyMessage, emitTurn, trackTurn, untrackTurn).
169
- * Public consumers see the narrower {@link Tree} interface.
581
+ * @returns A new {@link DefaultTree} instance. The session uses DefaultTree
582
+ * directly for internal methods (applyMessage, applyRunLifecycle,
583
+ * emitAblyMessage). Public consumers see the narrower {@link Tree} interface.
170
584
  */
171
- export declare const createTree: <TMessage>(logger: Logger) => DefaultTree<TMessage>;
585
+ export declare const createTree: <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection>(codec: Reducer<CodecEvent<TInput, TOutput>, TProjection>, logger: Logger) => DefaultTree<TInput, TOutput, TProjection>;