@ably/ai-transport 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +10 -19
  2. package/dist/ably-ai-transport.js +1790 -1091
  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 +2 -2
  7. package/dist/core/agent.d.ts +20 -5
  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 +4 -1
  11. package/dist/core/codec/define-codec.d.ts +100 -0
  12. package/dist/core/codec/encoder.d.ts +2 -7
  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 -1
  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/output-descriptor-decoder.d.ts +29 -0
  20. package/dist/core/codec/output-descriptor-encoder.d.ts +31 -0
  21. package/dist/core/codec/output-descriptors.d.ts +237 -0
  22. package/dist/core/codec/types.d.ts +95 -36
  23. package/dist/core/codec/well-known-inputs.d.ts +52 -0
  24. package/dist/core/transport/agent-view.d.ts +296 -0
  25. package/dist/core/transport/decode-fold.d.ts +40 -32
  26. package/dist/core/transport/headers.d.ts +30 -1
  27. package/dist/core/transport/index.d.ts +1 -1
  28. package/dist/core/transport/invocation.d.ts +1 -1
  29. package/dist/core/transport/load-history-pages.d.ts +71 -0
  30. package/dist/core/transport/load-history.d.ts +21 -16
  31. package/dist/core/transport/run-manager.d.ts +9 -11
  32. package/dist/core/transport/session-support.d.ts +55 -0
  33. package/dist/core/transport/tree.d.ts +165 -15
  34. package/dist/core/transport/types/agent.d.ts +120 -98
  35. package/dist/core/transport/types/client.d.ts +45 -12
  36. package/dist/core/transport/types/tree.d.ts +52 -10
  37. package/dist/core/transport/types/view.d.ts +55 -28
  38. package/dist/core/transport/view.d.ts +176 -58
  39. package/dist/core/transport/wire-log.d.ts +102 -0
  40. package/dist/errors.d.ts +10 -4
  41. package/dist/index.d.ts +6 -5
  42. package/dist/react/ably-ai-transport-react.js +784 -415
  43. package/dist/react/ably-ai-transport-react.js.map +1 -1
  44. package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
  45. package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
  46. package/dist/react/contexts/client-session-context.d.ts +2 -1
  47. package/dist/react/contexts/client-session-provider.d.ts +3 -0
  48. package/dist/react/index.d.ts +2 -1
  49. package/dist/react/internal/skipped-session.d.ts +8 -0
  50. package/dist/react/use-view.d.ts +3 -3
  51. package/dist/utils.d.ts +22 -54
  52. package/dist/vercel/ably-ai-transport-vercel.js +2297 -2026
  53. package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
  54. package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
  55. package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
  56. package/dist/vercel/codec/decode-lifecycle.d.ts +9 -0
  57. package/dist/vercel/codec/events.d.ts +1 -2
  58. package/dist/vercel/codec/fields.d.ts +44 -0
  59. package/dist/vercel/codec/fold-content.d.ts +16 -0
  60. package/dist/vercel/codec/fold-data.d.ts +16 -0
  61. package/dist/vercel/codec/fold-input.d.ts +67 -0
  62. package/dist/vercel/codec/fold-lifecycle.d.ts +16 -0
  63. package/dist/vercel/codec/fold-text.d.ts +16 -0
  64. package/dist/vercel/codec/fold-tool-input.d.ts +17 -0
  65. package/dist/vercel/codec/fold-tool-output.d.ts +16 -0
  66. package/dist/vercel/codec/index.d.ts +5 -30
  67. package/dist/vercel/codec/inputs.d.ts +11 -0
  68. package/dist/vercel/codec/outputs.d.ts +11 -0
  69. package/dist/vercel/codec/reducer-state.d.ts +121 -0
  70. package/dist/vercel/codec/reducer.d.ts +20 -102
  71. package/dist/vercel/codec/tool-transitions.d.ts +0 -6
  72. package/dist/vercel/codec/wire-data.d.ts +34 -0
  73. package/dist/vercel/index.d.ts +1 -0
  74. package/dist/vercel/react/ably-ai-transport-vercel-react.js +2013 -9500
  75. package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
  76. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +1 -70
  77. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
  78. package/dist/vercel/react/contexts/chat-transport-context.d.ts +2 -1
  79. package/dist/vercel/run-end-reason.d.ts +66 -11
  80. package/dist/vercel/tool-part.d.ts +21 -0
  81. package/dist/vercel/transport/chat-transport.d.ts +0 -2
  82. package/dist/vercel/transport/index.d.ts +1 -1
  83. package/dist/vercel/transport/run-output-stream.d.ts +6 -8
  84. package/dist/version.d.ts +1 -1
  85. package/package.json +2 -2
  86. package/src/constants.ts +2 -2
  87. package/src/core/agent.ts +43 -19
  88. package/src/core/channel-options.ts +89 -0
  89. package/src/core/codec/codec-event.ts +27 -0
  90. package/src/core/codec/decoder.ts +145 -21
  91. package/src/core/codec/define-codec.ts +432 -0
  92. package/src/core/codec/encoder.ts +13 -54
  93. package/src/core/codec/field-bag.ts +142 -0
  94. package/src/core/codec/fields.ts +193 -0
  95. package/src/core/codec/index.ts +43 -0
  96. package/src/core/codec/input-descriptor-decoder.ts +97 -0
  97. package/src/core/codec/input-descriptor-encoder.ts +150 -0
  98. package/src/core/codec/input-descriptors.ts +373 -0
  99. package/src/core/codec/output-descriptor-decoder.ts +139 -0
  100. package/src/core/codec/output-descriptor-encoder.ts +101 -0
  101. package/src/core/codec/output-descriptors.ts +307 -0
  102. package/src/core/codec/types.ts +99 -36
  103. package/src/core/codec/well-known-inputs.ts +96 -0
  104. package/src/core/transport/agent-session.ts +330 -589
  105. package/src/core/transport/agent-view.ts +738 -0
  106. package/src/core/transport/client-session.ts +74 -69
  107. package/src/core/transport/decode-fold.ts +57 -47
  108. package/src/core/transport/headers.ts +57 -4
  109. package/src/core/transport/index.ts +2 -1
  110. package/src/core/transport/invocation.ts +1 -1
  111. package/src/core/transport/load-history-pages.ts +220 -0
  112. package/src/core/transport/load-history.ts +63 -61
  113. package/src/core/transport/pipe-stream.ts +10 -1
  114. package/src/core/transport/run-manager.ts +25 -31
  115. package/src/core/transport/session-support.ts +96 -0
  116. package/src/core/transport/tree.ts +414 -47
  117. package/src/core/transport/types/agent.ts +129 -102
  118. package/src/core/transport/types/client.ts +49 -13
  119. package/src/core/transport/types/tree.ts +61 -12
  120. package/src/core/transport/types/view.ts +57 -28
  121. package/src/core/transport/view.ts +520 -172
  122. package/src/core/transport/wire-log.ts +189 -0
  123. package/src/errors.ts +10 -3
  124. package/src/index.ts +44 -11
  125. package/src/react/contexts/client-session-context.ts +1 -1
  126. package/src/react/contexts/client-session-provider.tsx +38 -2
  127. package/src/react/index.ts +2 -1
  128. package/src/react/internal/skipped-session.ts +62 -0
  129. package/src/react/use-client-session.ts +7 -30
  130. package/src/react/use-view.ts +3 -3
  131. package/src/utils.ts +31 -97
  132. package/src/vercel/codec/decode-lifecycle.ts +70 -0
  133. package/src/vercel/codec/events.ts +1 -3
  134. package/src/vercel/codec/fields.ts +58 -0
  135. package/src/vercel/codec/fold-content.ts +54 -0
  136. package/src/vercel/codec/fold-data.ts +46 -0
  137. package/src/vercel/codec/fold-input.ts +255 -0
  138. package/src/vercel/codec/fold-lifecycle.ts +85 -0
  139. package/src/vercel/codec/fold-text.ts +55 -0
  140. package/src/vercel/codec/fold-tool-input.ts +86 -0
  141. package/src/vercel/codec/fold-tool-output.ts +79 -0
  142. package/src/vercel/codec/index.ts +23 -63
  143. package/src/vercel/codec/inputs.ts +116 -0
  144. package/src/vercel/codec/outputs.ts +207 -0
  145. package/src/vercel/codec/reducer-state.ts +169 -0
  146. package/src/vercel/codec/reducer.ts +52 -838
  147. package/src/vercel/codec/tool-transitions.ts +1 -12
  148. package/src/vercel/codec/wire-data.ts +64 -0
  149. package/src/vercel/index.ts +1 -0
  150. package/src/vercel/react/contexts/chat-transport-context.ts +1 -1
  151. package/src/vercel/react/use-chat-transport.ts +8 -28
  152. package/src/vercel/react/use-message-sync.ts +5 -10
  153. package/src/vercel/run-end-reason.ts +95 -16
  154. package/src/vercel/tool-part.ts +25 -0
  155. package/src/vercel/transport/chat-transport.ts +10 -22
  156. package/src/vercel/transport/index.ts +1 -1
  157. package/src/vercel/transport/run-output-stream.ts +7 -8
  158. package/src/version.ts +1 -1
  159. package/dist/core/transport/branch-chain.d.ts +0 -43
  160. package/dist/core/transport/load-conversation.d.ts +0 -128
  161. package/dist/vercel/codec/decoder.d.ts +0 -9
  162. package/dist/vercel/codec/encoder.d.ts +0 -11
  163. package/src/core/transport/branch-chain.ts +0 -58
  164. package/src/core/transport/load-conversation.ts +0 -355
  165. package/src/vercel/codec/decoder.ts +0 -696
  166. package/src/vercel/codec/encoder.ts +0 -548
@@ -0,0 +1,281 @@
1
+ import { DataCodec, FieldFor, HeaderField } from './fields.js';
2
+ import { MessagePayload, WriteOptions } from './types.js';
3
+ /**
4
+ * Declarative input descriptors — the single source of truth for a codec's
5
+ * `ai-input` wire mapping, the input-side sibling of {@link import('./output-descriptors.js')}.
6
+ *
7
+ * Inputs come in two cardinalities: a single domain input ↔ one wire message
8
+ * (the `event` construct), and a single domain message ↔ many atomic wire events
9
+ * (the `batch` construct — the input sibling of the output `stream`). Both are
10
+ * declared once per codec and consumed by the generic input encode/decode
11
+ * drivers, so adding an input is one descriptor entry rather than a pair of
12
+ * hand-synchronised switch arms.
13
+ *
14
+ * Authoring is cast-free: the {@link inputBuilder} factory hands the codec a
15
+ * `{ event, batch }` pair curried on the codec's input union, so every `data` /
16
+ * `fields` / `parts` callback receives the exact narrowed member. The descriptors
17
+ * are then erased to a heterogeneous {@link InputDescriptor} via a single
18
+ * documented cast at each constructor boundary — never in author code.
19
+ */
20
+ import type * as Ably from 'ably';
21
+ /** Resolve the input union member a `kind` literal selects. */
22
+ export type ResolveInput<U extends {
23
+ kind: string;
24
+ }, K extends U['kind']> = Extract<U, {
25
+ kind: K;
26
+ }>;
27
+ /**
28
+ * The payload an input `event`'s `fields` / `data` operate on. Inputs nest their
29
+ * domain data under `payload` (the `{ kind, codecMessageId, payload }` envelope of
30
+ * the well-known input variants), so a single event's spec is authored against the
31
+ * payload, and the driver wraps/unwraps the envelope. A member with no `payload`
32
+ * (a `wireOnly` signal) resolves to `never` — such an event declares no `fields` /
33
+ * `data`, so the payload type is never used.
34
+ * @template C - The narrowed input member.
35
+ */
36
+ export type PayloadOf<C> = C extends {
37
+ payload: infer P;
38
+ } ? P : never;
39
+ /**
40
+ * Resolve the part union member a `partType` literal selects, mirroring the
41
+ * output {@link import('./output-descriptors.js').ResolveType} curry one level down.
42
+ * An exact match wins; a wildcard literal (`data-*`) resolves to the template
43
+ * member (`data-${string}`).
44
+ * @template P - The part union.
45
+ * @template T - The selected `partType` literal (or a `*-*` wildcard).
46
+ */
47
+ export type ResolvePart<P extends {
48
+ type: string;
49
+ }, T extends string> = Extract<P, {
50
+ type: T;
51
+ }> extends never ? T extends `${infer Pre}-*` ? Extract<P, {
52
+ type: `${Pre}-${string}`;
53
+ }> : never : Extract<P, {
54
+ type: T;
55
+ }>;
56
+ /**
57
+ * The encoder-core view the input encode driver receives: discrete publishes
58
+ * only — inputs never stream. The concrete {@link EncoderCore} satisfies this
59
+ * structurally.
60
+ */
61
+ export interface InputEncoderCore {
62
+ /** Publish a single discrete message. */
63
+ publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult>;
64
+ /** Publish multiple discrete messages atomically (the batch fan-out). */
65
+ publishDiscreteBatch(payloads: MessagePayload[], opts?: WriteOptions): Promise<Ably.PublishResult>;
66
+ }
67
+ /** Per-write context passed to the input encode driver. */
68
+ export interface InputEncodeContext {
69
+ /** Per-write overrides (the wire codec-message-id is stamped here by the client session). */
70
+ opts: WriteOptions | undefined;
71
+ }
72
+ /** Per-message context the input decode driver receives for one inbound `ai-input` message. */
73
+ export interface InputDecodeContext {
74
+ /** The codec `kind` header value (the input descriptor's dispatch key). */
75
+ codecKind: string;
76
+ /** The inbound message data. */
77
+ data: unknown;
78
+ /** The inbound codec-tier headers. */
79
+ codecHeaders: Record<string, string>;
80
+ /** The inbound transport-tier headers (role, codec-message-id, discrete marker). */
81
+ transportHeaders: Record<string, string>;
82
+ }
83
+ /**
84
+ * The spec the input `event` construct accepts for member `C`. A member with
85
+ * no `payload` has nothing for `fields` / `data` to lens onto, so it may only
86
+ * be declared `wireOnly` or escape-hatched; the driver also rejects a
87
+ * payload-less encode at runtime.
88
+ * @template C - The narrowed input member.
89
+ */
90
+ export type InputEventSpecFor<C> = [PayloadOf<C>] extends [never] ? Pick<InputEventSpec<C>, 'wireOnly'> : InputEventSpec<C>;
91
+ /**
92
+ * A single-event input descriptor spec, narrowed to input member `C`. `fields`
93
+ * and `data` operate on the member's {@link PayloadOf payload}; the driver wraps
94
+ * the `{ kind, codecMessageId, payload }` envelope on decode and unwraps it on
95
+ * encode. A `wireOnly` event carries no payload (kind only).
96
+ * @template C - The narrowed input member.
97
+ */
98
+ export interface InputEventSpec<C> {
99
+ /**
100
+ * Declared header fields over the member's payload, written on encode and
101
+ * read on decode. Each field's key names both the wire header and the
102
+ * payload property it carries (see {@link FieldFor}). Omit for none.
103
+ */
104
+ fields?: readonly FieldFor<PayloadOf<C>>[];
105
+ /** Wire `data` codec over the payload. Omit when the input carries no data (`data: ''`). */
106
+ data?: DataCodec<PayloadOf<C>>;
107
+ /** Wire-only signal: encode stamps only the `kind` header (empty data, no fields); decode yields `[]`. */
108
+ wireOnly?: boolean;
109
+ }
110
+ /**
111
+ * A per-part wire mapping inside a {@link BatchSpec}, narrowed to part member `Q`.
112
+ * `fields` and `data` operate on the selected part; the batch driver fans the
113
+ * domain message out into one wire event per part and reassembles them in the
114
+ * reducer (merge by codec-message-id).
115
+ * @template Q - The narrowed part member.
116
+ */
117
+ export interface PartSpec<Q> {
118
+ /**
119
+ * Declared header fields for this part, written on encode and read on
120
+ * decode. Each field's key names both the wire header and the part property
121
+ * it carries (see {@link FieldFor}). Omit for none.
122
+ */
123
+ fields?: readonly FieldFor<Q>[];
124
+ /** Wire `data` codec over the part. Omit when the part carries no data. */
125
+ data?: DataCodec<Q>;
126
+ }
127
+ /**
128
+ * The curried part sub-builder a {@link BatchSpec.parts} function receives.
129
+ * Mirrors the {@link inputBuilder} `event` curry one level down — and the
130
+ * output builder's wildcard idiom: `p(partType, spec)` narrows `spec` to the
131
+ * part member the literal selects, and a `-*` literal (e.g. `data-*`) declares
132
+ * a wildcard family whose dispatch predicate is derived from the literal's
133
+ * prefix, narrowing `spec` to the template member. Both forms are cast-free in
134
+ * author code.
135
+ * @template P - The part union.
136
+ */
137
+ export type PartBuilder<P extends {
138
+ type: string;
139
+ }> = <T extends P['type'] | `${string}-*`>(partType: T, spec: PartSpec<ResolvePart<P, T>>) => PartDescriptor;
140
+ /**
141
+ * Per-message wire headers a {@link BatchSpec.messageHeaders} stamps on every
142
+ * part of one batch. These carry metadata that belongs to the whole message
143
+ * rather than an individual part (e.g. the message id as a codec header, the
144
+ * sender role as a transport header), so the decode side can reconstruct the
145
+ * shared message envelope from any single part. Both tiers are optional.
146
+ */
147
+ export interface BatchMessageHeaders {
148
+ /** Codec-tier headers stamped on every part (e.g. a per-message id). */
149
+ codecHeaders?: Record<string, string>;
150
+ /** Transport-tier headers stamped on every part (e.g. the sender role). */
151
+ transportHeaders?: Record<string, string>;
152
+ }
153
+ /**
154
+ * The context a {@link BatchSpec.assemble} receives alongside one decoded part:
155
+ * the inbound header tiers of the wire event the part was decoded from. A batch
156
+ * fans out into N independent wire events, so each part arrives carrying the
157
+ * shared per-message headers ({@link BatchMessageHeaders}); `assemble` reads them
158
+ * to rebuild the message envelope (id, role, …) around its one part.
159
+ */
160
+ export interface BatchAssembleContext {
161
+ /** The inbound codec-tier headers (carries the per-message codec headers). */
162
+ codecHeaders: Record<string, string>;
163
+ /** The inbound transport-tier headers (carries the per-message transport headers). */
164
+ transportHeaders: Record<string, string>;
165
+ }
166
+ /**
167
+ * A multi-part input descriptor spec: one domain message decomposed into many
168
+ * atomic wire events sharing the input member's `kind` and codec-message-id, each
169
+ * carrying a `partType` sub-discriminator. The part union `P` is inferred from
170
+ * `explode`'s return type and threaded into `parts`'s curried `p` and `assemble`,
171
+ * so all three are cast-free in author code.
172
+ * @template C - The narrowed input member (the message-bearing input).
173
+ * @template P - The part union the message explodes into.
174
+ */
175
+ export interface BatchSpec<C, P extends {
176
+ type: string;
177
+ }> {
178
+ /** ENCODE: decompose the domain message into its parts. */
179
+ explode: (input: C) => readonly P[];
180
+ /** The `partType` sub-discriminator read off each part on encode. */
181
+ partTypeOf: (part: P) => string;
182
+ /** Declarative per-part wire mapping (a sub-table built via the curried `p`). */
183
+ parts: (p: PartBuilder<P>) => readonly PartDescriptor[];
184
+ /**
185
+ * ENCODE: per-message headers stamped on every part (the driver merges them
186
+ * onto each part's headers, including the ≥1-event fallback). Use for metadata
187
+ * shared by the whole message — e.g. a message-id codec header and a role
188
+ * transport header. Omit when parts carry no shared per-message metadata.
189
+ */
190
+ messageHeaders?: (input: C) => BatchMessageHeaders;
191
+ /**
192
+ * DECODE: shape one decoded wire part into a one-part input (the reducer merges
193
+ * parts by codec-message-id). `ctx` exposes the inbound header tiers so the
194
+ * shared per-message metadata stamped by `messageHeaders` can be read back.
195
+ * The driver stamps only `kind`; the per-message identity rides the
196
+ * transport header and is recovered through `ctx` when needed.
197
+ */
198
+ assemble: (part: P, ctx: BatchAssembleContext) => Omit<C, 'kind'>;
199
+ }
200
+ /** A single-event input descriptor erased to the codec's input union `U`. */
201
+ export interface InputEventDescriptor {
202
+ /** Discriminator. */
203
+ construct: 'event';
204
+ /** The wire `kind` this input dispatches on. */
205
+ kind: string;
206
+ /** Declared header fields (read/written against the member's payload). */
207
+ fields: readonly HeaderField<unknown>[];
208
+ /** Wire `data` codec, if any. */
209
+ data?: DataCodec<unknown>;
210
+ /** Wire-only signal flag. */
211
+ wireOnly: boolean;
212
+ }
213
+ /** An erased per-part wire mapping within a {@link BatchDescriptor}. */
214
+ export interface PartDescriptor {
215
+ /** The exact `partType` this part encodes as (the wildcard sentinel for a family). */
216
+ partType: string;
217
+ /** Decode-dispatch predicate for a wildcard part; absent for an exact part. */
218
+ match?: (partType: string) => boolean;
219
+ /** Declared header fields for this part. */
220
+ fields: readonly HeaderField<unknown>[];
221
+ /** Wire `data` codec over the part, if any. */
222
+ data?: DataCodec<unknown>;
223
+ }
224
+ /** A multi-part (batch) input descriptor erased to the codec's input union `U`. */
225
+ export interface BatchDescriptor<U> {
226
+ /** Discriminator. */
227
+ construct: 'batch';
228
+ /** The wire `kind` every part of this batch shares. */
229
+ kind: string;
230
+ /** Decompose the domain input into its parts. */
231
+ explode: (input: U) => readonly unknown[];
232
+ /** Read the `partType` sub-discriminator off a part. */
233
+ partTypeOf: (part: unknown) => string;
234
+ /** The per-part wire mappings. */
235
+ parts: readonly PartDescriptor[];
236
+ /** Build the per-message headers stamped on every part, if any. */
237
+ messageHeaders?: (input: U) => BatchMessageHeaders;
238
+ /** Shape one decoded part into a one-part input (sans the driver-stamped `kind`). */
239
+ assemble: (part: unknown, ctx: BatchAssembleContext) => Omit<U, 'kind'>;
240
+ }
241
+ /** An erased input descriptor — a single event or a multi-part batch. */
242
+ export type InputDescriptor<U> = InputEventDescriptor | BatchDescriptor<U>;
243
+ /**
244
+ * The direction-scoped input builder `defineCodec` injects into the `input`
245
+ * config function — `event` and `batch`, both curried on the codec's input union
246
+ * so author entries narrow cast-free.
247
+ * @template U - The codec's input union.
248
+ */
249
+ export interface InputBuilder<U extends {
250
+ kind: string;
251
+ }> {
252
+ /**
253
+ * Declare a single-event input. Narrows `spec` to the member `kind` selects;
254
+ * `fields` / `data` operate on that member's payload.
255
+ * @param kind - The input member's `kind` literal (the wire dispatch key).
256
+ * @param spec - The narrowed input spec. Omit for a bare-`kind` input.
257
+ * @returns An erased {@link InputDescriptor}.
258
+ */
259
+ event: <K extends U['kind']>(kind: K, spec?: InputEventSpecFor<ResolveInput<U, K>>) => InputDescriptor<U>;
260
+ /**
261
+ * Declare a multi-part (batch) input. Narrows the spec to the message-bearing
262
+ * member `kind` selects; `explode`'s return type fixes the part union `P`, which
263
+ * threads into `parts`'s curried `p` and `assemble` cast-free.
264
+ * @param kind - The input member's `kind` literal (the shared wire dispatch key).
265
+ * @param spec - The narrowed batch spec.
266
+ * @returns An erased {@link InputDescriptor}.
267
+ */
268
+ batch: <K extends U['kind'], P extends {
269
+ type: string;
270
+ }>(kind: K, spec: BatchSpec<ResolveInput<U, K>, P>) => InputDescriptor<U>;
271
+ }
272
+ /**
273
+ * Build the curried `{ event, batch }` input builder for a codec's input union.
274
+ * `defineCodec` calls this once and hands the result to the `input` config
275
+ * function; mirrors the output side's {@link import('./output-descriptors.js').outputBuilder}.
276
+ * @template U - The codec's input union.
277
+ * @returns The direction-scoped {@link InputBuilder}.
278
+ */
279
+ export declare const inputBuilder: <U extends {
280
+ kind: string;
281
+ }>() => InputBuilder<U>;
@@ -0,0 +1,29 @@
1
+ import { OutputDescriptor } from './output-descriptors.js';
2
+ import { StreamTrackerState } from './types.js';
3
+ /** Decodes wire messages of union `U` from a descriptor set. */
4
+ export interface OutputDescriptorDecoder<U> {
5
+ /** Rebuild the chunk(s) emitted when a stream starts. */
6
+ buildStart(tracker: StreamTrackerState): U[];
7
+ /** Rebuild the chunk(s) for a stream delta. */
8
+ buildDelta(tracker: StreamTrackerState, delta: string): U[];
9
+ /** Rebuild the chunk(s) emitted when a stream completes. */
10
+ buildEnd(tracker: StreamTrackerState, closingCodecHeaders: Record<string, string>): U[];
11
+ /**
12
+ * Decode a discrete message by its codec `kind`.
13
+ * @param codecKind - The codec `kind` header value (the dispatch key).
14
+ * @param codecHeaders - The inbound codec-tier headers.
15
+ * @param transportHeaders - The inbound transport-tier headers.
16
+ * @param data - The inbound message data.
17
+ * @returns The decoded events (empty if no descriptor matches).
18
+ */
19
+ decodeDiscrete(codecKind: string, codecHeaders: Record<string, string>, transportHeaders: Record<string, string>, data: unknown): U[];
20
+ }
21
+ /**
22
+ * Build an output decode driver for a descriptor set.
23
+ * @template U - The codec's event union.
24
+ * @param descriptors - The descriptor set (events + streamed families).
25
+ * @returns An {@link OutputDescriptorDecoder} that reconstructs events from the wire.
26
+ */
27
+ export declare const createOutputDescriptorDecoder: <U extends {
28
+ type: string;
29
+ }>(descriptors: readonly OutputDescriptor<U>[]) => OutputDescriptorDecoder<U>;
@@ -0,0 +1,31 @@
1
+ import { EncoderCore } from './encoder.js';
2
+ import { OutputDescriptor } from './output-descriptors.js';
3
+ import { WriteOptions } from './types.js';
4
+ /** Per-write output encode context threaded from the encoder. */
5
+ export interface OutputEncodeContext {
6
+ /** The encoder's configured fallback message id, if any. */
7
+ messageId: string | undefined;
8
+ /** Per-write overrides. */
9
+ opts: WriteOptions | undefined;
10
+ }
11
+ /** Encodes events of union `U` to channel operations via a descriptor set. */
12
+ export interface OutputDescriptorEncoder<U> {
13
+ /**
14
+ * Encode one event through its descriptor.
15
+ * @param chunk - The event to encode.
16
+ * @param core - The encoder core to publish/stream through.
17
+ * @param ctx - Per-write context (fallback message id, write options).
18
+ * @returns A promise resolving when the publish/stream operation completes.
19
+ */
20
+ encode(chunk: U, core: EncoderCore, ctx: OutputEncodeContext): Promise<void>;
21
+ }
22
+ /**
23
+ * Build an output encode driver for a descriptor set bound to a wire message name.
24
+ * @template U - The codec's event union.
25
+ * @param descriptors - The descriptor set (events + streamed families).
26
+ * @param wireName - The Ably message name for this direction (`ai-output` / `ai-input`).
27
+ * @returns An {@link OutputDescriptorEncoder} routing each event through its descriptor.
28
+ */
29
+ export declare const createOutputDescriptorEncoder: <U extends {
30
+ type: string;
31
+ }>(descriptors: readonly OutputDescriptor<U>[], wireName: string) => OutputDescriptorEncoder<U>;
@@ -0,0 +1,237 @@
1
+ import { DataCodec, FieldFor, HeaderField } from './fields.js';
2
+ import { MessagePayload, StreamPayload, WriteOptions } from './types.js';
3
+ /**
4
+ * Declarative output descriptors — the single source of truth for a codec's
5
+ * `ai-output` wire mapping, the output-side sibling of {@link import('./input-descriptors.js')}.
6
+ *
7
+ * A codec declares each ordinary output event once, as a descriptor built on the
8
+ * typed header-field bindings ({@link HeaderField}). The generic encode/decode
9
+ * drivers consume the descriptor set, so adding an ordinary event is one
10
+ * descriptor entry instead of three hand-synchronised switch arms (encoder,
11
+ * decoder, stream reconstruction).
12
+ *
13
+ * Authoring is cast-free: the {@link outputBuilder} factory hands the codec an
14
+ * `{ event, stream }` pair curried on the codec's output union, so every `data` /
15
+ * `encode` / `decode` callback receives the exact narrowed member. The descriptors
16
+ * are then erased to a heterogeneous {@link OutputDescriptor} via a single
17
+ * documented cast at each constructor boundary — never in author code.
18
+ */
19
+ import type * as Ably from 'ably';
20
+ /** The string-valued keys of `C` — the only keys `idField`/`deltaField` may name. */
21
+ export type StringKeyOf<C> = {
22
+ [K in keyof C]-?: C[K] extends string ? K : never;
23
+ }[keyof C];
24
+ /**
25
+ * Resolve the union member a descriptor `type` literal selects. An exact match
26
+ * wins; a wildcard literal (`'data-*'`) resolves to the template member
27
+ * (`data-${string}`), so wildcard descriptors still narrow to the real member.
28
+ */
29
+ export type ResolveType<U extends {
30
+ type: string;
31
+ }, T extends string> = Extract<U, {
32
+ type: T;
33
+ }> extends never ? T extends `${infer P}-*` ? Extract<U, {
34
+ type: `${P}-${string}`;
35
+ }> : never : Extract<U, {
36
+ type: T;
37
+ }>;
38
+ /**
39
+ * The narrowed view of the encoder core that escape-hatch `encode` functions
40
+ * receive — only the publish/stream operations a hatch legitimately needs. The
41
+ * full internal `EncoderCore` satisfies this structurally.
42
+ */
43
+ export interface EscapeHatchCore {
44
+ /** Publish a single discrete message. */
45
+ publishDiscrete(payload: MessagePayload, opts?: WriteOptions): Promise<Ably.PublishResult>;
46
+ /** Start a streamed message. */
47
+ startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void>;
48
+ /** Append a fragment to an in-flight stream (fire-and-forget). */
49
+ appendStream(streamId: string, data: string): void;
50
+ /** Close a streamed message. */
51
+ closeStream(streamId: string, payload: StreamPayload): Promise<void>;
52
+ /** Cancel all in-progress streams. */
53
+ cancelAllStreams(opts?: WriteOptions): Promise<void>;
54
+ }
55
+ /**
56
+ * Builds a codec headers record from a chunk through the descriptor's declared
57
+ * fields, stamping the dispatch `type` plus each field read off `chunk`. An
58
+ * optional `keys` subset restricts which declared fields are written; the keys
59
+ * are checked against the chunk so the imperative path can't drift.
60
+ * @template C - The narrowed chunk member.
61
+ */
62
+ export type HeaderBuilder<C> = <K extends keyof C & string>(chunk: C, keys?: readonly K[]) => Record<string, string>;
63
+ /**
64
+ * Context passed to an escape-hatch `encode` function.
65
+ * @template C - The narrowed chunk member.
66
+ */
67
+ export interface OutputEncodeHatchContext<C> {
68
+ /** Header builder bound to the descriptor's declared fields. */
69
+ h: HeaderBuilder<C>;
70
+ /** The wire message name for this direction (`ai-output` / `ai-input`). */
71
+ name: string;
72
+ /** The encoder's configured fallback message id, if any. */
73
+ messageId: string | undefined;
74
+ /** Per-write overrides to thread into the hatch's publish/cancel calls. */
75
+ opts: WriteOptions | undefined;
76
+ }
77
+ /** Context passed to a discrete escape-hatch `decode` function. */
78
+ export interface OutputDecodeContext {
79
+ /** The codec `kind` header value the message dispatched on (mirrors the input context's `codecKind`). */
80
+ codecKind: string;
81
+ /** The inbound codec-tier headers. */
82
+ codecHeaders: Record<string, string>;
83
+ /** The inbound transport-tier headers. */
84
+ transportHeaders: Record<string, string>;
85
+ /** The inbound message data. */
86
+ data: unknown;
87
+ }
88
+ /** Context passed to a stream descriptor's `decodeEnd` escape hatch. */
89
+ export interface OutputStreamEndContext {
90
+ /** The stream identifier (e.g. chunk id, toolCallId). */
91
+ streamId: string;
92
+ /** The full accumulated stream text. */
93
+ accumulated: string;
94
+ /** The stream's persistent (start) codec headers. */
95
+ codecHeaders: Record<string, string>;
96
+ /** The codec headers carried on close (may differ from the start headers). */
97
+ closingCodecHeaders: Record<string, string>;
98
+ }
99
+ /**
100
+ * A discrete (non-streaming) output event descriptor spec, narrowed to chunk member `C`.
101
+ * @template C - The narrowed chunk member.
102
+ */
103
+ export interface OutputEventSpec<C> {
104
+ /**
105
+ * Declared header fields, written on encode and read on decode. Each field's
106
+ * key names both the wire header and the chunk property it carries (see
107
+ * {@link FieldFor}). Omit for a header-less event.
108
+ */
109
+ fields?: readonly FieldFor<C>[];
110
+ /** Wire `data` codec. Omit when the event carries no data (`data: ''`). */
111
+ data?: DataCodec<C>;
112
+ /** Whether the publish is ephemeral (not persisted). Default false. */
113
+ ephemeral?: (chunk: C) => boolean;
114
+ /** Escape-hatch encode — overrides the default discrete publish. */
115
+ encode?: (chunk: C, core: EscapeHatchCore, ctx: OutputEncodeHatchContext<C>) => Promise<void>;
116
+ }
117
+ /**
118
+ * A streamed-family descriptor spec. `start`/`delta`/`end` are the domain chunk
119
+ * `type` literals; the family id (the {@link OutputBuilder.stream} first argument)
120
+ * is the wire `kind` header all three phases stamp.
121
+ * @template U - The codec's event union.
122
+ * @template S - The start chunk `type` literal.
123
+ * @template D - The delta chunk `type` literal.
124
+ * @template E - The end chunk `type` literal.
125
+ */
126
+ export interface OutputStreamSpec<U extends {
127
+ type: string;
128
+ }, S extends U['type'], D extends U['type'], E extends U['type']> {
129
+ /** The start chunk `type` literal. */
130
+ start: S;
131
+ /** The delta chunk `type` literal. */
132
+ delta: D;
133
+ /** The end chunk `type` literal. */
134
+ end: E;
135
+ /** The string-valued chunk key carrying the stream id (e.g. `id`, `toolCallId`). */
136
+ idField: StringKeyOf<ResolveType<U, S>> & StringKeyOf<ResolveType<U, D>> & StringKeyOf<ResolveType<U, E>>;
137
+ /** The string-valued delta chunk key carrying the appended fragment. */
138
+ deltaField: StringKeyOf<ResolveType<U, D>>;
139
+ /**
140
+ * Declared header fields written/read on the start and end chunks. Each
141
+ * field's key names both the wire header and the chunk property (see
142
+ * {@link FieldFor}); a field may bind a property carried by either phase.
143
+ */
144
+ fields: readonly (FieldFor<ResolveType<U, S>> | FieldFor<ResolveType<U, E>>)[];
145
+ /** Escape-hatch override for the stream-close step only (e.g. close-or-discrete fallback). */
146
+ onEnd?: (chunk: ResolveType<U, E>, core: EscapeHatchCore, ctx: OutputEncodeHatchContext<ResolveType<U, E>>) => Promise<void>;
147
+ /** Escape-hatch override for the end-chunk rebuild (e.g. input from accumulated text). */
148
+ decodeEnd?: (ctx: OutputStreamEndContext) => ResolveType<U, E>[];
149
+ /**
150
+ * Escape-hatch decode for when the family arrives as a discrete (non-streamed)
151
+ * message — the wire `kind` equals the family id but the wire wasn't streamed
152
+ * (e.g. history compaction). Reconstructs the start/end chunk pair.
153
+ */
154
+ decodeDiscrete?: (ctx: OutputDecodeContext) => ResolveType<U, S | E>[];
155
+ }
156
+ /** A discrete output event descriptor erased to the codec's union `U`. */
157
+ export interface OutputEventDescriptor<U> {
158
+ /** Discriminator — the construct this descriptor was built with. */
159
+ construct: 'event';
160
+ /** The dispatch `type` literal (or wildcard sentinel), stamped as the wire `kind` header. */
161
+ type: string;
162
+ /** Declared header fields. */
163
+ fields: readonly HeaderField<unknown>[];
164
+ /** Wire `data` codec, if any. */
165
+ data?: DataCodec<U>;
166
+ /** Ephemeral predicate, if any. */
167
+ ephemeral?: (chunk: U) => boolean;
168
+ /** Wildcard dispatch predicate (both directions), derived by the builder from a `-*` type literal. */
169
+ match?: (type: string) => boolean;
170
+ /** Escape-hatch encode, if any. */
171
+ encode?: (chunk: U, core: EscapeHatchCore, ctx: OutputEncodeHatchContext<U>) => Promise<void>;
172
+ }
173
+ /** A streamed-family descriptor erased to the codec's union `U`. */
174
+ export interface OutputStreamDescriptor<U> {
175
+ /** Discriminator — the construct this descriptor was built with. */
176
+ construct: 'stream';
177
+ /** The stream family id, stamped as the wire `kind` header on every phase. */
178
+ kind: string;
179
+ /** The start chunk `type`. */
180
+ start: string;
181
+ /** The delta chunk `type`. */
182
+ delta: string;
183
+ /** The end chunk `type`. */
184
+ end: string;
185
+ /** The chunk key carrying the stream id. */
186
+ idField: string;
187
+ /** The delta chunk key carrying the appended fragment. */
188
+ deltaField: string;
189
+ /** Declared header fields. */
190
+ fields: readonly HeaderField<unknown>[];
191
+ /** Escape-hatch close override, if any. */
192
+ onEnd?: (chunk: U, core: EscapeHatchCore, ctx: OutputEncodeHatchContext<U>) => Promise<void>;
193
+ /** Escape-hatch end-rebuild override, if any. */
194
+ decodeEnd?: (ctx: OutputStreamEndContext) => U[];
195
+ /** Escape-hatch non-streamed decode, if any. */
196
+ decodeDiscrete?: (ctx: OutputDecodeContext) => U[];
197
+ }
198
+ /** An erased output descriptor — a discrete event or a streamed family. */
199
+ export type OutputDescriptor<U> = OutputEventDescriptor<U> | OutputStreamDescriptor<U>;
200
+ /**
201
+ * The direction-scoped output builder `defineCodec` injects into the `output`
202
+ * config function — `event` (single discrete) and `stream` (streamed family),
203
+ * both curried on the codec's output union so author entries narrow cast-free.
204
+ * @template U - The codec's output union.
205
+ */
206
+ export interface OutputBuilder<U extends {
207
+ type: string;
208
+ }> {
209
+ /**
210
+ * Declare a single discrete output event. Curried on the output union; narrows
211
+ * `spec` to the member the `type` literal selects. The `type` literal is stamped
212
+ * as the wire `kind` dispatch header.
213
+ * @param type - The event's `type` literal (or a `*-*` wildcard); stamped as the wire `kind` header.
214
+ * @param spec - The narrowed output event spec. Omit for a header-less event with no data.
215
+ * @returns An erased {@link OutputDescriptor}.
216
+ */
217
+ event: <T extends U['type'] | `${string}-*`>(type: T, spec?: OutputEventSpec<ResolveType<U, T>>) => OutputDescriptor<U>;
218
+ /**
219
+ * Declare a streamed output family (start / delta / end). `start`/`delta`/`end`
220
+ * are domain chunk `type` literals; the first argument is the family id, stamped
221
+ * as the wire `kind` dispatch header on every phase.
222
+ * @param kind - The stream family id, stamped as the wire `kind` header on every phase.
223
+ * @param spec - The narrowed stream spec.
224
+ * @returns An erased {@link OutputDescriptor}.
225
+ */
226
+ stream: <S extends U['type'], D extends U['type'], E extends U['type']>(kind: string, spec: OutputStreamSpec<U, S, D, E>) => OutputDescriptor<U>;
227
+ }
228
+ /**
229
+ * Build the curried `{ event, stream }` output builder for a codec's output union.
230
+ * `defineCodec` calls this once and hands the result to the `output` config
231
+ * function; mirrors the input side's {@link import('./input-descriptors.js').inputBuilder}.
232
+ * @template U - The codec's output union.
233
+ * @returns The direction-scoped {@link OutputBuilder}.
234
+ */
235
+ export declare const outputBuilder: <U extends {
236
+ type: string;
237
+ }>() => OutputBuilder<U>;