@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,8 +1,12 @@
1
1
  /**
2
- * Core codec interfaces as defined in the general codec specification.
2
+ * Core codec interfaces for the event-sourced model.
3
3
  *
4
- * These types define the contract between domain event streams and Ably's
5
- * native message primitives (publish, append, update, delete).
4
+ * The codec describes the wire as a flat stream of TEvent values. A reducer
5
+ * folds events into an opaque TProjection. The SDK extracts TMessage[] from
6
+ * the projection to populate the conversation Tree.
7
+ *
8
+ * All types are framework-agnostic. Domain codecs (e.g. the Vercel codec)
9
+ * choose concrete shapes for TEvent / TProjection / TMessage.
6
10
  */
7
11
 
8
12
  import type * as Ably from 'ably';
@@ -36,62 +40,72 @@ export interface ChannelWriter {
36
40
  }
37
41
 
38
42
  // ---------------------------------------------------------------------------
39
- // WriteOptions — per-write overrides for encoder operations
43
+ // Extras / WriteOptions — per-write overrides for encoder operations
40
44
  // ---------------------------------------------------------------------------
41
45
 
42
- /** Shape of the extras object passed through WriteOptions and EncoderOptions. */
46
+ /** Shape of the extras config passed through WriteOptions and EncoderOptions. */
43
47
  export interface Extras {
44
- /** Headers to attach to the Ably message extras. */
48
+ /** Transport-tier headers to attach to the message's `extras.ai.transport` namespace. */
45
49
  headers?: Record<string, string>;
46
50
  }
47
51
 
48
52
  /** Per-write overrides for encoder operations. */
49
53
  export interface WriteOptions {
50
- /** Override the default clientId for this write. */
51
- clientId?: string;
52
54
  /** Override the default extras for this write. */
53
55
  extras?: Extras;
54
- /** Message identity for accumulator correlation. Stamped as `x-ably-msg-id`. */
56
+ /** Message identity for projection routing. Stamped as `codec-message-id`. */
55
57
  messageId?: string;
56
58
  }
57
59
 
58
60
  // ---------------------------------------------------------------------------
59
- // MessagePayload shared description of a message for encode and decode
61
+ // MessagePayload / StreamPayload codec-internal wire descriptions
60
62
  // ---------------------------------------------------------------------------
61
63
 
62
64
  /**
63
65
  * A codec-agnostic description of a discrete Ably message. Used on both sides:
64
66
  * - **Encode:** the domain encoder describes what to publish; the encoder core
65
- * handles header merging, clientId resolution, and the actual publish.
66
- * - **Decode:** the decoder core extracts these fields from an `Ably.InboundMessage`
67
- * before calling domain hooks, keeping hooks free of Ably SDK types.
67
+ * handles header merging and the actual publish.
68
+ * - **Decode:** the decoder core extracts these fields from an
69
+ * `Ably.InboundMessage` before calling domain hooks, keeping hooks free of
70
+ * Ably SDK types.
68
71
  *
69
72
  * Data is `unknown` because discrete messages can carry arbitrary payloads
70
73
  * (strings, objects, etc.) — Ably handles serialization natively.
71
74
  */
72
75
  export interface MessagePayload {
73
- /** Ably message name (e.g. "text", "tool-input", "user-message"). */
76
+ /** Ably message name — the wire direction (`ai-output` / `ai-input`). */
74
77
  name: string;
75
78
  /** Message data. Ably handles serialization — strings, objects, and arrays are all valid. */
76
79
  data: unknown;
77
- /** Headers from the Ably message extras. */
78
- headers?: Record<string, string>;
80
+ /** Codec-tier headers the codec's own fields, carried under `extras.ai.codec`. */
81
+ codecHeaders?: Record<string, string>;
82
+ /**
83
+ * Transport-tier headers a codec needs to stamp directly (e.g. `role`,
84
+ * `status`), carried under `extras.ai.transport`. Most codec payloads leave
85
+ * this unset and let the transport layer supply transport headers via config.
86
+ */
87
+ transportHeaders?: Record<string, string>;
79
88
  /** Mark this message as ephemeral (not persisted in channel history). Only meaningful on encode. */
80
89
  ephemeral?: boolean;
81
90
  }
82
91
 
83
92
  /**
84
- * Payload for streamed messages. Data must be a string because
85
- * the message append lifecycle uses text append/accumulate semantics —
86
- * deltas are concatenated for recovery and prefix-matching on the decoder.
93
+ * Payload for streamed messages. Data must be a string because the message
94
+ * append lifecycle uses text append/accumulate semantics — deltas are
95
+ * concatenated for recovery and prefix-matching on the decoder.
87
96
  */
88
97
  export interface StreamPayload {
89
- /** Ably message name (e.g. "text", "reasoning", "tool-input"). */
98
+ /** Ably message name — `ai-output` (only outputs stream); not the codec `kind` / stream family. */
90
99
  name: string;
91
100
  /** Initial or closing data for the stream. Must be a string for append/accumulate semantics. */
92
101
  data: string;
93
- /** Headers from the Ably message extras. */
94
- headers?: Record<string, string>;
102
+ /** Codec-tier headers the codec's own fields, carried under `extras.ai.codec`. */
103
+ codecHeaders?: Record<string, string>;
104
+ /**
105
+ * Transport-tier headers a codec needs to stamp directly (e.g. `role`,
106
+ * `status`), carried under `extras.ai.transport`.
107
+ */
108
+ transportHeaders?: Record<string, string>;
95
109
  }
96
110
 
97
111
  // ---------------------------------------------------------------------------
@@ -103,163 +117,528 @@ export interface StreamPayload {
103
117
  * Accumulates text across appends and tracks lifecycle (open/closed).
104
118
  */
105
119
  export interface StreamTrackerState {
106
- /** Ably message name (e.g. "text", "reasoning", "tool-input"). */
120
+ /** Ably message name — `ai-output` (only outputs stream); not the codec `kind` / stream family. */
107
121
  name: string;
108
122
  /** Stream identifier (e.g. chunk.id for text, toolCallId for tool-input). */
109
123
  streamId: string;
110
124
  /** Full accumulated text so far. */
111
125
  accumulated: string;
112
- /** Current headers for this stream. Initially set from the first publish, but may be replaced on update. */
113
- headers: Record<string, string>;
114
- /** Whether this stream has been closed (finished or aborted). */
126
+ /**
127
+ * Current codec-tier headers (`extras.ai.codec`) for this stream. Initially
128
+ * set from the first publish, but may be replaced on update.
129
+ */
130
+ codecHeaders: Record<string, string>;
131
+ /**
132
+ * Current transport-tier headers (`extras.ai.transport`) for this stream.
133
+ * Initially set from the first publish, but may be replaced on update.
134
+ */
135
+ transportHeaders: Record<string, string>;
136
+ /**
137
+ * Highest `Message.version.serial` incorporated into this tracker.
138
+ * Versions are lexicographically comparable within one message serial, so
139
+ * a delivery carrying a version at or below this value is already
140
+ * incorporated and decodes to nothing. Stamped at first contact (a
141
+ * never-mutated message's version serial equals the message serial, which
142
+ * is also the fallback when the version carries no serial) and advanced by
143
+ * each version-bearing delivery.
144
+ */
145
+ version: string;
146
+ /** Whether this stream has been closed (complete or cancelled). */
115
147
  closed: boolean;
116
148
  }
117
149
 
118
150
  // ---------------------------------------------------------------------------
119
- // DiscreteEncoderstateless discrete publish operations
151
+ // Reducerpure event-sourced state machine
120
152
  // ---------------------------------------------------------------------------
121
153
 
122
154
  /**
123
- * The subset of encoder operations that are stateless — safe for long-lived
124
- * reuse across turns. Publishes complete messages and discrete events without
125
- * any streaming lifecycle (no trackers, no pending appends, no close).
155
+ * Transport-derived metadata passed alongside each TEvent into `fold`. Read
156
+ * by the SDK from the inbound Ably message and stamped before each fold call.
157
+ */
158
+ export interface ReducerMeta {
159
+ /**
160
+ * Ably channel serial of the wire message that produced this event, or `''`
161
+ * for a not-yet-sequenced optimistic (local) fold. Ordering context only:
162
+ * the transport invokes `fold` in canonical serial order and exactly once
163
+ * per event, so the reducer must not treat a same-or-lower serial as a
164
+ * replay to skip — ordering and dedup are the transport's job, not the
165
+ * reducer's.
166
+ */
167
+ serial: string;
168
+ /**
169
+ * Optional `codec-message-id` from the inbound Ably message. Reducers use this
170
+ * to route an event to a target message within the projection (e.g. to
171
+ * amend an existing assistant message addressed by its codec-message-id).
172
+ */
173
+ messageId?: string;
174
+ }
175
+
176
+ /**
177
+ * Pure, stateless reducer contract. A reducer folds TEvents into an opaque
178
+ * TProjection. The same `(state, event, meta)` triple must produce the same
179
+ * result every time — `fold` is a pure function and the reducer holds no
180
+ * instance state.
126
181
  *
127
- * The server transport calls `writeMessages` to publish user messages to the
128
- * channel. All messages in a single call are published atomically and share
129
- * a single `x-ably-msg-id`, forming one node in the conversation tree.
130
- * `writeEvent` is a public API for consumers to publish standalone discrete
131
- * events outside the streaming flow it is not called by the transport internally.
182
+ * Ordering, deduplication, and replay are the transport's responsibility, not
183
+ * the reducer's. The transport invokes `fold` exactly once per event, in
184
+ * canonical order wire messages ascending by serial, events within a wire in
185
+ * decode order — refolding a node from a fresh `init` when a late wire would
186
+ * otherwise land out of order. The reducer therefore folds unconditionally: it
187
+ * must not keep a serial high-water-mark or skip "already-seen" events.
188
+ * Last-writer-wins for events competing over the same state falls out of fold
189
+ * order, since the highest-serial event folds last.
190
+ *
191
+ * Mutation: `fold` is allowed to mutate the projection passed in and return
192
+ * it. The caller treats the projection as single-owner and never retains a
193
+ * reference to an old state.
132
194
  */
133
- export interface DiscreteEncoder<TEvent, TMessage> {
195
+ export interface Reducer<TEvent, TProjection> {
134
196
  /**
135
- * Encode and publish one or more domain messages atomically in a single
136
- * channel publish. All messages share the encoder's transport headers
137
- * (including `x-ably-msg-id`), so they form one logical unit in the
138
- * conversation tree.
197
+ * Build an empty initial projection. Called once per conversation node a
198
+ * Run node or a run-less input node — before any of that node's events are
199
+ * folded, and again on every refold of that node.
139
200
  */
140
- writeMessages(messages: TMessage[], options?: WriteOptions): Promise<Ably.PublishResult>;
201
+ init(): TProjection;
141
202
  /**
142
- * Encode and publish a single domain event as a standalone discrete message.
143
- * Available for consumers to publish events outside the streaming flow.
144
- * Implementations should throw for event types that are only meaningful
145
- * within a stream (e.g. text deltas).
203
+ * Fold one TEvent into the projection and return the updated projection.
204
+ * Invoked exactly once per event, in canonical order; the reducer may mutate
205
+ * `state` in place.
146
206
  */
147
- writeEvent(event: TEvent, options?: WriteOptions): Promise<Ably.PublishResult>;
207
+ fold(state: TProjection, event: TEvent, meta: ReducerMeta): TProjection;
148
208
  }
149
209
 
210
+ /**
211
+ * A decoded event tagged with the wire direction it arrived on. The reducer
212
+ * folds this union (not a bare `TInput | TOutput`) so it can dispatch on
213
+ * `direction` rather than inspecting the event's shape. Direction is derived
214
+ * once, from the Ably message name, at decode time (see `toCodecEvents`) — the
215
+ * authoritative signal, since a single message is either `ai-input` or
216
+ * `ai-output` but never both.
217
+ * @template TInput - The codec's input union.
218
+ * @template TOutput - The codec's output union.
219
+ */
220
+ export type CodecEvent<TInput, TOutput> =
221
+ | {
222
+ /** The event arrived on the `ai-input` wire. */
223
+ readonly direction: 'input';
224
+ /** The decoded input event. */
225
+ readonly event: TInput;
226
+ }
227
+ | {
228
+ /** The event arrived on the `ai-output` wire. */
229
+ readonly direction: 'output';
230
+ /** The decoded output event. */
231
+ readonly event: TOutput;
232
+ };
233
+
150
234
  // ---------------------------------------------------------------------------
151
- // StreamEncodermaps domain events to Ably channel operations
235
+ // Encoderdirection-typed publication API
152
236
  // ---------------------------------------------------------------------------
153
237
 
238
+ /** Options passed to a codec's `createEncoder` factory. */
239
+ export interface EncoderOptions {
240
+ /** Default extras (e.g. headers) merged into every Ably message. */
241
+ extras?: Extras;
242
+ /** Hook called before each Ably message is published. Mutate the message in place to add transport-level headers under `extras.ai`. */
243
+ onMessage?: (message: Ably.Message) => void;
244
+ /**
245
+ * Fallback domain message id surfaced to output escape hatches as
246
+ * `ctx.messageId` (e.g. the Vercel `start` hatch injects it when a chunk
247
+ * carries no `messageId` of its own). Unrelated to the wire
248
+ * codec-message-id transport header, which `WriteOptions.messageId` stamps.
249
+ */
250
+ messageId?: string;
251
+ }
252
+
154
253
  /**
155
- * Full streaming encoder with single-turn lifecycle. Extends
156
- * `DiscreteEncoder` with stateful streaming operations (`appendEvent` for
157
- * content streams, `close` to flush). Used by the server transport.
254
+ * Stateful encoder for a single channel. Two publish methods enforce
255
+ * direction at the call site `publishInput` for client-published events
256
+ * (`ai-input` wire) and `publishOutput` for agent-published events
257
+ * (`ai-output` wire). Stream-tracker state lives inside the encoder and
258
+ * is shared across both directions.
158
259
  */
159
- export interface StreamEncoder<TEvent, TMessage> extends DiscreteEncoder<TEvent, TMessage> {
160
- /** Encode and append a streaming domain event to an in-progress stream (delta semantics). */
161
- appendEvent(event: TEvent, options?: WriteOptions): Promise<void>;
260
+ export interface Encoder<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent> {
261
+ /**
262
+ * Encode and publish a single client input on the `ai-input` wire.
263
+ * Rejects if the codec cannot encode the given input
264
+ * variant.
265
+ */
266
+ publishInput(input: TInput, options?: WriteOptions): Promise<void>;
267
+ /**
268
+ * Encode and publish a single agent output on the `ai-output` wire.
269
+ * Rejects if the codec cannot encode the given output
270
+ * variant.
271
+ */
272
+ publishOutput(output: TOutput, options?: WriteOptions): Promise<void>;
162
273
  /**
163
- * Abort all in-progress streams and publish a codec-specific abort signal.
164
- * Called by the transport when a turn is cancelled. Idempotent — calling
165
- * abort after all streams are already aborted is a no-op.
166
- * @param reason - Optional reason string for the abort (e.g. 'cancelled').
274
+ * Close all in-progress streamed messages as cancelled (status:cancelled) and
275
+ * flush pending appends. Pure transport mechanics emits no codec output.
276
+ * Idempotent: streams already cancelled are not re-appended. Must not be
277
+ * called after `close`; doing so throws because the encoder is already closed.
278
+ * Run termination is signalled separately by the transport `ai-run-end` event.
167
279
  */
168
- abort(reason?: string): Promise<void>;
169
- /** Flush all pending appends and close the encoder. */
280
+ cancelStreams(): Promise<void>;
281
+ /** Flush pending appends and release encoder resources. */
170
282
  close(): Promise<void>;
171
283
  }
172
284
 
173
285
  // ---------------------------------------------------------------------------
174
- // DecoderOutputunified return type from the decoder
286
+ // Decoderdirection-tagged output
175
287
  // ---------------------------------------------------------------------------
176
288
 
177
289
  /**
178
- * A single output from the decoder: either a domain event or a complete domain message.
179
- * Event outputs may carry a `messageId` read from the `x-ably-msg-id` header, which the
180
- * accumulator uses to route events to the correct in-progress message.
290
+ * Tagged result of decoding one inbound Ably message the codec routes
291
+ * by the wire `name` and returns inputs and outputs separately so the
292
+ * SDK never has to introspect direction.
181
293
  */
182
- export type DecoderOutput<TEvent, TMessage> =
183
- | { kind: 'event'; event: TEvent; messageId?: string }
184
- | { kind: 'message'; message: TMessage };
185
-
186
- // ---------------------------------------------------------------------------
187
- // StreamDecoder — maps Ably messages to decoder outputs
188
- // ---------------------------------------------------------------------------
294
+ export interface DecodedMessage<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent> {
295
+ /** Inputs decoded from the inbound message (only populated when the wire `name` is `ai-input`). */
296
+ inputs: TInput[];
297
+ /** Outputs decoded from the inbound message (only populated when the wire `name` is `ai-output`). */
298
+ outputs: TOutput[];
299
+ }
189
300
 
190
- /** Decodes Ably messages into domain events and messages. */
191
- export interface StreamDecoder<TEvent, TMessage> {
192
- /** Decode a single Ably message into zero or more domain outputs. */
193
- decode(message: Ably.InboundMessage): DecoderOutput<TEvent, TMessage>[];
301
+ /**
302
+ * Stateful decoder for a single channel subscription. Maintains internal
303
+ * stream-tracker state across messages so that mid-stream join (history
304
+ * compaction, partial-history page boundary, rewind miss) synthesizes any
305
+ * missing start events before deltas reach the SDK — the reducer always
306
+ * sees a clean `(start, delta*, end)` sequence.
307
+ *
308
+ * Trackers are version-guarded: a delivery whose `Message.version.serial`
309
+ * is at or below the version already incorporated decodes to nothing. One
310
+ * decoder instance can therefore be shared by the live subscription and
311
+ * history hydration — whichever route delivers a message's content first
312
+ * wins, and the other route's covered deliveries are no-ops.
313
+ */
314
+ export interface Decoder<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent> {
315
+ /** Decode one Ably inbound message into the input/output halves. */
316
+ decode(message: Ably.InboundMessage): DecodedMessage<TInput, TOutput>;
194
317
  }
195
318
 
196
319
  // ---------------------------------------------------------------------------
197
- // MessageAccumulatorbuilds messages from decoder outputs
320
+ // Codec input events well-known input variant shapes
198
321
  // ---------------------------------------------------------------------------
199
322
 
200
- /** Accumulates decoder outputs into a list of domain messages, tracking active streams. */
201
- export interface MessageAccumulator<TEvent, TMessage> {
202
- /** Process a batch of decoder outputs, updating internal message state. */
203
- processOutputs(outputs: DecoderOutput<TEvent, TMessage>[]): void;
204
- /** Apply an external update to a message (e.g. from an update callback). */
205
- updateMessage(message: TMessage): void;
323
+ /**
324
+ * Structural base every codec input variant must satisfy. Codec authors
325
+ * compose their `TInput` union from variants extending this type; the
326
+ * transport reads the routing fields directly off the variant to stamp
327
+ * wire headers, so no runtime classification step is needed.
328
+ *
329
+ * The `kind` discriminator is required on every variant so the codec's
330
+ * reducer can switch on it. Codec authors pick the literal value per
331
+ * variant; the SDK ships well-known literals (`'user-message'`,
332
+ * `'regenerate'`) — see {@link UserMessage}, {@link Regenerate}.
333
+ */
334
+ export interface CodecInputEvent {
335
+ /**
336
+ * Discriminator. Codec authors pick the literal value per variant. The
337
+ * SDK reserves the literals used by well-known variants
338
+ * ({@link UserMessage}, {@link Regenerate}); codec-specific variants pick
339
+ * any other literal.
340
+ */
341
+ kind: string;
206
342
  /**
207
- * Ensure the accumulator is ready to process events for the given message.
208
- * If not already active, creates internal tracking state from the message.
209
- * If already active, syncs internal state with the provided message
210
- * (picking up external changes like cross-turn amendments).
211
- * Idempotent — safe to call before every processOutputs.
343
+ * Sets the wire `parent` header the codec-message-id of the
344
+ * preceding codec-message on this branch. When omitted, the SDK
345
+ * auto-computes the parent from the visible branch tail at send time.
212
346
  */
213
- initMessage(messageId: string, message: TMessage): void;
347
+ parent?: string;
214
348
  /**
215
- * Mark a message as completed. Removes it from active tracking so it
216
- * appears in {@link completedMessages}. No-op if not active.
349
+ * Pointer to another codec-message this input references. The semantic
350
+ * depends on `kind` for `regenerate`, the assistant codec-message to
351
+ * regenerate; codec-specific `kind`s may give it other meanings. The
352
+ * input event itself does not create a fork — it requests one: the
353
+ * transport reads `target` off the input (e.g. the client session maps a
354
+ * regenerate's target into its transport headers) and the fork
355
+ * relationship is established on the agent's response (and on
356
+ * `ai-run-start`).
217
357
  */
218
- completeMessage(messageId: string): void;
219
- /** All messages accumulated so far (in-progress and completed). */
220
- readonly messages: TMessage[];
221
- /** Only messages whose streams have finished. */
222
- readonly completedMessages: TMessage[];
223
- /** Whether any stream is still actively receiving data. */
224
- readonly hasActiveStream: boolean;
358
+ target?: string;
359
+ /**
360
+ * Targets an existing codec-message-id instead of minting a fresh one.
361
+ * Used by continuation inputs (tool results, approval responses) that
362
+ * amend an existing assistant message rather than creating a new one;
363
+ * the wire's `codec-message-id` is stamped with this value so
364
+ * the reducer's direct-fold path matches by codec-message-id.
365
+ */
366
+ codecMessageId?: string;
367
+ }
368
+
369
+ /**
370
+ * Well-known input variant: a new user message in the codec's domain
371
+ * representation. Pinned `kind: 'user-message'`. Produced by the SDK's
372
+ * `Codec.createUserMessage` factory and published via `View.send`
373
+ * (e.g. `view.send(codec.createUserMessage(message))`).
374
+ * @template TMessage - The codec's per-message domain type.
375
+ */
376
+ export interface UserMessage<TMessage> extends CodecInputEvent {
377
+ /** Discriminator. */
378
+ kind: 'user-message';
379
+ /** The user's message in the codec's domain representation. */
380
+ message: TMessage;
381
+ }
382
+
383
+ /**
384
+ * Well-known input variant: request that an existing assistant
385
+ * codec-message be regenerated. Pinned `kind: 'regenerate'`. This event
386
+ * is a signal, not itself a fork — `target` names the assistant message
387
+ * to regenerate, and `parent` names the user message the new assistant
388
+ * threads under. Both are required; the codec cannot regenerate without
389
+ * both. Produced by the SDK's `Codec.createRegenerate` factory and
390
+ * surfaced via `View.regenerate`.
391
+ */
392
+ export interface Regenerate extends CodecInputEvent {
393
+ /** Discriminator. */
394
+ kind: 'regenerate';
395
+ /** The codec-message-id of the assistant to regenerate. Required. */
396
+ target: string;
397
+ /** The codec-message-id of the parent user message the new assistant threads under. Required. */
398
+ parent: string;
399
+ }
400
+
401
+ /**
402
+ * Well-known input variant: client-published tool result (success). The
403
+ * tool ran and produced output. Mutates the assistant codec-message
404
+ * addressed by `codecMessageId` — the codec's reducer applies the result
405
+ * onto the existing tool-call state of the referenced assistant.
406
+ *
407
+ * The core is domain-independent: it knows only that this input amends the
408
+ * assistant at `codecMessageId` and carries a codec-defined `payload`. The
409
+ * shape of `payload` (e.g. the tool-call id and output value) is supplied
410
+ * by the codec via `TPayload` (e.g. a tool-call id and output value).
411
+ *
412
+ * Codecs opt in to client-side tool resolution by including this variant
413
+ * in their `TInput` union. Codecs whose domain model doesn't natively
414
+ * distinguish client-side tool results as a top-level event type (e.g.
415
+ * Anthropic Messages, where tool results are normally embedded in a
416
+ * user-role message) can still use this variant — the codec's reducer
417
+ * translates the wire-level update into the codec's domain representation.
418
+ * @template TPayload - The codec's domain payload for a tool result.
419
+ */
420
+ export interface ToolResult<TPayload> extends CodecInputEvent {
421
+ /** Discriminator. */
422
+ kind: 'tool-result';
423
+ /** The assistant codec-message containing the tool call. Required. */
424
+ codecMessageId: string;
425
+ /** The codec's domain payload describing the tool result. */
426
+ payload: TPayload;
427
+ }
428
+
429
+ /**
430
+ * Well-known input variant: client-published tool result (failure). The
431
+ * tool ran and failed. Mutates the assistant codec-message addressed by
432
+ * `codecMessageId`. The failure detail (e.g. tool-call id and error text)
433
+ * is the codec's domain `payload`.
434
+ * @template TPayload - The codec's domain payload for a tool-result failure.
435
+ */
436
+ export interface ToolResultError<TPayload> extends CodecInputEvent {
437
+ /** Discriminator. */
438
+ kind: 'tool-result-error';
439
+ /** The assistant codec-message containing the tool call. Required. */
440
+ codecMessageId: string;
441
+ /** The codec's domain payload describing the failure. */
442
+ payload: TPayload;
443
+ }
444
+
445
+ /**
446
+ * Well-known input variant: client-published response to an
447
+ * agent-emitted tool-approval request. Mutates the assistant
448
+ * codec-message addressed by `codecMessageId` — flipping the targeted
449
+ * tool call from pending-approval to approved or denied. The decision
450
+ * detail (e.g. tool-call id, approved flag, reason) is the codec's domain
451
+ * `payload`.
452
+ *
453
+ * Codecs may layer approval semantics on top of domain models that don't
454
+ * natively support gating tool execution behind an approval — the codec
455
+ * is responsible for mapping the decision into its own representation.
456
+ * @template TPayload - The codec's domain payload for an approval response.
457
+ */
458
+ export interface ToolApprovalResponse<TPayload> extends CodecInputEvent {
459
+ /** Discriminator. */
460
+ kind: 'tool-approval-response';
461
+ /** The assistant codec-message containing the tool call. Required. */
462
+ codecMessageId: string;
463
+ /** The codec's domain payload describing the approval decision. */
464
+ payload: TPayload;
225
465
  }
226
466
 
467
+ /**
468
+ * Extract the domain `payload` type of a codec's {@link ToolResult} member
469
+ * from its `TInput` union, or `never` if the codec has no tool-result
470
+ * variant. Used to type {@link Codec.createToolResult} without adding a
471
+ * standalone type parameter to the codec.
472
+ * @template TInput - The codec's input union.
473
+ */
474
+ export type ToolResultPayloadOf<TInput> = TInput extends ToolResult<infer P> ? P : never;
475
+
476
+ /**
477
+ * Extract the domain `payload` type of a codec's {@link ToolResultError}
478
+ * member from its `TInput` union, or `never` if absent.
479
+ * @template TInput - The codec's input union.
480
+ */
481
+ export type ToolResultErrorPayloadOf<TInput> = TInput extends ToolResultError<infer P> ? P : never;
482
+
483
+ /**
484
+ * Extract the domain `payload` type of a codec's {@link ToolApprovalResponse}
485
+ * member from its `TInput` union, or `never` if absent.
486
+ * @template TInput - The codec's input union.
487
+ */
488
+ export type ToolApprovalResponsePayloadOf<TInput> = TInput extends ToolApprovalResponse<infer P> ? P : never;
489
+
490
+ /**
491
+ * Extract the domain message type (`TMessage`) carried by a codec's
492
+ * {@link UserMessage} member from its `TInput` union, or `never` if the codec
493
+ * has no user-message variant. Lets the core well-known input factories type
494
+ * `createUserMessage` from `TInput` alone, without a separate message type
495
+ * parameter.
496
+ * @template TInput - The codec's input union.
497
+ */
498
+ export type UserMessageOf<TInput> = TInput extends UserMessage<infer M> ? M : never;
499
+
227
500
  // ---------------------------------------------------------------------------
228
- // Codec — composite interface for transport use
501
+ // Codec output events base shape for the output side
229
502
  // ---------------------------------------------------------------------------
230
503
 
231
- /** Options passed to a codec's `createEncoder` factory to configure default identity and message hooks. */
232
- export interface EncoderOptions {
233
- /** Default clientId for all writes. */
234
- clientId?: string;
235
- /** Default extras (e.g. headers) merged into every Ably message. */
236
- extras?: Extras;
237
- /** Hook called before each Ably message is published. Mutate the message in place to add transport-level headers. */
238
- onMessage?: (message: Ably.Message) => void;
239
- /**
240
- * Domain-level message identity. Domain encoders use this as a fallback
241
- * messageId when a lifecycle chunk (e.g. `start`) does not provide one,
242
- * ensuring useChat and the transport accumulator assign the same ID.
243
- */
244
- messageId?: string;
504
+ /**
505
+ * Structural base every codec output variant must satisfy. The output
506
+ * counterpart of {@link CodecInputEvent}: pins a `type` discriminator so
507
+ * the SDK can reliably narrow `TInput | TOutput` (inputs carry `kind`,
508
+ * outputs carry `type`) and reserves a contract for future routing
509
+ * fields on outputs without a breaking generic-arity change.
510
+ *
511
+ * The `type` discriminator is required on every variant so the codec's
512
+ * reducer can switch on it — any domain union whose members carry a `type`
513
+ * string literal satisfies it structurally.
514
+ *
515
+ * No routing fields today: outputs carry no per-event `codecMessageId` /
516
+ * `parent` / `forkOf` overrides. Those move onto this base when a concrete
517
+ * output needs to carry them.
518
+ */
519
+ export interface CodecOutputEvent {
520
+ /** Discriminator. Codec authors pick the literal value per variant. */
521
+ type: string;
245
522
  }
246
523
 
524
+ // ---------------------------------------------------------------------------
525
+ // Codec message — a per-message domain object paired with its identity
526
+ // ---------------------------------------------------------------------------
527
+
247
528
  /**
248
- * The complete codec contract that a core transport needs.
529
+ * A single domain message paired with the codec-message-id that identifies
530
+ * it on the wire. Returned from {@link Codec.getMessages}.
249
531
  *
250
- * Combines factory methods (createEncoder, createDecoder, createAccumulator)
251
- * with protocol knowledge (isTerminal). Transport-level concerns like turn
252
- * correlation, optimistic reconciliation, and cancel signals
253
- * are handled by the transport layer using standard `x-ably-*` headers.
532
+ * The two fields are deliberately separate: `message` is reconstructed to
533
+ * faithfully reproduce the values the source produced (e.g. the id the AI
534
+ * SDK stream assigned) and is surfaced to the application as-is; the SDK
535
+ * never inspects `message` for identity. All internal correlation
536
+ * Tree indexing, parent/fork/regenerate routing, branch grouping — keys on
537
+ * `codecMessageId`, the SDK's own client-minted identifier. The two need
538
+ * not be equal.
539
+ * @template TMessage - The codec's per-message domain type.
254
540
  */
255
- export interface Codec<TEvent, TMessage> {
256
- /** Create a streaming encoder bound to the given channel. */
257
- createEncoder(channel: ChannelWriter, options?: EncoderOptions): StreamEncoder<TEvent, TMessage>;
258
- /** Create a decoder for converting Ably messages back into domain outputs. */
259
- createDecoder(): StreamDecoder<TEvent, TMessage>;
260
- /** Create an accumulator for building domain messages from decoder outputs. */
261
- createAccumulator(): MessageAccumulator<TEvent, TMessage>;
262
-
263
- /** Whether an event signals stream completion (finish, error, abort). */
264
- isTerminal(event: TEvent): boolean;
541
+ export interface CodecMessage<TMessage> {
542
+ /** The SDK's codec-message-id for this message the correlation key. */
543
+ codecMessageId: string;
544
+ /** The domain message, reconstructed verbatim from the source values. */
545
+ message: TMessage;
546
+ }
547
+
548
+ // ---------------------------------------------------------------------------
549
+ // Codec full contract for the transport
550
+ // ---------------------------------------------------------------------------
551
+
552
+ /**
553
+ * The codec describes the wire and folds events into a per-node projection.
554
+ *
555
+ * Type parameters:
556
+ * - `TInput` — the union of input variants the client publishes on the
557
+ * `ai-input` wire. Every variant extends {@link CodecInputEvent}.
558
+ * - `TOutput` — the union of output variants the agent publishes on the
559
+ * `ai-output` wire. Every variant extends {@link CodecOutputEvent}.
560
+ * - `TProjection` — the opaque per-node state the reducer folds events into
561
+ * (one projection per node, whether a RunNode or a run-less input node).
562
+ * The SDK never inspects it directly; use {@link Codec.getMessages} to
563
+ * extract messages for the conversation Tree.
564
+ * - `TMessage` — the per-message shape consumed by the Tree. Returned from
565
+ * {@link Codec.getMessages}.
566
+ */
567
+ export interface Codec<
568
+ TInput extends CodecInputEvent,
569
+ TOutput extends CodecOutputEvent,
570
+ TProjection,
571
+ TMessage,
572
+ > extends Reducer<CodecEvent<TInput, TOutput>, TProjection> {
573
+ /**
574
+ * Optional Ably-Agent identifier. When present, the agent-registration path
575
+ * registers it on the channel (so traffic is attributed to this codec); when
576
+ * absent, the codec opts out of registration. Read directly by `registerAgent`.
577
+ */
578
+ readonly adapterTag?: string;
579
+ /** Create a stateful encoder bound to the given channel. */
580
+ createEncoder(channel: ChannelWriter, options?: EncoderOptions): Encoder<TInput, TOutput>;
581
+ /** Create a stateful decoder for converting Ably inbound messages into typed inputs and outputs. */
582
+ createDecoder(): Decoder<TInput, TOutput>;
583
+ /**
584
+ * Extract the per-message list from a projection, each message paired
585
+ * with its codec-message-id (see {@link CodecMessage}). The SDK uses the
586
+ * `codecMessageId` to correlate messages — it never reads identity from
587
+ * the message itself — and surfaces `message` to the application
588
+ * unchanged.
589
+ */
590
+ getMessages(projection: TProjection): CodecMessage<TMessage>[];
591
+ /**
592
+ * Wrap a TMessage as the codec's well-known {@link UserMessage} variant,
593
+ * returned as a `TInput` ready to publish on the `ai-input` wire. This is
594
+ * the public way to turn a caller-provided TMessage into an input for
595
+ * `View.send`; the client session's seed-message path uses it too. The
596
+ * returned value is a `UserMessage<TMessage>` member of the codec's
597
+ * `TInput` union — typed as `TInput` so callers need no cast.
598
+ * @param message - The user's message in the codec's domain representation.
599
+ * @returns A `TInput` (the codec's {@link UserMessage} variant).
600
+ */
601
+ createUserMessage(message: TMessage): TInput;
602
+ /**
603
+ * Build a {@link Regenerate} for the codec, returned as a `TInput`. The
604
+ * View calls this from `regenerate(messageId)`; the input event is a
605
+ * signal (not itself a fork) that targets the assistant codec-message to
606
+ * be regenerated.
607
+ *
608
+ * The new Run is a CONTINUATION of the regenerated message's Run, not a
609
+ * fork at the Run level. The message-level replacement (the new
610
+ * assistant supersedes the original) happens at the View's
611
+ * projection-extraction step (Spec: AIT-CT13d).
612
+ * @param target - The codec-message-id of the assistant being regenerated.
613
+ * @param parent - The codec-message-id of the parent user message the new assistant threads under.
614
+ * @returns A `TInput` (the codec's {@link Regenerate} variant).
615
+ */
616
+ createRegenerate(target: string, parent: string): TInput;
617
+ /**
618
+ * Build a {@link ToolResult} for the codec, returned as a `TInput`.
619
+ * Amends the assistant at `codecMessageId` with the codec's domain
620
+ * `payload`. Optional — only codecs whose `TInput` includes the
621
+ * {@link ToolResult} variant implement it.
622
+ * @param codecMessageId - The assistant codec-message the result amends.
623
+ * @param payload - The codec's domain tool-result payload.
624
+ * @returns A `TInput` (the codec's {@link ToolResult} variant).
625
+ */
626
+ createToolResult?(codecMessageId: string, payload: ToolResultPayloadOf<TInput>): TInput;
627
+ /**
628
+ * Build a {@link ToolResultError} for the codec, returned as a `TInput`.
629
+ * Optional — only codecs whose `TInput` includes the variant implement it.
630
+ * @param codecMessageId - The assistant codec-message the error amends.
631
+ * @param payload - The codec's domain tool-result-failure payload.
632
+ * @returns A `TInput` (the codec's {@link ToolResultError} variant).
633
+ */
634
+ createToolResultError?(codecMessageId: string, payload: ToolResultErrorPayloadOf<TInput>): TInput;
635
+ /**
636
+ * Build a {@link ToolApprovalResponse} for the codec, returned as a
637
+ * `TInput`. Optional — only codecs whose `TInput` includes the variant
638
+ * implement it.
639
+ * @param codecMessageId - The assistant codec-message the response amends.
640
+ * @param payload - The codec's domain approval-decision payload.
641
+ * @returns A `TInput` (the codec's {@link ToolApprovalResponse} variant).
642
+ */
643
+ createToolApprovalResponse?(codecMessageId: string, payload: ToolApprovalResponsePayloadOf<TInput>): TInput;
265
644
  }