@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
@@ -116,13 +116,13 @@ export declare const EVENT_RUN_END = "ai-run-end";
116
116
  * Message name: every agent-published codec event (text, reasoning, tool calls,
117
117
  * tool outputs, lifecycle helpers, file / source parts, data-* chunks) rides
118
118
  * this single wire name. The codec event's own `type` is carried in the
119
- * codec-level `type` header so the decoder can dispatch.
119
+ * SDK-controlled codec-level `kind` header so the decoder can dispatch.
120
120
  */
121
121
  export declare const EVENT_AI_OUTPUT = "ai-output";
122
122
  /**
123
123
  * Message name: every client-published codec event (user-message parts,
124
124
  * tool-approval responses, regenerate signals) rides this single wire
125
- * name. The codec event's own kind is carried in the codec-level `type`
125
+ * name. The codec event's own kind is carried in the codec-level `kind`
126
126
  * header so the decoder can dispatch.
127
127
  */
128
128
  export declare const EVENT_AI_INPUT = "ai-input";
@@ -9,20 +9,35 @@
9
9
  * `RealtimeWithOptions` shape to write it.
10
10
  */
11
11
  import type * as Ably from 'ably';
12
+ /**
13
+ * The space-separated `params.agent` string this SDK stamps on channel ATTACH —
14
+ * `ai-transport-js/<version>` plus the codec's adapter tag when present. Pure:
15
+ * unlike {@link registerAgent} it does not mutate the client. Use it to seed a
16
+ * `<ChannelProvider options>` so ably-js's React hooks append their own agent
17
+ * additively (`channelOptionsForReactHooks`) rather than overwriting this SDK's.
18
+ * @param codec - The codec instance whose optional identifier opts into registration.
19
+ * @param codec.adapterTag - The optional Ably-Agent identifier; registered as an agent when present.
20
+ * @returns The channel `params.agent` string.
21
+ */
22
+ export declare const channelAgent: (codec?: {
23
+ readonly adapterTag?: string;
24
+ }) => string;
12
25
  /**
13
26
  * Register this SDK (and optionally a codec) on the supplied Realtime client
14
27
  * and return the channel options the caller should pass to
15
28
  * `client.channels.get(...)` so the agent is also carried on channel ATTACH.
16
- * Sets `options.agents['ai-transport-js'] = VERSION`. When the codec carries
17
- * an internal `adapterTag` field (via {@link AdapterTagHolder}), also sets
18
- * `options.agents[adapterTag] = VERSION`.
29
+ * Sets `options.agents['ai-transport-js'] = VERSION`. When the codec carries an
30
+ * `adapterTag`, also sets `options.agents[adapterTag] = VERSION`.
19
31
  * Idempotent — repeated calls with the same client and codec produce the same keys/values.
20
32
  * Spec: AIT-CT1a, AIT-CT1a2, AIT-CT1a3, AIT-ST1a, AIT-ST1a2, AIT-ST1a3.
21
33
  * @param client - The Ably Realtime client to register on.
22
- * @param codec - The codec instance; cast to {@link AdapterTagHolder} to detect an optional identifier.
34
+ * @param codec - The codec instance whose optional identifier opts into registration.
35
+ * @param codec.adapterTag - The optional Ably-Agent identifier; registered as an agent when present.
23
36
  * @returns Channel options containing `params.agent` for `channels.get`.
24
37
  */
25
- export declare const registerAgent: (client: Ably.Realtime, codec?: unknown) => {
38
+ export declare const registerAgent: (client: Ably.Realtime, codec?: {
39
+ readonly adapterTag?: string;
40
+ }) => {
26
41
  params: {
27
42
  agent: string;
28
43
  };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Channel-mode resolution shared by the sessions and the React provider.
3
+ *
4
+ * Presence, pub/sub, and annotation publishing are all part of the server's
5
+ * default channel-mode set, so a channel attached with no mode flags is granted
6
+ * them automatically. LiveObjects is different: object operations require the
7
+ * `object_subscribe` / `object_publish` modes, which are NOT in the default
8
+ * set, so the channel must be attached with explicit modes to use them.
9
+ *
10
+ * Setting `modes` on the wire is a full replacement, not additive: the moment
11
+ * an ATTACH carries any mode flag the server takes that bitfield verbatim and
12
+ * never falls back to its default set. So requesting object modes means also
13
+ * requesting everything the default set would grant, plus the object modes.
14
+ * {@link AIT_BASE_MODES} is exactly the server default, so opting into extra
15
+ * modes adds the extras and changes nothing else.
16
+ *
17
+ * Every place that resolves the session's channel options — both session
18
+ * constructors and the React `<ChannelProvider>` — must funnel through
19
+ * {@link resolveChannelModes} so they all request the SAME modes in the SAME
20
+ * order. ably-js compares modes order- and duplicate-sensitively when deciding
21
+ * whether a `setOptions` call needs a reattach; identical arrays compare equal,
22
+ * so consistent resolution avoids both spurious reattaches and silent mode
23
+ * reversion when one writer omits modes another set.
24
+ */
25
+ import type * as Ably from 'ably';
26
+ /**
27
+ * The modes AI Transport always needs — byte-for-byte the server's default
28
+ * channel-mode set (`PUBLISH | SUBSCRIBE | PRESENCE | PRESENCE_SUBSCRIBE |
29
+ * ANNOTATION_PUBLISH`). Because this equals the default, requesting it plus
30
+ * extra modes (e.g. {@link OBJECT_MODES}) grants the same access as the default
31
+ * set plus those extras.
32
+ */
33
+ export declare const AIT_BASE_MODES: readonly Ably.ChannelMode[];
34
+ /**
35
+ * The channel modes required to read and write Ably LiveObjects. Pass as the
36
+ * session's `channelModes` option (`channelModes: OBJECT_MODES`) to enable the
37
+ * `object` accessor on a session and the LiveObjects channel hooks under a
38
+ * `<ClientSessionProvider>`.
39
+ */
40
+ export declare const OBJECT_MODES: readonly Ably.ChannelMode[];
41
+ /**
42
+ * Resolve the channel modes AI Transport should request on its channel.
43
+ *
44
+ * Returns `undefined` when the caller asks for no extra modes, so the channel
45
+ * attaches with no mode flags and the server applies its default set. When
46
+ * extra modes are supplied (e.g. {@link OBJECT_MODES}),
47
+ * returns {@link AIT_BASE_MODES} unioned with them, de-duplicated and in a
48
+ * fixed canonical order so repeated resolutions compare equal.
49
+ *
50
+ * Modes outside {@link MODE_ORDER} (which should not occur for a valid
51
+ * {@link Ably.ChannelMode}, but is possible with the type's lowercase aliases)
52
+ * are appended after the canonical modes, sorted alphabetically, so the result
53
+ * is still deterministic.
54
+ * @param extraModes - Additional modes to request on top of {@link AIT_BASE_MODES}. Omit or pass an empty array to request no modes at all.
55
+ * @returns The canonically-ordered, de-duplicated mode set, or `undefined` when no extra modes were requested.
56
+ */
57
+ export declare const resolveChannelModes: (extraModes?: readonly Ably.ChannelMode[]) => Ably.ChannelMode[] | undefined;
@@ -0,0 +1,9 @@
1
+ import { CodecEvent, CodecInputEvent, CodecOutputEvent, DecodedMessage } from './types.js';
2
+ /**
3
+ * Tag a decoded message's events with their wire direction.
4
+ * @template TInput - The codec's input union.
5
+ * @template TOutput - The codec's output union.
6
+ * @param decoded - The decoder's input/output split for one inbound message.
7
+ * @returns The events as a direction-tagged {@link CodecEvent} list, inputs first.
8
+ */
9
+ export declare const toCodecEvents: <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent>(decoded: DecodedMessage<TInput, TOutput>) => CodecEvent<TInput, TOutput>[];
@@ -5,7 +5,10 @@ import { MessagePayload, StreamTrackerState } from './types.js';
5
5
  *
6
6
  * Handles the Ably message action patterns (create, append, update, delete)
7
7
  * and delegates to domain-specific hooks for event building and discrete
8
- * event decoding.
8
+ * event decoding. Stream trackers are version-guarded: a delivery whose
9
+ * `Message.version.serial` the tracker has already incorporated decodes to
10
+ * nothing, so the same decoder instance can serve both the live
11
+ * subscription and history hydration without double-decoding.
9
12
  *
10
13
  * Domain decoders call `createDecoderCore(hooks, options)` and provide hooks
11
14
  * for stream classification, event building, and discrete decoding. Hooks
@@ -0,0 +1,100 @@
1
+ import { InputBuilder, InputDescriptor } from './input-descriptors.js';
2
+ import { OutputBuilder, OutputDescriptor } from './output-descriptors.js';
3
+ import { Codec, CodecEvent, CodecInputEvent, CodecMessage, CodecOutputEvent, ReducerMeta, StreamTrackerState } from './types.js';
4
+ import { WellKnownInputFactories } from './well-known-inputs.js';
5
+ export type { InputBuilder } from './input-descriptors.js';
6
+ export type { OutputBuilder } from './output-descriptors.js';
7
+ /** Context passed to a {@link LifecyclePolicy} `onDiscrete` repair function. */
8
+ export interface LifecycleDiscreteContext {
9
+ /** The inbound codec-tier headers (e.g. to recover a stream's message id). */
10
+ codecHeaders: Record<string, string>;
11
+ }
12
+ /**
13
+ * Declarative decode-time lifecycle repair, applied when joining a stream
14
+ * mid-flight (history compaction, rewind miss, partial page). Each function
15
+ * performs its side effect on the codec's lifecycle tracker (captured by the
16
+ * factory that builds the policy) and RETURNS lead-in events to PREPEND; the
17
+ * generic decoder ALWAYS runs the descriptor driver after and appends its
18
+ * output, so the policy never replaces a decode. A codec with no repair
19
+ * supplies no policy.
20
+ * @template TOutput - The codec's output union.
21
+ */
22
+ export interface LifecyclePolicy<TOutput> {
23
+ /**
24
+ * Keyed on the discrete codec `kind`. Returns lead-in events to prepend
25
+ * (empty array = none) after applying any tracker side effect.
26
+ */
27
+ onDiscrete?: Record<string, (runId: string, ctx: LifecycleDiscreteContext) => TOutput[]>;
28
+ /** Lead-in prepended to a stream's start events (mid-stream-join pre-roll). */
29
+ onStreamStart?: (runId: string, tracker: StreamTrackerState) => TOutput[];
30
+ }
31
+ /**
32
+ * The reducer parts a codec supplies. `TProjection` and `TMessage` infer from
33
+ * these, so `defineCodec` callers need not spell them out.
34
+ * @template TInput - The codec's input union.
35
+ * @template TOutput - The codec's output union.
36
+ * @template TProjection - The per-node projection the reducer folds into.
37
+ * @template TMessage - The per-message domain type.
38
+ */
39
+ export interface CodecReducer<TInput, TOutput, TProjection, TMessage> {
40
+ /** Build an empty projection for a node. */
41
+ init: () => TProjection;
42
+ /** Fold one direction-tagged input or output event into the projection. */
43
+ fold: (state: TProjection, event: CodecEvent<TInput, TOutput>, meta: ReducerMeta) => TProjection;
44
+ /** Extract the per-message list (each paired with its codec-message-id). */
45
+ getMessages: (projection: TProjection) => CodecMessage<TMessage>[];
46
+ }
47
+ /**
48
+ * The parts a codec supplies to {@link defineCodec}.
49
+ * @template TInput - The codec's input union.
50
+ * @template TOutput - The codec's output union.
51
+ * @template TProjection - The per-node projection the reducer folds into.
52
+ * @template TMessage - The per-message domain type.
53
+ */
54
+ export interface DefineCodecConfig<TInput extends {
55
+ kind: string;
56
+ }, TOutput extends {
57
+ type: string;
58
+ }, TProjection, TMessage> {
59
+ /** Optional Ably-Agent identifier registered on the channel; omit to opt out. */
60
+ adapterTag?: string;
61
+ /** Reducer parts; `TProjection` / `TMessage` infer from here. */
62
+ reducer: CodecReducer<TInput, TOutput, TProjection, TMessage>;
63
+ /**
64
+ * The declarative output (`ai-output`) descriptor table, returned from the
65
+ * injected `{ event, stream }` builder (both curried on `TOutput`).
66
+ */
67
+ output: (b: OutputBuilder<TOutput>) => readonly OutputDescriptor<TOutput>[];
68
+ /**
69
+ * The declarative input (`ai-input`) descriptor table, returned from the
70
+ * injected `{ event, batch }` builder (both curried on `TInput`).
71
+ */
72
+ input: (b: InputBuilder<TInput>) => readonly InputDescriptor<TInput>[];
73
+ /**
74
+ * Factory for a fresh decode lifecycle policy per decoder instance (the
75
+ * policy's closures capture a fresh, per-decoder lifecycle tracker). Omit
76
+ * for a codec with no mid-stream-join repair.
77
+ */
78
+ decodeLifecycle?: () => LifecyclePolicy<TOutput>;
79
+ }
80
+ /**
81
+ * A codec assembled by {@link defineCodec}: a conforming {@link Codec} whose
82
+ * well-known input factories are typed concretely by {@link WellKnownInputFactories}
83
+ * (so `createToolResult` etc. are callable without a guard). The factory methods
84
+ * are sourced from `WellKnownInputFactories` rather than `Codec` because the
85
+ * former types them against `UserMessageOf<TInput>` / `ToolResultPayloadOf<TInput>`
86
+ * — equal to the codec's `TMessage` / payloads for every real codec, but not
87
+ * provably so to the generic type system. At a concrete call site a
88
+ * `DefinedCodec` is assignable to the corresponding `Codec`.
89
+ */
90
+ export type DefinedCodec<TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage> = Omit<Codec<TInput, TOutput, TProjection, TMessage>, keyof WellKnownInputFactories<TInput>> & WellKnownInputFactories<TInput>;
91
+ /**
92
+ * Assemble a fully-formed {@link Codec} from a codec's parts. Curried on the
93
+ * input/output unions so `TProjection` / `TMessage` infer from `config.reducer`
94
+ * — a caller writes `defineCodec<TInput, TOutput>()({ ... })` and never spells
95
+ * out the projection or message types.
96
+ * @template TInput - The codec's input union.
97
+ * @template TOutput - The codec's output union.
98
+ * @returns A function taking the codec's parts and returning the assembled codec.
99
+ */
100
+ export declare const defineCodec: <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent>() => <TProjection, TMessage>(config: DefineCodecConfig<TInput, TOutput, TProjection, TMessage>) => DefinedCodec<TInput, TOutput, TProjection, TMessage>;
@@ -26,7 +26,7 @@ export interface EncoderCore {
26
26
  startStream(streamId: string, payload: StreamPayload, opts?: WriteOptions): Promise<void>;
27
27
  /**
28
28
  * Append data to an in-flight streamed message. Fire-and-forget: errors are
29
- * collected internally and surfaced by {@link closeStream}, {@link cancelStream},
29
+ * collected internally and surfaced by {@link closeStream},
30
30
  * {@link cancelAllStreams} or {@link close}.
31
31
  * @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId` or the core is closed.
32
32
  */
@@ -37,11 +37,6 @@ export interface EncoderCore {
37
37
  * @throws {Ably.ErrorInfo} InvalidArgument if there is no active stream for `streamId`, or the encoder has been closed; EncoderRecoveryFailed if a failed append cannot be recovered during the flush.
38
38
  */
39
39
  closeStream(streamId: string, payload: StreamPayload): Promise<void>;
40
- /**
41
- * Cancel a single in-progress stream (status:cancelled) and flush all
42
- * pending appends for recovery before returning.
43
- */
44
- cancelStream(streamId: string, opts?: WriteOptions): Promise<void>;
45
40
  /**
46
41
  * Cancel all in-progress streams (status:cancelled) and flush all
47
42
  * pending appends for recovery before returning.
@@ -53,7 +48,7 @@ export interface EncoderCore {
53
48
  /**
54
49
  * Create an encoder core bound to the given channel writer.
55
50
  * @param writer - The channel writer to publish messages through.
56
- * @param options - Encoder configuration (clientId, extras, hooks, logger).
51
+ * @param options - Encoder configuration (extras, hooks, logger).
57
52
  * @returns A new {@link EncoderCore} instance.
58
53
  */
59
54
  export declare const createEncoderCore: (writer: ChannelWriter, options?: EncoderCoreOptions) => EncoderCore;
@@ -0,0 +1,85 @@
1
+ import { HeaderField } from './fields.js';
2
+ import { OutputDescriptor, OutputEventDescriptor } from './output-descriptors.js';
3
+ /** The codec header carrying the SDK-controlled dispatch kind / stream family id. */
4
+ export declare const KIND_HEADER = "kind";
5
+ /**
6
+ * Derive a wildcard dispatch predicate from a descriptor literal: a literal
7
+ * ending in `-*` matches any value sharing its prefix, so the literal and its
8
+ * predicate can never disagree. Returns `undefined` for an exact literal.
9
+ * Shared by the output event builder and the input part builder so the `-*`
10
+ * sentinel rule lives in one place, next to the {@link partFor} that consumes it.
11
+ * @param literal - The declared descriptor literal (`type` / `partType`).
12
+ * @returns A prefix-match predicate for a wildcard literal, else `undefined`.
13
+ */
14
+ export declare const wildcardMatcher: (literal: string) => ((value: string) => boolean) | undefined;
15
+ /**
16
+ * The codec header carrying a batch part's sub-discriminator. A batch stamps it
17
+ * on every exploded part on encode; the decoder reads it back to resolve the
18
+ * matching part descriptor. Centralised so the key has one home across the
19
+ * input encode and decode drivers and cannot drift between them.
20
+ */
21
+ export declare const PART_TYPE_HEADER = "partType";
22
+ /**
23
+ * Read the value at a declared field key off a source object.
24
+ * @param source - The object to index (a chunk, or a lensed sub-object such as a payload).
25
+ * @param key - The declared field key.
26
+ * @returns The value at `key`, typed `unknown`.
27
+ */
28
+ export declare const prop: (source: object, key: string) => unknown;
29
+ /**
30
+ * Build a codec-headers record from a source object through declared fields,
31
+ * seeded with the dispatch `kind`. Each field writes the value at its key on
32
+ * `source`; an optional `keys` subset restricts which fields are written.
33
+ * @param fields - The declared header fields.
34
+ * @param kindValue - The dispatch kind / stream family id to seed under {@link KIND_HEADER}.
35
+ * @param source - The object to read field values from (a chunk, or a lensed payload).
36
+ * @param keys - Optional subset of field keys to write; omit to write all.
37
+ * @returns The codec-headers record.
38
+ */
39
+ export declare const writeFields: (fields: readonly HeaderField<unknown>[], kindValue: string, source: object, keys?: readonly string[]) => Record<string, string>;
40
+ /**
41
+ * Read declared fields out of a codec-headers record into a bag keyed by field key.
42
+ * A field that reads `undefined` (absent, with no default) contributes no key — the
43
+ * bag carries only the values that are actually present.
44
+ * @param fields - The declared header fields.
45
+ * @param headers - The inbound codec-tier headers.
46
+ * @returns A bag of the present field values, keyed by each field's key.
47
+ */
48
+ /** The structural slice of a part descriptor {@link partFor} dispatches on. */
49
+ interface PartDispatch {
50
+ /** The exact `partType` literal, or the `-*` wildcard literal for a family. */
51
+ partType: string;
52
+ /** Wildcard dispatch predicate; absent for an exact part. */
53
+ match?: (partType: string) => boolean;
54
+ }
55
+ /**
56
+ * Resolve the part descriptor for a `partType`: an exact non-wildcard match,
57
+ * else a wildcard whose derived predicate accepts it. Wildcards are excluded
58
+ * from the exact pass — only their predicate may route to them. Shared by the
59
+ * input encode and decode drivers.
60
+ * @param parts - The batch's part descriptor sub-table.
61
+ * @param partType - The `partType` to resolve (from `partTypeOf` on encode, the wire header on decode).
62
+ * @returns The matching part descriptor, or undefined when none matches.
63
+ */
64
+ export declare const partFor: <P extends PartDispatch>(parts: readonly P[], partType: string) => P | undefined;
65
+ /** An output descriptor set's event descriptors, split for dispatch. */
66
+ export interface OutputEventDispatch<U> {
67
+ /** Exact (non-wildcard) event descriptors, keyed by `type`. */
68
+ discreteByType: Map<string, OutputEventDescriptor<U>>;
69
+ /** Wildcard event descriptors, dispatched by their `match` predicate. */
70
+ wildcards: OutputEventDescriptor<U>[];
71
+ }
72
+ /**
73
+ * Partition an output descriptor set's `event` descriptors into an exact-type
74
+ * map and a wildcard list. Stream descriptors are skipped — each driver indexes
75
+ * those by its own key (phase on encode, kind on decode). Shared by the output
76
+ * encode and decode drivers so the exact-vs-wildcard split has one home.
77
+ * @template U - The codec's event union.
78
+ * @param descriptors - The full descriptor set (events + streamed families).
79
+ * @returns The event descriptors split into {@link OutputEventDispatch}.
80
+ */
81
+ export declare const partitionOutputEvents: <U extends {
82
+ type: string;
83
+ }>(descriptors: readonly OutputDescriptor<U>[]) => OutputEventDispatch<U>;
84
+ export declare const readFields: (fields: readonly HeaderField<unknown>[], headers: Record<string, string>) => Record<string, unknown>;
85
+ export {};
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Typed header-field bindings.
3
+ *
4
+ * A {@link HeaderField} binds a codec header key to its value type **once**,
5
+ * and exposes a symmetric {@link HeaderField.read | read} / {@link
6
+ * HeaderField.write | write} pair over a raw `Record<string, string>` headers
7
+ * record. Because a single binding drives both the encode and decode side, the
8
+ * header key and its value type stay in lockstep across directions — a key
9
+ * cannot be misspelled on one side and silently read as absent on the other.
10
+ *
11
+ * This is deliberately **not** a schema library: it is a thin bidirectional
12
+ * string (de)serializer over the headers record. The SDK ships no hard runtime
13
+ * dependencies, and a schema approach would force re-declaring peer-SDK-owned
14
+ * types. The four constructors cover every header value shape the codecs use:
15
+ *
16
+ * - {@link strField} — string values, optional default.
17
+ * - {@link boolField} — `"true"`/`"false"` booleans, optional default.
18
+ * - {@link jsonField} — JSON-serialized structured values.
19
+ * - {@link enumField} — string values validated against an allow-list with a
20
+ * fallback (e.g. a finish reason).
21
+ *
22
+ * Passing a default to `strField`/`boolField` makes the field **total**: its
23
+ * `read` returns `V` rather than `V | undefined`, for required headers that
24
+ * should always decode to a concrete value.
25
+ */
26
+ /**
27
+ * A header key bound to its value type, with symmetric read/write over a raw
28
+ * headers record. Created via {@link strField}, {@link boolField}, {@link
29
+ * jsonField}, or {@link enumField}.
30
+ *
31
+ * The `key` plays a dual role in descriptor `fields` tables: it is the wire
32
+ * header key AND the property name the drivers read off the source object on
33
+ * encode and write back into the rebuilt object on decode. {@link FieldFor}
34
+ * enforces this — a declared field's key must name a real property of the
35
+ * member it lenses onto.
36
+ * @template V - The decoded value type this field reads and writes.
37
+ * @template K - The header key literal (preserved so {@link FieldFor} can match it against the member's property names).
38
+ */
39
+ export interface HeaderField<V, K extends string = string> {
40
+ /** The raw header key this field reads from and writes to — also the source/rebuilt property name in descriptor tables. */
41
+ readonly key: K;
42
+ /**
43
+ * Read and decode this field's value from a headers record.
44
+ * @param headers - The raw codec headers record to read from.
45
+ * @returns The decoded value. For defaulted/validated fields this is total
46
+ * (the default/fallback is returned when the header is absent or invalid);
47
+ * otherwise `undefined` when the header is absent.
48
+ */
49
+ read(headers: Record<string, string>): V;
50
+ /**
51
+ * Encode and write this field's value into a headers record, mutating it in
52
+ * place. `undefined` (and `null`, for JSON values), and values whose runtime
53
+ * type doesn't match the field, are skipped — the key is left unset rather
54
+ * than written. The parameter is `unknown` (not `V`) so a field keeps `V` in
55
+ * a single covariant position (`read`); this lets heterogeneous fields share a
56
+ * `HeaderField<unknown>[]` array, which the descriptor drivers rely on. At a
57
+ * typed call site the caller still passes a `V`.
58
+ * @param headers - The headers record to mutate.
59
+ * @param value - The value to encode and set.
60
+ */
61
+ write(headers: Record<string, string>, value: unknown): void;
62
+ }
63
+ /**
64
+ * Symmetric codec for a descriptor's wire `data`. Many wire payloads are object
65
+ * envelopes a decode reads several chunk props out of (e.g. `{ errorText, input }`),
66
+ * so a single field can't model them. `encode` produces the wire data from the
67
+ * chunk; `decode` returns the chunk props the envelope contributes, merged into
68
+ * the rebuilt chunk by the driver.
69
+ * @template C - The narrowed chunk member.
70
+ */
71
+ export interface DataCodec<C> {
72
+ /** Produce the wire `data` from the chunk. */
73
+ encode: (chunk: C) => unknown;
74
+ /**
75
+ * Extract the chunk props this envelope contributes from the wire `data`.
76
+ * Undefined-valued props are stripped when the driver rebuilds the object —
77
+ * every rebuild seam (output chunk, input payload, batch part) applies the
78
+ * same rule, since absent and undefined are indistinguishable on the wire.
79
+ */
80
+ decode: (data: unknown) => Partial<C>;
81
+ }
82
+ /**
83
+ * The header fields a descriptor may declare against member `C`. For each
84
+ * string-keyed property of `C`, a field is acceptable when its key IS that
85
+ * property name and its value type can hold the property. A mistyped key or a
86
+ * wrong-typed field (e.g. a `boolField` on a string property) is a compile
87
+ * error instead of a silently absent header.
88
+ * @template C - The member (chunk, payload, or part) the fields lens onto.
89
+ */
90
+ export type FieldFor<C> = {
91
+ [K in keyof C & string]-?: HeaderField<C[K] | undefined, K>;
92
+ }[keyof C & string];
93
+ /**
94
+ * Bind a string-valued header field.
95
+ * @param key - The header key (and source property name in descriptor tables).
96
+ * @returns A field whose `read` yields `string | undefined` (absent → `undefined`).
97
+ */
98
+ export declare function strField<K extends string>(key: K): HeaderField<string | undefined, K>;
99
+ /**
100
+ * Bind a string-valued header field with a default, making it total.
101
+ * @param key - The header key (and source property name in descriptor tables).
102
+ * @param fallback - Value returned by `read` when the header is absent.
103
+ * @returns A field whose `read` yields `string` (absent → `fallback`).
104
+ */
105
+ export declare function strField<K extends string>(key: K, fallback: string): HeaderField<string, K>;
106
+ /**
107
+ * Bind a boolean-valued header field, serialized as `"true"`/`"false"`.
108
+ * @param key - The header key (and source property name in descriptor tables).
109
+ * @returns A field whose `read` yields `boolean | undefined` (absent → `undefined`).
110
+ */
111
+ export declare function boolField<K extends string>(key: K): HeaderField<boolean | undefined, K>;
112
+ /**
113
+ * Bind a boolean-valued header field with a default, making it total.
114
+ * @param key - The header key (and source property name in descriptor tables).
115
+ * @param fallback - Value returned by `read` when the header is absent.
116
+ * @returns A field whose `read` yields `boolean` (absent → `fallback`).
117
+ */
118
+ export declare function boolField<K extends string>(key: K, fallback: boolean): HeaderField<boolean, K>;
119
+ /**
120
+ * Bind a JSON-serialized header field. The value is written with
121
+ * `JSON.stringify` and read back with `JSON.parse`; malformed JSON reads as
122
+ * `undefined`. The decoded shape is a trust boundary — the caller asserts it
123
+ * via the `V` type parameter.
124
+ * @template V - The expected decoded shape of the JSON value.
125
+ * @template K - The header key literal. Inferred when `V` is omitted; pass it explicitly alongside `V` when the field participates in a typed descriptor `fields` table.
126
+ * @param key - The header key (and source property name in descriptor tables).
127
+ * @returns A field whose `read` yields `V | undefined` (absent or malformed → `undefined`).
128
+ */
129
+ export declare const jsonField: <V, K extends string = string>(key: K) => HeaderField<V | undefined, K>;
130
+ /**
131
+ * Bind a string-valued header field validated against a fixed allow-list,
132
+ * falling back to a given value when the header is absent or unrecognized. Use
133
+ * for headers with a small closed set of valid values (e.g. a finish reason).
134
+ * @template T - The union of allowed string literals, inferred from `allowed`.
135
+ * @template K - The header key literal, inferred from `key`.
136
+ * @param key - The header key (and source property name in descriptor tables).
137
+ * @param allowed - The exhaustive list of valid values.
138
+ * @param fallback - Value returned by `read` when the header is absent or not in `allowed`.
139
+ * @returns A total field whose `read` yields one of the allowed literals.
140
+ */
141
+ export declare const enumField: <const T extends string, K extends string>(key: K, allowed: readonly T[], fallback: NoInfer<T>) => HeaderField<T, K>;
@@ -1,7 +1,14 @@
1
- export type { ChannelWriter, Codec, CodecInputEvent, CodecMessage, CodecOutputEvent, DecodedMessage, Decoder, Encoder, EncoderOptions, Extras, MessagePayload, Reducer, ReducerMeta, Regenerate, StreamPayload, StreamTrackerState, ToolApprovalResponse, ToolResult, ToolResultError, UserMessage, WriteOptions, } from './types.js';
1
+ export type { ChannelWriter, Codec, CodecEvent, CodecInputEvent, CodecMessage, CodecOutputEvent, DecodedMessage, Decoder, Encoder, EncoderOptions, Extras, MessagePayload, Reducer, ReducerMeta, Regenerate, StreamPayload, StreamTrackerState, ToolApprovalResponse, ToolResult, ToolResultError, UserMessage, WriteOptions, } from './types.js';
2
2
  export type { EncoderCore, EncoderCoreOptions } from './encoder.js';
3
3
  export { createEncoderCore } from './encoder.js';
4
4
  export type { DecoderCore, DecoderCoreHooks, DecoderCoreOptions } from './decoder.js';
5
5
  export { createDecoderCore } from './decoder.js';
6
6
  export type { LifecycleTracker, PhaseConfig } from './lifecycle-tracker.js';
7
7
  export { createLifecycleTracker } from './lifecycle-tracker.js';
8
+ export type { DataCodec, FieldFor, HeaderField } from './fields.js';
9
+ export { boolField, enumField, jsonField, strField } from './fields.js';
10
+ export type { WellKnownInputFactories } from './well-known-inputs.js';
11
+ export type { EscapeHatchCore, HeaderBuilder, OutputDecodeContext, OutputDescriptor, OutputEncodeHatchContext, OutputEventSpec, OutputStreamEndContext, OutputStreamSpec, } from './output-descriptors.js';
12
+ export type { BatchAssembleContext, BatchMessageHeaders, BatchSpec, InputDescriptor, InputEventSpec, PartBuilder, PartSpec, } from './input-descriptors.js';
13
+ export type { CodecReducer, DefineCodecConfig, DefinedCodec, InputBuilder, LifecycleDiscreteContext, LifecyclePolicy, OutputBuilder, } from './define-codec.js';
14
+ export { defineCodec } from './define-codec.js';
@@ -0,0 +1,19 @@
1
+ import { InputDecodeContext, InputDescriptor } from './input-descriptors.js';
2
+ /** Decodes inbound `ai-input` messages of union `U` from an input descriptor set. */
3
+ export interface InputDescriptorDecoder<U> {
4
+ /**
5
+ * Rebuild zero or more inputs from one inbound `ai-input` message.
6
+ * @param ctx - The inbound message context (codec kind, data, header tiers).
7
+ * @returns The decoded inputs (empty when no descriptor matches or the input is wire-only).
8
+ */
9
+ decode(ctx: InputDecodeContext): U[];
10
+ }
11
+ /**
12
+ * Build an input decode driver for an input descriptor set.
13
+ * @template U - The codec's input union.
14
+ * @param descriptors - The input descriptor set (events + batches).
15
+ * @returns An {@link InputDescriptorDecoder} that reconstructs inputs from the wire.
16
+ */
17
+ export declare const createInputDescriptorDecoder: <U extends {
18
+ kind: string;
19
+ }>(descriptors: readonly InputDescriptor<U>[]) => InputDescriptorDecoder<U>;
@@ -0,0 +1,22 @@
1
+ import { InputDescriptor, InputEncodeContext, InputEncoderCore } from './input-descriptors.js';
2
+ /** Encodes inputs of union `U` to channel operations via an input descriptor set. */
3
+ export interface InputDescriptorEncoder<U> {
4
+ /**
5
+ * Encode one input through its descriptor.
6
+ * @param input - The input to encode.
7
+ * @param core - The input encoder core to publish through.
8
+ * @param ctx - Per-write context (write options, carrying the codec-message-id).
9
+ * @returns A promise resolving when the publish operation completes.
10
+ */
11
+ encode(input: U, core: InputEncoderCore, ctx: InputEncodeContext): Promise<void>;
12
+ }
13
+ /**
14
+ * Build an input encode driver for an input descriptor set bound to a wire name.
15
+ * @template U - The codec's input union.
16
+ * @param descriptors - The input descriptor set (events + batches).
17
+ * @param wireName - The Ably message name for the input direction (`ai-input`).
18
+ * @returns An {@link InputDescriptorEncoder} routing each input through its descriptor.
19
+ */
20
+ export declare const createInputDescriptorEncoder: <U extends {
21
+ kind: string;
22
+ }>(descriptors: readonly InputDescriptor<U>[], wireName: string) => InputDescriptorEncoder<U>;