@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
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Invocation — value object wrapping the JSON body that a client sends to
3
+ * an agent's HTTP endpoint to start a run.
4
+ *
5
+ * The data shape is the wire contract; the {@link Invocation} class is a
6
+ * runtime view of that data with the same fields. {@link Invocation.fromJSON}
7
+ * is the entry point used by agent handlers:
8
+ *
9
+ * ```ts
10
+ * const data = (await req.json()) as InvocationData;
11
+ * const invocation = Invocation.fromJSON(data);
12
+ * const run = session.createRun(invocation, { signal: req.signal });
13
+ * await run.start();
14
+ * await run.loadConversation(); // walk channel history into the session tree
15
+ * const messages = run.messages;
16
+ * ```
17
+ *
18
+ * The body carries only what the agent needs out-of-band before the channel
19
+ * is observable: the session/channel name and the `inputEventId` that triggered
20
+ * the invocation. The agent mints the `invocationId` itself (one per HTTP
21
+ * request) and returns it on the HTTP response, so it is not a body field. Run
22
+ * identity also lives on the channel: the agent mints the `runId` for a fresh
23
+ * run and reads the existing `runId` off the triggering input event for a
24
+ * continuation — so the body carries no run-id either. Per-message metadata —
25
+ * `clientId`, `parent`, `forkOf`, continuation status — likewise lives on the
26
+ * channel and is resolved by the agent from the triggering input event, not
27
+ * from the body. The `inputClientId` the agent re-stamps on its own publishes
28
+ * comes from the publisher's Ably `clientId` on the matched input event, not
29
+ * from a body field.
30
+ */
31
+ /**
32
+ * Wire shape of a single invocation — the JSON body sent from the client
33
+ * transport's HTTP POST to the agent endpoint.
34
+ */
35
+ export interface InvocationData {
36
+ /**
37
+ * Identifier for the specific input event on the channel that triggered
38
+ * this invocation. The agent locates the event via the `event-id`
39
+ * header. Its wire headers carry the run-id for a continuation (absent for
40
+ * a fresh run), so run identity is resolved from the channel, not the body.
41
+ */
42
+ inputEventId: string;
43
+ /** Logical name of the session (chat) — used as the Ably channel name. */
44
+ sessionName: string;
45
+ }
46
+ /**
47
+ * Runtime view of an {@link InvocationData}. Constructed via
48
+ * {@link Invocation.fromJSON}. Read-only; carries no behaviour beyond
49
+ * exposing its fields.
50
+ */
51
+ export declare class Invocation {
52
+ /**
53
+ * Identifier for the specific input event on the channel that triggered
54
+ * this invocation. Run identity is resolved from that event's wire headers
55
+ * (or minted), not from the body.
56
+ */
57
+ readonly inputEventId: string;
58
+ /** Logical name of the session (chat). Used as the Ably channel name. */
59
+ readonly sessionName: string;
60
+ private constructor();
61
+ /**
62
+ * Build an Invocation from its JSON wire shape.
63
+ * @param data - Parsed JSON body matching {@link InvocationData}.
64
+ * @returns A new Invocation exposing the same fields.
65
+ */
66
+ static fromJSON(data: InvocationData): Invocation;
67
+ /**
68
+ * Serialise this invocation to its JSON wire shape — the body a client
69
+ * POSTs to the agent's endpoint to wake a run. Round-trips through
70
+ * {@link Invocation.fromJSON}.
71
+ * @returns The {@link InvocationData} carrying this invocation's identity.
72
+ */
73
+ toJSON(): InvocationData;
74
+ }
@@ -0,0 +1,71 @@
1
+ import { Logger } from '../../logger.js';
2
+ /**
3
+ * loadHistoryPages — shared low-level history pagination primitive.
4
+ *
5
+ * Consumed by both client (via `load-history.ts`, which layers a complete-
6
+ * domain-message counter on top) and agent (directly, for input-event lookup
7
+ * and conversation hydration). Returns raw Ably messages; does NOT decode.
8
+ *
9
+ * Behaviour:
10
+ * - Attaches the channel (idempotent) then pages via `channel.history()`,
11
+ * using `untilAttach: true` for gapless continuity with any live subscription.
12
+ * - Exposes the underlying pagination as a cursor with `hasNext()` (cheap,
13
+ * no network) and `next()` (one Ably page per call, newest-first within
14
+ * the page).
15
+ * - Per-page failures are retried with bounded exponential backoff; on
16
+ * exhaustion throws `Ably.ErrorInfo` with code `HistoryFetchFailed`.
17
+ * - `signal.aborted` is checked between pages; rejects with
18
+ * `Ably.ErrorInfo` (InvalidArgument) when aborted.
19
+ *
20
+ * Spec: AIT-CT11 / AIT-ST hydration.
21
+ */
22
+ import * as Ably from 'ably';
23
+ /** Options for {@link loadHistoryPages}. */
24
+ export interface LoadHistoryPagesOptions {
25
+ /** Wire-message limit per Ably page. */
26
+ pageLimit: number;
27
+ /** Set `untilAttach: true` on the underlying history query for gapless continuity with live subscriptions. Default: true. */
28
+ untilAttach?: boolean;
29
+ /** AbortSignal checked between pages. Rejects with InvalidArgument when aborted. */
30
+ signal?: AbortSignal;
31
+ /** Max retries per `page.next()` / initial `history()` failure. Default: 3. */
32
+ maxRetries?: number;
33
+ /** Initial retry backoff in ms (doubled per attempt). Default: 100. */
34
+ retryBackoffMs?: number;
35
+ /** Logger for diagnostic output. */
36
+ logger?: Logger;
37
+ }
38
+ /**
39
+ * Cursor over the channel's history pages.
40
+ *
41
+ * `hasNext()` is cheap (cursor-only, no network); `next()` issues one Ably
42
+ * page fetch (with retry/backoff) and returns its messages. Once `next()`
43
+ * returns `undefined` the cursor is exhausted.
44
+ */
45
+ export interface HistoryPagesCursor {
46
+ /** True when another Ably page is available (cheap to check; no network). */
47
+ hasNext(): boolean;
48
+ /**
49
+ * Fetch the next Ably page's messages (newest-first within the page).
50
+ * Returns `undefined` when no more pages are available or the abort
51
+ * signal has fired.
52
+ */
53
+ next(): Promise<readonly Ably.InboundMessage[] | undefined>;
54
+ }
55
+ /**
56
+ * Page through channel history, returning a cursor over Ably pages.
57
+ *
58
+ * Newest-first within each yielded page (matching Ably's native ordering).
59
+ * Caller drives the cursor — calling `next()` until it returns `undefined`
60
+ * or stopping early when a domain-specific stop condition is met
61
+ * (e.g. complete-message counter satisfied, target codec-message-id found,
62
+ * parent chain walk reaches root).
63
+ *
64
+ * The initial Ably history call is awaited eagerly so the returned cursor
65
+ * already knows whether there are pages available (via `hasNext()`).
66
+ * @param channel - The Ably channel to read history from.
67
+ * @param options - Pagination options.
68
+ * @returns A cursor with `hasNext()` (cheap, cursor-only) and `next()` (fetches one page with retry).
69
+ * @throws {Ably.ErrorInfo} `HistoryFetchFailed` on exhausted retry of the initial fetch, or `InvalidArgument` on signal abort.
70
+ */
71
+ export declare const loadHistoryPages: (channel: Ably.RealtimeChannel, options: LoadHistoryPagesOptions) => Promise<HistoryPagesCursor>;
@@ -0,0 +1,44 @@
1
+ import { Logger } from '../../logger.js';
2
+ import { HistoryPage, LoadHistoryOptions } from './types.js';
3
+ /**
4
+ * loadHistory — load conversation history from an Ably channel and return
5
+ * the raw Ably messages as a paginated HistoryPage result.
6
+ *
7
+ * This does NOT decode: it pages back through Ably history via the shared
8
+ * {@link loadHistoryPages} primitive until `limit` complete messages are
9
+ * present, then hands the raw Ably messages (oldest-first) to the caller.
10
+ * The View re-decodes them into the Tree itself, so load-history only needs
11
+ * a cheap, header-based completion counter to decide when to stop paging.
12
+ *
13
+ * The `limit` option controls the number of complete **messages** per page,
14
+ * not the number of Ably messages fetched. A message is complete when
15
+ * its terminal wire signal — `status: "complete"` / `"cancelled"`, or a
16
+ * `discrete` create — has been seen. Runs that span a page boundary are
17
+ * handled by the counter requiring both a start and a terminal signal
18
+ * before counting a message complete.
19
+ *
20
+ * Because Ably history returns newest-first, each page's `rawMessages` are
21
+ * reversed to chronological (oldest-first) so the caller can fold them in
22
+ * order.
23
+ *
24
+ * Spec: AIT-CT11, AIT-CT11b.
25
+ */
26
+ import type * as Ably from 'ably';
27
+ /**
28
+ * Load conversation history from a channel and return the raw Ably messages.
29
+ *
30
+ * Drives the shared {@link loadHistoryPages} primitive with
31
+ * `untilAttach: true` (gapless continuity with the live subscription) and a
32
+ * Ably message page limit that overprovisions for the many-Ably-messages-per-domain-message
33
+ * ratio. Each `HistoryPage` returned covers up to `limit` complete domain
34
+ * messages; subsequent pages drain over-collected messages from the buffer
35
+ * before pulling more from the iterator.
36
+ *
37
+ * The `limit` option controls the number of complete messages returned per
38
+ * page, not the number of Ably messages fetched.
39
+ * @param channel - The Ably channel to load history from.
40
+ * @param options - Pagination options.
41
+ * @param logger - Logger for diagnostic output.
42
+ * @returns The first page of raw history messages.
43
+ */
44
+ export declare const loadHistory: (channel: Ably.RealtimeChannel, options: LoadHistoryOptions | undefined, logger: Logger) => Promise<HistoryPage>;
@@ -1,17 +1,17 @@
1
1
  import { Logger } from '../../logger.js';
2
- import { StreamEncoder, WriteOptions } from '../codec/types.js';
2
+ import { CodecInputEvent, CodecOutputEvent, Encoder, WriteOptions } from '../codec/types.js';
3
3
  import { StreamResult } from './types.js';
4
4
  /**
5
- * Pipe an event stream through an encoder to the channel.
5
+ * Pipe an output stream through an encoder to the channel.
6
6
  *
7
7
  * Returns when the stream completes, is cancelled (via signal), or errors.
8
8
  * The `reason` field of the result indicates which case occurred.
9
- * @param stream - The event stream to read from.
10
- * @param encoder - The streaming encoder to write events through.
11
- * @param signal - Abort signal to monitor for cancellation.
12
- * @param onAbort - Optional callback invoked when the stream is cancelled, before the stream ends.
13
- * @param resolveWriteOptions - Optional per-event hook returning {@link WriteOptions} overrides to pass to `encoder.appendEvent`.
9
+ * @param stream - The output stream to read from.
10
+ * @param encoder - The encoder to publish outputs through.
11
+ * @param signal - AbortSignal to monitor for cancellation.
12
+ * @param onCancelled - Optional callback invoked when the stream is cancelled, before the stream ends.
13
+ * @param resolveWriteOptions - Optional per-output hook returning {@link WriteOptions} overrides to pass to `encoder.publishOutput`.
14
14
  * @param logger - Optional logger for diagnostic output.
15
- * @returns The reason the pipe ended.
15
+ * @returns A {@link StreamResult}: `reason` is why the pipe ended, and `error` holds the caught error when `reason` is `'error'`.
16
16
  */
17
- export declare const pipeStream: <TEvent, TMessage>(stream: ReadableStream<TEvent>, encoder: StreamEncoder<TEvent, TMessage>, signal: AbortSignal | undefined, onAbort?: (write: (event: TEvent) => Promise<void>) => void | Promise<void>, resolveWriteOptions?: (event: TEvent) => WriteOptions | undefined, logger?: Logger) => Promise<StreamResult>;
17
+ export declare const pipeStream: <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent>(stream: ReadableStream<TOutput>, encoder: Encoder<TInput, TOutput>, signal: AbortSignal | undefined, onCancelled?: (write: (output: TOutput) => Promise<void>) => void | Promise<void>, resolveWriteOptions?: (output: TOutput) => WriteOptions | undefined, logger?: Logger) => Promise<StreamResult>;
@@ -0,0 +1,76 @@
1
+ import { Logger } from '../../logger.js';
2
+ import { RunEndReason } from './types.js';
3
+ /**
4
+ * Server-side run state management and lifecycle event publishing.
5
+ *
6
+ * Owns the authoritative run lifecycle. Tracks active runs with their
7
+ * AbortControllers and clientIds. Publishes run-start, run-resume, run-suspend, and
8
+ * run-end events on the Ably channel so all clients can react to run
9
+ * state changes.
10
+ */
11
+ import type * as Ably from 'ably';
12
+ /**
13
+ * Per-invocation metadata carried on a run's opening lifecycle event. A
14
+ * continuation (re-entering an existing run) sets `continuation` and omits the
15
+ * structural `parent` / `forkOf` / `regenerates` fields.
16
+ */
17
+ interface StartRunMetadata {
18
+ /** Structural parent codec-message-id (fresh run-start only). */
19
+ parent?: string;
20
+ /** Forked user-prompt codec-message-id for an edit (fresh run-start only). */
21
+ forkOf?: string;
22
+ /** Regenerated assistant codec-message-id (fresh run-start only). */
23
+ regenerates?: string;
24
+ /** Agent-minted invocation id, carried on the lifecycle event. */
25
+ invocationId?: string;
26
+ /** ClientId of the triggering input event. */
27
+ inputClientId?: string;
28
+ /** Codec-message-id of the triggering input event. */
29
+ inputCodecMessageId?: string;
30
+ /** When true, publish `ai-run-resume` (re-entry) instead of `ai-run-start`. */
31
+ continuation?: boolean;
32
+ }
33
+ /** Manages active runs and publishes run lifecycle events on the channel. */
34
+ export interface RunManager {
35
+ /**
36
+ * Register a run and publish its opening lifecycle event. Publishes
37
+ * `ai-run-start` for a fresh run, or `ai-run-resume` when `metadata.continuation`
38
+ * is set (a subsequent invocation re-entering an existing run). A resume omits
39
+ * the structural `parent` / `forkOf` / `regenerates` headers — the original
40
+ * run-start owns the run's structure.
41
+ */
42
+ startRun(runId: string, clientId?: string, controller?: AbortController, metadata?: StartRunMetadata): Promise<void>;
43
+ /**
44
+ * Suspend a run. Publishes run-suspend on the channel and drops the run's
45
+ * active-run entry — the agent process terminates on suspend, so there is no
46
+ * live AbortController to retain. A cancel arriving during suspension is a
47
+ * no-op; the resuming invocation re-registers the run via {@link startRun}.
48
+ * Carries the same per-invocation attribution as {@link endRun}
49
+ * (`inputClientId`, `inputCodecMessageId`), since a suspend is the terminal
50
+ * event of the suspending invocation just as run-end is of an ending one.
51
+ */
52
+ suspendRun(runId: string, invocationId?: string, inputClientId?: string, inputCodecMessageId?: string): Promise<void>;
53
+ /**
54
+ * End a run. Publishes run-end on the channel (stamping `reason` as the
55
+ * run-reason header) and drops the run's active-run entry. Carries the same
56
+ * per-invocation attribution as {@link suspendRun} (`invocationId`,
57
+ * `inputClientId`, `inputCodecMessageId`), since run-end is the terminal event
58
+ * of the ending invocation. When `reason` is `'error'` and an `error` is
59
+ * supplied, its `code` and `message` are additionally stamped as the
60
+ * `error-code` / `error-message` headers — a codec-agnostic baseline failure
61
+ * detail for consumers; omitting `error` publishes a bare `reason: 'error'`.
62
+ */
63
+ endRun(runId: string, reason: RunEndReason, invocationId?: string, inputClientId?: string, inputCodecMessageId?: string, error?: Ably.ErrorInfo): Promise<void>;
64
+ /** Get the clientId that owns a run. */
65
+ getClientId(runId: string): string | undefined;
66
+ /** Cancel all active runs and clear state. */
67
+ close(): void;
68
+ }
69
+ /**
70
+ * Create a run manager bound to the given channel.
71
+ * @param channel - The Ably channel to publish lifecycle events on.
72
+ * @param logger - Optional logger for diagnostic output.
73
+ * @returns A new {@link RunManager} instance.
74
+ */
75
+ export declare const createRunManager: (channel: Ably.RealtimeChannel, logger?: Logger) => RunManager;
76
+ export {};
@@ -0,0 +1,55 @@
1
+ import { Logger } from '../../logger.js';
2
+ /**
3
+ * Shared lifecycle plumbing for the client and agent sessions.
4
+ *
5
+ * Both `DefaultClientSession` and `DefaultAgentSession` gate their writes on
6
+ * `connect()` having run, detach their channel best-effort on close, and react
7
+ * to channel continuity loss with the same detection rule and error shape.
8
+ * These helpers own that common machinery so the two sessions cannot drift on
9
+ * the connection guard, the detach-swallow behaviour, or — most importantly —
10
+ * the continuity-loss predicate, which encodes channel protocol semantics
11
+ * (Spec AIT-CT19 / AIT-ST12). Each session keeps its own divergent reaction to
12
+ * continuity loss (the client emits; the agent aborts runs and swaps its Tree).
13
+ */
14
+ import * as Ably from 'ably';
15
+ /**
16
+ * Resolve a session's connect guard: return the in-flight/settled connect
17
+ * promise, or reject with `InvalidArgument` when `connect()` has not been
18
+ * called. Callers `await` the result before any write.
19
+ * @param connectPromise - The session's connect promise, or `undefined` when not yet connected.
20
+ * @param method - The method name being guarded, for the error message.
21
+ * @returns The connect promise.
22
+ * @throws {Ably.ErrorInfo} `InvalidArgument` when `connectPromise` is `undefined`.
23
+ */
24
+ export declare const requireConnected: (connectPromise: Promise<void> | undefined, method: string) => Promise<void>;
25
+ /**
26
+ * Detach the session's channel on close, best-effort. `connect()` subscribes
27
+ * (which implicitly attaches), so a detach is only attempted when `connect()`
28
+ * ran. A detach failure (e.g. the channel is already FAILED) must not throw out
29
+ * of `close()`, so it is swallowed and logged at debug.
30
+ * @param channel - The session's channel.
31
+ * @param connectPromise - The session's connect promise; detach is skipped when `undefined`.
32
+ * @param logger - Logger for the swallowed-failure debug line, or `undefined`.
33
+ * @param component - The owning class name, used as the log message prefix.
34
+ */
35
+ export declare const bestEffortDetach: (channel: Ably.RealtimeChannel, connectPromise: Promise<void> | undefined, logger: Logger | undefined, component: string) => Promise<void>;
36
+ /**
37
+ * Whether a channel state change breaks message continuity:
38
+ * - FAILED, SUSPENDED, DETACHED — no more messages expected (or a gap)
39
+ * - ATTACHED with `resumed: false` (an UPDATE) — messages were lost
40
+ *
41
+ * The initial attach (ATTACHED with no prior attach) is the caller's concern
42
+ * and is not handled here.
43
+ * @param stateChange - The channel state change to classify.
44
+ * @returns True when continuity was lost.
45
+ */
46
+ export declare const isContinuityLost: (stateChange: Ably.ChannelStateChange) => boolean;
47
+ /**
48
+ * Build the `ChannelContinuityLost` error for a continuity-breaking state
49
+ * change, attaching the state change's `reason` as `cause`.
50
+ * @param stateChange - The continuity-breaking state change.
51
+ * @param verb - The operation that can no longer proceed, for the
52
+ * `unable to <verb>; ...` message (e.g. "deliver events", "continue").
53
+ * @returns The continuity-loss error.
54
+ */
55
+ export declare const continuityLostError: (stateChange: Ably.ChannelStateChange, verb: string) => Ably.ErrorInfo;