@runtypelabs/persona 3.16.0 → 3.18.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 (71) hide show
  1. package/README.md +142 -0
  2. package/dist/animations/glyph-cycle.cjs +279 -0
  3. package/dist/animations/glyph-cycle.d.cts +5 -0
  4. package/dist/animations/glyph-cycle.d.ts +5 -0
  5. package/dist/animations/glyph-cycle.js +252 -0
  6. package/dist/animations/types-cwY5HaFD.d.cts +307 -0
  7. package/dist/animations/types-cwY5HaFD.d.ts +307 -0
  8. package/dist/animations/wipe.cjs +107 -0
  9. package/dist/animations/wipe.d.cts +5 -0
  10. package/dist/animations/wipe.d.ts +5 -0
  11. package/dist/animations/wipe.js +80 -0
  12. package/dist/index.cjs +49 -48
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +504 -1
  15. package/dist/index.d.ts +504 -1
  16. package/dist/index.global.js +143 -88
  17. package/dist/index.global.js.map +1 -1
  18. package/dist/index.js +49 -48
  19. package/dist/index.js.map +1 -1
  20. package/dist/testing.cjs +85 -0
  21. package/dist/testing.d.cts +39 -0
  22. package/dist/testing.d.ts +39 -0
  23. package/dist/testing.js +56 -0
  24. package/dist/theme-editor.cjs +2095 -207
  25. package/dist/theme-editor.d.cts +432 -2
  26. package/dist/theme-editor.d.ts +432 -2
  27. package/dist/theme-editor.js +2093 -207
  28. package/dist/theme-reference.cjs +1 -1
  29. package/dist/theme-reference.d.cts +14 -0
  30. package/dist/theme-reference.d.ts +14 -0
  31. package/dist/widget.css +565 -0
  32. package/package.json +20 -3
  33. package/src/animations/glyph-cycle.ts +332 -0
  34. package/src/animations/wipe.ts +66 -0
  35. package/src/client.test.ts +275 -0
  36. package/src/client.ts +99 -0
  37. package/src/components/ask-user-question-bubble.test.ts +583 -0
  38. package/src/components/ask-user-question-bubble.ts +924 -0
  39. package/src/components/composer-builder.ts +61 -10
  40. package/src/components/message-bubble.test.ts +181 -2
  41. package/src/components/message-bubble.ts +209 -14
  42. package/src/components/messages.ts +33 -1
  43. package/src/components/panel.ts +45 -5
  44. package/src/defaults.ts +37 -0
  45. package/src/index-global.ts +31 -0
  46. package/src/index.ts +34 -1
  47. package/src/plugins/types.ts +57 -0
  48. package/src/session.test.ts +276 -1
  49. package/src/session.ts +247 -3
  50. package/src/styles/widget.css +565 -0
  51. package/src/testing/index.ts +11 -0
  52. package/src/testing/mock-stream.test.ts +80 -0
  53. package/src/testing/mock-stream.ts +94 -0
  54. package/src/testing.ts +2 -0
  55. package/src/theme-editor/index.ts +4 -0
  56. package/src/theme-editor/preview-utils.test.ts +60 -0
  57. package/src/theme-editor/preview-utils.ts +129 -0
  58. package/src/theme-editor/sections.test.ts +19 -0
  59. package/src/theme-editor/sections.ts +84 -1
  60. package/src/types/theme.ts +15 -0
  61. package/src/types.ts +360 -0
  62. package/src/ui.ask-user-question-plugin.test.ts +649 -0
  63. package/src/ui.stop-button.test.ts +165 -0
  64. package/src/ui.ts +706 -11
  65. package/src/utils/message-fingerprint.ts +2 -0
  66. package/src/utils/morph.ts +7 -0
  67. package/src/utils/storage.ts +10 -2
  68. package/src/utils/stream-animation.test.ts +417 -0
  69. package/src/utils/stream-animation.ts +449 -0
  70. package/src/utils/theme.test.ts +36 -0
  71. package/src/utils/tokens.ts +23 -0
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Text content part for multi-modal messages
3
+ */
4
+ type TextContentPart = {
5
+ type: 'text';
6
+ text: string;
7
+ };
8
+ /**
9
+ * Image content part for multi-modal messages
10
+ * Supports base64 data URIs or URLs
11
+ */
12
+ type ImageContentPart = {
13
+ type: 'image';
14
+ image: string;
15
+ mimeType?: string;
16
+ alt?: string;
17
+ };
18
+ /**
19
+ * File content part for multi-modal messages
20
+ * Supports PDF, TXT, DOCX, and other document types
21
+ */
22
+ type FileContentPart = {
23
+ type: 'file';
24
+ data: string;
25
+ mimeType: string;
26
+ filename: string;
27
+ };
28
+ /**
29
+ * Union type for all content part types
30
+ */
31
+ type ContentPart = TextContentPart | ImageContentPart | FileContentPart;
32
+ /**
33
+ * Metadata attached to messages created during agent execution.
34
+ */
35
+ type AgentMessageMetadata = {
36
+ executionId?: string;
37
+ iteration?: number;
38
+ turnId?: string;
39
+ agentName?: string;
40
+ /**
41
+ * When this message was produced by a step inside a nested flow executed
42
+ * as a tool, identifies the parent tool call id. Enables renderers to
43
+ * visually group or indent nested-flow output under its parent tool.
44
+ */
45
+ parentToolId?: string;
46
+ /**
47
+ * Nested flow step id that produced this message (e.g. a `send-stream`
48
+ * or `prompt` step inside the nested flow). Stable key for that step.
49
+ */
50
+ parentStepId?: string;
51
+ /**
52
+ * Set to `true` on a tool-variant message produced from a `step_await`
53
+ * event (`awaitReason: "local_tool_required"`). Signals to UI code that
54
+ * the tool call is a LOCAL tool and the server is paused waiting for a
55
+ * `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
56
+ */
57
+ awaitingLocalTool?: boolean;
58
+ /**
59
+ * Set to `true` once the user has picked / typed / dismissed an answer for
60
+ * an `ask_user_question` tool call, so renderers stop re-mounting the
61
+ * answer-pill sheet for this tool call on subsequent render passes.
62
+ */
63
+ askUserQuestionAnswered?: boolean;
64
+ /**
65
+ * In-progress answers for a multi-question `ask_user_question` payload,
66
+ * keyed by question text. Persisted across refresh so the user lands back
67
+ * where they were if the page reloads mid-flow. Cleared once
68
+ * `askUserQuestionAnswered` flips to `true`.
69
+ */
70
+ askUserQuestionAnswers?: Record<string, string | string[]>;
71
+ /**
72
+ * Current page index for a multi-question `ask_user_question` payload's
73
+ * paginated stepper. Persists alongside `askUserQuestionAnswers`.
74
+ */
75
+ askUserQuestionIndex?: number;
76
+ };
77
+ /**
78
+ * Context passed to plugin lifecycle hooks. Carries the live DOM references
79
+ * and resolved animation settings for the currently-streaming message.
80
+ */
81
+ type StreamAnimationContext = {
82
+ /** The `.persona-message-content` element owning the streamed text. */
83
+ container: HTMLElement;
84
+ /** The outer message bubble element. */
85
+ bubble: HTMLElement;
86
+ /** ID of the streaming message. */
87
+ messageId: string;
88
+ /** Read-only reference to the message being streamed. */
89
+ message: AgentWidgetMessage;
90
+ /** Effective `speed` from `streamAnimation.speed`. */
91
+ speed: number;
92
+ /** Effective `duration` from `streamAnimation.duration`. */
93
+ duration: number;
94
+ };
95
+ /**
96
+ * Pluggable stream animation. Third-party packages and inline registrations
97
+ * implement this interface to add custom reveal effects.
98
+ *
99
+ * Lifecycle:
100
+ * - When the widget mounts and detects a plugin (either passed via config or
101
+ * auto-registered in the IIFE bundle), it injects `styles` once into the
102
+ * widget's style host.
103
+ * - For each streaming assistant message whose `type` matches `name`, the
104
+ * widget applies `containerClass` / `bubbleClass`, wraps text per `wrap`,
105
+ * and — if `useCaret` is true — appends a blinking caret.
106
+ * - Hooks fire after the live DOM is morphed; plugins use stable element IDs
107
+ * and `data-preserve-animation` to safely mutate per-char or per-word spans
108
+ * without idiomorph clobbering in-flight work.
109
+ */
110
+ type StreamAnimationPlugin = {
111
+ /** Plugin identifier. Matches the `type` field in `streamAnimation`. */
112
+ name: string;
113
+ /** Class added to `.persona-message-content` while streaming. */
114
+ containerClass?: string;
115
+ /** Class added to the bubble element (e.g. a one-shot scale animation). */
116
+ bubbleClass?: string;
117
+ /** Wrap mode applied to text nodes during streaming. @default "none" */
118
+ wrap?: "none" | "char" | "word";
119
+ /**
120
+ * HTML tags whose descendant text is skipped during wrapping. Defaults to
121
+ * `["pre", "code", "a", "script", "style"]` — useful for keeping code
122
+ * blocks legible and link click-targets intact. Plugins that want to
123
+ * animate characters inside inline code (e.g. `glyph-cycle`) can narrow
124
+ * the list.
125
+ */
126
+ skipTags?: string[];
127
+ /** Append a blinking caret after the last rendered char/word. */
128
+ useCaret?: boolean;
129
+ /** CSS string injected into the widget style host on first activation. */
130
+ styles?: string;
131
+ /**
132
+ * Optional custom buffering strategy. Returns the portion of `content`
133
+ * that should be rendered during streaming. Use this for buffering
134
+ * schemes beyond the built-in `word` / `line` strategies.
135
+ */
136
+ bufferContent?: (content: string, message: AgentWidgetMessage) => string;
137
+ /**
138
+ * Fires once when the plugin is first activated inside a widget instance.
139
+ * Use this to set up MutationObservers or other long-lived listeners.
140
+ * Return an optional cleanup function that runs on widget destroy.
141
+ */
142
+ onAttach?: (root: HTMLElement | ShadowRoot) => (() => void) | void;
143
+ /** Fires after each render that reaches the live DOM. */
144
+ onAfterRender?: (ctx: StreamAnimationContext) => void;
145
+ /** Fires when a streamed message's `streaming` flag flips to false. */
146
+ onStreamComplete?: (ctx: StreamAnimationContext) => void;
147
+ /**
148
+ * Report whether the plugin still has in-flight animation work for a
149
+ * message. When `true`, the widget keeps rendering the message in its
150
+ * "streaming-animated" mode even after `message.streaming` flips false —
151
+ * preventing the final non-animated render from yanking the rug out from
152
+ * under unfinished per-char cycles or reveals.
153
+ */
154
+ isAnimating?: (message: AgentWidgetMessage) => boolean;
155
+ };
156
+ type AgentWidgetMessageRole = "user" | "assistant" | "system";
157
+ type AgentWidgetReasoning = {
158
+ id: string;
159
+ status: "pending" | "streaming" | "complete";
160
+ chunks: string[];
161
+ startedAt?: number;
162
+ completedAt?: number;
163
+ durationMs?: number;
164
+ };
165
+ type AgentWidgetToolCall = {
166
+ id: string;
167
+ name?: string;
168
+ status: "pending" | "running" | "complete";
169
+ args?: unknown;
170
+ chunks?: string[];
171
+ result?: unknown;
172
+ duration?: number;
173
+ startedAt?: number;
174
+ completedAt?: number;
175
+ durationMs?: number;
176
+ };
177
+ /**
178
+ * Represents a tool approval request in the chat conversation.
179
+ * Created when the agent requires human approval before executing a tool.
180
+ */
181
+ type AgentWidgetApproval = {
182
+ id: string;
183
+ status: "pending" | "approved" | "denied" | "timeout";
184
+ agentId: string;
185
+ executionId: string;
186
+ toolName: string;
187
+ toolType?: string;
188
+ description: string;
189
+ parameters?: unknown;
190
+ resolvedAt?: number;
191
+ };
192
+ type AgentWidgetMessageVariant = "assistant" | "reasoning" | "tool" | "approval";
193
+ /**
194
+ * Per-turn / per-step stop reason emitted by the runtime on
195
+ * `agent_turn_complete` and `step_complete` SSE events. The vocabulary is
196
+ * owned by the upstream Runtype API — do not extend without coordination.
197
+ *
198
+ * - `end_turn` — natural completion (no affordance needed)
199
+ * - `max_tool_calls` — agent loop tripped the configured tool-call ceiling
200
+ * - `length` — provider hit max output tokens
201
+ * - `content_filter` — provider content filter intervened
202
+ * - `error` — provider/runtime error (prefer existing error rendering)
203
+ * - `unknown` — explicitly reported but uninformative
204
+ *
205
+ * Absent (`undefined`) means "not reported" — distinct from `'unknown'`.
206
+ */
207
+ type StopReasonKind = 'end_turn' | 'max_tool_calls' | 'length' | 'content_filter' | 'error' | 'unknown';
208
+ /**
209
+ * Represents a message in the chat conversation.
210
+ *
211
+ * @property id - Unique message identifier
212
+ * @property role - Message role: "user", "assistant", or "system"
213
+ * @property content - Message text content (for display)
214
+ * @property contentParts - Original multi-modal content parts (for API requests)
215
+ * @property createdAt - ISO timestamp when message was created
216
+ * @property streaming - Whether message is still streaming (for assistant messages)
217
+ * @property variant - Message variant for assistant messages: "assistant", "reasoning", or "tool"
218
+ * @property sequence - Message ordering number
219
+ * @property reasoning - Reasoning data for assistant reasoning messages
220
+ * @property toolCall - Tool call data for assistant tool messages
221
+ * @property tools - Array of tool calls
222
+ * @property viaVoice - Set to `true` when a user message is sent via voice recognition.
223
+ * Useful for implementing voice-specific behaviors like auto-reactivation.
224
+ */
225
+ type AgentWidgetMessage = {
226
+ id: string;
227
+ role: AgentWidgetMessageRole;
228
+ content: string;
229
+ createdAt: string;
230
+ /**
231
+ * Original multi-modal content parts for this message.
232
+ * When present, this is sent to the API instead of `content`.
233
+ * The `content` field contains the text-only representation for display.
234
+ */
235
+ contentParts?: ContentPart[];
236
+ streaming?: boolean;
237
+ variant?: AgentWidgetMessageVariant;
238
+ sequence?: number;
239
+ reasoning?: AgentWidgetReasoning;
240
+ toolCall?: AgentWidgetToolCall;
241
+ tools?: AgentWidgetToolCall[];
242
+ /** Approval data for messages with variant "approval" */
243
+ approval?: AgentWidgetApproval;
244
+ viaVoice?: boolean;
245
+ /**
246
+ * Set to `true` on placeholder messages injected during Runtype voice processing.
247
+ * Use this in `messageTransform` to detect and customize voice processing placeholders.
248
+ *
249
+ * @example
250
+ * messageTransform: ({ text, message }) => {
251
+ * if (message.voiceProcessing && message.role === 'user') {
252
+ * return '<div class="my-voice-spinner">Transcribing...</div>';
253
+ * }
254
+ * return text;
255
+ * }
256
+ */
257
+ voiceProcessing?: boolean;
258
+ /**
259
+ * Raw structured payload for this message (e.g., JSON action response).
260
+ * Populated automatically when structured parsers run.
261
+ */
262
+ rawContent?: string;
263
+ /**
264
+ * LLM-specific content for API requests.
265
+ * When present, this is sent to the LLM instead of `content`.
266
+ *
267
+ * Priority for API payload:
268
+ * 1. `contentParts` (if present, used as-is for multi-modal)
269
+ * 2. `llmContent` (if present, sent as string)
270
+ * 3. `rawContent` (backward compatibility with structured parsers)
271
+ * 4. `content` (fallback - display content)
272
+ *
273
+ * The `content` field is always used for UI display.
274
+ *
275
+ * @example
276
+ * // Show full details to user, send summary to LLM
277
+ * {
278
+ * content: "**Product:** iPhone 15 Pro\n**Price:** $1,199\n**SKU:** IP15P-256",
279
+ * llmContent: "[Product search: iPhone 15 Pro, $1199]"
280
+ * }
281
+ */
282
+ llmContent?: string;
283
+ /**
284
+ * Text segment identity for chronological ordering.
285
+ * When present, identifies which text segment this message represents
286
+ * (e.g., "text_0", "text_1") for messages split at tool boundaries.
287
+ */
288
+ partId?: string;
289
+ /**
290
+ * Metadata for messages created during agent loop execution.
291
+ * Contains execution context like iteration number and turn ID.
292
+ */
293
+ agentMetadata?: AgentMessageMetadata;
294
+ /**
295
+ * Per-turn stop reason reported by the runtime on `agent_turn_complete`
296
+ * (agent-loop path) or the last `step_complete` for a prompt step
297
+ * (dispatch / flow path). Absent when the API did not report a value.
298
+ *
299
+ * When set to a non-natural value (`max_tool_calls`, `length`,
300
+ * `content_filter`, `error`), the widget renders an inline notice on
301
+ * the assistant bubble. See `config.copy.stopReasonNotice` to override
302
+ * the default copy.
303
+ */
304
+ stopReason?: StopReasonKind;
305
+ };
306
+
307
+ export type { StreamAnimationPlugin as S };
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Text content part for multi-modal messages
3
+ */
4
+ type TextContentPart = {
5
+ type: 'text';
6
+ text: string;
7
+ };
8
+ /**
9
+ * Image content part for multi-modal messages
10
+ * Supports base64 data URIs or URLs
11
+ */
12
+ type ImageContentPart = {
13
+ type: 'image';
14
+ image: string;
15
+ mimeType?: string;
16
+ alt?: string;
17
+ };
18
+ /**
19
+ * File content part for multi-modal messages
20
+ * Supports PDF, TXT, DOCX, and other document types
21
+ */
22
+ type FileContentPart = {
23
+ type: 'file';
24
+ data: string;
25
+ mimeType: string;
26
+ filename: string;
27
+ };
28
+ /**
29
+ * Union type for all content part types
30
+ */
31
+ type ContentPart = TextContentPart | ImageContentPart | FileContentPart;
32
+ /**
33
+ * Metadata attached to messages created during agent execution.
34
+ */
35
+ type AgentMessageMetadata = {
36
+ executionId?: string;
37
+ iteration?: number;
38
+ turnId?: string;
39
+ agentName?: string;
40
+ /**
41
+ * When this message was produced by a step inside a nested flow executed
42
+ * as a tool, identifies the parent tool call id. Enables renderers to
43
+ * visually group or indent nested-flow output under its parent tool.
44
+ */
45
+ parentToolId?: string;
46
+ /**
47
+ * Nested flow step id that produced this message (e.g. a `send-stream`
48
+ * or `prompt` step inside the nested flow). Stable key for that step.
49
+ */
50
+ parentStepId?: string;
51
+ /**
52
+ * Set to `true` on a tool-variant message produced from a `step_await`
53
+ * event (`awaitReason: "local_tool_required"`). Signals to UI code that
54
+ * the tool call is a LOCAL tool and the server is paused waiting for a
55
+ * `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
56
+ */
57
+ awaitingLocalTool?: boolean;
58
+ /**
59
+ * Set to `true` once the user has picked / typed / dismissed an answer for
60
+ * an `ask_user_question` tool call, so renderers stop re-mounting the
61
+ * answer-pill sheet for this tool call on subsequent render passes.
62
+ */
63
+ askUserQuestionAnswered?: boolean;
64
+ /**
65
+ * In-progress answers for a multi-question `ask_user_question` payload,
66
+ * keyed by question text. Persisted across refresh so the user lands back
67
+ * where they were if the page reloads mid-flow. Cleared once
68
+ * `askUserQuestionAnswered` flips to `true`.
69
+ */
70
+ askUserQuestionAnswers?: Record<string, string | string[]>;
71
+ /**
72
+ * Current page index for a multi-question `ask_user_question` payload's
73
+ * paginated stepper. Persists alongside `askUserQuestionAnswers`.
74
+ */
75
+ askUserQuestionIndex?: number;
76
+ };
77
+ /**
78
+ * Context passed to plugin lifecycle hooks. Carries the live DOM references
79
+ * and resolved animation settings for the currently-streaming message.
80
+ */
81
+ type StreamAnimationContext = {
82
+ /** The `.persona-message-content` element owning the streamed text. */
83
+ container: HTMLElement;
84
+ /** The outer message bubble element. */
85
+ bubble: HTMLElement;
86
+ /** ID of the streaming message. */
87
+ messageId: string;
88
+ /** Read-only reference to the message being streamed. */
89
+ message: AgentWidgetMessage;
90
+ /** Effective `speed` from `streamAnimation.speed`. */
91
+ speed: number;
92
+ /** Effective `duration` from `streamAnimation.duration`. */
93
+ duration: number;
94
+ };
95
+ /**
96
+ * Pluggable stream animation. Third-party packages and inline registrations
97
+ * implement this interface to add custom reveal effects.
98
+ *
99
+ * Lifecycle:
100
+ * - When the widget mounts and detects a plugin (either passed via config or
101
+ * auto-registered in the IIFE bundle), it injects `styles` once into the
102
+ * widget's style host.
103
+ * - For each streaming assistant message whose `type` matches `name`, the
104
+ * widget applies `containerClass` / `bubbleClass`, wraps text per `wrap`,
105
+ * and — if `useCaret` is true — appends a blinking caret.
106
+ * - Hooks fire after the live DOM is morphed; plugins use stable element IDs
107
+ * and `data-preserve-animation` to safely mutate per-char or per-word spans
108
+ * without idiomorph clobbering in-flight work.
109
+ */
110
+ type StreamAnimationPlugin = {
111
+ /** Plugin identifier. Matches the `type` field in `streamAnimation`. */
112
+ name: string;
113
+ /** Class added to `.persona-message-content` while streaming. */
114
+ containerClass?: string;
115
+ /** Class added to the bubble element (e.g. a one-shot scale animation). */
116
+ bubbleClass?: string;
117
+ /** Wrap mode applied to text nodes during streaming. @default "none" */
118
+ wrap?: "none" | "char" | "word";
119
+ /**
120
+ * HTML tags whose descendant text is skipped during wrapping. Defaults to
121
+ * `["pre", "code", "a", "script", "style"]` — useful for keeping code
122
+ * blocks legible and link click-targets intact. Plugins that want to
123
+ * animate characters inside inline code (e.g. `glyph-cycle`) can narrow
124
+ * the list.
125
+ */
126
+ skipTags?: string[];
127
+ /** Append a blinking caret after the last rendered char/word. */
128
+ useCaret?: boolean;
129
+ /** CSS string injected into the widget style host on first activation. */
130
+ styles?: string;
131
+ /**
132
+ * Optional custom buffering strategy. Returns the portion of `content`
133
+ * that should be rendered during streaming. Use this for buffering
134
+ * schemes beyond the built-in `word` / `line` strategies.
135
+ */
136
+ bufferContent?: (content: string, message: AgentWidgetMessage) => string;
137
+ /**
138
+ * Fires once when the plugin is first activated inside a widget instance.
139
+ * Use this to set up MutationObservers or other long-lived listeners.
140
+ * Return an optional cleanup function that runs on widget destroy.
141
+ */
142
+ onAttach?: (root: HTMLElement | ShadowRoot) => (() => void) | void;
143
+ /** Fires after each render that reaches the live DOM. */
144
+ onAfterRender?: (ctx: StreamAnimationContext) => void;
145
+ /** Fires when a streamed message's `streaming` flag flips to false. */
146
+ onStreamComplete?: (ctx: StreamAnimationContext) => void;
147
+ /**
148
+ * Report whether the plugin still has in-flight animation work for a
149
+ * message. When `true`, the widget keeps rendering the message in its
150
+ * "streaming-animated" mode even after `message.streaming` flips false —
151
+ * preventing the final non-animated render from yanking the rug out from
152
+ * under unfinished per-char cycles or reveals.
153
+ */
154
+ isAnimating?: (message: AgentWidgetMessage) => boolean;
155
+ };
156
+ type AgentWidgetMessageRole = "user" | "assistant" | "system";
157
+ type AgentWidgetReasoning = {
158
+ id: string;
159
+ status: "pending" | "streaming" | "complete";
160
+ chunks: string[];
161
+ startedAt?: number;
162
+ completedAt?: number;
163
+ durationMs?: number;
164
+ };
165
+ type AgentWidgetToolCall = {
166
+ id: string;
167
+ name?: string;
168
+ status: "pending" | "running" | "complete";
169
+ args?: unknown;
170
+ chunks?: string[];
171
+ result?: unknown;
172
+ duration?: number;
173
+ startedAt?: number;
174
+ completedAt?: number;
175
+ durationMs?: number;
176
+ };
177
+ /**
178
+ * Represents a tool approval request in the chat conversation.
179
+ * Created when the agent requires human approval before executing a tool.
180
+ */
181
+ type AgentWidgetApproval = {
182
+ id: string;
183
+ status: "pending" | "approved" | "denied" | "timeout";
184
+ agentId: string;
185
+ executionId: string;
186
+ toolName: string;
187
+ toolType?: string;
188
+ description: string;
189
+ parameters?: unknown;
190
+ resolvedAt?: number;
191
+ };
192
+ type AgentWidgetMessageVariant = "assistant" | "reasoning" | "tool" | "approval";
193
+ /**
194
+ * Per-turn / per-step stop reason emitted by the runtime on
195
+ * `agent_turn_complete` and `step_complete` SSE events. The vocabulary is
196
+ * owned by the upstream Runtype API — do not extend without coordination.
197
+ *
198
+ * - `end_turn` — natural completion (no affordance needed)
199
+ * - `max_tool_calls` — agent loop tripped the configured tool-call ceiling
200
+ * - `length` — provider hit max output tokens
201
+ * - `content_filter` — provider content filter intervened
202
+ * - `error` — provider/runtime error (prefer existing error rendering)
203
+ * - `unknown` — explicitly reported but uninformative
204
+ *
205
+ * Absent (`undefined`) means "not reported" — distinct from `'unknown'`.
206
+ */
207
+ type StopReasonKind = 'end_turn' | 'max_tool_calls' | 'length' | 'content_filter' | 'error' | 'unknown';
208
+ /**
209
+ * Represents a message in the chat conversation.
210
+ *
211
+ * @property id - Unique message identifier
212
+ * @property role - Message role: "user", "assistant", or "system"
213
+ * @property content - Message text content (for display)
214
+ * @property contentParts - Original multi-modal content parts (for API requests)
215
+ * @property createdAt - ISO timestamp when message was created
216
+ * @property streaming - Whether message is still streaming (for assistant messages)
217
+ * @property variant - Message variant for assistant messages: "assistant", "reasoning", or "tool"
218
+ * @property sequence - Message ordering number
219
+ * @property reasoning - Reasoning data for assistant reasoning messages
220
+ * @property toolCall - Tool call data for assistant tool messages
221
+ * @property tools - Array of tool calls
222
+ * @property viaVoice - Set to `true` when a user message is sent via voice recognition.
223
+ * Useful for implementing voice-specific behaviors like auto-reactivation.
224
+ */
225
+ type AgentWidgetMessage = {
226
+ id: string;
227
+ role: AgentWidgetMessageRole;
228
+ content: string;
229
+ createdAt: string;
230
+ /**
231
+ * Original multi-modal content parts for this message.
232
+ * When present, this is sent to the API instead of `content`.
233
+ * The `content` field contains the text-only representation for display.
234
+ */
235
+ contentParts?: ContentPart[];
236
+ streaming?: boolean;
237
+ variant?: AgentWidgetMessageVariant;
238
+ sequence?: number;
239
+ reasoning?: AgentWidgetReasoning;
240
+ toolCall?: AgentWidgetToolCall;
241
+ tools?: AgentWidgetToolCall[];
242
+ /** Approval data for messages with variant "approval" */
243
+ approval?: AgentWidgetApproval;
244
+ viaVoice?: boolean;
245
+ /**
246
+ * Set to `true` on placeholder messages injected during Runtype voice processing.
247
+ * Use this in `messageTransform` to detect and customize voice processing placeholders.
248
+ *
249
+ * @example
250
+ * messageTransform: ({ text, message }) => {
251
+ * if (message.voiceProcessing && message.role === 'user') {
252
+ * return '<div class="my-voice-spinner">Transcribing...</div>';
253
+ * }
254
+ * return text;
255
+ * }
256
+ */
257
+ voiceProcessing?: boolean;
258
+ /**
259
+ * Raw structured payload for this message (e.g., JSON action response).
260
+ * Populated automatically when structured parsers run.
261
+ */
262
+ rawContent?: string;
263
+ /**
264
+ * LLM-specific content for API requests.
265
+ * When present, this is sent to the LLM instead of `content`.
266
+ *
267
+ * Priority for API payload:
268
+ * 1. `contentParts` (if present, used as-is for multi-modal)
269
+ * 2. `llmContent` (if present, sent as string)
270
+ * 3. `rawContent` (backward compatibility with structured parsers)
271
+ * 4. `content` (fallback - display content)
272
+ *
273
+ * The `content` field is always used for UI display.
274
+ *
275
+ * @example
276
+ * // Show full details to user, send summary to LLM
277
+ * {
278
+ * content: "**Product:** iPhone 15 Pro\n**Price:** $1,199\n**SKU:** IP15P-256",
279
+ * llmContent: "[Product search: iPhone 15 Pro, $1199]"
280
+ * }
281
+ */
282
+ llmContent?: string;
283
+ /**
284
+ * Text segment identity for chronological ordering.
285
+ * When present, identifies which text segment this message represents
286
+ * (e.g., "text_0", "text_1") for messages split at tool boundaries.
287
+ */
288
+ partId?: string;
289
+ /**
290
+ * Metadata for messages created during agent loop execution.
291
+ * Contains execution context like iteration number and turn ID.
292
+ */
293
+ agentMetadata?: AgentMessageMetadata;
294
+ /**
295
+ * Per-turn stop reason reported by the runtime on `agent_turn_complete`
296
+ * (agent-loop path) or the last `step_complete` for a prompt step
297
+ * (dispatch / flow path). Absent when the API did not report a value.
298
+ *
299
+ * When set to a non-natural value (`max_tool_calls`, `length`,
300
+ * `content_filter`, `error`), the widget renders an inline notice on
301
+ * the assistant bubble. See `config.copy.stopReasonNotice` to override
302
+ * the default copy.
303
+ */
304
+ stopReason?: StopReasonKind;
305
+ };
306
+
307
+ export type { StreamAnimationPlugin as S };