@runtypelabs/persona 4.4.2 → 4.6.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 (46) hide show
  1. package/dist/animations/glyph-cycle.d.cts +1 -1
  2. package/dist/animations/glyph-cycle.d.ts +1 -1
  3. package/dist/animations/{types-C6tFDxKy.d.cts → types-8RICZWQe.d.cts} +6 -0
  4. package/dist/animations/{types-C6tFDxKy.d.ts → types-8RICZWQe.d.ts} +6 -0
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/chunk-DFBSCFYN.js +1 -0
  8. package/dist/codegen.cjs +1 -1
  9. package/dist/codegen.js +1 -1
  10. package/dist/index.cjs +50 -50
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +286 -11
  13. package/dist/index.d.ts +286 -11
  14. package/dist/index.global.js +39 -39
  15. package/dist/index.global.js.map +1 -1
  16. package/dist/index.js +51 -51
  17. package/dist/index.js.map +1 -1
  18. package/dist/launcher.global.js.map +1 -1
  19. package/dist/markdown-parsers-entry-NVFT3TE6.js +1 -0
  20. package/dist/runtype-tts-entry-HFUV2UF7.js +1 -0
  21. package/dist/session-reconnect-U77QFUR7.js +1 -0
  22. package/dist/smart-dom-reader.d.cts +147 -8
  23. package/dist/smart-dom-reader.d.ts +147 -8
  24. package/dist/theme-editor-preview.cjs +48 -48
  25. package/dist/theme-editor-preview.d.cts +187 -9
  26. package/dist/theme-editor-preview.d.ts +187 -9
  27. package/dist/theme-editor-preview.js +48 -48
  28. package/dist/theme-editor.cjs +1 -1
  29. package/dist/theme-editor.d.cts +147 -8
  30. package/dist/theme-editor.d.ts +147 -8
  31. package/dist/theme-editor.js +1 -1
  32. package/package.json +2 -2
  33. package/src/client.ts +48 -7
  34. package/src/defaults.ts +9 -1
  35. package/src/generated/runtype-openapi-contract.ts +6 -0
  36. package/src/index-core.ts +5 -0
  37. package/src/reconnect-wake.test.ts +162 -0
  38. package/src/reconnect.test.ts +430 -0
  39. package/src/session-reconnect.ts +282 -0
  40. package/src/session.ts +408 -5
  41. package/src/types.ts +165 -9
  42. package/src/ui.scroll-additive.test.ts +451 -0
  43. package/src/ui.scroll.test.ts +8 -2
  44. package/src/ui.stream-animation-update.test.ts +99 -0
  45. package/src/ui.ts +371 -41
  46. package/src/utils/constants.ts +3 -1
@@ -1053,12 +1053,15 @@ type RuntypeExecutionStreamEvent = ({
1053
1053
  seq: number;
1054
1054
  type: "approval_complete";
1055
1055
  }) | ({
1056
+ awaitReason?: string;
1056
1057
  awaitedAt?: string;
1058
+ crawlId?: string;
1057
1059
  executionId: string;
1058
1060
  origin?: "webmcp" | "sdk";
1059
1061
  pageOrigin?: string;
1060
1062
  parameters?: Record<string, unknown>;
1061
1063
  seq: number;
1064
+ stepId?: string;
1062
1065
  toolCallId?: string;
1063
1066
  toolId?: string;
1064
1067
  toolName?: string;
@@ -1147,13 +1150,16 @@ type RuntypeFlowSSEEvent = {
1147
1150
  type: "flow_error";
1148
1151
  upgradeUrl?: string;
1149
1152
  }) | ({
1153
+ awaitReason?: string;
1150
1154
  awaitedAt: string;
1155
+ crawlId?: string;
1151
1156
  executionId?: string;
1152
1157
  flowId: string;
1153
1158
  origin?: "webmcp" | "sdk";
1154
1159
  pageOrigin?: string;
1155
1160
  parameters?: Record<string, unknown>;
1156
1161
  seq?: number;
1162
+ stepId?: string;
1157
1163
  toolCallId?: string;
1158
1164
  toolId?: string;
1159
1165
  toolName?: string;
@@ -1693,6 +1699,27 @@ type AgentWidgetWebMcpConfig = {
1693
1699
  */
1694
1700
  onConfirm?: WebMcpConfirmHandler;
1695
1701
  };
1702
+ /**
1703
+ * The coordinates needed to resume a durable agent turn after the SSE
1704
+ * connection drops (any resumable, server-persisted execution, e.g. Claude
1705
+ * Managed agents or async/background runs). Held on the session while a
1706
+ * resumable stream is in flight
1707
+ * and surfaced to the host via {@link AgentWidgetConfig.onExecutionState} so it
1708
+ * can persist `{ executionId, lastEventId }` next to its own `conversationId`
1709
+ * for the tab-reload path.
1710
+ *
1711
+ * `conversationId` is intentionally absent: it is host-owned (it lives in the
1712
+ * host's `reconnectStream`/`customFetch` closure; the widget never sees it).
1713
+ */
1714
+ type ResumableHandle = {
1715
+ /** The durable turn to reconnect to (from `agentMetadata.executionId`). */
1716
+ executionId: string;
1717
+ /** Highest SSE `id:` seq applied: the `?after=` reconnect cursor. */
1718
+ lastEventId: string;
1719
+ /** The open assistant message being streamed into, kept open on resume. */
1720
+ assistantMessageId: string;
1721
+ status: 'running';
1722
+ };
1696
1723
  /**
1697
1724
  * Metadata attached to messages created during agent execution.
1698
1725
  */
@@ -2059,25 +2086,74 @@ type AgentWidgetArtifactsFeature = {
2059
2086
  /**
2060
2087
  * How the transcript scrolls while an assistant response streams in.
2061
2088
  *
2062
- * - `"follow"` (default): keep the newest content pinned to the bottom of the
2063
- * viewport, pausing when the user scrolls up and resuming when they return
2064
- * to the bottom.
2065
- * - `"anchor-top"`: on send, scroll the user's message near the top of the
2066
- * viewport and hold it there while the response streams in beneath it
2067
- * (ChatGPT-style). The transcript never auto-scrolls during streaming.
2089
+ * - `"anchor-top"` (default): on send, scroll the user's message near the top
2090
+ * of the viewport and hold it there while the response streams in beneath it
2091
+ * (ChatGPT-style). When a turn has no user send to anchor to (a proactive
2092
+ * greeting, an injected assistant message, a resubmit, or first-load
2093
+ * streaming), it falls back to `"follow"` for that turn so content never
2094
+ * streams in off-screen.
2095
+ * - `"follow"`: keep the newest content pinned to the bottom of the viewport,
2096
+ * pausing when the user scrolls up and resuming when they return to the
2097
+ * bottom. This was the default before 4.x.
2068
2098
  * - `"none"`: never auto-scroll; the scroll-to-bottom affordance is the only
2069
2099
  * way back to the latest content.
2070
2100
  */
2071
2101
  type AgentWidgetScrollMode = "follow" | "anchor-top" | "none";
2102
+ /**
2103
+ * Where the transcript lands when a *saved* conversation is reopened (restored
2104
+ * from the storage adapter / `initialMessages`), as opposed to a fresh send.
2105
+ *
2106
+ * - `"bottom"` (default): jump to the absolute end of the transcript, matching
2107
+ * the historical behavior.
2108
+ * - `"last-user-turn"`: pin the last user message near the top of the viewport
2109
+ * (the last point the reader was actively driving the conversation) so a long
2110
+ * restored thread opens at a readable place rather than at the very bottom.
2111
+ */
2112
+ type AgentWidgetScrollRestorePosition = "bottom" | "last-user-turn";
2072
2113
  type AgentWidgetScrollBehaviorFeature = {
2073
- /** Scroll behavior during streamed responses. @default "follow" */
2114
+ /** Scroll behavior during streamed responses. @default "anchor-top" */
2074
2115
  mode?: AgentWidgetScrollMode;
2075
2116
  /**
2076
2117
  * Gap (px) kept between the anchored user message and the top of the
2077
- * viewport in `"anchor-top"` mode.
2118
+ * viewport in `"anchor-top"` mode. Also used as the gap for
2119
+ * `restorePosition: "last-user-turn"`.
2078
2120
  * @default 16
2079
2121
  */
2080
2122
  anchorTopOffset?: number;
2123
+ /**
2124
+ * Where to land when a saved conversation reopens. Additive and opt-in: the
2125
+ * default preserves the historical "jump to the bottom" behavior.
2126
+ * @default "bottom"
2127
+ */
2128
+ restorePosition?: AgentWidgetScrollRestorePosition;
2129
+ /**
2130
+ * When true, interactions beyond text selection — keyboard navigation
2131
+ * (PageUp/PageDown/Home/End/arrows) inside the transcript and focusing a
2132
+ * link or other interactive element within it — also pause auto-follow in
2133
+ * `"follow"` mode. Treats "the reader is doing something here" as intent to
2134
+ * stay put, not just "the reader dragged a selection". Opt-in; default keeps
2135
+ * only the existing wheel/scroll/selection triggers.
2136
+ * @default false
2137
+ */
2138
+ pauseOnInteraction?: boolean;
2139
+ /**
2140
+ * When true, the scroll-to-bottom affordance also surfaces new-content and
2141
+ * still-streaming activity while the reader is pinned away from the latest
2142
+ * content in `"anchor-top"` mode. Lets the reader know a response is arriving
2143
+ * offscreen below. Defaults on alongside the `"anchor-top"` default; set
2144
+ * `false` to keep the count + streaming hint silent while pinned.
2145
+ * @default true
2146
+ */
2147
+ showActivityWhilePinned?: boolean;
2148
+ /**
2149
+ * When true, Persona maintains a visually-hidden `aria-live="polite"` region
2150
+ * that announces important transcript events — a response starting, a
2151
+ * response finishing, and "N new messages below" while the reader is scrolled
2152
+ * away — at a comfortable cadence (never token-by-token). Opt-in so existing
2153
+ * embeds don't suddenly gain screen-reader announcements.
2154
+ * @default false
2155
+ */
2156
+ announce?: boolean;
2081
2157
  };
2082
2158
  type AgentWidgetScrollToBottomFeature = {
2083
2159
  /**
@@ -2960,6 +3036,10 @@ type AgentWidgetStatusIndicatorConfig = {
2960
3036
  connectingText?: string;
2961
3037
  connectedText?: string;
2962
3038
  errorText?: string;
3039
+ /** Status text while a dropped durable stream is awaiting reconnect. */
3040
+ pausedText?: string;
3041
+ /** Status text while a reconnect attempt is in flight. */
3042
+ resumingText?: string;
2963
3043
  };
2964
3044
  type AgentWidgetVoiceRecognitionConfig = {
2965
3045
  enabled?: boolean;
@@ -5041,6 +5121,65 @@ type AgentWidgetConfig = {
5041
5121
  * ```
5042
5122
  */
5043
5123
  customFetch?: AgentWidgetCustomFetch;
5124
+ /**
5125
+ * Durable-session reconnect transport (host-owned, symmetric to
5126
+ * {@link customFetch}). When a durable agent stream drops mid-turn (any
5127
+ * resumable, server-persisted execution, e.g. Claude Managed agents or
5128
+ * async/background runs), the widget calls this to fetch the read-only
5129
+ * reconnect stream and pipes its body through the normal event pipeline to
5130
+ * resume from where it left off.
5131
+ *
5132
+ * The host closure owns `agentId` / `conversationId` / base URL / auth and
5133
+ * builds the events request, e.g.:
5134
+ *
5135
+ * ```typescript
5136
+ * reconnectStream: ({ executionId, after, signal }) =>
5137
+ * fetch(
5138
+ * `${baseUrl}/v1/agents/${agentId}/executions/${executionId}` +
5139
+ * `/events?conversationId=${conversationId}&after=${after}`,
5140
+ * { headers: { Authorization: `Bearer ${token}`, "X-Persona-Version": ver }, signal }
5141
+ * )
5142
+ * ```
5143
+ *
5144
+ * Resolve with a `text/event-stream` `Response`. Throw or resolve non-ok to
5145
+ * signal "this attempt failed" (the widget backs off and retries, then gives
5146
+ * up after the bounded attempts). Reconnect only ever arms on the durable
5147
+ * lane (streams carrying SSE `id:` lines); without this hook a drop finalizes
5148
+ * exactly as before.
5149
+ */
5150
+ reconnectStream?: (ctx: {
5151
+ executionId: string;
5152
+ after: string;
5153
+ signal: AbortSignal;
5154
+ }) => Promise<Response>;
5155
+ /**
5156
+ * Tuning for the auto-reconnect backoff. Defaults to ~5 attempts over ~30s
5157
+ * (`backoffMs: [1000, 2000, 4000, 8000, 8000]`).
5158
+ */
5159
+ reconnect?: {
5160
+ /** Max reconnect attempts before giving up and finalizing. @default backoffMs.length */
5161
+ maxAttempts?: number;
5162
+ /** Per-attempt delay (ms) before each retry. @default [1000, 2000, 4000, 8000, 8000] */
5163
+ backoffMs?: number[];
5164
+ };
5165
+ /**
5166
+ * Called whenever the durable resume handle changes: created when a durable
5167
+ * turn starts streaming, updated as the cursor advances (throttled), and
5168
+ * `null` when the turn finishes, errors, or is torn down. Persist
5169
+ * `{ executionId, lastEventId }` next to your `conversationId` and pass it
5170
+ * back via {@link resume} on the next mount to survive a tab reload.
5171
+ */
5172
+ onExecutionState?: (handle: ResumableHandle | null) => void;
5173
+ /**
5174
+ * Resume a durable turn on boot (tab-reload path). When present alongside
5175
+ * {@link reconnectStream}, the widget enters `resuming` immediately on mount
5176
+ * and reconnects from `after`, replaying everything past the cursor into the
5177
+ * restored conversation.
5178
+ */
5179
+ resume?: {
5180
+ executionId: string;
5181
+ after: string;
5182
+ };
5044
5183
  /**
5045
5184
  * Custom SSE event parser for non-standard streaming response formats.
5046
5185
  *
@@ -1053,12 +1053,15 @@ type RuntypeExecutionStreamEvent = ({
1053
1053
  seq: number;
1054
1054
  type: "approval_complete";
1055
1055
  }) | ({
1056
+ awaitReason?: string;
1056
1057
  awaitedAt?: string;
1058
+ crawlId?: string;
1057
1059
  executionId: string;
1058
1060
  origin?: "webmcp" | "sdk";
1059
1061
  pageOrigin?: string;
1060
1062
  parameters?: Record<string, unknown>;
1061
1063
  seq: number;
1064
+ stepId?: string;
1062
1065
  toolCallId?: string;
1063
1066
  toolId?: string;
1064
1067
  toolName?: string;
@@ -1147,13 +1150,16 @@ type RuntypeFlowSSEEvent = {
1147
1150
  type: "flow_error";
1148
1151
  upgradeUrl?: string;
1149
1152
  }) | ({
1153
+ awaitReason?: string;
1150
1154
  awaitedAt: string;
1155
+ crawlId?: string;
1151
1156
  executionId?: string;
1152
1157
  flowId: string;
1153
1158
  origin?: "webmcp" | "sdk";
1154
1159
  pageOrigin?: string;
1155
1160
  parameters?: Record<string, unknown>;
1156
1161
  seq?: number;
1162
+ stepId?: string;
1157
1163
  toolCallId?: string;
1158
1164
  toolId?: string;
1159
1165
  toolName?: string;
@@ -1693,6 +1699,27 @@ type AgentWidgetWebMcpConfig = {
1693
1699
  */
1694
1700
  onConfirm?: WebMcpConfirmHandler;
1695
1701
  };
1702
+ /**
1703
+ * The coordinates needed to resume a durable agent turn after the SSE
1704
+ * connection drops (any resumable, server-persisted execution, e.g. Claude
1705
+ * Managed agents or async/background runs). Held on the session while a
1706
+ * resumable stream is in flight
1707
+ * and surfaced to the host via {@link AgentWidgetConfig.onExecutionState} so it
1708
+ * can persist `{ executionId, lastEventId }` next to its own `conversationId`
1709
+ * for the tab-reload path.
1710
+ *
1711
+ * `conversationId` is intentionally absent: it is host-owned (it lives in the
1712
+ * host's `reconnectStream`/`customFetch` closure; the widget never sees it).
1713
+ */
1714
+ type ResumableHandle = {
1715
+ /** The durable turn to reconnect to (from `agentMetadata.executionId`). */
1716
+ executionId: string;
1717
+ /** Highest SSE `id:` seq applied: the `?after=` reconnect cursor. */
1718
+ lastEventId: string;
1719
+ /** The open assistant message being streamed into, kept open on resume. */
1720
+ assistantMessageId: string;
1721
+ status: 'running';
1722
+ };
1696
1723
  /**
1697
1724
  * Metadata attached to messages created during agent execution.
1698
1725
  */
@@ -2059,25 +2086,74 @@ type AgentWidgetArtifactsFeature = {
2059
2086
  /**
2060
2087
  * How the transcript scrolls while an assistant response streams in.
2061
2088
  *
2062
- * - `"follow"` (default): keep the newest content pinned to the bottom of the
2063
- * viewport, pausing when the user scrolls up and resuming when they return
2064
- * to the bottom.
2065
- * - `"anchor-top"`: on send, scroll the user's message near the top of the
2066
- * viewport and hold it there while the response streams in beneath it
2067
- * (ChatGPT-style). The transcript never auto-scrolls during streaming.
2089
+ * - `"anchor-top"` (default): on send, scroll the user's message near the top
2090
+ * of the viewport and hold it there while the response streams in beneath it
2091
+ * (ChatGPT-style). When a turn has no user send to anchor to (a proactive
2092
+ * greeting, an injected assistant message, a resubmit, or first-load
2093
+ * streaming), it falls back to `"follow"` for that turn so content never
2094
+ * streams in off-screen.
2095
+ * - `"follow"`: keep the newest content pinned to the bottom of the viewport,
2096
+ * pausing when the user scrolls up and resuming when they return to the
2097
+ * bottom. This was the default before 4.x.
2068
2098
  * - `"none"`: never auto-scroll; the scroll-to-bottom affordance is the only
2069
2099
  * way back to the latest content.
2070
2100
  */
2071
2101
  type AgentWidgetScrollMode = "follow" | "anchor-top" | "none";
2102
+ /**
2103
+ * Where the transcript lands when a *saved* conversation is reopened (restored
2104
+ * from the storage adapter / `initialMessages`), as opposed to a fresh send.
2105
+ *
2106
+ * - `"bottom"` (default): jump to the absolute end of the transcript, matching
2107
+ * the historical behavior.
2108
+ * - `"last-user-turn"`: pin the last user message near the top of the viewport
2109
+ * (the last point the reader was actively driving the conversation) so a long
2110
+ * restored thread opens at a readable place rather than at the very bottom.
2111
+ */
2112
+ type AgentWidgetScrollRestorePosition = "bottom" | "last-user-turn";
2072
2113
  type AgentWidgetScrollBehaviorFeature = {
2073
- /** Scroll behavior during streamed responses. @default "follow" */
2114
+ /** Scroll behavior during streamed responses. @default "anchor-top" */
2074
2115
  mode?: AgentWidgetScrollMode;
2075
2116
  /**
2076
2117
  * Gap (px) kept between the anchored user message and the top of the
2077
- * viewport in `"anchor-top"` mode.
2118
+ * viewport in `"anchor-top"` mode. Also used as the gap for
2119
+ * `restorePosition: "last-user-turn"`.
2078
2120
  * @default 16
2079
2121
  */
2080
2122
  anchorTopOffset?: number;
2123
+ /**
2124
+ * Where to land when a saved conversation reopens. Additive and opt-in: the
2125
+ * default preserves the historical "jump to the bottom" behavior.
2126
+ * @default "bottom"
2127
+ */
2128
+ restorePosition?: AgentWidgetScrollRestorePosition;
2129
+ /**
2130
+ * When true, interactions beyond text selection — keyboard navigation
2131
+ * (PageUp/PageDown/Home/End/arrows) inside the transcript and focusing a
2132
+ * link or other interactive element within it — also pause auto-follow in
2133
+ * `"follow"` mode. Treats "the reader is doing something here" as intent to
2134
+ * stay put, not just "the reader dragged a selection". Opt-in; default keeps
2135
+ * only the existing wheel/scroll/selection triggers.
2136
+ * @default false
2137
+ */
2138
+ pauseOnInteraction?: boolean;
2139
+ /**
2140
+ * When true, the scroll-to-bottom affordance also surfaces new-content and
2141
+ * still-streaming activity while the reader is pinned away from the latest
2142
+ * content in `"anchor-top"` mode. Lets the reader know a response is arriving
2143
+ * offscreen below. Defaults on alongside the `"anchor-top"` default; set
2144
+ * `false` to keep the count + streaming hint silent while pinned.
2145
+ * @default true
2146
+ */
2147
+ showActivityWhilePinned?: boolean;
2148
+ /**
2149
+ * When true, Persona maintains a visually-hidden `aria-live="polite"` region
2150
+ * that announces important transcript events — a response starting, a
2151
+ * response finishing, and "N new messages below" while the reader is scrolled
2152
+ * away — at a comfortable cadence (never token-by-token). Opt-in so existing
2153
+ * embeds don't suddenly gain screen-reader announcements.
2154
+ * @default false
2155
+ */
2156
+ announce?: boolean;
2081
2157
  };
2082
2158
  type AgentWidgetScrollToBottomFeature = {
2083
2159
  /**
@@ -2960,6 +3036,10 @@ type AgentWidgetStatusIndicatorConfig = {
2960
3036
  connectingText?: string;
2961
3037
  connectedText?: string;
2962
3038
  errorText?: string;
3039
+ /** Status text while a dropped durable stream is awaiting reconnect. */
3040
+ pausedText?: string;
3041
+ /** Status text while a reconnect attempt is in flight. */
3042
+ resumingText?: string;
2963
3043
  };
2964
3044
  type AgentWidgetVoiceRecognitionConfig = {
2965
3045
  enabled?: boolean;
@@ -5041,6 +5121,65 @@ type AgentWidgetConfig = {
5041
5121
  * ```
5042
5122
  */
5043
5123
  customFetch?: AgentWidgetCustomFetch;
5124
+ /**
5125
+ * Durable-session reconnect transport (host-owned, symmetric to
5126
+ * {@link customFetch}). When a durable agent stream drops mid-turn (any
5127
+ * resumable, server-persisted execution, e.g. Claude Managed agents or
5128
+ * async/background runs), the widget calls this to fetch the read-only
5129
+ * reconnect stream and pipes its body through the normal event pipeline to
5130
+ * resume from where it left off.
5131
+ *
5132
+ * The host closure owns `agentId` / `conversationId` / base URL / auth and
5133
+ * builds the events request, e.g.:
5134
+ *
5135
+ * ```typescript
5136
+ * reconnectStream: ({ executionId, after, signal }) =>
5137
+ * fetch(
5138
+ * `${baseUrl}/v1/agents/${agentId}/executions/${executionId}` +
5139
+ * `/events?conversationId=${conversationId}&after=${after}`,
5140
+ * { headers: { Authorization: `Bearer ${token}`, "X-Persona-Version": ver }, signal }
5141
+ * )
5142
+ * ```
5143
+ *
5144
+ * Resolve with a `text/event-stream` `Response`. Throw or resolve non-ok to
5145
+ * signal "this attempt failed" (the widget backs off and retries, then gives
5146
+ * up after the bounded attempts). Reconnect only ever arms on the durable
5147
+ * lane (streams carrying SSE `id:` lines); without this hook a drop finalizes
5148
+ * exactly as before.
5149
+ */
5150
+ reconnectStream?: (ctx: {
5151
+ executionId: string;
5152
+ after: string;
5153
+ signal: AbortSignal;
5154
+ }) => Promise<Response>;
5155
+ /**
5156
+ * Tuning for the auto-reconnect backoff. Defaults to ~5 attempts over ~30s
5157
+ * (`backoffMs: [1000, 2000, 4000, 8000, 8000]`).
5158
+ */
5159
+ reconnect?: {
5160
+ /** Max reconnect attempts before giving up and finalizing. @default backoffMs.length */
5161
+ maxAttempts?: number;
5162
+ /** Per-attempt delay (ms) before each retry. @default [1000, 2000, 4000, 8000, 8000] */
5163
+ backoffMs?: number[];
5164
+ };
5165
+ /**
5166
+ * Called whenever the durable resume handle changes: created when a durable
5167
+ * turn starts streaming, updated as the cursor advances (throttled), and
5168
+ * `null` when the turn finishes, errors, or is torn down. Persist
5169
+ * `{ executionId, lastEventId }` next to your `conversationId` and pass it
5170
+ * back via {@link resume} on the next mount to survive a tab reload.
5171
+ */
5172
+ onExecutionState?: (handle: ResumableHandle | null) => void;
5173
+ /**
5174
+ * Resume a durable turn on boot (tab-reload path). When present alongside
5175
+ * {@link reconnectStream}, the widget enters `resuming` immediately on mount
5176
+ * and reconnects from `after`, replaying everything past the cursor into the
5177
+ * restored conversation.
5178
+ */
5179
+ resume?: {
5180
+ executionId: string;
5181
+ after: string;
5182
+ };
5044
5183
  /**
5045
5184
  * Custom SSE event parser for non-standard streaming response formats.
5046
5185
  *