@runtypelabs/persona 4.5.0 → 4.6.1
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.
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-C6tFDxKy.d.cts → types-CSmiKRVa.d.cts} +11 -0
- package/dist/animations/{types-C6tFDxKy.d.ts → types-CSmiKRVa.d.ts} +11 -0
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/chunk-DFBSCFYN.js +1 -0
- package/dist/codegen.cjs +4 -4
- package/dist/codegen.js +4 -4
- package/dist/index.cjs +51 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +238 -3
- package/dist/index.d.ts +238 -3
- package/dist/index.global.js +37 -37
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +51 -51
- package/dist/index.js.map +1 -1
- package/dist/launcher.global.js.map +1 -1
- package/dist/markdown-parsers-entry-NVFT3TE6.js +1 -0
- package/dist/runtype-tts-entry-HFUV2UF7.js +1 -0
- package/dist/session-reconnect-U77QFUR7.js +1 -0
- package/dist/smart-dom-reader.d.cts +95 -0
- package/dist/smart-dom-reader.d.ts +95 -0
- package/dist/theme-editor-preview.cjs +48 -48
- package/dist/theme-editor-preview.d.cts +135 -1
- package/dist/theme-editor-preview.d.ts +135 -1
- package/dist/theme-editor-preview.js +48 -48
- package/dist/theme-editor.d.cts +95 -0
- package/dist/theme-editor.d.ts +95 -0
- package/package.json +5 -3
- package/src/client.ts +55 -9
- package/src/generated/runtype-openapi-contract.ts +16 -1
- package/src/reconnect-wake.test.ts +162 -0
- package/src/reconnect.test.ts +430 -0
- package/src/session-reconnect.ts +282 -0
- package/src/session.ts +408 -5
- package/src/types.ts +107 -1
- package/src/ui.stream-animation-update.test.ts +99 -0
- package/src/ui.ts +71 -1
- package/src/utils/constants.ts +3 -1
- package/src/utils/morph.test.ts +47 -0
- package/src/utils/morph.ts +18 -0
package/dist/theme-editor.d.cts
CHANGED
|
@@ -783,6 +783,7 @@ type RuntypeExecutionStreamEvent = ({
|
|
|
783
783
|
};
|
|
784
784
|
type: "execution_complete";
|
|
785
785
|
}) | ({
|
|
786
|
+
blockReason?: string;
|
|
786
787
|
code?: string;
|
|
787
788
|
completedAt?: string;
|
|
788
789
|
error: string | {
|
|
@@ -1037,6 +1038,7 @@ type RuntypeExecutionStreamEvent = ({
|
|
|
1037
1038
|
startedAt?: string;
|
|
1038
1039
|
subagent?: {
|
|
1039
1040
|
agentName?: string;
|
|
1041
|
+
parentToolCallId?: string;
|
|
1040
1042
|
toolName: string;
|
|
1041
1043
|
};
|
|
1042
1044
|
timeout?: number;
|
|
@@ -1053,12 +1055,15 @@ type RuntypeExecutionStreamEvent = ({
|
|
|
1053
1055
|
seq: number;
|
|
1054
1056
|
type: "approval_complete";
|
|
1055
1057
|
}) | ({
|
|
1058
|
+
awaitReason?: string;
|
|
1056
1059
|
awaitedAt?: string;
|
|
1060
|
+
crawlId?: string;
|
|
1057
1061
|
executionId: string;
|
|
1058
1062
|
origin?: "webmcp" | "sdk";
|
|
1059
1063
|
pageOrigin?: string;
|
|
1060
1064
|
parameters?: Record<string, unknown>;
|
|
1061
1065
|
seq: number;
|
|
1066
|
+
stepId?: string;
|
|
1062
1067
|
toolCallId?: string;
|
|
1063
1068
|
toolId?: string;
|
|
1064
1069
|
toolName?: string;
|
|
@@ -1127,6 +1132,7 @@ type RuntypeFlowSSEEvent = {
|
|
|
1127
1132
|
totalTokensUsed?: number;
|
|
1128
1133
|
type: "flow_complete";
|
|
1129
1134
|
} | ({
|
|
1135
|
+
blockReason?: string;
|
|
1130
1136
|
code?: string;
|
|
1131
1137
|
error: string | {
|
|
1132
1138
|
code: string;
|
|
@@ -1147,13 +1153,18 @@ type RuntypeFlowSSEEvent = {
|
|
|
1147
1153
|
type: "flow_error";
|
|
1148
1154
|
upgradeUrl?: string;
|
|
1149
1155
|
}) | ({
|
|
1156
|
+
approvalId?: string;
|
|
1157
|
+
awaitReason?: string;
|
|
1150
1158
|
awaitedAt: string;
|
|
1159
|
+
crawlId?: string;
|
|
1151
1160
|
executionId?: string;
|
|
1152
1161
|
flowId: string;
|
|
1153
1162
|
origin?: "webmcp" | "sdk";
|
|
1154
1163
|
pageOrigin?: string;
|
|
1155
1164
|
parameters?: Record<string, unknown>;
|
|
1156
1165
|
seq?: number;
|
|
1166
|
+
stepId?: string;
|
|
1167
|
+
timeout?: number;
|
|
1157
1168
|
toolCallId?: string;
|
|
1158
1169
|
toolId?: string;
|
|
1159
1170
|
toolName?: string;
|
|
@@ -1693,6 +1704,27 @@ type AgentWidgetWebMcpConfig = {
|
|
|
1693
1704
|
*/
|
|
1694
1705
|
onConfirm?: WebMcpConfirmHandler;
|
|
1695
1706
|
};
|
|
1707
|
+
/**
|
|
1708
|
+
* The coordinates needed to resume a durable agent turn after the SSE
|
|
1709
|
+
* connection drops (any resumable, server-persisted execution, e.g. Claude
|
|
1710
|
+
* Managed agents or async/background runs). Held on the session while a
|
|
1711
|
+
* resumable stream is in flight
|
|
1712
|
+
* and surfaced to the host via {@link AgentWidgetConfig.onExecutionState} so it
|
|
1713
|
+
* can persist `{ executionId, lastEventId }` next to its own `conversationId`
|
|
1714
|
+
* for the tab-reload path.
|
|
1715
|
+
*
|
|
1716
|
+
* `conversationId` is intentionally absent: it is host-owned (it lives in the
|
|
1717
|
+
* host's `reconnectStream`/`customFetch` closure; the widget never sees it).
|
|
1718
|
+
*/
|
|
1719
|
+
type ResumableHandle = {
|
|
1720
|
+
/** The durable turn to reconnect to (from `agentMetadata.executionId`). */
|
|
1721
|
+
executionId: string;
|
|
1722
|
+
/** Highest SSE `id:` seq applied: the `?after=` reconnect cursor. */
|
|
1723
|
+
lastEventId: string;
|
|
1724
|
+
/** The open assistant message being streamed into, kept open on resume. */
|
|
1725
|
+
assistantMessageId: string;
|
|
1726
|
+
status: 'running';
|
|
1727
|
+
};
|
|
1696
1728
|
/**
|
|
1697
1729
|
* Metadata attached to messages created during agent execution.
|
|
1698
1730
|
*/
|
|
@@ -3009,6 +3041,10 @@ type AgentWidgetStatusIndicatorConfig = {
|
|
|
3009
3041
|
connectingText?: string;
|
|
3010
3042
|
connectedText?: string;
|
|
3011
3043
|
errorText?: string;
|
|
3044
|
+
/** Status text while a dropped durable stream is awaiting reconnect. */
|
|
3045
|
+
pausedText?: string;
|
|
3046
|
+
/** Status text while a reconnect attempt is in flight. */
|
|
3047
|
+
resumingText?: string;
|
|
3012
3048
|
};
|
|
3013
3049
|
type AgentWidgetVoiceRecognitionConfig = {
|
|
3014
3050
|
enabled?: boolean;
|
|
@@ -5090,6 +5126,65 @@ type AgentWidgetConfig = {
|
|
|
5090
5126
|
* ```
|
|
5091
5127
|
*/
|
|
5092
5128
|
customFetch?: AgentWidgetCustomFetch;
|
|
5129
|
+
/**
|
|
5130
|
+
* Durable-session reconnect transport (host-owned, symmetric to
|
|
5131
|
+
* {@link customFetch}). When a durable agent stream drops mid-turn (any
|
|
5132
|
+
* resumable, server-persisted execution, e.g. Claude Managed agents or
|
|
5133
|
+
* async/background runs), the widget calls this to fetch the read-only
|
|
5134
|
+
* reconnect stream and pipes its body through the normal event pipeline to
|
|
5135
|
+
* resume from where it left off.
|
|
5136
|
+
*
|
|
5137
|
+
* The host closure owns `agentId` / `conversationId` / base URL / auth and
|
|
5138
|
+
* builds the events request, e.g.:
|
|
5139
|
+
*
|
|
5140
|
+
* ```typescript
|
|
5141
|
+
* reconnectStream: ({ executionId, after, signal }) =>
|
|
5142
|
+
* fetch(
|
|
5143
|
+
* `${baseUrl}/v1/agents/${agentId}/executions/${executionId}` +
|
|
5144
|
+
* `/events?conversationId=${conversationId}&after=${after}`,
|
|
5145
|
+
* { headers: { Authorization: `Bearer ${token}`, "X-Persona-Version": ver }, signal }
|
|
5146
|
+
* )
|
|
5147
|
+
* ```
|
|
5148
|
+
*
|
|
5149
|
+
* Resolve with a `text/event-stream` `Response`. Throw or resolve non-ok to
|
|
5150
|
+
* signal "this attempt failed" (the widget backs off and retries, then gives
|
|
5151
|
+
* up after the bounded attempts). Reconnect only ever arms on the durable
|
|
5152
|
+
* lane (streams carrying SSE `id:` lines); without this hook a drop finalizes
|
|
5153
|
+
* exactly as before.
|
|
5154
|
+
*/
|
|
5155
|
+
reconnectStream?: (ctx: {
|
|
5156
|
+
executionId: string;
|
|
5157
|
+
after: string;
|
|
5158
|
+
signal: AbortSignal;
|
|
5159
|
+
}) => Promise<Response>;
|
|
5160
|
+
/**
|
|
5161
|
+
* Tuning for the auto-reconnect backoff. Defaults to ~5 attempts over ~30s
|
|
5162
|
+
* (`backoffMs: [1000, 2000, 4000, 8000, 8000]`).
|
|
5163
|
+
*/
|
|
5164
|
+
reconnect?: {
|
|
5165
|
+
/** Max reconnect attempts before giving up and finalizing. @default backoffMs.length */
|
|
5166
|
+
maxAttempts?: number;
|
|
5167
|
+
/** Per-attempt delay (ms) before each retry. @default [1000, 2000, 4000, 8000, 8000] */
|
|
5168
|
+
backoffMs?: number[];
|
|
5169
|
+
};
|
|
5170
|
+
/**
|
|
5171
|
+
* Called whenever the durable resume handle changes: created when a durable
|
|
5172
|
+
* turn starts streaming, updated as the cursor advances (throttled), and
|
|
5173
|
+
* `null` when the turn finishes, errors, or is torn down. Persist
|
|
5174
|
+
* `{ executionId, lastEventId }` next to your `conversationId` and pass it
|
|
5175
|
+
* back via {@link resume} on the next mount to survive a tab reload.
|
|
5176
|
+
*/
|
|
5177
|
+
onExecutionState?: (handle: ResumableHandle | null) => void;
|
|
5178
|
+
/**
|
|
5179
|
+
* Resume a durable turn on boot (tab-reload path). When present alongside
|
|
5180
|
+
* {@link reconnectStream}, the widget enters `resuming` immediately on mount
|
|
5181
|
+
* and reconnects from `after`, replaying everything past the cursor into the
|
|
5182
|
+
* restored conversation.
|
|
5183
|
+
*/
|
|
5184
|
+
resume?: {
|
|
5185
|
+
executionId: string;
|
|
5186
|
+
after: string;
|
|
5187
|
+
};
|
|
5093
5188
|
/**
|
|
5094
5189
|
* Custom SSE event parser for non-standard streaming response formats.
|
|
5095
5190
|
*
|
package/dist/theme-editor.d.ts
CHANGED
|
@@ -783,6 +783,7 @@ type RuntypeExecutionStreamEvent = ({
|
|
|
783
783
|
};
|
|
784
784
|
type: "execution_complete";
|
|
785
785
|
}) | ({
|
|
786
|
+
blockReason?: string;
|
|
786
787
|
code?: string;
|
|
787
788
|
completedAt?: string;
|
|
788
789
|
error: string | {
|
|
@@ -1037,6 +1038,7 @@ type RuntypeExecutionStreamEvent = ({
|
|
|
1037
1038
|
startedAt?: string;
|
|
1038
1039
|
subagent?: {
|
|
1039
1040
|
agentName?: string;
|
|
1041
|
+
parentToolCallId?: string;
|
|
1040
1042
|
toolName: string;
|
|
1041
1043
|
};
|
|
1042
1044
|
timeout?: number;
|
|
@@ -1053,12 +1055,15 @@ type RuntypeExecutionStreamEvent = ({
|
|
|
1053
1055
|
seq: number;
|
|
1054
1056
|
type: "approval_complete";
|
|
1055
1057
|
}) | ({
|
|
1058
|
+
awaitReason?: string;
|
|
1056
1059
|
awaitedAt?: string;
|
|
1060
|
+
crawlId?: string;
|
|
1057
1061
|
executionId: string;
|
|
1058
1062
|
origin?: "webmcp" | "sdk";
|
|
1059
1063
|
pageOrigin?: string;
|
|
1060
1064
|
parameters?: Record<string, unknown>;
|
|
1061
1065
|
seq: number;
|
|
1066
|
+
stepId?: string;
|
|
1062
1067
|
toolCallId?: string;
|
|
1063
1068
|
toolId?: string;
|
|
1064
1069
|
toolName?: string;
|
|
@@ -1127,6 +1132,7 @@ type RuntypeFlowSSEEvent = {
|
|
|
1127
1132
|
totalTokensUsed?: number;
|
|
1128
1133
|
type: "flow_complete";
|
|
1129
1134
|
} | ({
|
|
1135
|
+
blockReason?: string;
|
|
1130
1136
|
code?: string;
|
|
1131
1137
|
error: string | {
|
|
1132
1138
|
code: string;
|
|
@@ -1147,13 +1153,18 @@ type RuntypeFlowSSEEvent = {
|
|
|
1147
1153
|
type: "flow_error";
|
|
1148
1154
|
upgradeUrl?: string;
|
|
1149
1155
|
}) | ({
|
|
1156
|
+
approvalId?: string;
|
|
1157
|
+
awaitReason?: string;
|
|
1150
1158
|
awaitedAt: string;
|
|
1159
|
+
crawlId?: string;
|
|
1151
1160
|
executionId?: string;
|
|
1152
1161
|
flowId: string;
|
|
1153
1162
|
origin?: "webmcp" | "sdk";
|
|
1154
1163
|
pageOrigin?: string;
|
|
1155
1164
|
parameters?: Record<string, unknown>;
|
|
1156
1165
|
seq?: number;
|
|
1166
|
+
stepId?: string;
|
|
1167
|
+
timeout?: number;
|
|
1157
1168
|
toolCallId?: string;
|
|
1158
1169
|
toolId?: string;
|
|
1159
1170
|
toolName?: string;
|
|
@@ -1693,6 +1704,27 @@ type AgentWidgetWebMcpConfig = {
|
|
|
1693
1704
|
*/
|
|
1694
1705
|
onConfirm?: WebMcpConfirmHandler;
|
|
1695
1706
|
};
|
|
1707
|
+
/**
|
|
1708
|
+
* The coordinates needed to resume a durable agent turn after the SSE
|
|
1709
|
+
* connection drops (any resumable, server-persisted execution, e.g. Claude
|
|
1710
|
+
* Managed agents or async/background runs). Held on the session while a
|
|
1711
|
+
* resumable stream is in flight
|
|
1712
|
+
* and surfaced to the host via {@link AgentWidgetConfig.onExecutionState} so it
|
|
1713
|
+
* can persist `{ executionId, lastEventId }` next to its own `conversationId`
|
|
1714
|
+
* for the tab-reload path.
|
|
1715
|
+
*
|
|
1716
|
+
* `conversationId` is intentionally absent: it is host-owned (it lives in the
|
|
1717
|
+
* host's `reconnectStream`/`customFetch` closure; the widget never sees it).
|
|
1718
|
+
*/
|
|
1719
|
+
type ResumableHandle = {
|
|
1720
|
+
/** The durable turn to reconnect to (from `agentMetadata.executionId`). */
|
|
1721
|
+
executionId: string;
|
|
1722
|
+
/** Highest SSE `id:` seq applied: the `?after=` reconnect cursor. */
|
|
1723
|
+
lastEventId: string;
|
|
1724
|
+
/** The open assistant message being streamed into, kept open on resume. */
|
|
1725
|
+
assistantMessageId: string;
|
|
1726
|
+
status: 'running';
|
|
1727
|
+
};
|
|
1696
1728
|
/**
|
|
1697
1729
|
* Metadata attached to messages created during agent execution.
|
|
1698
1730
|
*/
|
|
@@ -3009,6 +3041,10 @@ type AgentWidgetStatusIndicatorConfig = {
|
|
|
3009
3041
|
connectingText?: string;
|
|
3010
3042
|
connectedText?: string;
|
|
3011
3043
|
errorText?: string;
|
|
3044
|
+
/** Status text while a dropped durable stream is awaiting reconnect. */
|
|
3045
|
+
pausedText?: string;
|
|
3046
|
+
/** Status text while a reconnect attempt is in flight. */
|
|
3047
|
+
resumingText?: string;
|
|
3012
3048
|
};
|
|
3013
3049
|
type AgentWidgetVoiceRecognitionConfig = {
|
|
3014
3050
|
enabled?: boolean;
|
|
@@ -5090,6 +5126,65 @@ type AgentWidgetConfig = {
|
|
|
5090
5126
|
* ```
|
|
5091
5127
|
*/
|
|
5092
5128
|
customFetch?: AgentWidgetCustomFetch;
|
|
5129
|
+
/**
|
|
5130
|
+
* Durable-session reconnect transport (host-owned, symmetric to
|
|
5131
|
+
* {@link customFetch}). When a durable agent stream drops mid-turn (any
|
|
5132
|
+
* resumable, server-persisted execution, e.g. Claude Managed agents or
|
|
5133
|
+
* async/background runs), the widget calls this to fetch the read-only
|
|
5134
|
+
* reconnect stream and pipes its body through the normal event pipeline to
|
|
5135
|
+
* resume from where it left off.
|
|
5136
|
+
*
|
|
5137
|
+
* The host closure owns `agentId` / `conversationId` / base URL / auth and
|
|
5138
|
+
* builds the events request, e.g.:
|
|
5139
|
+
*
|
|
5140
|
+
* ```typescript
|
|
5141
|
+
* reconnectStream: ({ executionId, after, signal }) =>
|
|
5142
|
+
* fetch(
|
|
5143
|
+
* `${baseUrl}/v1/agents/${agentId}/executions/${executionId}` +
|
|
5144
|
+
* `/events?conversationId=${conversationId}&after=${after}`,
|
|
5145
|
+
* { headers: { Authorization: `Bearer ${token}`, "X-Persona-Version": ver }, signal }
|
|
5146
|
+
* )
|
|
5147
|
+
* ```
|
|
5148
|
+
*
|
|
5149
|
+
* Resolve with a `text/event-stream` `Response`. Throw or resolve non-ok to
|
|
5150
|
+
* signal "this attempt failed" (the widget backs off and retries, then gives
|
|
5151
|
+
* up after the bounded attempts). Reconnect only ever arms on the durable
|
|
5152
|
+
* lane (streams carrying SSE `id:` lines); without this hook a drop finalizes
|
|
5153
|
+
* exactly as before.
|
|
5154
|
+
*/
|
|
5155
|
+
reconnectStream?: (ctx: {
|
|
5156
|
+
executionId: string;
|
|
5157
|
+
after: string;
|
|
5158
|
+
signal: AbortSignal;
|
|
5159
|
+
}) => Promise<Response>;
|
|
5160
|
+
/**
|
|
5161
|
+
* Tuning for the auto-reconnect backoff. Defaults to ~5 attempts over ~30s
|
|
5162
|
+
* (`backoffMs: [1000, 2000, 4000, 8000, 8000]`).
|
|
5163
|
+
*/
|
|
5164
|
+
reconnect?: {
|
|
5165
|
+
/** Max reconnect attempts before giving up and finalizing. @default backoffMs.length */
|
|
5166
|
+
maxAttempts?: number;
|
|
5167
|
+
/** Per-attempt delay (ms) before each retry. @default [1000, 2000, 4000, 8000, 8000] */
|
|
5168
|
+
backoffMs?: number[];
|
|
5169
|
+
};
|
|
5170
|
+
/**
|
|
5171
|
+
* Called whenever the durable resume handle changes: created when a durable
|
|
5172
|
+
* turn starts streaming, updated as the cursor advances (throttled), and
|
|
5173
|
+
* `null` when the turn finishes, errors, or is torn down. Persist
|
|
5174
|
+
* `{ executionId, lastEventId }` next to your `conversationId` and pass it
|
|
5175
|
+
* back via {@link resume} on the next mount to survive a tab reload.
|
|
5176
|
+
*/
|
|
5177
|
+
onExecutionState?: (handle: ResumableHandle | null) => void;
|
|
5178
|
+
/**
|
|
5179
|
+
* Resume a durable turn on boot (tab-reload path). When present alongside
|
|
5180
|
+
* {@link reconnectStream}, the widget enters `resuming` immediately on mount
|
|
5181
|
+
* and reconnects from `after`, replaying everything past the cursor into the
|
|
5182
|
+
* restored conversation.
|
|
5183
|
+
*/
|
|
5184
|
+
resume?: {
|
|
5185
|
+
executionId: string;
|
|
5186
|
+
after: string;
|
|
5187
|
+
};
|
|
5093
5188
|
/**
|
|
5094
5189
|
* Custom SSE event parser for non-standard streaming response formats.
|
|
5095
5190
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.1",
|
|
4
4
|
"description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@size-limit/file": "^12.1.0",
|
|
82
|
+
"acorn": "^8.15.0",
|
|
82
83
|
"@types/node": "^20.12.7",
|
|
83
84
|
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
84
85
|
"@typescript-eslint/parser": "^7.0.0",
|
|
@@ -120,11 +121,12 @@
|
|
|
120
121
|
"access": "public"
|
|
121
122
|
},
|
|
122
123
|
"scripts": {
|
|
123
|
-
"build": "rimraf dist && pnpm run build:styles && pnpm run build:markdown-parsers && pnpm run build:client && pnpm run build:installer && pnpm run build:launcher && pnpm run build:webmcp-polyfill && pnpm run build:runtype-tts && pnpm run build:theme-ref && pnpm run build:codegen && pnpm run build:theme-editor && pnpm run build:theme-editor-preview && pnpm run build:testing && pnpm run build:smart-dom-reader && pnpm run build:voice-worklet-player && pnpm run build:plugin-kit && pnpm run build:animations",
|
|
124
|
+
"build": "rimraf dist && pnpm run build:styles && pnpm run build:markdown-parsers && pnpm run build:client && pnpm run build:installer && pnpm run build:launcher && pnpm run build:webmcp-polyfill && pnpm run build:runtype-tts && pnpm run build:theme-ref && pnpm run build:codegen && pnpm run build:theme-editor && pnpm run build:theme-editor-preview && pnpm run build:testing && pnpm run build:smart-dom-reader && pnpm run build:voice-worklet-player && pnpm run build:plugin-kit && pnpm run build:animations && pnpm run check:dist-vite8",
|
|
125
|
+
"check:dist-vite8": "node scripts/check-dist-no-in-for-init.mjs",
|
|
124
126
|
"build:markdown-parsers": "tsup --config tsup.markdown-parsers.config.ts",
|
|
125
127
|
"build:plugin-kit": "tsup src/plugin-kit.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
|
126
128
|
"build:theme-editor": "tsup src/theme-editor.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
|
127
|
-
"build:theme-editor-preview": "tsup src/theme-editor-preview.ts --format esm,cjs --minify --dts --out-dir dist
|
|
129
|
+
"build:theme-editor-preview": "tsup src/theme-editor-preview.ts --format esm,cjs --minify --dts --out-dir dist",
|
|
128
130
|
"build:testing": "tsup src/testing.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
|
129
131
|
"build:smart-dom-reader": "tsup src/smart-dom-reader.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
|
130
132
|
"build:voice-worklet-player": "tsup src/voice-worklet-player.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
package/src/client.ts
CHANGED
|
@@ -954,11 +954,12 @@ export class AgentWidgetClient {
|
|
|
954
954
|
public async processStream(
|
|
955
955
|
body: ReadableStream<Uint8Array>,
|
|
956
956
|
onEvent: SSEHandler,
|
|
957
|
-
assistantMessageId?: string
|
|
957
|
+
assistantMessageId?: string,
|
|
958
|
+
seedContent?: string
|
|
958
959
|
): Promise<void> {
|
|
959
960
|
onEvent({ type: "status", status: "connected" });
|
|
960
961
|
try {
|
|
961
|
-
await this.streamResponse(body, onEvent, assistantMessageId);
|
|
962
|
+
await this.streamResponse(body, onEvent, assistantMessageId, seedContent);
|
|
962
963
|
} finally {
|
|
963
964
|
onEvent({ type: "status", status: "idle" });
|
|
964
965
|
}
|
|
@@ -1353,7 +1354,12 @@ export class AgentWidgetClient {
|
|
|
1353
1354
|
private async streamResponse(
|
|
1354
1355
|
body: ReadableStream<Uint8Array>,
|
|
1355
1356
|
onEvent: SSEHandler,
|
|
1356
|
-
assistantMessageId?: string
|
|
1357
|
+
assistantMessageId?: string,
|
|
1358
|
+
// Durable reconnect: seed the assistant accumulator with the text already
|
|
1359
|
+
// shown before the drop, so replayed post-cursor deltas APPEND to it
|
|
1360
|
+
// instead of a fresh stream clobbering it (the replay carries only
|
|
1361
|
+
// `seq > after`, i.e. the new deltas, not the full text).
|
|
1362
|
+
seedContent?: string
|
|
1357
1363
|
) {
|
|
1358
1364
|
const reader = body.getReader();
|
|
1359
1365
|
const decoder = new TextDecoder();
|
|
@@ -1490,10 +1496,14 @@ export class AgentWidgetClient {
|
|
|
1490
1496
|
const ensureAssistantMessage = () => {
|
|
1491
1497
|
if (assistantMessage) return assistantMessage;
|
|
1492
1498
|
let id: string;
|
|
1499
|
+
let initialContent = "";
|
|
1493
1500
|
const segment = currentTextBlockId;
|
|
1494
1501
|
if (!assistantIdConsumed && baseAssistantId) {
|
|
1495
1502
|
id = baseAssistantId;
|
|
1496
1503
|
assistantIdConsumed = true;
|
|
1504
|
+
// First (and only) time we reuse the caller-supplied id: this is the
|
|
1505
|
+
// bubble a durable reconnect resumes into, so continue its text.
|
|
1506
|
+
initialContent = seedContent ?? "";
|
|
1497
1507
|
} else if (baseAssistantId && segment) {
|
|
1498
1508
|
id = `${baseAssistantId}_${segment}`;
|
|
1499
1509
|
} else {
|
|
@@ -1502,7 +1512,7 @@ export class AgentWidgetClient {
|
|
|
1502
1512
|
assistantMessage = {
|
|
1503
1513
|
id,
|
|
1504
1514
|
role: "assistant",
|
|
1505
|
-
content:
|
|
1515
|
+
content: initialContent,
|
|
1506
1516
|
createdAt: new Date().toISOString(),
|
|
1507
1517
|
streaming: true,
|
|
1508
1518
|
sequence: nextSequence()
|
|
@@ -2552,7 +2562,11 @@ export class AgentWidgetClient {
|
|
|
2552
2562
|
const message =
|
|
2553
2563
|
typeof e === "string" && e !== ""
|
|
2554
2564
|
? e
|
|
2555
|
-
:
|
|
2565
|
+
: // Reflect.has, not `in`: the `in` operator inside an arrow body can
|
|
2566
|
+
// be minified into a `for(init;;)` head, which Oxc mis-parses and
|
|
2567
|
+
// Rolldown (Vite 8) silently emits as an empty chunk. Same
|
|
2568
|
+
// [[HasProperty]] semantics. Enforced by scripts/check-dist-no-in-for-init.mjs.
|
|
2569
|
+
e != null && typeof e === "object" && Reflect.has(e, "message")
|
|
2556
2570
|
? String((e as { message?: unknown }).message ?? "Step failed")
|
|
2557
2571
|
: "Step failed";
|
|
2558
2572
|
onEvent({ type: "error", error: new Error(message) });
|
|
@@ -2877,7 +2891,11 @@ export class AgentWidgetClient {
|
|
|
2877
2891
|
pendingFlowRaw = "";
|
|
2878
2892
|
lastSealedFlowBubble = null;
|
|
2879
2893
|
|
|
2880
|
-
|
|
2894
|
+
// `terminal: true` marks this as a graceful finish (not a drop). The
|
|
2895
|
+
// session uses it to distinguish the real end-of-turn from the plain
|
|
2896
|
+
// `idle` the dispatch wrappers emit in their `finally` when a durable
|
|
2897
|
+
// connection drops mid-stream (durable-reconnect drop detection).
|
|
2898
|
+
onEvent({ type: "status", status: "idle", terminal: true });
|
|
2881
2899
|
} else if (payloadType === "execution_error") {
|
|
2882
2900
|
// Terminal failure. The non-terminal `error` is handled
|
|
2883
2901
|
// separately (recoverable → warn).
|
|
@@ -3131,7 +3149,8 @@ export class AgentWidgetClient {
|
|
|
3131
3149
|
const e = payload.error;
|
|
3132
3150
|
if (typeof e === "string" && e !== "") {
|
|
3133
3151
|
resolvedError = new Error(e);
|
|
3134
|
-
} else if (e != null && typeof e === "object" && "message"
|
|
3152
|
+
} else if (e != null && typeof e === "object" && Reflect.has(e, "message")) {
|
|
3153
|
+
// Reflect.has, not `in` — see the note on the equivalent guard above.
|
|
3135
3154
|
resolvedError = new Error(String((e as { message?: unknown }).message ?? e));
|
|
3136
3155
|
}
|
|
3137
3156
|
}
|
|
@@ -3163,20 +3182,43 @@ export class AgentWidgetClient {
|
|
|
3163
3182
|
const lines = event.split("\n");
|
|
3164
3183
|
let eventType = "message";
|
|
3165
3184
|
let data = "";
|
|
3185
|
+
// Durable-reconnect cursor: the SSE `id:` line (the durable row seq).
|
|
3186
|
+
// Only durable, resumable agent executions stamp these (e.g. Claude
|
|
3187
|
+
// Managed agents, or any async/background run the backend persists and
|
|
3188
|
+
// can replay); other streams carry no cursor. We emit a `cursor`
|
|
3189
|
+
// event AFTER the frame is fully parsed and dispatched, so the session's
|
|
3190
|
+
// `lastEventId` only advances past frames it has actually applied, so the
|
|
3191
|
+
// happy path has no dupes against the server's `seq > after` replay.
|
|
3192
|
+
let frameId: string | null = null;
|
|
3166
3193
|
|
|
3167
3194
|
for (const line of lines) {
|
|
3168
3195
|
if (line.startsWith("event:")) {
|
|
3169
3196
|
eventType = line.replace("event:", "").trim();
|
|
3170
3197
|
} else if (line.startsWith("data:")) {
|
|
3171
3198
|
data += line.replace("data:", "").trim();
|
|
3199
|
+
} else if (line.startsWith("id:")) {
|
|
3200
|
+
frameId = line.slice(3).trim();
|
|
3172
3201
|
}
|
|
3173
3202
|
}
|
|
3174
3203
|
|
|
3175
|
-
|
|
3204
|
+
const advanceCursor = () => {
|
|
3205
|
+
if (frameId !== null && frameId !== "") {
|
|
3206
|
+
onEvent({ type: "cursor", id: frameId });
|
|
3207
|
+
}
|
|
3208
|
+
};
|
|
3209
|
+
|
|
3210
|
+
// A frame with an `id:` but no `data:` (e.g. a bare keepalive line) is
|
|
3211
|
+
// still a received durable row, so advance the cursor past it.
|
|
3212
|
+
if (!data) {
|
|
3213
|
+
advanceCursor();
|
|
3214
|
+
continue;
|
|
3215
|
+
}
|
|
3176
3216
|
let payload: any;
|
|
3177
3217
|
try {
|
|
3178
3218
|
payload = JSON.parse(data);
|
|
3179
3219
|
} catch (error) {
|
|
3220
|
+
// Parse failure: the frame was NOT applied. Do NOT advance the cursor
|
|
3221
|
+
// so a reconnect re-fetches this row.
|
|
3180
3222
|
onEvent({
|
|
3181
3223
|
type: "error",
|
|
3182
3224
|
error:
|
|
@@ -3209,7 +3251,10 @@ export class AgentWidgetClient {
|
|
|
3209
3251
|
if (assistantMessageRef.current && assistantMessageRef.current !== assistantMessage) {
|
|
3210
3252
|
assistantMessage = assistantMessageRef.current;
|
|
3211
3253
|
}
|
|
3212
|
-
if (handled)
|
|
3254
|
+
if (handled) {
|
|
3255
|
+
advanceCursor();
|
|
3256
|
+
continue; // Skip default handling if custom handler processed it
|
|
3257
|
+
}
|
|
3213
3258
|
}
|
|
3214
3259
|
|
|
3215
3260
|
// The wire is the wire vocabulary; the handler consumes it
|
|
@@ -3217,6 +3262,7 @@ export class AgentWidgetClient {
|
|
|
3217
3262
|
// drains straight through.
|
|
3218
3263
|
seqReadyQueue.push({ payloadType, payload });
|
|
3219
3264
|
drainReadyQueue();
|
|
3265
|
+
advanceCursor();
|
|
3220
3266
|
}
|
|
3221
3267
|
}
|
|
3222
3268
|
|
|
@@ -41,6 +41,7 @@ export type RuntypeExecutionStreamEvent = ({
|
|
|
41
41
|
};
|
|
42
42
|
type: "execution_complete";
|
|
43
43
|
}) | ({
|
|
44
|
+
blockReason?: string;
|
|
44
45
|
code?: string;
|
|
45
46
|
completedAt?: string;
|
|
46
47
|
error: string | {
|
|
@@ -295,6 +296,7 @@ export type RuntypeExecutionStreamEvent = ({
|
|
|
295
296
|
startedAt?: string;
|
|
296
297
|
subagent?: {
|
|
297
298
|
agentName?: string;
|
|
299
|
+
parentToolCallId?: string;
|
|
298
300
|
toolName: string;
|
|
299
301
|
};
|
|
300
302
|
timeout?: number;
|
|
@@ -311,12 +313,15 @@ export type RuntypeExecutionStreamEvent = ({
|
|
|
311
313
|
seq: number;
|
|
312
314
|
type: "approval_complete";
|
|
313
315
|
}) | ({
|
|
316
|
+
awaitReason?: string;
|
|
314
317
|
awaitedAt?: string;
|
|
318
|
+
crawlId?: string;
|
|
315
319
|
executionId: string;
|
|
316
320
|
origin?: "webmcp" | "sdk";
|
|
317
321
|
pageOrigin?: string;
|
|
318
322
|
parameters?: Record<string, unknown>;
|
|
319
323
|
seq: number;
|
|
324
|
+
stepId?: string;
|
|
320
325
|
toolCallId?: string;
|
|
321
326
|
toolId?: string;
|
|
322
327
|
toolName?: string;
|
|
@@ -386,6 +391,7 @@ export type RuntypeFlowSSEEvent = {
|
|
|
386
391
|
totalTokensUsed?: number;
|
|
387
392
|
type: "flow_complete";
|
|
388
393
|
} | ({
|
|
394
|
+
blockReason?: string;
|
|
389
395
|
code?: string;
|
|
390
396
|
error: string | {
|
|
391
397
|
code: string;
|
|
@@ -406,13 +412,18 @@ export type RuntypeFlowSSEEvent = {
|
|
|
406
412
|
type: "flow_error";
|
|
407
413
|
upgradeUrl?: string;
|
|
408
414
|
}) | ({
|
|
415
|
+
approvalId?: string;
|
|
416
|
+
awaitReason?: string;
|
|
409
417
|
awaitedAt: string;
|
|
418
|
+
crawlId?: string;
|
|
410
419
|
executionId?: string;
|
|
411
420
|
flowId: string;
|
|
412
421
|
origin?: "webmcp" | "sdk";
|
|
413
422
|
pageOrigin?: string;
|
|
414
423
|
parameters?: Record<string, unknown>;
|
|
415
424
|
seq?: number;
|
|
425
|
+
stepId?: string;
|
|
426
|
+
timeout?: number;
|
|
416
427
|
toolCallId?: string;
|
|
417
428
|
toolId?: string;
|
|
418
429
|
toolName?: string;
|
|
@@ -667,7 +678,7 @@ export type RuntypeClientInitResponse = {
|
|
|
667
678
|
welcomeMessage?: string | null;
|
|
668
679
|
};
|
|
669
680
|
expiresAt: string;
|
|
670
|
-
flow
|
|
681
|
+
flow?: {
|
|
671
682
|
description?: string | null;
|
|
672
683
|
id: string;
|
|
673
684
|
name?: string | null;
|
|
@@ -689,6 +700,10 @@ export type RuntypeClientChatRequest = {
|
|
|
689
700
|
untrustedContentHint?: boolean;
|
|
690
701
|
}>;
|
|
691
702
|
clientToolsFingerprint?: string;
|
|
703
|
+
identityProof?: {
|
|
704
|
+
provider: string;
|
|
705
|
+
token: string;
|
|
706
|
+
};
|
|
692
707
|
inputs?: Record<string, unknown>;
|
|
693
708
|
messages: Array<{
|
|
694
709
|
content: string | (Array<{
|