@runtypelabs/persona 3.21.3 → 3.23.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.
- package/README.md +67 -0
- package/dist/animations/glyph-cycle.cjs +2 -262
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/glyph-cycle.js +2 -235
- package/dist/animations/{types-CWPIj66R.d.cts → types-BZVr1YOV.d.cts} +10 -0
- package/dist/animations/{types-CWPIj66R.d.ts → types-BZVr1YOV.d.ts} +10 -0
- package/dist/animations/wipe.cjs +2 -72
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/animations/wipe.js +2 -45
- package/dist/index.cjs +52 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +474 -6
- package/dist/index.d.ts +474 -6
- package/dist/index.global.js +107 -97
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +52 -45
- package/dist/index.js.map +1 -1
- package/dist/smart-dom-reader.cjs +23 -0
- package/dist/smart-dom-reader.d.cts +4521 -0
- package/dist/smart-dom-reader.d.ts +4521 -0
- package/dist/smart-dom-reader.js +23 -0
- package/dist/testing.cjs +3 -84
- package/dist/testing.js +3 -55
- package/dist/theme-editor.cjs +57 -22501
- package/dist/theme-editor.d.cts +348 -1
- package/dist/theme-editor.d.ts +348 -1
- package/dist/theme-editor.js +57 -22503
- package/package.json +16 -6
- package/src/client.test.ts +165 -0
- package/src/client.ts +144 -23
- package/src/components/event-stream-view.ts +122 -1
- package/src/index.ts +26 -0
- package/src/session.test.ts +258 -0
- package/src/session.ts +886 -30
- package/src/session.webmcp.test.ts +815 -0
- package/src/smart-dom-reader.test.ts +135 -0
- package/src/smart-dom-reader.ts +135 -0
- package/src/theme-editor/color-utils.test.ts +59 -0
- package/src/theme-editor/color-utils.ts +38 -2
- package/src/theme-editor/index.ts +35 -0
- package/src/theme-editor/webmcp/coerce.test.ts +86 -0
- package/src/theme-editor/webmcp/coerce.ts +286 -0
- package/src/theme-editor/webmcp/index.ts +45 -0
- package/src/theme-editor/webmcp/summary.ts +324 -0
- package/src/theme-editor/webmcp/tools.test.ts +205 -0
- package/src/theme-editor/webmcp/tools.ts +795 -0
- package/src/theme-editor/webmcp/types.ts +87 -0
- package/src/types.ts +186 -0
- package/src/ui.composer-keyboard.test.ts +229 -0
- package/src/ui.ts +151 -8
- package/src/utils/composer-history.test.ts +128 -0
- package/src/utils/composer-history.ts +113 -0
- package/src/utils/message-fingerprint.test.ts +20 -0
- package/src/utils/message-fingerprint.ts +2 -0
- package/src/utils/smart-dom-adapter.test.ts +257 -0
- package/src/utils/smart-dom-adapter.ts +217 -0
- package/src/utils/throughput-tracker.test.ts +366 -0
- package/src/utils/throughput-tracker.ts +427 -0
- package/{LICENSE → src/vendor/smart-dom-reader/LICENSE} +2 -2
- package/src/vendor/smart-dom-reader/README.md +61 -0
- package/src/vendor/smart-dom-reader/index.d.ts +476 -0
- package/src/vendor/smart-dom-reader/index.js +1618 -0
- package/src/webmcp-bridge.test.ts +429 -0
- package/src/webmcp-bridge.ts +547 -0
|
@@ -0,0 +1,4521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VENDORED type declarations — @mcp-b/smart-dom-reader v2.3.1 (MIT).
|
|
3
|
+
* Copied verbatim from upstream dist/index.d.mts (only the trailing sourceMappingURL
|
|
4
|
+
* comment removed). See ./index.js and ./README.md for why this is vendored.
|
|
5
|
+
*/
|
|
6
|
+
/* eslint-disable */
|
|
7
|
+
//#region src/types.d.ts
|
|
8
|
+
type ExtractionMode = 'interactive' | 'full' | 'structure' | 'content';
|
|
9
|
+
interface ElementSelector {
|
|
10
|
+
css: string;
|
|
11
|
+
xpath: string;
|
|
12
|
+
textBased?: string;
|
|
13
|
+
dataTestId?: string;
|
|
14
|
+
ariaLabel?: string;
|
|
15
|
+
candidates?: ElementSelectorCandidate[];
|
|
16
|
+
}
|
|
17
|
+
interface ElementSelectorCandidate {
|
|
18
|
+
type: 'id' | 'data-testid' | 'role-aria' | 'name' | 'class-path' | 'css-path' | 'xpath' | 'text';
|
|
19
|
+
value: string;
|
|
20
|
+
score: number;
|
|
21
|
+
}
|
|
22
|
+
interface ElementContext {
|
|
23
|
+
nearestForm?: string;
|
|
24
|
+
nearestSection?: string;
|
|
25
|
+
nearestMain?: string;
|
|
26
|
+
nearestNav?: string;
|
|
27
|
+
parentChain: string[];
|
|
28
|
+
}
|
|
29
|
+
interface ElementInteraction {
|
|
30
|
+
click?: boolean;
|
|
31
|
+
change?: boolean;
|
|
32
|
+
submit?: boolean;
|
|
33
|
+
nav?: boolean;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
hidden?: boolean;
|
|
36
|
+
role?: string;
|
|
37
|
+
form?: string;
|
|
38
|
+
}
|
|
39
|
+
interface ExtractedElement {
|
|
40
|
+
tag: string;
|
|
41
|
+
text: string;
|
|
42
|
+
selector: ElementSelector;
|
|
43
|
+
attributes: Record<string, string>;
|
|
44
|
+
context: ElementContext;
|
|
45
|
+
interaction: ElementInteraction;
|
|
46
|
+
children?: ExtractedElement[];
|
|
47
|
+
}
|
|
48
|
+
interface FormInfo {
|
|
49
|
+
selector: string;
|
|
50
|
+
action?: string;
|
|
51
|
+
method?: string;
|
|
52
|
+
inputs: ExtractedElement[];
|
|
53
|
+
buttons: ExtractedElement[];
|
|
54
|
+
}
|
|
55
|
+
interface PageLandmarks {
|
|
56
|
+
navigation: string[];
|
|
57
|
+
main: string[];
|
|
58
|
+
forms: string[];
|
|
59
|
+
headers: string[];
|
|
60
|
+
footers: string[];
|
|
61
|
+
articles: string[];
|
|
62
|
+
sections: string[];
|
|
63
|
+
}
|
|
64
|
+
interface PageState {
|
|
65
|
+
url: string;
|
|
66
|
+
title: string;
|
|
67
|
+
hasErrors: boolean;
|
|
68
|
+
isLoading: boolean;
|
|
69
|
+
hasModals: boolean;
|
|
70
|
+
hasFocus?: string;
|
|
71
|
+
}
|
|
72
|
+
interface SmartDOMResult {
|
|
73
|
+
mode: ExtractionMode;
|
|
74
|
+
timestamp: number;
|
|
75
|
+
page: PageState;
|
|
76
|
+
landmarks: PageLandmarks;
|
|
77
|
+
interactive: {
|
|
78
|
+
buttons: ExtractedElement[];
|
|
79
|
+
links: ExtractedElement[];
|
|
80
|
+
inputs: ExtractedElement[];
|
|
81
|
+
forms: FormInfo[];
|
|
82
|
+
clickable: ExtractedElement[];
|
|
83
|
+
};
|
|
84
|
+
semantic?: {
|
|
85
|
+
headings: ExtractedElement[];
|
|
86
|
+
images: ExtractedElement[];
|
|
87
|
+
tables: ExtractedElement[];
|
|
88
|
+
lists: ExtractedElement[];
|
|
89
|
+
articles: ExtractedElement[];
|
|
90
|
+
};
|
|
91
|
+
metadata?: {
|
|
92
|
+
totalElements: number;
|
|
93
|
+
extractedElements: number;
|
|
94
|
+
mainContent?: string;
|
|
95
|
+
language?: string;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
interface FilterOptions {
|
|
99
|
+
includeSelectors?: string[];
|
|
100
|
+
excludeSelectors?: string[];
|
|
101
|
+
textContains?: string[];
|
|
102
|
+
textMatches?: RegExp[];
|
|
103
|
+
hasAttributes?: string[];
|
|
104
|
+
attributeValues?: Record<string, string | RegExp>;
|
|
105
|
+
tags?: string[];
|
|
106
|
+
interactionTypes?: Array<keyof ElementInteraction>;
|
|
107
|
+
withinSelectors?: string[];
|
|
108
|
+
nearText?: string;
|
|
109
|
+
}
|
|
110
|
+
interface ExtractionOptions {
|
|
111
|
+
mode: ExtractionMode;
|
|
112
|
+
maxDepth?: number;
|
|
113
|
+
includeHidden?: boolean;
|
|
114
|
+
includeShadowDOM?: boolean;
|
|
115
|
+
includeIframes?: boolean;
|
|
116
|
+
viewportOnly?: boolean;
|
|
117
|
+
mainContentOnly?: boolean;
|
|
118
|
+
customSelectors?: string[];
|
|
119
|
+
attributeTruncateLength?: number;
|
|
120
|
+
dataAttributeTruncateLength?: number;
|
|
121
|
+
textTruncateLength?: number;
|
|
122
|
+
filter?: FilterOptions;
|
|
123
|
+
}
|
|
124
|
+
interface ContentExtractionOptions {
|
|
125
|
+
includeHeadings?: boolean;
|
|
126
|
+
includeLists?: boolean;
|
|
127
|
+
includeTables?: boolean;
|
|
128
|
+
includeMedia?: boolean;
|
|
129
|
+
preserveFormatting?: boolean;
|
|
130
|
+
maxTextLength?: number;
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/markdown-formatter.d.ts
|
|
134
|
+
type MarkdownDetailLevel = 'summary' | 'region' | 'deep';
|
|
135
|
+
interface MarkdownFormatOptions {
|
|
136
|
+
detail?: MarkdownDetailLevel;
|
|
137
|
+
maxTextLength?: number;
|
|
138
|
+
maxElements?: number;
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/bundle-types.d.ts
|
|
142
|
+
type ExtractionMethod = 'extractStructure' | 'extractRegion' | 'extractContent' | 'extractInteractive' | 'extractFull';
|
|
143
|
+
interface BaseExtractionArgs {
|
|
144
|
+
frameSelector?: string;
|
|
145
|
+
formatOptions?: MarkdownFormatOptions;
|
|
146
|
+
}
|
|
147
|
+
interface ExtractStructureArgs extends BaseExtractionArgs {
|
|
148
|
+
selector?: string;
|
|
149
|
+
}
|
|
150
|
+
interface ExtractRegionArgs extends BaseExtractionArgs {
|
|
151
|
+
selector: string;
|
|
152
|
+
mode?: 'interactive' | 'full';
|
|
153
|
+
options?: Partial<ExtractionOptions>;
|
|
154
|
+
}
|
|
155
|
+
interface ExtractContentArgs extends BaseExtractionArgs {
|
|
156
|
+
selector: string;
|
|
157
|
+
options?: ContentExtractionOptions;
|
|
158
|
+
}
|
|
159
|
+
interface ExtractInteractiveArgs extends BaseExtractionArgs {
|
|
160
|
+
selector?: string;
|
|
161
|
+
options?: Partial<ExtractionOptions>;
|
|
162
|
+
}
|
|
163
|
+
interface ExtractFullArgs extends BaseExtractionArgs {
|
|
164
|
+
selector?: string;
|
|
165
|
+
options?: Partial<ExtractionOptions>;
|
|
166
|
+
}
|
|
167
|
+
type ExtractionArgs = {
|
|
168
|
+
extractStructure: ExtractStructureArgs;
|
|
169
|
+
extractRegion: ExtractRegionArgs;
|
|
170
|
+
extractContent: ExtractContentArgs;
|
|
171
|
+
extractInteractive: ExtractInteractiveArgs;
|
|
172
|
+
extractFull: ExtractFullArgs;
|
|
173
|
+
};
|
|
174
|
+
interface ExtractionError {
|
|
175
|
+
error: string;
|
|
176
|
+
}
|
|
177
|
+
type ExtractionResult = string | ExtractionError;
|
|
178
|
+
interface SmartDOMReaderBundle {
|
|
179
|
+
executeExtraction<M extends ExtractionMethod>(method: M, args: ExtractionArgs[M]): ExtractionResult;
|
|
180
|
+
}
|
|
181
|
+
declare global {
|
|
182
|
+
interface Window {
|
|
183
|
+
SmartDOMReaderBundle: SmartDOMReaderBundle;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Enriched DOM context collection for providing richer page information to AI.
|
|
189
|
+
*
|
|
190
|
+
* Captures interactive elements, stable CSS selectors, ARIA roles, data attributes,
|
|
191
|
+
* and visibility state — giving the LLM much better context than basic className/innerText.
|
|
192
|
+
*
|
|
193
|
+
* ## Modes
|
|
194
|
+
*
|
|
195
|
+
* - **structured** (default): collects candidates, scores them with optional {@link ParseRule}
|
|
196
|
+
* hooks, then applies `maxElements`. Rich containers (e.g. product cards) can surface
|
|
197
|
+
* before unrelated static noise.
|
|
198
|
+
* - **simple**: legacy behavior — cap during traversal, interactive-first ordering, no rule
|
|
199
|
+
* scoring or {@link EnrichedPageElement.formattedSummary}.
|
|
200
|
+
*/
|
|
201
|
+
interface EnrichedPageElement {
|
|
202
|
+
/** Stable CSS selector the LLM can use directly */
|
|
203
|
+
selector: string;
|
|
204
|
+
/** Lowercase tag name */
|
|
205
|
+
tagName: string;
|
|
206
|
+
/** Visible text content, trimmed */
|
|
207
|
+
text: string;
|
|
208
|
+
/** ARIA role or null */
|
|
209
|
+
role: string | null;
|
|
210
|
+
/** Interactivity classification */
|
|
211
|
+
interactivity: "clickable" | "input" | "navigable" | "static";
|
|
212
|
+
/** Relevant attributes: id, data-*, href, aria-label, type, value, name */
|
|
213
|
+
attributes: Record<string, string>;
|
|
214
|
+
/**
|
|
215
|
+
* When set (structured mode + matching rule), {@link formatEnrichedContext} prefers this
|
|
216
|
+
* markdown-like line instead of raw `text`.
|
|
217
|
+
*/
|
|
218
|
+
formattedSummary?: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Pure mapper: `@mcp-b/smart-dom-reader` output → Persona's {@link EnrichedPageElement}[].
|
|
223
|
+
*
|
|
224
|
+
* This module imports the smart-dom-reader types with `import type` ONLY, so the
|
|
225
|
+
* library's runtime value is never pulled in here. That keeps the mapper — and its
|
|
226
|
+
* unit test — free of any DOM and free of the (vendored) library at runtime; the
|
|
227
|
+
* types are erased during compilation.
|
|
228
|
+
*
|
|
229
|
+
* The smart-dom-reader returns a structured {@link SmartDOMResult} JSON object with
|
|
230
|
+
* rich selectors (CSS + XPath + ranked candidates). Persona's collect → reason → act
|
|
231
|
+
* loop drives clicks through `document.querySelector` (see `utils/actions.ts`), which
|
|
232
|
+
* cannot pierce shadow roots or evaluate XPath. So this mapper deliberately prefers
|
|
233
|
+
* the **best plain-CSS candidate selector** for each element and skips XPath / text
|
|
234
|
+
* pseudo-selectors, keeping results actionable.
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
/** Options for {@link smartDomResultToEnriched}. */
|
|
238
|
+
interface SmartDomAdapterOptions {
|
|
239
|
+
/**
|
|
240
|
+
* Include non-interactive `semantic` groups (headings, images, tables, lists,
|
|
241
|
+
* articles) when the result has them (i.e. full-mode extraction). Default: true.
|
|
242
|
+
*/
|
|
243
|
+
includeSemantic?: boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Skip elements whose own selector / ancestor chain contains this selector string,
|
|
246
|
+
* so the widget never reports its own shadow-DOM UI. Matched as a substring against
|
|
247
|
+
* the element's candidate selectors and `context.parentChain`. Default: ".persona-host".
|
|
248
|
+
* Pass "" to disable.
|
|
249
|
+
*/
|
|
250
|
+
excludeSelector?: string;
|
|
251
|
+
/** Truncate each element's text to this many characters. Default: 200. */
|
|
252
|
+
maxTextLength?: number;
|
|
253
|
+
/** Optional cap on the number of mapped elements returned. */
|
|
254
|
+
maxElements?: number;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Map a {@link SmartDOMResult} into Persona's {@link EnrichedPageElement}[] shape so it
|
|
258
|
+
* can be formatted by `formatEnrichedContext` and consumed wherever the default
|
|
259
|
+
* `collectEnrichedPageContext` output is.
|
|
260
|
+
*
|
|
261
|
+
* - Maps `interactive.{buttons, links, inputs, clickable}` and, when present and
|
|
262
|
+
* `includeSemantic` is not false, `semantic.{headings, images, tables, lists, articles}`.
|
|
263
|
+
* - Chooses the best plain-CSS selector per element (skipping XPath / shadow-piercing
|
|
264
|
+
* selectors) so results stay actionable via `document.querySelector`.
|
|
265
|
+
* - Drops elements under `excludeSelector` (default `.persona-host`).
|
|
266
|
+
* - Deduplicates by selector and preserves discovery order (interactive before semantic).
|
|
267
|
+
*/
|
|
268
|
+
declare function smartDomResultToEnriched(result: SmartDOMResult, opts?: SmartDomAdapterOptions): EnrichedPageElement[];
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Plugin interface for customizing widget components
|
|
272
|
+
*/
|
|
273
|
+
interface AgentWidgetPlugin {
|
|
274
|
+
/**
|
|
275
|
+
* Unique identifier for the plugin
|
|
276
|
+
*/
|
|
277
|
+
id: string;
|
|
278
|
+
/**
|
|
279
|
+
* Optional priority (higher = runs first). Default: 0
|
|
280
|
+
*/
|
|
281
|
+
priority?: number;
|
|
282
|
+
/**
|
|
283
|
+
* Custom renderer for message bubbles
|
|
284
|
+
* Return null to use default renderer
|
|
285
|
+
*/
|
|
286
|
+
renderMessage?: (context: {
|
|
287
|
+
message: AgentWidgetMessage;
|
|
288
|
+
defaultRenderer: () => HTMLElement;
|
|
289
|
+
config: AgentWidgetConfig;
|
|
290
|
+
}) => HTMLElement | null;
|
|
291
|
+
/**
|
|
292
|
+
* Custom renderer for launcher button
|
|
293
|
+
* Return null to use default renderer
|
|
294
|
+
*/
|
|
295
|
+
renderLauncher?: (context: {
|
|
296
|
+
config: AgentWidgetConfig;
|
|
297
|
+
defaultRenderer: () => HTMLElement;
|
|
298
|
+
onToggle: () => void;
|
|
299
|
+
}) => HTMLElement | null;
|
|
300
|
+
/**
|
|
301
|
+
* Custom renderer for panel header
|
|
302
|
+
* Return null to use default renderer
|
|
303
|
+
*/
|
|
304
|
+
renderHeader?: (context: {
|
|
305
|
+
config: AgentWidgetConfig;
|
|
306
|
+
defaultRenderer: () => HTMLElement;
|
|
307
|
+
onClose?: () => void;
|
|
308
|
+
}) => HTMLElement | null;
|
|
309
|
+
/**
|
|
310
|
+
* Custom renderer for composer/input area
|
|
311
|
+
* Return null to use default renderer
|
|
312
|
+
*/
|
|
313
|
+
renderComposer?: (context: {
|
|
314
|
+
config: AgentWidgetConfig;
|
|
315
|
+
defaultRenderer: () => HTMLElement;
|
|
316
|
+
onSubmit: (text: string) => void;
|
|
317
|
+
/**
|
|
318
|
+
* When true, the assistant stream is active — same moment `session.isStreaming()` becomes true.
|
|
319
|
+
* Prefer wiring controls to `data-persona-composer-disable-when-streaming` plus `setComposerDisabled`
|
|
320
|
+
* in the host, or react to `footer.dataset.personaComposerStreaming === "true"`.
|
|
321
|
+
*/
|
|
322
|
+
streaming: boolean;
|
|
323
|
+
/**
|
|
324
|
+
* Legacy alias: host disables the primary submit control while `streaming` is true.
|
|
325
|
+
* @deprecated Use `streaming` for new plugins.
|
|
326
|
+
*/
|
|
327
|
+
disabled: boolean;
|
|
328
|
+
/** Opens the hidden file input when `config.attachments.enabled` is true (no-op otherwise). */
|
|
329
|
+
openAttachmentPicker: () => void;
|
|
330
|
+
/** From `config.composer.models` */
|
|
331
|
+
models?: Array<{
|
|
332
|
+
id: string;
|
|
333
|
+
label: string;
|
|
334
|
+
}>;
|
|
335
|
+
/** From `config.composer.selectedModelId` */
|
|
336
|
+
selectedModelId?: string;
|
|
337
|
+
/** Updates `config.composer.selectedModelId` for the running widget instance. */
|
|
338
|
+
onModelChange?: (modelId: string) => void;
|
|
339
|
+
/**
|
|
340
|
+
* Same behavior as the built-in mic when voice is enabled.
|
|
341
|
+
* Omitted when `config.voiceRecognition.enabled` is not true.
|
|
342
|
+
*/
|
|
343
|
+
onVoiceToggle?: () => void;
|
|
344
|
+
}) => HTMLElement | null;
|
|
345
|
+
/**
|
|
346
|
+
* Custom renderer for reasoning bubbles
|
|
347
|
+
* Return null to use default renderer
|
|
348
|
+
*/
|
|
349
|
+
renderReasoning?: (context: {
|
|
350
|
+
message: AgentWidgetMessage;
|
|
351
|
+
defaultRenderer: () => HTMLElement;
|
|
352
|
+
config: AgentWidgetConfig;
|
|
353
|
+
}) => HTMLElement | null;
|
|
354
|
+
/**
|
|
355
|
+
* Custom renderer for tool call bubbles
|
|
356
|
+
* Return null to use default renderer
|
|
357
|
+
*/
|
|
358
|
+
renderToolCall?: (context: {
|
|
359
|
+
message: AgentWidgetMessage;
|
|
360
|
+
defaultRenderer: () => HTMLElement;
|
|
361
|
+
config: AgentWidgetConfig;
|
|
362
|
+
}) => HTMLElement | null;
|
|
363
|
+
/**
|
|
364
|
+
* Custom renderer for `ask_user_question` tool calls.
|
|
365
|
+
*
|
|
366
|
+
* When a plugin returns an `HTMLElement`, it is inserted into the transcript
|
|
367
|
+
* in place of the default (which is no transcript bubble — the built-in
|
|
368
|
+
* renders a sheet over the composer). The built-in composer-overlay sheet
|
|
369
|
+
* is suppressed so the plugin's UI fully owns the interaction.
|
|
370
|
+
*
|
|
371
|
+
* Return `null` to fall through to the built-in overlay sheet.
|
|
372
|
+
*
|
|
373
|
+
* The context gives you a pre-parsed `payload` (may be partial while the
|
|
374
|
+
* tool call is still streaming — check `complete`) and two callbacks:
|
|
375
|
+
* `resolve(answer)` resumes the paused LOCAL tool with the user's answer,
|
|
376
|
+
* and `dismiss()` cancels with the sentinel `"(dismissed)"`.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```typescript
|
|
380
|
+
* renderAskUserQuestion: ({ payload, resolve, dismiss }) => {
|
|
381
|
+
* const prompt = payload.questions?.[0];
|
|
382
|
+
* if (!prompt) return null;
|
|
383
|
+
* const root = document.createElement("div");
|
|
384
|
+
* root.textContent = prompt.question ?? "";
|
|
385
|
+
* (prompt.options ?? []).forEach((option) => {
|
|
386
|
+
* const btn = document.createElement("button");
|
|
387
|
+
* btn.textContent = option.label;
|
|
388
|
+
* btn.addEventListener("click", () => resolve(option.label));
|
|
389
|
+
* root.appendChild(btn);
|
|
390
|
+
* });
|
|
391
|
+
* return root;
|
|
392
|
+
* }
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
renderAskUserQuestion?: (context: {
|
|
396
|
+
message: AgentWidgetMessage;
|
|
397
|
+
/**
|
|
398
|
+
* Parsed `{ questions: [...] }` payload. May be partial while the tool
|
|
399
|
+
* call is still streaming; see `complete`. `null` when no payload has
|
|
400
|
+
* arrived yet.
|
|
401
|
+
*/
|
|
402
|
+
payload: Partial<AskUserQuestionPayload> | null;
|
|
403
|
+
/** `true` once the tool-call args have fully streamed in. */
|
|
404
|
+
complete: boolean;
|
|
405
|
+
/**
|
|
406
|
+
* Resume the paused LOCAL tool with the user's answer. Posts to the
|
|
407
|
+
* resume endpoint, pipes the SSE stream back into the session, and
|
|
408
|
+
* appends a user-visible answer bubble to the transcript.
|
|
409
|
+
*/
|
|
410
|
+
resolve: (answer: string) => void;
|
|
411
|
+
/**
|
|
412
|
+
* Cancel the question. Resumes with the sentinel `"(dismissed)"` so the
|
|
413
|
+
* server doesn't sit in `waiting_for_local` forever. Idempotent.
|
|
414
|
+
*/
|
|
415
|
+
dismiss: () => void;
|
|
416
|
+
config: AgentWidgetConfig;
|
|
417
|
+
}) => HTMLElement | null;
|
|
418
|
+
/**
|
|
419
|
+
* Custom renderer for approval bubbles
|
|
420
|
+
* Return null to use default renderer
|
|
421
|
+
*/
|
|
422
|
+
renderApproval?: (context: {
|
|
423
|
+
message: AgentWidgetMessage;
|
|
424
|
+
defaultRenderer: () => HTMLElement;
|
|
425
|
+
config: AgentWidgetConfig;
|
|
426
|
+
}) => HTMLElement | null;
|
|
427
|
+
/**
|
|
428
|
+
* Custom renderer for loading indicator
|
|
429
|
+
* Return null to use default renderer (or config-based renderer)
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```typescript
|
|
433
|
+
* renderLoadingIndicator: ({ location, defaultRenderer }) => {
|
|
434
|
+
* if (location === 'standalone') {
|
|
435
|
+
* const el = document.createElement('div');
|
|
436
|
+
* el.textContent = 'Thinking...';
|
|
437
|
+
* return el;
|
|
438
|
+
* }
|
|
439
|
+
* return defaultRenderer();
|
|
440
|
+
* }
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
renderLoadingIndicator?: (context: LoadingIndicatorRenderContext) => HTMLElement | null;
|
|
444
|
+
/**
|
|
445
|
+
* Custom renderer for idle state indicator.
|
|
446
|
+
* Called when the widget is idle (not streaming) and has at least one message.
|
|
447
|
+
* Return an HTMLElement to display, or null to hide (default).
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```typescript
|
|
451
|
+
* renderIdleIndicator: ({ lastMessage, messageCount }) => {
|
|
452
|
+
* if (messageCount === 0) return null;
|
|
453
|
+
* if (lastMessage?.role !== 'assistant') return null;
|
|
454
|
+
* const el = document.createElement('div');
|
|
455
|
+
* el.className = 'idle-pulse';
|
|
456
|
+
* el.setAttribute('data-preserve-animation', 'true');
|
|
457
|
+
* return el;
|
|
458
|
+
* }
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
renderIdleIndicator?: (context: IdleIndicatorRenderContext) => HTMLElement | null;
|
|
462
|
+
/**
|
|
463
|
+
* Custom renderer for the entire event stream view.
|
|
464
|
+
* Return null to use default renderer.
|
|
465
|
+
*/
|
|
466
|
+
renderEventStreamView?: (context: EventStreamViewRenderContext) => HTMLElement | null;
|
|
467
|
+
/**
|
|
468
|
+
* Custom renderer for individual event stream rows.
|
|
469
|
+
* Return null to use default renderer.
|
|
470
|
+
*/
|
|
471
|
+
renderEventStreamRow?: (context: EventStreamRowRenderContext) => HTMLElement | null;
|
|
472
|
+
/**
|
|
473
|
+
* Custom renderer for the event stream toolbar/header bar.
|
|
474
|
+
* Return null to use default renderer.
|
|
475
|
+
*/
|
|
476
|
+
renderEventStreamToolbar?: (context: EventStreamToolbarRenderContext) => HTMLElement | null;
|
|
477
|
+
/**
|
|
478
|
+
* Custom renderer for the expanded event payload display.
|
|
479
|
+
* Return null to use default renderer.
|
|
480
|
+
*/
|
|
481
|
+
renderEventStreamPayload?: (context: EventStreamPayloadRenderContext) => HTMLElement | null;
|
|
482
|
+
/**
|
|
483
|
+
* Called when plugin is registered
|
|
484
|
+
*/
|
|
485
|
+
onRegister?: () => void;
|
|
486
|
+
/**
|
|
487
|
+
* Called when plugin is unregistered
|
|
488
|
+
*/
|
|
489
|
+
onUnregister?: () => void;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
type TokenType = 'color' | 'spacing' | 'typography' | 'shadow' | 'border' | 'radius';
|
|
493
|
+
type TokenReference<_T extends TokenType = TokenType> = string;
|
|
494
|
+
interface ColorShade {
|
|
495
|
+
50?: string;
|
|
496
|
+
100?: string;
|
|
497
|
+
200?: string;
|
|
498
|
+
300?: string;
|
|
499
|
+
400?: string;
|
|
500
|
+
500?: string;
|
|
501
|
+
600?: string;
|
|
502
|
+
700?: string;
|
|
503
|
+
800?: string;
|
|
504
|
+
900?: string;
|
|
505
|
+
950?: string;
|
|
506
|
+
[key: string]: string | undefined;
|
|
507
|
+
}
|
|
508
|
+
interface ColorPalette {
|
|
509
|
+
gray: ColorShade;
|
|
510
|
+
primary: ColorShade;
|
|
511
|
+
secondary: ColorShade;
|
|
512
|
+
accent: ColorShade;
|
|
513
|
+
success: ColorShade;
|
|
514
|
+
warning: ColorShade;
|
|
515
|
+
error: ColorShade;
|
|
516
|
+
info: ColorShade;
|
|
517
|
+
[key: string]: ColorShade;
|
|
518
|
+
}
|
|
519
|
+
interface SpacingScale {
|
|
520
|
+
0: string;
|
|
521
|
+
1: string;
|
|
522
|
+
2: string;
|
|
523
|
+
3: string;
|
|
524
|
+
4: string;
|
|
525
|
+
5: string;
|
|
526
|
+
6: string;
|
|
527
|
+
8: string;
|
|
528
|
+
10: string;
|
|
529
|
+
12: string;
|
|
530
|
+
16: string;
|
|
531
|
+
20: string;
|
|
532
|
+
24: string;
|
|
533
|
+
32: string;
|
|
534
|
+
40: string;
|
|
535
|
+
48: string;
|
|
536
|
+
56: string;
|
|
537
|
+
64: string;
|
|
538
|
+
[key: string]: string;
|
|
539
|
+
}
|
|
540
|
+
interface ShadowScale {
|
|
541
|
+
none: string;
|
|
542
|
+
sm: string;
|
|
543
|
+
md: string;
|
|
544
|
+
lg: string;
|
|
545
|
+
xl: string;
|
|
546
|
+
'2xl': string;
|
|
547
|
+
[key: string]: string;
|
|
548
|
+
}
|
|
549
|
+
interface BorderScale {
|
|
550
|
+
none: string;
|
|
551
|
+
sm: string;
|
|
552
|
+
md: string;
|
|
553
|
+
lg: string;
|
|
554
|
+
[key: string]: string;
|
|
555
|
+
}
|
|
556
|
+
interface RadiusScale {
|
|
557
|
+
none: string;
|
|
558
|
+
sm: string;
|
|
559
|
+
md: string;
|
|
560
|
+
lg: string;
|
|
561
|
+
xl: string;
|
|
562
|
+
full: string;
|
|
563
|
+
[key: string]: string;
|
|
564
|
+
}
|
|
565
|
+
interface TypographyScale {
|
|
566
|
+
fontFamily: {
|
|
567
|
+
sans: string;
|
|
568
|
+
serif: string;
|
|
569
|
+
mono: string;
|
|
570
|
+
};
|
|
571
|
+
fontSize: {
|
|
572
|
+
xs: string;
|
|
573
|
+
sm: string;
|
|
574
|
+
base: string;
|
|
575
|
+
lg: string;
|
|
576
|
+
xl: string;
|
|
577
|
+
'2xl': string;
|
|
578
|
+
'3xl': string;
|
|
579
|
+
'4xl': string;
|
|
580
|
+
};
|
|
581
|
+
fontWeight: {
|
|
582
|
+
normal: string;
|
|
583
|
+
medium: string;
|
|
584
|
+
semibold: string;
|
|
585
|
+
bold: string;
|
|
586
|
+
};
|
|
587
|
+
lineHeight: {
|
|
588
|
+
tight: string;
|
|
589
|
+
normal: string;
|
|
590
|
+
relaxed: string;
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
interface SemanticColors {
|
|
594
|
+
primary: TokenReference<'color'>;
|
|
595
|
+
secondary: TokenReference<'color'>;
|
|
596
|
+
accent: TokenReference<'color'>;
|
|
597
|
+
surface: TokenReference<'color'>;
|
|
598
|
+
background: TokenReference<'color'>;
|
|
599
|
+
container: TokenReference<'color'>;
|
|
600
|
+
text: TokenReference<'color'>;
|
|
601
|
+
textMuted: TokenReference<'color'>;
|
|
602
|
+
textInverse: TokenReference<'color'>;
|
|
603
|
+
border: TokenReference<'color'>;
|
|
604
|
+
divider: TokenReference<'color'>;
|
|
605
|
+
interactive: {
|
|
606
|
+
default: TokenReference<'color'>;
|
|
607
|
+
hover: TokenReference<'color'>;
|
|
608
|
+
focus: TokenReference<'color'>;
|
|
609
|
+
active: TokenReference<'color'>;
|
|
610
|
+
disabled: TokenReference<'color'>;
|
|
611
|
+
};
|
|
612
|
+
feedback: {
|
|
613
|
+
success: TokenReference<'color'>;
|
|
614
|
+
warning: TokenReference<'color'>;
|
|
615
|
+
error: TokenReference<'color'>;
|
|
616
|
+
info: TokenReference<'color'>;
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
interface SemanticSpacing {
|
|
620
|
+
xs: TokenReference<'spacing'>;
|
|
621
|
+
sm: TokenReference<'spacing'>;
|
|
622
|
+
md: TokenReference<'spacing'>;
|
|
623
|
+
lg: TokenReference<'spacing'>;
|
|
624
|
+
xl: TokenReference<'spacing'>;
|
|
625
|
+
'2xl': TokenReference<'spacing'>;
|
|
626
|
+
}
|
|
627
|
+
interface SemanticTypography {
|
|
628
|
+
fontFamily: TokenReference<'typography'>;
|
|
629
|
+
fontSize: TokenReference<'typography'>;
|
|
630
|
+
fontWeight: TokenReference<'typography'>;
|
|
631
|
+
lineHeight: TokenReference<'typography'>;
|
|
632
|
+
}
|
|
633
|
+
interface SemanticTokens {
|
|
634
|
+
colors: SemanticColors;
|
|
635
|
+
spacing: SemanticSpacing;
|
|
636
|
+
typography: SemanticTypography;
|
|
637
|
+
}
|
|
638
|
+
interface ComponentTokenSet {
|
|
639
|
+
background?: TokenReference<'color'>;
|
|
640
|
+
foreground?: TokenReference<'color'>;
|
|
641
|
+
border?: TokenReference<'color'>;
|
|
642
|
+
borderRadius?: TokenReference<'radius'>;
|
|
643
|
+
padding?: TokenReference<'spacing'>;
|
|
644
|
+
margin?: TokenReference<'spacing'>;
|
|
645
|
+
shadow?: TokenReference<'shadow'>;
|
|
646
|
+
opacity?: number;
|
|
647
|
+
}
|
|
648
|
+
interface ButtonTokens extends ComponentTokenSet {
|
|
649
|
+
primary: ComponentTokenSet;
|
|
650
|
+
secondary: ComponentTokenSet;
|
|
651
|
+
ghost: ComponentTokenSet;
|
|
652
|
+
}
|
|
653
|
+
interface InputTokens extends ComponentTokenSet {
|
|
654
|
+
background: TokenReference<'color'>;
|
|
655
|
+
placeholder: TokenReference<'color'>;
|
|
656
|
+
focus: {
|
|
657
|
+
border: TokenReference<'color'>;
|
|
658
|
+
ring: TokenReference<'color'>;
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
interface LauncherTokens extends ComponentTokenSet {
|
|
662
|
+
size: string;
|
|
663
|
+
iconSize: string;
|
|
664
|
+
shadow: TokenReference<'shadow'>;
|
|
665
|
+
}
|
|
666
|
+
interface PanelTokens extends ComponentTokenSet {
|
|
667
|
+
width: string;
|
|
668
|
+
maxWidth: string;
|
|
669
|
+
height: string;
|
|
670
|
+
maxHeight: string;
|
|
671
|
+
}
|
|
672
|
+
interface HeaderTokens extends ComponentTokenSet {
|
|
673
|
+
background: TokenReference<'color'>;
|
|
674
|
+
border: TokenReference<'color'>;
|
|
675
|
+
borderRadius: TokenReference<'radius'>;
|
|
676
|
+
/** Background of the rounded avatar tile next to the title (Lucide / emoji / image). */
|
|
677
|
+
iconBackground: TokenReference<'color'>;
|
|
678
|
+
/** Foreground (glyph stroke or emoji text) on the header avatar tile. */
|
|
679
|
+
iconForeground: TokenReference<'color'>;
|
|
680
|
+
/** Header title line (next to the icon, or minimal layout title). */
|
|
681
|
+
titleForeground: TokenReference<'color'>;
|
|
682
|
+
/** Header subtitle line under the title. */
|
|
683
|
+
subtitleForeground: TokenReference<'color'>;
|
|
684
|
+
/** Default color for clear / close icon buttons when launcher overrides are unset. */
|
|
685
|
+
actionIconForeground: TokenReference<'color'>;
|
|
686
|
+
/** Box-shadow on the header (e.g., a fade shadow to replace the default border). */
|
|
687
|
+
shadow?: string;
|
|
688
|
+
/** Override the header bottom border (e.g., `none`). */
|
|
689
|
+
borderBottom?: string;
|
|
690
|
+
}
|
|
691
|
+
interface MessageTokens {
|
|
692
|
+
user: {
|
|
693
|
+
background: TokenReference<'color'>;
|
|
694
|
+
text: TokenReference<'color'>;
|
|
695
|
+
borderRadius: TokenReference<'radius'>;
|
|
696
|
+
/** User bubble box-shadow (token ref or raw CSS, e.g. `none`). */
|
|
697
|
+
shadow?: string;
|
|
698
|
+
};
|
|
699
|
+
assistant: {
|
|
700
|
+
background: TokenReference<'color'>;
|
|
701
|
+
text: TokenReference<'color'>;
|
|
702
|
+
borderRadius: TokenReference<'radius'>;
|
|
703
|
+
/** Assistant bubble border color (CSS color). */
|
|
704
|
+
border?: TokenReference<'color'>;
|
|
705
|
+
/** Assistant bubble box-shadow (token ref or raw CSS, e.g. `none`). */
|
|
706
|
+
shadow?: string;
|
|
707
|
+
};
|
|
708
|
+
/** Border color between messages in the thread. */
|
|
709
|
+
border?: TokenReference<'color'>;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Welcome / intro card rendered above the message list when no messages exist.
|
|
713
|
+
* Set `copy.showWelcomeCard: false` to hide it; use `layout.slots["body-top"]`
|
|
714
|
+
* to replace it wholesale.
|
|
715
|
+
*/
|
|
716
|
+
interface IntroCardTokens extends ComponentTokenSet {
|
|
717
|
+
background?: TokenReference<'color'>;
|
|
718
|
+
borderRadius?: TokenReference<'radius'>;
|
|
719
|
+
padding?: TokenReference<'spacing'>;
|
|
720
|
+
/** Box-shadow on the intro card (token ref or raw CSS, e.g. `none`). */
|
|
721
|
+
shadow?: string;
|
|
722
|
+
}
|
|
723
|
+
/** Collapsible widget chrome (tool bubbles, reasoning bubbles, approval bubbles). */
|
|
724
|
+
interface CollapsibleWidgetTokens {
|
|
725
|
+
/** Background for content areas. */
|
|
726
|
+
container?: TokenReference<'color'>;
|
|
727
|
+
/** Background for code blocks inside collapsible sections. */
|
|
728
|
+
surface?: TokenReference<'color'>;
|
|
729
|
+
/** Border color for collapsible sections. */
|
|
730
|
+
border?: TokenReference<'color'>;
|
|
731
|
+
}
|
|
732
|
+
interface MarkdownTokens {
|
|
733
|
+
inlineCode: {
|
|
734
|
+
background: TokenReference<'color'>;
|
|
735
|
+
foreground: TokenReference<'color'>;
|
|
736
|
+
};
|
|
737
|
+
/** Foreground for `<a>` in rendered markdown (assistant bubbles + artifact pane). */
|
|
738
|
+
link?: {
|
|
739
|
+
foreground: TokenReference<'color'>;
|
|
740
|
+
};
|
|
741
|
+
/**
|
|
742
|
+
* Body font for rendered markdown blocks (artifact pane + markdown bubbles).
|
|
743
|
+
* Use a raw CSS `font-family` value, e.g. `Georgia, serif`.
|
|
744
|
+
*/
|
|
745
|
+
prose?: {
|
|
746
|
+
fontFamily?: string;
|
|
747
|
+
};
|
|
748
|
+
/** Optional heading scale overrides (raw CSS or resolvable token paths). */
|
|
749
|
+
heading?: {
|
|
750
|
+
h1?: {
|
|
751
|
+
fontSize?: string;
|
|
752
|
+
fontWeight?: string;
|
|
753
|
+
};
|
|
754
|
+
h2?: {
|
|
755
|
+
fontSize?: string;
|
|
756
|
+
fontWeight?: string;
|
|
757
|
+
};
|
|
758
|
+
};
|
|
759
|
+
/** Fenced code block styling. */
|
|
760
|
+
codeBlock?: {
|
|
761
|
+
background?: TokenReference<'color'>;
|
|
762
|
+
borderColor?: TokenReference<'color'>;
|
|
763
|
+
textColor?: TokenReference<'color'>;
|
|
764
|
+
};
|
|
765
|
+
/** Table styling. */
|
|
766
|
+
table?: {
|
|
767
|
+
headerBackground?: TokenReference<'color'>;
|
|
768
|
+
borderColor?: TokenReference<'color'>;
|
|
769
|
+
};
|
|
770
|
+
/** Horizontal rule styling. */
|
|
771
|
+
hr?: {
|
|
772
|
+
color?: TokenReference<'color'>;
|
|
773
|
+
};
|
|
774
|
+
/** Blockquote styling. */
|
|
775
|
+
blockquote?: {
|
|
776
|
+
borderColor?: TokenReference<'color'>;
|
|
777
|
+
background?: TokenReference<'color'>;
|
|
778
|
+
textColor?: TokenReference<'color'>;
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
interface VoiceTokens {
|
|
782
|
+
recording: {
|
|
783
|
+
indicator: TokenReference<'color'>;
|
|
784
|
+
background: TokenReference<'color'>;
|
|
785
|
+
border: TokenReference<'color'>;
|
|
786
|
+
};
|
|
787
|
+
processing: {
|
|
788
|
+
icon: TokenReference<'color'>;
|
|
789
|
+
background: TokenReference<'color'>;
|
|
790
|
+
};
|
|
791
|
+
speaking: {
|
|
792
|
+
icon: TokenReference<'color'>;
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
interface ApprovalTokens {
|
|
796
|
+
requested: {
|
|
797
|
+
background: TokenReference<'color'>;
|
|
798
|
+
border: TokenReference<'color'>;
|
|
799
|
+
text: TokenReference<'color'>;
|
|
800
|
+
};
|
|
801
|
+
approve: ComponentTokenSet;
|
|
802
|
+
deny: ComponentTokenSet;
|
|
803
|
+
}
|
|
804
|
+
interface AttachmentTokens {
|
|
805
|
+
image: {
|
|
806
|
+
background: TokenReference<'color'>;
|
|
807
|
+
border: TokenReference<'color'>;
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
/** Tool-call row chrome (collapsible tool bubbles). */
|
|
811
|
+
interface ToolBubbleTokens {
|
|
812
|
+
/** Box-shadow for tool bubbles (token ref or raw CSS, e.g. `none`). */
|
|
813
|
+
shadow: string;
|
|
814
|
+
}
|
|
815
|
+
/** Reasoning / “thinking” row chrome. */
|
|
816
|
+
interface ReasoningBubbleTokens {
|
|
817
|
+
shadow: string;
|
|
818
|
+
}
|
|
819
|
+
/** Composer (message input) chrome. */
|
|
820
|
+
interface ComposerChromeTokens {
|
|
821
|
+
/** Box-shadow on the composer form (raw CSS, e.g. `none`). */
|
|
822
|
+
shadow: string;
|
|
823
|
+
}
|
|
824
|
+
/** Artifact toolbar chrome. */
|
|
825
|
+
interface ArtifactToolbarTokens {
|
|
826
|
+
iconHoverColor?: string;
|
|
827
|
+
iconHoverBackground?: string;
|
|
828
|
+
iconPadding?: string;
|
|
829
|
+
iconBorderRadius?: string;
|
|
830
|
+
iconBorder?: string;
|
|
831
|
+
toggleGroupGap?: string;
|
|
832
|
+
toggleBorderRadius?: string;
|
|
833
|
+
copyBackground?: string;
|
|
834
|
+
copyBorder?: string;
|
|
835
|
+
copyColor?: string;
|
|
836
|
+
copyBorderRadius?: string;
|
|
837
|
+
copyPadding?: string;
|
|
838
|
+
copyMenuBackground?: string;
|
|
839
|
+
copyMenuBorder?: string;
|
|
840
|
+
copyMenuShadow?: string;
|
|
841
|
+
copyMenuBorderRadius?: string;
|
|
842
|
+
copyMenuItemHoverBackground?: string;
|
|
843
|
+
/** Base background of icon buttons (defaults to --persona-surface). */
|
|
844
|
+
iconBackground?: string;
|
|
845
|
+
/** Border on the toolbar (e.g., `none` to remove the bottom border). */
|
|
846
|
+
toolbarBorder?: string;
|
|
847
|
+
}
|
|
848
|
+
/** Artifact tab strip chrome. */
|
|
849
|
+
interface ArtifactTabTokens {
|
|
850
|
+
background?: string;
|
|
851
|
+
activeBackground?: string;
|
|
852
|
+
activeBorder?: string;
|
|
853
|
+
borderRadius?: string;
|
|
854
|
+
textColor?: string;
|
|
855
|
+
/** Hover background for inactive tabs. */
|
|
856
|
+
hoverBackground?: string;
|
|
857
|
+
/** Tab list container background. */
|
|
858
|
+
listBackground?: string;
|
|
859
|
+
/** Tab list container border color. */
|
|
860
|
+
listBorderColor?: string;
|
|
861
|
+
/** Tab list container padding (CSS shorthand). */
|
|
862
|
+
listPadding?: string;
|
|
863
|
+
}
|
|
864
|
+
/** Artifact pane chrome. */
|
|
865
|
+
interface ArtifactPaneTokens {
|
|
866
|
+
/**
|
|
867
|
+
* Background for the artifact column (toolbar + content), resolved from the theme.
|
|
868
|
+
* Defaults to `semantic.colors.container` so the pane matches assistant message surfaces.
|
|
869
|
+
* `features.artifacts.layout.paneBackground` still wins when set (layout escape hatch).
|
|
870
|
+
*/
|
|
871
|
+
background?: string;
|
|
872
|
+
toolbarBackground?: string;
|
|
873
|
+
}
|
|
874
|
+
/** Icon button chrome (used by createIconButton). */
|
|
875
|
+
interface IconButtonTokens {
|
|
876
|
+
background?: string;
|
|
877
|
+
border?: string;
|
|
878
|
+
color?: string;
|
|
879
|
+
padding?: string;
|
|
880
|
+
borderRadius?: string;
|
|
881
|
+
hoverBackground?: string;
|
|
882
|
+
hoverColor?: string;
|
|
883
|
+
/** Background when aria-pressed="true". */
|
|
884
|
+
activeBackground?: string;
|
|
885
|
+
/** Border color when aria-pressed="true". */
|
|
886
|
+
activeBorder?: string;
|
|
887
|
+
}
|
|
888
|
+
/** Label button chrome (used by createLabelButton). */
|
|
889
|
+
interface LabelButtonTokens {
|
|
890
|
+
background?: string;
|
|
891
|
+
border?: string;
|
|
892
|
+
color?: string;
|
|
893
|
+
padding?: string;
|
|
894
|
+
borderRadius?: string;
|
|
895
|
+
hoverBackground?: string;
|
|
896
|
+
fontSize?: string;
|
|
897
|
+
gap?: string;
|
|
898
|
+
}
|
|
899
|
+
/** Scroll-to-bottom pill chrome shared by transcript + event stream. */
|
|
900
|
+
interface ScrollToBottomTokens extends ComponentTokenSet {
|
|
901
|
+
size?: string;
|
|
902
|
+
gap?: string;
|
|
903
|
+
fontSize?: string;
|
|
904
|
+
iconSize?: string;
|
|
905
|
+
}
|
|
906
|
+
/** Toggle group chrome (used by createToggleGroup). */
|
|
907
|
+
interface ToggleGroupTokens {
|
|
908
|
+
/** Gap between toggle buttons. Default: 0 (connected). */
|
|
909
|
+
gap?: string;
|
|
910
|
+
/** Border radius for first/last buttons. */
|
|
911
|
+
borderRadius?: string;
|
|
912
|
+
}
|
|
913
|
+
interface ComponentTokens {
|
|
914
|
+
button: ButtonTokens;
|
|
915
|
+
input: InputTokens;
|
|
916
|
+
launcher: LauncherTokens;
|
|
917
|
+
panel: PanelTokens;
|
|
918
|
+
header: HeaderTokens;
|
|
919
|
+
message: MessageTokens;
|
|
920
|
+
/** Welcome / intro card shown above the message list. */
|
|
921
|
+
introCard?: IntroCardTokens;
|
|
922
|
+
/** Markdown surfaces (chat + artifact pane). */
|
|
923
|
+
markdown?: MarkdownTokens;
|
|
924
|
+
voice: VoiceTokens;
|
|
925
|
+
approval: ApprovalTokens;
|
|
926
|
+
attachment: AttachmentTokens;
|
|
927
|
+
toolBubble: ToolBubbleTokens;
|
|
928
|
+
reasoningBubble: ReasoningBubbleTokens;
|
|
929
|
+
composer: ComposerChromeTokens;
|
|
930
|
+
/** Icon button styling tokens. */
|
|
931
|
+
iconButton?: IconButtonTokens;
|
|
932
|
+
/** Label button styling tokens. */
|
|
933
|
+
labelButton?: LabelButtonTokens;
|
|
934
|
+
/** Scroll-to-bottom indicator styling tokens. */
|
|
935
|
+
scrollToBottom?: ScrollToBottomTokens;
|
|
936
|
+
/** Toggle group styling tokens. */
|
|
937
|
+
toggleGroup?: ToggleGroupTokens;
|
|
938
|
+
/** Artifact toolbar, tab strip, and pane chrome. */
|
|
939
|
+
artifact?: {
|
|
940
|
+
toolbar?: ArtifactToolbarTokens;
|
|
941
|
+
tab?: ArtifactTabTokens;
|
|
942
|
+
pane?: ArtifactPaneTokens;
|
|
943
|
+
};
|
|
944
|
+
/** Collapsible widget chrome (tool/reasoning/approval bubbles). */
|
|
945
|
+
collapsibleWidget?: CollapsibleWidgetTokens;
|
|
946
|
+
}
|
|
947
|
+
interface PaletteExtras {
|
|
948
|
+
transitions?: Record<string, string>;
|
|
949
|
+
easings?: Record<string, string>;
|
|
950
|
+
}
|
|
951
|
+
interface PersonaThemeBase {
|
|
952
|
+
palette: {
|
|
953
|
+
colors: ColorPalette;
|
|
954
|
+
spacing: SpacingScale;
|
|
955
|
+
typography: TypographyScale;
|
|
956
|
+
shadows: ShadowScale;
|
|
957
|
+
borders: BorderScale;
|
|
958
|
+
radius: RadiusScale;
|
|
959
|
+
} & PaletteExtras;
|
|
960
|
+
}
|
|
961
|
+
interface PersonaThemeSemantic {
|
|
962
|
+
semantic: SemanticTokens;
|
|
963
|
+
}
|
|
964
|
+
interface PersonaThemeComponents {
|
|
965
|
+
components: ComponentTokens;
|
|
966
|
+
}
|
|
967
|
+
type PersonaTheme = PersonaThemeBase & PersonaThemeSemantic & PersonaThemeComponents;
|
|
968
|
+
/** Recursive partial for `config.theme` / `config.darkTheme` overrides. */
|
|
969
|
+
type DeepPartial<T> = T extends object ? {
|
|
970
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
971
|
+
} : T;
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Text content part for multi-modal messages
|
|
975
|
+
*/
|
|
976
|
+
type TextContentPart = {
|
|
977
|
+
type: 'text';
|
|
978
|
+
text: string;
|
|
979
|
+
};
|
|
980
|
+
/**
|
|
981
|
+
* Image content part for multi-modal messages
|
|
982
|
+
* Supports base64 data URIs or URLs
|
|
983
|
+
*/
|
|
984
|
+
type ImageContentPart = {
|
|
985
|
+
type: 'image';
|
|
986
|
+
image: string;
|
|
987
|
+
mimeType?: string;
|
|
988
|
+
alt?: string;
|
|
989
|
+
};
|
|
990
|
+
/**
|
|
991
|
+
* File content part for multi-modal messages
|
|
992
|
+
* Supports PDF, TXT, DOCX, and other document types
|
|
993
|
+
*/
|
|
994
|
+
type FileContentPart = {
|
|
995
|
+
type: 'file';
|
|
996
|
+
data: string;
|
|
997
|
+
mimeType: string;
|
|
998
|
+
filename: string;
|
|
999
|
+
};
|
|
1000
|
+
/**
|
|
1001
|
+
* Audio content part for multi-modal messages
|
|
1002
|
+
* Supports base64 data URIs or URLs
|
|
1003
|
+
*/
|
|
1004
|
+
type AudioContentPart = {
|
|
1005
|
+
type: 'audio';
|
|
1006
|
+
audio: string;
|
|
1007
|
+
mimeType?: string;
|
|
1008
|
+
};
|
|
1009
|
+
/**
|
|
1010
|
+
* Video content part for multi-modal messages
|
|
1011
|
+
* Supports base64 data URIs or URLs
|
|
1012
|
+
*/
|
|
1013
|
+
type VideoContentPart = {
|
|
1014
|
+
type: 'video';
|
|
1015
|
+
video: string;
|
|
1016
|
+
mimeType?: string;
|
|
1017
|
+
};
|
|
1018
|
+
/**
|
|
1019
|
+
* Union type for all content part types
|
|
1020
|
+
*/
|
|
1021
|
+
type ContentPart = TextContentPart | ImageContentPart | FileContentPart | AudioContentPart | VideoContentPart;
|
|
1022
|
+
/**
|
|
1023
|
+
* Message content can be a simple string or an array of content parts
|
|
1024
|
+
*/
|
|
1025
|
+
type MessageContent = string | ContentPart[];
|
|
1026
|
+
type AgentWidgetContextProviderContext = {
|
|
1027
|
+
messages: AgentWidgetMessage[];
|
|
1028
|
+
config: AgentWidgetConfig;
|
|
1029
|
+
};
|
|
1030
|
+
type AgentWidgetContextProvider = (context: AgentWidgetContextProviderContext) => Record<string, unknown> | void | Promise<Record<string, unknown> | void>;
|
|
1031
|
+
type AgentWidgetRequestPayloadMessage = {
|
|
1032
|
+
role: AgentWidgetMessageRole;
|
|
1033
|
+
content: MessageContent;
|
|
1034
|
+
createdAt: string;
|
|
1035
|
+
};
|
|
1036
|
+
type AgentWidgetRequestPayload = {
|
|
1037
|
+
messages: AgentWidgetRequestPayloadMessage[];
|
|
1038
|
+
flowId?: string;
|
|
1039
|
+
context?: Record<string, unknown>;
|
|
1040
|
+
metadata?: Record<string, unknown>;
|
|
1041
|
+
/** Per-turn template variables for /v1/client/chat (merged as root-level {{var}} in Runtype). */
|
|
1042
|
+
inputs?: Record<string, unknown>;
|
|
1043
|
+
/**
|
|
1044
|
+
* Per-turn page-discovered tools (WebMCP). Sent to Runtype's dispatch so the
|
|
1045
|
+
* agent can call them as `webmcp:<name>`. The widget snapshots
|
|
1046
|
+
* `document.modelContext.__getRegisteredTools()` each turn and ships only
|
|
1047
|
+
* the JSON-serializable surface (no `execute`).
|
|
1048
|
+
*/
|
|
1049
|
+
clientTools?: ClientToolDefinition[];
|
|
1050
|
+
};
|
|
1051
|
+
/**
|
|
1052
|
+
* Configuration for agent loop behavior.
|
|
1053
|
+
*/
|
|
1054
|
+
type AgentLoopConfig = {
|
|
1055
|
+
/** Maximum number of agent turns (1-100). The loop continues while the model calls tools. */
|
|
1056
|
+
maxTurns: number;
|
|
1057
|
+
/** Maximum cost budget in USD. Agent stops when exceeded. */
|
|
1058
|
+
maxCost?: number;
|
|
1059
|
+
/** Enable periodic reflection during execution */
|
|
1060
|
+
enableReflection?: boolean;
|
|
1061
|
+
/** Number of iterations between reflections (1-50) */
|
|
1062
|
+
reflectionInterval?: number;
|
|
1063
|
+
};
|
|
1064
|
+
/**
|
|
1065
|
+
* Configuration for agent tools (search, code execution, MCP servers, etc.)
|
|
1066
|
+
*/
|
|
1067
|
+
type AgentToolsConfig = {
|
|
1068
|
+
/** Tool IDs to enable (e.g., "builtin:exa", "builtin:dalle", "builtin:openai_web_search") */
|
|
1069
|
+
toolIds?: string[];
|
|
1070
|
+
/** Per-tool configuration overrides keyed by tool ID */
|
|
1071
|
+
toolConfigs?: Record<string, Record<string, unknown>>;
|
|
1072
|
+
/** Inline tool definitions for runtime-defined tools */
|
|
1073
|
+
runtimeTools?: Array<Record<string, unknown>>;
|
|
1074
|
+
/** Custom MCP server connections */
|
|
1075
|
+
mcpServers?: Array<Record<string, unknown>>;
|
|
1076
|
+
/** Maximum number of tool invocations per execution */
|
|
1077
|
+
maxToolCalls?: number;
|
|
1078
|
+
/** Tool approval configuration for human-in-the-loop workflows */
|
|
1079
|
+
approval?: {
|
|
1080
|
+
/** Tool names/patterns to require approval for, or true for all tools */
|
|
1081
|
+
require: string[] | boolean;
|
|
1082
|
+
/** Approval timeout in milliseconds (default: 300000 / 5 minutes) */
|
|
1083
|
+
timeout?: number;
|
|
1084
|
+
};
|
|
1085
|
+
};
|
|
1086
|
+
/** Artifact kinds for the Persona sidebar and dispatch payload */
|
|
1087
|
+
type PersonaArtifactKind = "markdown" | "component";
|
|
1088
|
+
/**
|
|
1089
|
+
* Agent configuration for agent execution mode.
|
|
1090
|
+
* When provided in the widget config, enables agent loop execution instead of flow dispatch.
|
|
1091
|
+
*/
|
|
1092
|
+
type ArtifactConfigPayload = {
|
|
1093
|
+
enabled: true;
|
|
1094
|
+
types: PersonaArtifactKind[];
|
|
1095
|
+
};
|
|
1096
|
+
type AgentConfig = {
|
|
1097
|
+
/** Agent display name */
|
|
1098
|
+
name: string;
|
|
1099
|
+
/** Model identifier (e.g., 'openai:gpt-4o-mini', 'qwen/qwen3-8b') */
|
|
1100
|
+
model: string;
|
|
1101
|
+
/** System prompt for the agent */
|
|
1102
|
+
systemPrompt: string;
|
|
1103
|
+
/** Temperature for model responses */
|
|
1104
|
+
temperature?: number;
|
|
1105
|
+
/** Tool configuration for the agent */
|
|
1106
|
+
tools?: AgentToolsConfig;
|
|
1107
|
+
/** Persona artifacts — sibling of tools (virtual agent / API parity) */
|
|
1108
|
+
artifacts?: ArtifactConfigPayload;
|
|
1109
|
+
/** Loop configuration for multi-turn execution */
|
|
1110
|
+
loopConfig?: AgentLoopConfig;
|
|
1111
|
+
};
|
|
1112
|
+
/**
|
|
1113
|
+
* Options for agent execution requests.
|
|
1114
|
+
*/
|
|
1115
|
+
type AgentRequestOptions = {
|
|
1116
|
+
/** Whether to stream the response (should be true for widget usage) */
|
|
1117
|
+
streamResponse?: boolean;
|
|
1118
|
+
/** Record mode: 'virtual' for no persistence, 'existing'/'create' for database records */
|
|
1119
|
+
recordMode?: 'virtual' | 'existing' | 'create';
|
|
1120
|
+
/** Whether to store results server-side */
|
|
1121
|
+
storeResults?: boolean;
|
|
1122
|
+
/** Enable debug mode for additional event data */
|
|
1123
|
+
debugMode?: boolean;
|
|
1124
|
+
};
|
|
1125
|
+
/**
|
|
1126
|
+
* Wire shape for a single client-discovered tool sent on `dispatch.clientTools[]`.
|
|
1127
|
+
*
|
|
1128
|
+
* Mirrors the SDK's `ClientToolDefinition` in `@runtypelabs/sdk`. Only the
|
|
1129
|
+
* JSON-serializable surface of a WebMCP tool — the `execute` function stays
|
|
1130
|
+
* client-side; the server merges these into the agent's tool catalog under
|
|
1131
|
+
* the `webmcp:` namespace.
|
|
1132
|
+
*/
|
|
1133
|
+
type ClientToolDefinition = {
|
|
1134
|
+
/** Bare tool name; the server prepends `webmcp:` on the wire. */
|
|
1135
|
+
name: string;
|
|
1136
|
+
description: string;
|
|
1137
|
+
/** JSON Schema (per WebMCP spec) — passed through as-is. */
|
|
1138
|
+
parametersSchema?: object;
|
|
1139
|
+
/** Set to `'webmcp'` for tools discovered via the polyfill. */
|
|
1140
|
+
origin?: 'webmcp' | 'local';
|
|
1141
|
+
/** Origin of the page that registered the tool — for server-side audit. */
|
|
1142
|
+
pageOrigin?: string;
|
|
1143
|
+
/**
|
|
1144
|
+
* WebMCP `Tool.annotations` (spec). Not used for gating server-side; the
|
|
1145
|
+
* widget reads these client-side. Forwarded so traces/dashboards can show
|
|
1146
|
+
* `readOnlyHint` / `untrustedContentHint` on tool-call records.
|
|
1147
|
+
*/
|
|
1148
|
+
annotations?: {
|
|
1149
|
+
readOnlyHint?: boolean;
|
|
1150
|
+
untrustedContentHint?: boolean;
|
|
1151
|
+
};
|
|
1152
|
+
};
|
|
1153
|
+
/**
|
|
1154
|
+
* Information passed to the confirm-bubble handler before a `webmcp:*` tool
|
|
1155
|
+
* call executes. Every WebMCP tool routes through this single gate.
|
|
1156
|
+
*/
|
|
1157
|
+
type WebMcpConfirmInfo = {
|
|
1158
|
+
/** Bare tool name (no `webmcp:` prefix). */
|
|
1159
|
+
toolName: string;
|
|
1160
|
+
args: unknown;
|
|
1161
|
+
description?: string;
|
|
1162
|
+
annotations?: {
|
|
1163
|
+
readOnlyHint?: boolean;
|
|
1164
|
+
untrustedContentHint?: boolean;
|
|
1165
|
+
};
|
|
1166
|
+
/**
|
|
1167
|
+
* Why the confirm was requested. Currently always `'gate'` — the default
|
|
1168
|
+
* confirm-by-default gate that fires before every `webmcp:*` call. (The
|
|
1169
|
+
* `@mcp-b/webmcp-polyfill` owns the spec's `requestUserInteraction` callback
|
|
1170
|
+
* internally, so Persona no longer surfaces a nested in-tool confirm.)
|
|
1171
|
+
*/
|
|
1172
|
+
reason: 'gate';
|
|
1173
|
+
};
|
|
1174
|
+
/**
|
|
1175
|
+
* Resolves to `true` if the user approves the tool call; `false` to decline.
|
|
1176
|
+
*/
|
|
1177
|
+
type WebMcpConfirmHandler = (info: WebMcpConfirmInfo) => Promise<boolean>;
|
|
1178
|
+
/**
|
|
1179
|
+
* Widget-level WebMCP configuration. Set `enabled: true` to opt in. The
|
|
1180
|
+
* surface's server-side `webmcp` policy is the source of truth for which
|
|
1181
|
+
* tools are accepted — these client-side options are convenience filters.
|
|
1182
|
+
*/
|
|
1183
|
+
type AgentWidgetWebMcpConfig = {
|
|
1184
|
+
/** Master switch. Default: `false` (widget never installs the polyfill). */
|
|
1185
|
+
enabled?: boolean;
|
|
1186
|
+
/**
|
|
1187
|
+
* Glob-ish name patterns to include client-side. `'*'` matches any chars
|
|
1188
|
+
* except `:`. Patterns are matched against the bare tool name (no `webmcp:`
|
|
1189
|
+
* prefix). If unset, all registered tools are included.
|
|
1190
|
+
*/
|
|
1191
|
+
allowlist?: string[];
|
|
1192
|
+
/**
|
|
1193
|
+
* Per-tool gate policy. Called before the confirm gate for every
|
|
1194
|
+
* `webmcp:*` call; return `true` to approve immediately and skip the
|
|
1195
|
+
* confirmation UI entirely. Use this to auto-allow read-only tools (e.g.
|
|
1196
|
+
* a catalog search) while still gating mutating ones. Only consulted on
|
|
1197
|
+
* the default-UI path — a custom `onConfirm` takes full control instead.
|
|
1198
|
+
*/
|
|
1199
|
+
autoApprove?: (info: WebMcpConfirmInfo) => boolean;
|
|
1200
|
+
/**
|
|
1201
|
+
* Confirm gate handler. When omitted, Persona renders its native in-panel
|
|
1202
|
+
* approval bubble (the same chrome used for server-driven tool approvals)
|
|
1203
|
+
* and resolves on the user's Approve/Deny click. Supply this to override
|
|
1204
|
+
* with a custom confirmer (e.g. a route-level modal). The legacy
|
|
1205
|
+
* `window.confirm` fallback only applies when no widget UI is attached.
|
|
1206
|
+
*/
|
|
1207
|
+
onConfirm?: WebMcpConfirmHandler;
|
|
1208
|
+
};
|
|
1209
|
+
/**
|
|
1210
|
+
* Metadata attached to messages created during agent execution.
|
|
1211
|
+
*/
|
|
1212
|
+
type AgentMessageMetadata = {
|
|
1213
|
+
executionId?: string;
|
|
1214
|
+
iteration?: number;
|
|
1215
|
+
turnId?: string;
|
|
1216
|
+
agentName?: string;
|
|
1217
|
+
/**
|
|
1218
|
+
* When this message was produced by a step inside a nested flow executed
|
|
1219
|
+
* as a tool, identifies the parent tool call id. Enables renderers to
|
|
1220
|
+
* visually group or indent nested-flow output under its parent tool.
|
|
1221
|
+
*/
|
|
1222
|
+
parentToolId?: string;
|
|
1223
|
+
/**
|
|
1224
|
+
* Nested flow step id that produced this message (e.g. a `send-stream`
|
|
1225
|
+
* or `prompt` step inside the nested flow). Stable key for that step.
|
|
1226
|
+
*/
|
|
1227
|
+
parentStepId?: string;
|
|
1228
|
+
/**
|
|
1229
|
+
* Set to `true` on a tool-variant message produced from a `step_await`
|
|
1230
|
+
* event (`awaitReason: "local_tool_required"`). Signals to UI code that
|
|
1231
|
+
* the tool call is a LOCAL tool and the server is paused waiting for a
|
|
1232
|
+
* `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
|
|
1233
|
+
*/
|
|
1234
|
+
awaitingLocalTool?: boolean;
|
|
1235
|
+
/**
|
|
1236
|
+
* The provider per-call id (`toolu_…`) carried on the `step_await` /
|
|
1237
|
+
* `flow_await` events for a LOCAL tool (core#3878). Present only when the
|
|
1238
|
+
* server emits it. Two PARALLEL calls to the same tool in one turn share a
|
|
1239
|
+
* `toolName` (and a collapsed `toolId`) but get DISTINCT `webMcpToolCallId`s,
|
|
1240
|
+
* so this is the key the widget batches a single `/resume` on — preferred
|
|
1241
|
+
* over tool name, which collides for same-tool parallel calls. Absent →
|
|
1242
|
+
* fall back to the legacy name-keyed resume contract.
|
|
1243
|
+
*/
|
|
1244
|
+
webMcpToolCallId?: string;
|
|
1245
|
+
/**
|
|
1246
|
+
* Set to `true` once the user has picked / typed / dismissed an answer for
|
|
1247
|
+
* an `ask_user_question` tool call, so renderers stop re-mounting the
|
|
1248
|
+
* answer-pill sheet for this tool call on subsequent render passes.
|
|
1249
|
+
*/
|
|
1250
|
+
askUserQuestionAnswered?: boolean;
|
|
1251
|
+
/**
|
|
1252
|
+
* In-progress answers for a multi-question `ask_user_question` payload,
|
|
1253
|
+
* keyed by question text. Persisted across refresh so the user lands back
|
|
1254
|
+
* where they were if the page reloads mid-flow. Cleared once
|
|
1255
|
+
* `askUserQuestionAnswered` flips to `true`.
|
|
1256
|
+
*/
|
|
1257
|
+
askUserQuestionAnswers?: Record<string, string | string[]>;
|
|
1258
|
+
/**
|
|
1259
|
+
* Current page index for a multi-question `ask_user_question` payload's
|
|
1260
|
+
* paginated stepper. Persists alongside `askUserQuestionAnswers`.
|
|
1261
|
+
*/
|
|
1262
|
+
askUserQuestionIndex?: number;
|
|
1263
|
+
};
|
|
1264
|
+
type AgentWidgetRequestMiddlewareContext = {
|
|
1265
|
+
payload: AgentWidgetRequestPayload;
|
|
1266
|
+
config: AgentWidgetConfig;
|
|
1267
|
+
};
|
|
1268
|
+
type AgentWidgetRequestMiddleware = (context: AgentWidgetRequestMiddlewareContext) => AgentWidgetRequestPayload | void | Promise<AgentWidgetRequestPayload | void>;
|
|
1269
|
+
type AgentWidgetParsedAction = {
|
|
1270
|
+
type: string;
|
|
1271
|
+
payload: Record<string, unknown>;
|
|
1272
|
+
raw?: unknown;
|
|
1273
|
+
};
|
|
1274
|
+
type AgentWidgetActionParserInput = {
|
|
1275
|
+
text: string;
|
|
1276
|
+
message: AgentWidgetMessage;
|
|
1277
|
+
};
|
|
1278
|
+
type AgentWidgetActionParser = (input: AgentWidgetActionParserInput) => AgentWidgetParsedAction | null | undefined;
|
|
1279
|
+
type AgentWidgetActionHandlerResult = {
|
|
1280
|
+
handled?: boolean;
|
|
1281
|
+
displayText?: string;
|
|
1282
|
+
persistMessage?: boolean;
|
|
1283
|
+
resubmit?: boolean;
|
|
1284
|
+
};
|
|
1285
|
+
type AgentWidgetActionContext = {
|
|
1286
|
+
message: AgentWidgetMessage;
|
|
1287
|
+
metadata: Record<string, unknown>;
|
|
1288
|
+
updateMetadata: (updater: (prev: Record<string, unknown>) => Record<string, unknown>) => void;
|
|
1289
|
+
document: Document | null;
|
|
1290
|
+
/**
|
|
1291
|
+
* Trigger automatic model continuation.
|
|
1292
|
+
* Call this AFTER completing async operations (e.g., injecting search results)
|
|
1293
|
+
* to have the model analyze the injected data.
|
|
1294
|
+
*
|
|
1295
|
+
* Use this instead of returning `resubmit: true` for handlers that do async work,
|
|
1296
|
+
* as it ensures the continuation happens after the data is available in context.
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* // In an action handler
|
|
1300
|
+
* const results = await fetchProducts(query);
|
|
1301
|
+
* session.injectAssistantMessage({ content: formatResults(results) });
|
|
1302
|
+
* context.triggerResubmit();
|
|
1303
|
+
*/
|
|
1304
|
+
triggerResubmit: () => void;
|
|
1305
|
+
};
|
|
1306
|
+
type AgentWidgetActionHandler = (action: AgentWidgetParsedAction, context: AgentWidgetActionContext) => AgentWidgetActionHandlerResult | void;
|
|
1307
|
+
type AgentWidgetStoredState = {
|
|
1308
|
+
messages?: AgentWidgetMessage[];
|
|
1309
|
+
metadata?: Record<string, unknown>;
|
|
1310
|
+
artifacts?: PersonaArtifactRecord[];
|
|
1311
|
+
selectedArtifactId?: string | null;
|
|
1312
|
+
};
|
|
1313
|
+
interface AgentWidgetStorageAdapter {
|
|
1314
|
+
load?: () => AgentWidgetStoredState | null | Promise<AgentWidgetStoredState | null>;
|
|
1315
|
+
save?: (state: AgentWidgetStoredState) => void | Promise<void>;
|
|
1316
|
+
clear?: () => void | Promise<void>;
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Feedback event payload for upvote/downvote actions on messages
|
|
1320
|
+
*/
|
|
1321
|
+
type AgentWidgetMessageFeedback = {
|
|
1322
|
+
type: "upvote" | "downvote";
|
|
1323
|
+
messageId: string;
|
|
1324
|
+
message: AgentWidgetMessage;
|
|
1325
|
+
};
|
|
1326
|
+
/**
|
|
1327
|
+
* Configuration for message action buttons (copy, upvote, downvote)
|
|
1328
|
+
*
|
|
1329
|
+
* **Client Token Mode**: When using `clientToken`, feedback is automatically
|
|
1330
|
+
* sent to your Runtype backend. Just enable the buttons and you're done!
|
|
1331
|
+
* The `onFeedback` and `onCopy` callbacks are optional for additional local handling.
|
|
1332
|
+
*
|
|
1333
|
+
* @example
|
|
1334
|
+
* ```typescript
|
|
1335
|
+
* // With clientToken - feedback is automatic!
|
|
1336
|
+
* config: {
|
|
1337
|
+
* clientToken: 'ct_live_...',
|
|
1338
|
+
* messageActions: {
|
|
1339
|
+
* showUpvote: true,
|
|
1340
|
+
* showDownvote: true,
|
|
1341
|
+
* // No onFeedback needed - sent to backend automatically
|
|
1342
|
+
* }
|
|
1343
|
+
* }
|
|
1344
|
+
* ```
|
|
1345
|
+
*/
|
|
1346
|
+
type AgentWidgetMessageActionsConfig = {
|
|
1347
|
+
/**
|
|
1348
|
+
* Enable/disable message actions entirely
|
|
1349
|
+
* @default true
|
|
1350
|
+
*/
|
|
1351
|
+
enabled?: boolean;
|
|
1352
|
+
/**
|
|
1353
|
+
* Show copy button
|
|
1354
|
+
* @default true
|
|
1355
|
+
*/
|
|
1356
|
+
showCopy?: boolean;
|
|
1357
|
+
/**
|
|
1358
|
+
* Show upvote button.
|
|
1359
|
+
* When using `clientToken`, feedback is sent to the backend automatically.
|
|
1360
|
+
* @default false
|
|
1361
|
+
*/
|
|
1362
|
+
showUpvote?: boolean;
|
|
1363
|
+
/**
|
|
1364
|
+
* Show downvote button.
|
|
1365
|
+
* When using `clientToken`, feedback is sent to the backend automatically.
|
|
1366
|
+
* @default false
|
|
1367
|
+
*/
|
|
1368
|
+
showDownvote?: boolean;
|
|
1369
|
+
/**
|
|
1370
|
+
* Visibility mode: 'always' shows buttons always, 'hover' shows on hover only
|
|
1371
|
+
* @default 'hover'
|
|
1372
|
+
*/
|
|
1373
|
+
visibility?: "always" | "hover";
|
|
1374
|
+
/**
|
|
1375
|
+
* Horizontal alignment of action buttons
|
|
1376
|
+
* @default 'right'
|
|
1377
|
+
*/
|
|
1378
|
+
align?: "left" | "center" | "right";
|
|
1379
|
+
/**
|
|
1380
|
+
* Layout style for action buttons
|
|
1381
|
+
* - 'pill-inside': Compact floating pill around just the buttons (default for hover)
|
|
1382
|
+
* - 'row-inside': Full-width row at the bottom of the message
|
|
1383
|
+
* @default 'pill-inside'
|
|
1384
|
+
*/
|
|
1385
|
+
layout?: "pill-inside" | "row-inside";
|
|
1386
|
+
/**
|
|
1387
|
+
* Callback when user submits feedback (upvote/downvote).
|
|
1388
|
+
*
|
|
1389
|
+
* **Note**: When using `clientToken`, feedback is AUTOMATICALLY sent to your
|
|
1390
|
+
* backend via `/v1/client/feedback`. This callback is called IN ADDITION to
|
|
1391
|
+
* the automatic submission, useful for updating local UI or analytics.
|
|
1392
|
+
*/
|
|
1393
|
+
onFeedback?: (feedback: AgentWidgetMessageFeedback) => void;
|
|
1394
|
+
/**
|
|
1395
|
+
* Callback when user copies a message.
|
|
1396
|
+
*
|
|
1397
|
+
* **Note**: When using `clientToken`, copy events are AUTOMATICALLY tracked
|
|
1398
|
+
* via `/v1/client/feedback`. This callback is called IN ADDITION to the
|
|
1399
|
+
* automatic tracking.
|
|
1400
|
+
*/
|
|
1401
|
+
onCopy?: (message: AgentWidgetMessage) => void;
|
|
1402
|
+
};
|
|
1403
|
+
/**
|
|
1404
|
+
* Layout for the artifact split / drawer (CSS lengths unless noted).
|
|
1405
|
+
*
|
|
1406
|
+
* **Close behavior:** In desktop split mode, the artifact chrome `Close` control uses the same
|
|
1407
|
+
* dismiss path as the mobile drawer (`onDismiss` on the artifact pane): the pane is hidden until
|
|
1408
|
+
* new artifact content arrives or the host calls `showArtifacts()` on the widget handle.
|
|
1409
|
+
*/
|
|
1410
|
+
type AgentWidgetArtifactsLayoutConfig = {
|
|
1411
|
+
/** Flex gap between chat column and artifact pane. @default 0.5rem */
|
|
1412
|
+
splitGap?: string;
|
|
1413
|
+
/** Artifact column width in split mode. @default 40% */
|
|
1414
|
+
paneWidth?: string;
|
|
1415
|
+
/** Max width of artifact column. @default 28rem */
|
|
1416
|
+
paneMaxWidth?: string;
|
|
1417
|
+
/** Min width of artifact column (optional). */
|
|
1418
|
+
paneMinWidth?: string;
|
|
1419
|
+
/**
|
|
1420
|
+
* When the floating panel is at most this wide (px), use in-panel drawer for artifacts
|
|
1421
|
+
* instead of a side-by-side split (viewport can still be wide).
|
|
1422
|
+
* @default 520
|
|
1423
|
+
*/
|
|
1424
|
+
narrowHostMaxWidth?: number;
|
|
1425
|
+
/**
|
|
1426
|
+
* When true (default), widen the launcher panel while artifacts are visible and not user-dismissed.
|
|
1427
|
+
* No-op for inline embed (`launcher.enabled === false`).
|
|
1428
|
+
*/
|
|
1429
|
+
expandLauncherPanelWhenOpen?: boolean;
|
|
1430
|
+
/** Panel width when expanded (launcher + artifacts visible). @default min(720px, calc(100vw - 24px)) */
|
|
1431
|
+
expandedPanelWidth?: string;
|
|
1432
|
+
/**
|
|
1433
|
+
* When true, shows a drag handle between chat and artifact columns in desktop split mode only
|
|
1434
|
+
* (hidden in narrow-host drawer and viewport ≤640px). Width is not persisted across reloads.
|
|
1435
|
+
*/
|
|
1436
|
+
resizable?: boolean;
|
|
1437
|
+
/** Min artifact column width while resizing. Only `px` strings are supported. @default 200px */
|
|
1438
|
+
resizableMinWidth?: string;
|
|
1439
|
+
/** Optional max artifact width cap while resizing (`px` only). Layout still bounds by chat min width. */
|
|
1440
|
+
resizableMaxWidth?: string;
|
|
1441
|
+
/**
|
|
1442
|
+
* Visual treatment for the artifact column in split mode.
|
|
1443
|
+
* - `'panel'` — bordered sidebar with left border, gap, and shadow (default).
|
|
1444
|
+
* - `'seamless'` — flush with chat: no border or shadow, container background, zero gap.
|
|
1445
|
+
* @default 'panel'
|
|
1446
|
+
*/
|
|
1447
|
+
paneAppearance?: "panel" | "seamless";
|
|
1448
|
+
/** Border radius on the artifact pane (CSS length). Works with any `paneAppearance`. */
|
|
1449
|
+
paneBorderRadius?: string;
|
|
1450
|
+
/** CSS `box-shadow` on the artifact pane. Set `"none"` to suppress the default shadow. */
|
|
1451
|
+
paneShadow?: string;
|
|
1452
|
+
/**
|
|
1453
|
+
* Full `border` shorthand for the artifact `<aside>` (all sides). Overrides default pane borders.
|
|
1454
|
+
* Example: `"1px solid #cccccc"`.
|
|
1455
|
+
*/
|
|
1456
|
+
paneBorder?: string;
|
|
1457
|
+
/**
|
|
1458
|
+
* `border-left` shorthand only — typical for split view next to chat (with or without resizer).
|
|
1459
|
+
* Ignored if `paneBorder` is set. Example: `"1px solid #cccccc"`.
|
|
1460
|
+
*/
|
|
1461
|
+
paneBorderLeft?: string;
|
|
1462
|
+
/**
|
|
1463
|
+
* Desktop split only (not narrow-host drawer / not ≤640px): square the **main chat card’s**
|
|
1464
|
+
* top-right and bottom-right radii, and round the **artifact pane’s** top-right and bottom-right
|
|
1465
|
+
* to match `persona-rounded-2xl` (`--persona-radius-lg`) so the two columns read as one shell.
|
|
1466
|
+
*/
|
|
1467
|
+
unifiedSplitChrome?: boolean;
|
|
1468
|
+
/**
|
|
1469
|
+
* When `unifiedSplitChrome` is true, outer-right corner radius on the artifact column (CSS length).
|
|
1470
|
+
* @default matches theme large radius (`--persona-radius-lg`)
|
|
1471
|
+
*/
|
|
1472
|
+
unifiedSplitOuterRadius?: string;
|
|
1473
|
+
/**
|
|
1474
|
+
* Strongest override: solid background for the artifact column (CSS color). Sets `--persona-artifact-pane-bg`
|
|
1475
|
+
* on the widget root. Leave unset to use theme `components.artifact.pane.background` (defaults to semantic
|
|
1476
|
+
* container) so light/dark stays consistent.
|
|
1477
|
+
*/
|
|
1478
|
+
paneBackground?: string;
|
|
1479
|
+
/**
|
|
1480
|
+
* Horizontal padding for artifact toolbar and content (CSS length), e.g. `24px`.
|
|
1481
|
+
*/
|
|
1482
|
+
panePadding?: string;
|
|
1483
|
+
/**
|
|
1484
|
+
* Toolbar layout preset.
|
|
1485
|
+
* - `default` — "Artifacts" title, horizontal tabs, text close.
|
|
1486
|
+
* - `document` — view/source toggle, document title, copy / refresh / close; tab strip hidden when only one artifact.
|
|
1487
|
+
* @default 'default'
|
|
1488
|
+
*/
|
|
1489
|
+
toolbarPreset?: "default" | "document";
|
|
1490
|
+
/**
|
|
1491
|
+
* When `toolbarPreset` is `document`, show a visible "Copy" label next to the copy icon.
|
|
1492
|
+
*/
|
|
1493
|
+
documentToolbarShowCopyLabel?: boolean;
|
|
1494
|
+
/**
|
|
1495
|
+
* When `toolbarPreset` is `document`, show a small chevron after the copy control (e.g. menu affordance).
|
|
1496
|
+
*/
|
|
1497
|
+
documentToolbarShowCopyChevron?: boolean;
|
|
1498
|
+
/** Document toolbar icon buttons (view, code, copy, refresh, close) — CSS color. Sets `--persona-artifact-doc-toolbar-icon-color` on the widget root. */
|
|
1499
|
+
documentToolbarIconColor?: string;
|
|
1500
|
+
/** Active view/source toggle background. Sets `--persona-artifact-doc-toggle-active-bg`. */
|
|
1501
|
+
documentToolbarToggleActiveBackground?: string;
|
|
1502
|
+
/** Active view/source toggle border color. Sets `--persona-artifact-doc-toggle-active-border`. */
|
|
1503
|
+
documentToolbarToggleActiveBorderColor?: string;
|
|
1504
|
+
/**
|
|
1505
|
+
* Invoked when the document toolbar Refresh control is used (before the pane re-renders).
|
|
1506
|
+
* Use to replay `connectStream`, refetch, etc.
|
|
1507
|
+
*/
|
|
1508
|
+
onDocumentToolbarRefresh?: () => void | Promise<void>;
|
|
1509
|
+
/**
|
|
1510
|
+
* Optional copy dropdown entries (shown when `documentToolbarShowCopyChevron` is true and this array is non-empty).
|
|
1511
|
+
* The main Copy control still performs default copy unless `onDocumentToolbarCopyMenuSelect` handles everything.
|
|
1512
|
+
*/
|
|
1513
|
+
documentToolbarCopyMenuItems?: Array<{
|
|
1514
|
+
id: string;
|
|
1515
|
+
label: string;
|
|
1516
|
+
}>;
|
|
1517
|
+
/**
|
|
1518
|
+
* When set, invoked for the chevron menu (and can override default copy per `actionId`).
|
|
1519
|
+
*/
|
|
1520
|
+
onDocumentToolbarCopyMenuSelect?: (payload: {
|
|
1521
|
+
actionId: string;
|
|
1522
|
+
artifactId: string | null;
|
|
1523
|
+
markdown: string;
|
|
1524
|
+
jsonPayload: string;
|
|
1525
|
+
}) => void | Promise<void>;
|
|
1526
|
+
};
|
|
1527
|
+
type AgentWidgetArtifactsFeature = {
|
|
1528
|
+
/** When true, Persona shows the artifact pane and handles artifact_* SSE events */
|
|
1529
|
+
enabled?: boolean;
|
|
1530
|
+
/** If set, artifact events for other types are ignored */
|
|
1531
|
+
allowedTypes?: PersonaArtifactKind[];
|
|
1532
|
+
/** Split / drawer dimensions and launcher widen behavior */
|
|
1533
|
+
layout?: AgentWidgetArtifactsLayoutConfig;
|
|
1534
|
+
/**
|
|
1535
|
+
* Called when an artifact card action is triggered (open, download).
|
|
1536
|
+
* Return `true` to prevent the default behavior.
|
|
1537
|
+
*/
|
|
1538
|
+
onArtifactAction?: (action: {
|
|
1539
|
+
type: 'open' | 'download';
|
|
1540
|
+
artifactId: string;
|
|
1541
|
+
}) => boolean | void;
|
|
1542
|
+
/**
|
|
1543
|
+
* Custom renderer for artifact reference cards shown in the message thread.
|
|
1544
|
+
* Return an HTMLElement to replace the default card, or `null` to use the default.
|
|
1545
|
+
*/
|
|
1546
|
+
renderCard?: (context: {
|
|
1547
|
+
artifact: {
|
|
1548
|
+
artifactId: string;
|
|
1549
|
+
title: string;
|
|
1550
|
+
artifactType: string;
|
|
1551
|
+
status: string;
|
|
1552
|
+
};
|
|
1553
|
+
config: AgentWidgetConfig;
|
|
1554
|
+
defaultRenderer: () => HTMLElement;
|
|
1555
|
+
}) => HTMLElement | null;
|
|
1556
|
+
};
|
|
1557
|
+
type AgentWidgetScrollToBottomFeature = {
|
|
1558
|
+
/**
|
|
1559
|
+
* When true, Persona shows a scroll-to-bottom affordance when the user breaks
|
|
1560
|
+
* away from the latest transcript or event stream content.
|
|
1561
|
+
* @default true
|
|
1562
|
+
*/
|
|
1563
|
+
enabled?: boolean;
|
|
1564
|
+
/**
|
|
1565
|
+
* Lucide icon name used for the affordance.
|
|
1566
|
+
* @default "arrow-down"
|
|
1567
|
+
*/
|
|
1568
|
+
iconName?: string;
|
|
1569
|
+
/**
|
|
1570
|
+
* Optional label text shown next to the icon. Set to an empty string for an
|
|
1571
|
+
* icon-only affordance.
|
|
1572
|
+
* @default ""
|
|
1573
|
+
*/
|
|
1574
|
+
label?: string;
|
|
1575
|
+
};
|
|
1576
|
+
type AgentWidgetToolCallCollapsedMode = "tool-call" | "tool-name" | "tool-preview";
|
|
1577
|
+
/**
|
|
1578
|
+
* Animation mode applied to tool call header text while the tool is running.
|
|
1579
|
+
* Character-by-character modes (`shimmer`, `shimmer-color`, `rainbow`) wrap each
|
|
1580
|
+
* character in a span with staggered `animation-delay`. `pulse` applies to the
|
|
1581
|
+
* entire text container. Honors `prefers-reduced-motion`.
|
|
1582
|
+
*/
|
|
1583
|
+
type AgentWidgetToolCallLoadingAnimation = "none" | "pulse" | "shimmer" | "shimmer-color" | "rainbow";
|
|
1584
|
+
type AgentWidgetToolCallDisplayFeature = {
|
|
1585
|
+
/**
|
|
1586
|
+
* Controls what collapsed tool call rows show in their header/summary area.
|
|
1587
|
+
* @default "tool-call"
|
|
1588
|
+
*/
|
|
1589
|
+
collapsedMode?: AgentWidgetToolCallCollapsedMode;
|
|
1590
|
+
/**
|
|
1591
|
+
* When true, active collapsed tool calls can render a lightweight preview block.
|
|
1592
|
+
* @default false
|
|
1593
|
+
*/
|
|
1594
|
+
activePreview?: boolean;
|
|
1595
|
+
/**
|
|
1596
|
+
* Optional CSS min-height applied to active collapsed tool call rows.
|
|
1597
|
+
* @default undefined (no min-height)
|
|
1598
|
+
* @example "100px"
|
|
1599
|
+
*/
|
|
1600
|
+
activeMinHeight?: string;
|
|
1601
|
+
/**
|
|
1602
|
+
* Maximum preview lines shown for collapsed active tool calls.
|
|
1603
|
+
* @default 3
|
|
1604
|
+
*/
|
|
1605
|
+
previewMaxLines?: number;
|
|
1606
|
+
/**
|
|
1607
|
+
* When true, consecutive tool call rows can be visually grouped.
|
|
1608
|
+
* @default false
|
|
1609
|
+
*/
|
|
1610
|
+
grouped?: boolean;
|
|
1611
|
+
/**
|
|
1612
|
+
* When false, tool call bubbles show only the collapsed summary with no
|
|
1613
|
+
* expand/collapse toggle. Users see tool awareness without full details.
|
|
1614
|
+
* @default true
|
|
1615
|
+
*/
|
|
1616
|
+
expandable?: boolean;
|
|
1617
|
+
/**
|
|
1618
|
+
* Animation mode applied to the tool call header text while the tool is active.
|
|
1619
|
+
* - "none" — static text, no animation
|
|
1620
|
+
* - "pulse" — opacity pulse on the entire header text
|
|
1621
|
+
* - "shimmer" — monochrome opacity sweep per character
|
|
1622
|
+
* - "shimmer-color" — color gradient sweep per character
|
|
1623
|
+
* - "rainbow" — rainbow color cycle per character
|
|
1624
|
+
* @default "none"
|
|
1625
|
+
*/
|
|
1626
|
+
loadingAnimation?: AgentWidgetToolCallLoadingAnimation;
|
|
1627
|
+
};
|
|
1628
|
+
type AgentWidgetReasoningDisplayFeature = {
|
|
1629
|
+
/**
|
|
1630
|
+
* When true, active collapsed reasoning rows can render a lightweight preview block.
|
|
1631
|
+
* @default false
|
|
1632
|
+
*/
|
|
1633
|
+
activePreview?: boolean;
|
|
1634
|
+
/**
|
|
1635
|
+
* Optional CSS min-height applied to active collapsed reasoning rows.
|
|
1636
|
+
*/
|
|
1637
|
+
activeMinHeight?: string;
|
|
1638
|
+
/**
|
|
1639
|
+
* Maximum preview lines shown for collapsed active reasoning rows.
|
|
1640
|
+
* @default 3
|
|
1641
|
+
*/
|
|
1642
|
+
previewMaxLines?: number;
|
|
1643
|
+
/**
|
|
1644
|
+
* When false, reasoning bubbles show only the collapsed summary with no
|
|
1645
|
+
* expand/collapse toggle. Users see reasoning awareness without full details.
|
|
1646
|
+
* @default true
|
|
1647
|
+
*/
|
|
1648
|
+
expandable?: boolean;
|
|
1649
|
+
/**
|
|
1650
|
+
* Animation mode applied to the reasoning header text while reasoning is active.
|
|
1651
|
+
* Reuses the same modes as tool call animations.
|
|
1652
|
+
* - "none" — static text, no animation
|
|
1653
|
+
* - "pulse" — opacity pulse on the entire header text
|
|
1654
|
+
* - "shimmer" — monochrome opacity sweep per character
|
|
1655
|
+
* - "shimmer-color" — color gradient sweep per character
|
|
1656
|
+
* - "rainbow" — rainbow color cycle per character
|
|
1657
|
+
* @default "none"
|
|
1658
|
+
*/
|
|
1659
|
+
loadingAnimation?: AgentWidgetToolCallLoadingAnimation;
|
|
1660
|
+
};
|
|
1661
|
+
/**
|
|
1662
|
+
* Reveal animation applied to assistant message text while it is streaming.
|
|
1663
|
+
*
|
|
1664
|
+
* Built-in types always available:
|
|
1665
|
+
* - `none` — text appears as tokens arrive (default).
|
|
1666
|
+
* - `typewriter` — characters fade in with a blinking caret.
|
|
1667
|
+
* - `pop-bubble` — the bubble scales in; text streams normally afterward.
|
|
1668
|
+
* - `letter-rise` — per-char translateY + fade reveal.
|
|
1669
|
+
* - `word-fade` — per-word blur + translateY fade-in.
|
|
1670
|
+
*
|
|
1671
|
+
* Subpath plugins (import from `@runtypelabs/persona/animations/*` to register):
|
|
1672
|
+
* - `wipe`, `glyph-cycle`.
|
|
1673
|
+
*
|
|
1674
|
+
* Custom types are allowed — register a plugin with any string name and
|
|
1675
|
+
* reference it by that name in `type`.
|
|
1676
|
+
*/
|
|
1677
|
+
type AgentWidgetStreamAnimationBuiltinType = "none" | "typewriter" | "word-fade" | "letter-rise" | "glyph-cycle" | "wipe" | "pop-bubble";
|
|
1678
|
+
type AgentWidgetStreamAnimationType = AgentWidgetStreamAnimationBuiltinType | (string & {});
|
|
1679
|
+
/**
|
|
1680
|
+
* Placeholder shown inside a streaming assistant bubble before the first token arrives.
|
|
1681
|
+
* - `none` — use the default typing-dots indicator (existing behavior).
|
|
1682
|
+
* - `skeleton` — shimmer bars, replaced by streaming content once it starts.
|
|
1683
|
+
*/
|
|
1684
|
+
type AgentWidgetStreamAnimationPlaceholder = "none" | "skeleton";
|
|
1685
|
+
/**
|
|
1686
|
+
* How much of the accumulated streaming content to display while tokens are
|
|
1687
|
+
* still arriving. Trimming to a boundary means in-progress words or lines
|
|
1688
|
+
* stay hidden until they complete — useful for animations that benefit from
|
|
1689
|
+
* unit-complete reveals (e.g. wipe, glyph-cycle).
|
|
1690
|
+
* - `none` — show every character as it arrives (default).
|
|
1691
|
+
* - `word` — trim to the last whitespace boundary.
|
|
1692
|
+
* - `line` — trim to the last newline boundary.
|
|
1693
|
+
*/
|
|
1694
|
+
type AgentWidgetStreamAnimationBuffer = "none" | "word" | "line";
|
|
1695
|
+
/**
|
|
1696
|
+
* Context passed to plugin lifecycle hooks. Carries the live DOM references
|
|
1697
|
+
* and resolved animation settings for the currently-streaming message.
|
|
1698
|
+
*/
|
|
1699
|
+
type StreamAnimationContext = {
|
|
1700
|
+
/** The `.persona-message-content` element owning the streamed text. */
|
|
1701
|
+
container: HTMLElement;
|
|
1702
|
+
/** The outer message bubble element. */
|
|
1703
|
+
bubble: HTMLElement;
|
|
1704
|
+
/** ID of the streaming message. */
|
|
1705
|
+
messageId: string;
|
|
1706
|
+
/** Read-only reference to the message being streamed. */
|
|
1707
|
+
message: AgentWidgetMessage;
|
|
1708
|
+
/** Effective `speed` from `streamAnimation.speed`. */
|
|
1709
|
+
speed: number;
|
|
1710
|
+
/** Effective `duration` from `streamAnimation.duration`. */
|
|
1711
|
+
duration: number;
|
|
1712
|
+
};
|
|
1713
|
+
/**
|
|
1714
|
+
* Pluggable stream animation. Third-party packages and inline registrations
|
|
1715
|
+
* implement this interface to add custom reveal effects.
|
|
1716
|
+
*
|
|
1717
|
+
* Lifecycle:
|
|
1718
|
+
* - When the widget mounts and detects a plugin (either passed via config or
|
|
1719
|
+
* auto-registered in the IIFE bundle), it injects `styles` once into the
|
|
1720
|
+
* widget's style host.
|
|
1721
|
+
* - For each streaming assistant message whose `type` matches `name`, the
|
|
1722
|
+
* widget applies `containerClass` / `bubbleClass`, wraps text per `wrap`,
|
|
1723
|
+
* and — if `useCaret` is true — appends a blinking caret.
|
|
1724
|
+
* - Hooks fire after the live DOM is morphed; plugins use stable element IDs
|
|
1725
|
+
* and `data-preserve-animation` to safely mutate per-char or per-word spans
|
|
1726
|
+
* without idiomorph clobbering in-flight work.
|
|
1727
|
+
*/
|
|
1728
|
+
type StreamAnimationPlugin = {
|
|
1729
|
+
/** Plugin identifier. Matches the `type` field in `streamAnimation`. */
|
|
1730
|
+
name: string;
|
|
1731
|
+
/** Class added to `.persona-message-content` while streaming. */
|
|
1732
|
+
containerClass?: string;
|
|
1733
|
+
/** Class added to the bubble element (e.g. a one-shot scale animation). */
|
|
1734
|
+
bubbleClass?: string;
|
|
1735
|
+
/** Wrap mode applied to text nodes during streaming. @default "none" */
|
|
1736
|
+
wrap?: "none" | "char" | "word";
|
|
1737
|
+
/**
|
|
1738
|
+
* HTML tags whose descendant text is skipped during wrapping. Defaults to
|
|
1739
|
+
* `["pre", "code", "a", "script", "style"]` — useful for keeping code
|
|
1740
|
+
* blocks legible and link click-targets intact. Plugins that want to
|
|
1741
|
+
* animate characters inside inline code (e.g. `glyph-cycle`) can narrow
|
|
1742
|
+
* the list.
|
|
1743
|
+
*/
|
|
1744
|
+
skipTags?: string[];
|
|
1745
|
+
/** Append a blinking caret after the last rendered char/word. */
|
|
1746
|
+
useCaret?: boolean;
|
|
1747
|
+
/** CSS string injected into the widget style host on first activation. */
|
|
1748
|
+
styles?: string;
|
|
1749
|
+
/**
|
|
1750
|
+
* Optional custom buffering strategy. Returns the portion of `content`
|
|
1751
|
+
* that should be rendered during streaming. Use this for buffering
|
|
1752
|
+
* schemes beyond the built-in `word` / `line` strategies.
|
|
1753
|
+
*/
|
|
1754
|
+
bufferContent?: (content: string, message: AgentWidgetMessage) => string;
|
|
1755
|
+
/**
|
|
1756
|
+
* Fires once when the plugin is first activated inside a widget instance.
|
|
1757
|
+
* Use this to set up MutationObservers or other long-lived listeners.
|
|
1758
|
+
* Return an optional cleanup function that runs on widget destroy.
|
|
1759
|
+
*/
|
|
1760
|
+
onAttach?: (root: HTMLElement | ShadowRoot) => (() => void) | void;
|
|
1761
|
+
/** Fires after each render that reaches the live DOM. */
|
|
1762
|
+
onAfterRender?: (ctx: StreamAnimationContext) => void;
|
|
1763
|
+
/** Fires when a streamed message's `streaming` flag flips to false. */
|
|
1764
|
+
onStreamComplete?: (ctx: StreamAnimationContext) => void;
|
|
1765
|
+
/**
|
|
1766
|
+
* Report whether the plugin still has in-flight animation work for a
|
|
1767
|
+
* message. When `true`, the widget keeps rendering the message in its
|
|
1768
|
+
* "streaming-animated" mode even after `message.streaming` flips false —
|
|
1769
|
+
* preventing the final non-animated render from yanking the rug out from
|
|
1770
|
+
* under unfinished per-char cycles or reveals.
|
|
1771
|
+
*/
|
|
1772
|
+
isAnimating?: (message: AgentWidgetMessage) => boolean;
|
|
1773
|
+
};
|
|
1774
|
+
type AgentWidgetStreamAnimationFeature = {
|
|
1775
|
+
/** Reveal animation to apply while streaming. @default "none" */
|
|
1776
|
+
type?: AgentWidgetStreamAnimationType;
|
|
1777
|
+
/** Pre-first-token placeholder. @default "none" */
|
|
1778
|
+
placeholder?: AgentWidgetStreamAnimationPlaceholder;
|
|
1779
|
+
/**
|
|
1780
|
+
* Per-unit animation duration (ms) for `typewriter`, `letter-rise`, `word-fade`,
|
|
1781
|
+
* and per-unit plugin animations. Each arriving character/word animates from
|
|
1782
|
+
* invisible to visible over this duration, independent of its position — the
|
|
1783
|
+
* streaming cadence itself provides the visible stagger.
|
|
1784
|
+
* @default 120
|
|
1785
|
+
*/
|
|
1786
|
+
speed?: number;
|
|
1787
|
+
/**
|
|
1788
|
+
* Total duration of container-level animations (`pop-bubble` and custom
|
|
1789
|
+
* plugin animations), in milliseconds.
|
|
1790
|
+
* @default 1800
|
|
1791
|
+
*/
|
|
1792
|
+
duration?: number;
|
|
1793
|
+
/**
|
|
1794
|
+
* Trim the accumulated streaming content to a word or line boundary before
|
|
1795
|
+
* rendering. Hides in-progress units until they complete.
|
|
1796
|
+
* @default "none"
|
|
1797
|
+
*/
|
|
1798
|
+
buffer?: AgentWidgetStreamAnimationBuffer;
|
|
1799
|
+
/**
|
|
1800
|
+
* Extra animation plugins available to this widget instance. Keys are
|
|
1801
|
+
* plugin names; the matching plugin activates when `type` is set to that
|
|
1802
|
+
* name. Built-in types (`typewriter`, `pop-bubble`) are always registered.
|
|
1803
|
+
*/
|
|
1804
|
+
plugins?: Record<string, StreamAnimationPlugin>;
|
|
1805
|
+
};
|
|
1806
|
+
type AgentWidgetFeatureFlags = {
|
|
1807
|
+
showReasoning?: boolean;
|
|
1808
|
+
showToolCalls?: boolean;
|
|
1809
|
+
showEventStreamToggle?: boolean;
|
|
1810
|
+
/**
|
|
1811
|
+
* Up/Down arrow navigation through previously sent user messages in the
|
|
1812
|
+
* composer, for quick re-entry or editing (shell / Slack style). History is
|
|
1813
|
+
* only entered when the caret is at the start of the input, so normal
|
|
1814
|
+
* multi-line cursor movement is preserved. Set to `false` to disable.
|
|
1815
|
+
* @default true
|
|
1816
|
+
*/
|
|
1817
|
+
composerHistory?: boolean;
|
|
1818
|
+
/** Shared transcript + event stream scroll-to-bottom affordance. */
|
|
1819
|
+
scrollToBottom?: AgentWidgetScrollToBottomFeature;
|
|
1820
|
+
/** Collapsed transcript behavior for tool call rows. */
|
|
1821
|
+
toolCallDisplay?: AgentWidgetToolCallDisplayFeature;
|
|
1822
|
+
/** Collapsed transcript behavior for reasoning rows. */
|
|
1823
|
+
reasoningDisplay?: AgentWidgetReasoningDisplayFeature;
|
|
1824
|
+
/** Configuration for the Event Stream inspector view */
|
|
1825
|
+
eventStream?: EventStreamConfig;
|
|
1826
|
+
/** Optional artifact sidebar (split pane / mobile drawer) */
|
|
1827
|
+
artifacts?: AgentWidgetArtifactsFeature;
|
|
1828
|
+
/** Reveal animation for streaming assistant text. */
|
|
1829
|
+
streamAnimation?: AgentWidgetStreamAnimationFeature;
|
|
1830
|
+
/**
|
|
1831
|
+
* Built-in interactive answer-pill sheet shown when the assistant invokes
|
|
1832
|
+
* the `ask_user_question` tool. Slides up over the composer with tappable
|
|
1833
|
+
* pills + optional free-text input.
|
|
1834
|
+
*/
|
|
1835
|
+
askUserQuestion?: AgentWidgetAskUserQuestionFeature;
|
|
1836
|
+
};
|
|
1837
|
+
/**
|
|
1838
|
+
* Single selectable option in an `ask_user_question` prompt.
|
|
1839
|
+
* Mirrors Anthropic's AskUserQuestion schema.
|
|
1840
|
+
*/
|
|
1841
|
+
type AskUserQuestionOption = {
|
|
1842
|
+
/** Pill label (required). */
|
|
1843
|
+
label: string;
|
|
1844
|
+
/** Optional long-form description (shown as a subtitle on tap-hover). */
|
|
1845
|
+
description?: string;
|
|
1846
|
+
/** Optional rich preview — reserved for future rendering; ignored in v1. */
|
|
1847
|
+
preview?: string;
|
|
1848
|
+
};
|
|
1849
|
+
/**
|
|
1850
|
+
* A single question in an `ask_user_question` tool call.
|
|
1851
|
+
* The tool may carry 1–8 prompts. When more than one is supplied, the built-in
|
|
1852
|
+
* renderer paginates them as a "Question N of M" stepper with Back / Next /
|
|
1853
|
+
* Submit-all controls; single-question payloads render without stepper chrome.
|
|
1854
|
+
*/
|
|
1855
|
+
type AskUserQuestionPrompt = {
|
|
1856
|
+
/** The question text shown to the user. */
|
|
1857
|
+
question: string;
|
|
1858
|
+
/** Optional short header label (≤12 chars) used as a compact group title. */
|
|
1859
|
+
header?: string;
|
|
1860
|
+
/** 2–4 selectable options. */
|
|
1861
|
+
options: AskUserQuestionOption[];
|
|
1862
|
+
/** When true, the user can pick multiple options and submit together. Default false. */
|
|
1863
|
+
multiSelect?: boolean;
|
|
1864
|
+
/** When true, a free-text "Other…" pill expands to an input. Default true. */
|
|
1865
|
+
allowFreeText?: boolean;
|
|
1866
|
+
};
|
|
1867
|
+
/** Parsed payload of an `ask_user_question` tool call. */
|
|
1868
|
+
type AskUserQuestionPayload = {
|
|
1869
|
+
/** 1–8 questions. Anything beyond the renderer's cap is truncated with a console warning. */
|
|
1870
|
+
questions: AskUserQuestionPrompt[];
|
|
1871
|
+
};
|
|
1872
|
+
/**
|
|
1873
|
+
* Style overrides for the answer-pill sheet. All values are raw CSS strings
|
|
1874
|
+
* and are plumbed through as CSS custom properties on the sheet root.
|
|
1875
|
+
*/
|
|
1876
|
+
type AgentWidgetAskUserQuestionStyles = {
|
|
1877
|
+
sheetBackground?: string;
|
|
1878
|
+
sheetBorder?: string;
|
|
1879
|
+
sheetShadow?: string;
|
|
1880
|
+
pillBackground?: string;
|
|
1881
|
+
pillBackgroundSelected?: string;
|
|
1882
|
+
pillTextColor?: string;
|
|
1883
|
+
pillTextColorSelected?: string;
|
|
1884
|
+
pillBorderRadius?: string;
|
|
1885
|
+
customInputBackground?: string;
|
|
1886
|
+
};
|
|
1887
|
+
/**
|
|
1888
|
+
* Feature config for the built-in `ask_user_question` answer-pill sheet.
|
|
1889
|
+
* When a tool call with the name `ask_user_question` arrives, the widget
|
|
1890
|
+
* renders an interactive sheet over the composer in place of the generic
|
|
1891
|
+
* tool bubble.
|
|
1892
|
+
*/
|
|
1893
|
+
type AgentWidgetAskUserQuestionFeature = {
|
|
1894
|
+
/** Enable the feature. Defaults to true. When false, `ask_user_question` renders as a regular tool bubble. */
|
|
1895
|
+
enabled?: boolean;
|
|
1896
|
+
/** Slide-in animation duration in ms. Defaults to 180. */
|
|
1897
|
+
slideInMs?: number;
|
|
1898
|
+
/** Label for the free-text pill. Defaults to "Other…". */
|
|
1899
|
+
freeTextLabel?: string;
|
|
1900
|
+
/** Placeholder text in the free-text input. Defaults to "Type your answer…". */
|
|
1901
|
+
freeTextPlaceholder?: string;
|
|
1902
|
+
/** Button label for submitting multi-select / free-text answers. Defaults to "Send". */
|
|
1903
|
+
submitLabel?: string;
|
|
1904
|
+
/** Button label advancing to the next question in grouped (paginated) payloads. Defaults to "Next". */
|
|
1905
|
+
nextLabel?: string;
|
|
1906
|
+
/** Button label moving back to the previous question in grouped payloads. Defaults to "Back". */
|
|
1907
|
+
backLabel?: string;
|
|
1908
|
+
/** Button label submitting all answers from the final page of a grouped payload. Defaults to "Submit all". */
|
|
1909
|
+
submitAllLabel?: string;
|
|
1910
|
+
/**
|
|
1911
|
+
* In grouped (multi-question) mode, auto-advance to the next page after a
|
|
1912
|
+
* single-select pill pick or free-text submit on intermediate pages.
|
|
1913
|
+
* Defaults to `true`. The final page never auto-submits — users always
|
|
1914
|
+
* confirm with an explicit "Submit all" click. Multi-select pages always
|
|
1915
|
+
* require an explicit Next regardless of this setting.
|
|
1916
|
+
*/
|
|
1917
|
+
groupedAutoAdvance?: boolean;
|
|
1918
|
+
/**
|
|
1919
|
+
* Visual layout for the option list.
|
|
1920
|
+
* - `"rows"` (default) — full-width stacked rows with always-visible
|
|
1921
|
+
* descriptions, right-edge number badges (single-select) or checkboxes
|
|
1922
|
+
* (multi-select), and an always-visible inline "Other" input.
|
|
1923
|
+
* - `"pills"` — legacy compact pill list with horizontal wrap; description
|
|
1924
|
+
* surfaces as a tooltip and the "Other…" pill expands on click.
|
|
1925
|
+
*/
|
|
1926
|
+
layout?: "rows" | "pills";
|
|
1927
|
+
/**
|
|
1928
|
+
* Button label for skipping the current question in grouped payloads.
|
|
1929
|
+
* Defaults to "Skip". On intermediate pages Skip advances without recording
|
|
1930
|
+
* an answer; on the final page Skip submits the partial answer record
|
|
1931
|
+
* (skipped questions absent from the resolved object). For single-question
|
|
1932
|
+
* payloads Skip behaves like dismiss.
|
|
1933
|
+
*/
|
|
1934
|
+
skipLabel?: string;
|
|
1935
|
+
/** Style overrides for the sheet and pills. */
|
|
1936
|
+
styles?: AgentWidgetAskUserQuestionStyles;
|
|
1937
|
+
};
|
|
1938
|
+
type SSEEventRecord = {
|
|
1939
|
+
id: string;
|
|
1940
|
+
type: string;
|
|
1941
|
+
timestamp: number;
|
|
1942
|
+
payload: string;
|
|
1943
|
+
};
|
|
1944
|
+
/**
|
|
1945
|
+
* Badge color configuration for event stream event types.
|
|
1946
|
+
*/
|
|
1947
|
+
type EventStreamBadgeColor = {
|
|
1948
|
+
/** Background color (CSS value) */
|
|
1949
|
+
bg: string;
|
|
1950
|
+
/** Text color (CSS value) */
|
|
1951
|
+
text: string;
|
|
1952
|
+
};
|
|
1953
|
+
/**
|
|
1954
|
+
* Configuration for the Event Stream inspector view.
|
|
1955
|
+
*/
|
|
1956
|
+
type EventStreamConfig = {
|
|
1957
|
+
/**
|
|
1958
|
+
* Custom badge color mappings by event type prefix or exact type.
|
|
1959
|
+
* Keys are matched as exact match first, then prefix match (keys ending with "_").
|
|
1960
|
+
* @example { "flow_": { bg: "#dcfce7", text: "#166534" }, "error": { bg: "#fecaca", text: "#991b1b" } }
|
|
1961
|
+
*/
|
|
1962
|
+
badgeColors?: Record<string, EventStreamBadgeColor>;
|
|
1963
|
+
/**
|
|
1964
|
+
* Timestamp display format.
|
|
1965
|
+
* - "relative": Shows time offset from first event (+0.000s, +0.361s)
|
|
1966
|
+
* - "absolute": Shows wall-clock time (HH:MM:SS.mmm)
|
|
1967
|
+
* @default "relative"
|
|
1968
|
+
*/
|
|
1969
|
+
timestampFormat?: "absolute" | "relative";
|
|
1970
|
+
/**
|
|
1971
|
+
* Whether to show sequential event numbers (1, 2, 3...).
|
|
1972
|
+
* @default true
|
|
1973
|
+
*/
|
|
1974
|
+
showSequenceNumbers?: boolean;
|
|
1975
|
+
/**
|
|
1976
|
+
* Maximum events to keep in the ring buffer.
|
|
1977
|
+
* @default 500
|
|
1978
|
+
*/
|
|
1979
|
+
maxEvents?: number;
|
|
1980
|
+
/**
|
|
1981
|
+
* Fields to extract from event payloads for description text.
|
|
1982
|
+
* The first matching field value is displayed after the badge.
|
|
1983
|
+
* @default ["flowName", "stepName", "name", "tool", "toolName"]
|
|
1984
|
+
*/
|
|
1985
|
+
descriptionFields?: string[];
|
|
1986
|
+
/**
|
|
1987
|
+
* Custom CSS class names to append to event stream UI elements.
|
|
1988
|
+
* Each value is a space-separated class string appended to the element's default classes.
|
|
1989
|
+
*/
|
|
1990
|
+
classNames?: {
|
|
1991
|
+
/** The toggle button in the widget header (activity icon). */
|
|
1992
|
+
toggleButton?: string;
|
|
1993
|
+
/** Additional classes applied to the toggle button when the event stream is open. */
|
|
1994
|
+
toggleButtonActive?: string;
|
|
1995
|
+
/** The outer event stream panel/container. */
|
|
1996
|
+
panel?: string;
|
|
1997
|
+
/** The toolbar header bar (title, filter, copy all). */
|
|
1998
|
+
headerBar?: string;
|
|
1999
|
+
/** The search bar wrapper. */
|
|
2000
|
+
searchBar?: string;
|
|
2001
|
+
/** The search text input. */
|
|
2002
|
+
searchInput?: string;
|
|
2003
|
+
/** Each event row wrapper. */
|
|
2004
|
+
eventRow?: string;
|
|
2005
|
+
/** The "new events" scroll indicator pill. */
|
|
2006
|
+
scrollIndicator?: string;
|
|
2007
|
+
};
|
|
2008
|
+
};
|
|
2009
|
+
/**
|
|
2010
|
+
* Context for the renderEventStreamView plugin hook.
|
|
2011
|
+
*/
|
|
2012
|
+
type EventStreamViewRenderContext = {
|
|
2013
|
+
config: AgentWidgetConfig;
|
|
2014
|
+
events: SSEEventRecord[];
|
|
2015
|
+
defaultRenderer: () => HTMLElement;
|
|
2016
|
+
onClose?: () => void;
|
|
2017
|
+
};
|
|
2018
|
+
/**
|
|
2019
|
+
* Context for the renderEventStreamRow plugin hook.
|
|
2020
|
+
*/
|
|
2021
|
+
type EventStreamRowRenderContext = {
|
|
2022
|
+
event: SSEEventRecord;
|
|
2023
|
+
index: number;
|
|
2024
|
+
config: AgentWidgetConfig;
|
|
2025
|
+
defaultRenderer: () => HTMLElement;
|
|
2026
|
+
isExpanded: boolean;
|
|
2027
|
+
onToggleExpand: () => void;
|
|
2028
|
+
};
|
|
2029
|
+
/**
|
|
2030
|
+
* Context for the renderEventStreamToolbar plugin hook.
|
|
2031
|
+
*/
|
|
2032
|
+
type EventStreamToolbarRenderContext = {
|
|
2033
|
+
config: AgentWidgetConfig;
|
|
2034
|
+
defaultRenderer: () => HTMLElement;
|
|
2035
|
+
eventCount: number;
|
|
2036
|
+
filteredCount: number;
|
|
2037
|
+
onFilterChange: (type: string) => void;
|
|
2038
|
+
onSearchChange: (term: string) => void;
|
|
2039
|
+
};
|
|
2040
|
+
/**
|
|
2041
|
+
* Context for the renderEventStreamPayload plugin hook.
|
|
2042
|
+
*/
|
|
2043
|
+
type EventStreamPayloadRenderContext = {
|
|
2044
|
+
event: SSEEventRecord;
|
|
2045
|
+
config: AgentWidgetConfig;
|
|
2046
|
+
defaultRenderer: () => HTMLElement;
|
|
2047
|
+
parsedPayload: unknown;
|
|
2048
|
+
};
|
|
2049
|
+
type AgentWidgetDockConfig = {
|
|
2050
|
+
/**
|
|
2051
|
+
* Side of the wrapped container where the docked panel should render.
|
|
2052
|
+
* @default "right"
|
|
2053
|
+
*/
|
|
2054
|
+
side?: "left" | "right";
|
|
2055
|
+
/**
|
|
2056
|
+
* Expanded width of the docked panel.
|
|
2057
|
+
* @default "420px"
|
|
2058
|
+
*/
|
|
2059
|
+
width?: string;
|
|
2060
|
+
/**
|
|
2061
|
+
* When false, the dock column snaps between `0` and `width` with no CSS transition so main
|
|
2062
|
+
* content does not reflow during the open/close animation.
|
|
2063
|
+
* @default true
|
|
2064
|
+
*/
|
|
2065
|
+
animate?: boolean;
|
|
2066
|
+
/**
|
|
2067
|
+
* How the dock panel is shown.
|
|
2068
|
+
* - `"resize"` (default): a flex column grows/shrinks between `0` and `width` (main content reflows).
|
|
2069
|
+
* - `"overlay"`: panel is absolutely positioned and translates in/out **over** full-width content.
|
|
2070
|
+
* - `"push"`: a wide inner track `[content at shell width][panel]` translates horizontally so the panel
|
|
2071
|
+
* appears to push the workspace aside **without** animating the content column width (Shopify-style).
|
|
2072
|
+
* - `"emerge"`: like `"resize"`, the flex column animates so **page content reflows**; the chat
|
|
2073
|
+
* panel keeps a **fixed** `dock.width` (not squeezed while the column grows), clipped by the slot so
|
|
2074
|
+
* it appears to emerge at full width like a floating widget.
|
|
2075
|
+
*/
|
|
2076
|
+
reveal?: "resize" | "overlay" | "push" | "emerge";
|
|
2077
|
+
};
|
|
2078
|
+
/**
|
|
2079
|
+
* Layout configuration for `mountMode: "composer-bar"`. Controls how the
|
|
2080
|
+
* collapsed pill is positioned and sized, and how the panel expands when
|
|
2081
|
+
* the user opens it.
|
|
2082
|
+
*/
|
|
2083
|
+
type AgentWidgetComposerBarConfig = {
|
|
2084
|
+
/**
|
|
2085
|
+
* Max-width of the collapsed pill composer at the bottom of the viewport.
|
|
2086
|
+
* @default "720px"
|
|
2087
|
+
*/
|
|
2088
|
+
collapsedMaxWidth?: string;
|
|
2089
|
+
/**
|
|
2090
|
+
* Bottom offset (CSS length) from the viewport edge in the collapsed state.
|
|
2091
|
+
* @default "16px"
|
|
2092
|
+
*/
|
|
2093
|
+
bottomOffset?: string;
|
|
2094
|
+
/**
|
|
2095
|
+
* Auto-expand the panel when the user submits a message while the composer
|
|
2096
|
+
* is collapsed. When false, the message still sends but the panel remains
|
|
2097
|
+
* collapsed (the host can drive expansion programmatically).
|
|
2098
|
+
* @default true
|
|
2099
|
+
*/
|
|
2100
|
+
expandOnSubmit?: boolean;
|
|
2101
|
+
/**
|
|
2102
|
+
* Size of the expanded chat panel.
|
|
2103
|
+
* - `"anchored"` (default): the pill stays at the bottom of the viewport
|
|
2104
|
+
* and the chat history grows upward into a centered column above it.
|
|
2105
|
+
* Width is driven by `expandedMaxWidth`; the panel's top edge sits at
|
|
2106
|
+
* `expandedTopOffset` from the viewport top.
|
|
2107
|
+
* - `"fullscreen"`: covers the entire viewport (mobile-app style). Inner
|
|
2108
|
+
* content is centered horizontally via `contentMaxWidth`.
|
|
2109
|
+
* - `"modal"`: centered sheet with margins; size driven by
|
|
2110
|
+
* `modalMaxWidth` / `modalMaxHeight`.
|
|
2111
|
+
* @default "anchored"
|
|
2112
|
+
*/
|
|
2113
|
+
expandedSize?: "anchored" | "fullscreen" | "modal";
|
|
2114
|
+
/**
|
|
2115
|
+
* When `expandedSize === "anchored"`, max-width of the expanded panel
|
|
2116
|
+
* column. Capped at `calc(100vw - 32px)` on narrow viewports.
|
|
2117
|
+
* @default "880px"
|
|
2118
|
+
*/
|
|
2119
|
+
expandedMaxWidth?: string;
|
|
2120
|
+
/**
|
|
2121
|
+
* When `expandedSize === "anchored"`, distance from the viewport top to
|
|
2122
|
+
* the panel's top edge. Accepts any CSS length.
|
|
2123
|
+
* @default "5vh"
|
|
2124
|
+
*/
|
|
2125
|
+
expandedTopOffset?: string;
|
|
2126
|
+
/**
|
|
2127
|
+
* Max-width applied to messages, composer form, suggestions, and
|
|
2128
|
+
* attachment previews so they center horizontally inside the expanded
|
|
2129
|
+
* panel. Falls back to `layout.contentMaxWidth` when set; otherwise
|
|
2130
|
+
* defaults to this value.
|
|
2131
|
+
* @default "720px"
|
|
2132
|
+
*/
|
|
2133
|
+
contentMaxWidth?: string;
|
|
2134
|
+
/**
|
|
2135
|
+
* When `expandedSize === "modal"`, max-width of the expanded sheet.
|
|
2136
|
+
* @default "880px"
|
|
2137
|
+
*/
|
|
2138
|
+
modalMaxWidth?: string;
|
|
2139
|
+
/**
|
|
2140
|
+
* When `expandedSize === "modal"`, max-height of the expanded sheet.
|
|
2141
|
+
* @default "min(90vh, 800px)"
|
|
2142
|
+
*/
|
|
2143
|
+
modalMaxHeight?: string;
|
|
2144
|
+
/**
|
|
2145
|
+
* Configuration for the "peek" banner — the chrome-less row above the
|
|
2146
|
+
* collapsed pill that previews the most recent assistant message.
|
|
2147
|
+
*/
|
|
2148
|
+
peek?: AgentWidgetComposerBarPeekConfig;
|
|
2149
|
+
};
|
|
2150
|
+
/**
|
|
2151
|
+
* Configuration for the composer-bar peek banner. Reuses the same
|
|
2152
|
+
* `streamAnimation` shape developers already configure for the main message
|
|
2153
|
+
* stream, so the surface for animations is identical across both contexts.
|
|
2154
|
+
*
|
|
2155
|
+
* Resolution order:
|
|
2156
|
+
* - If `peek.streamAnimation` is set, those values apply to the peek.
|
|
2157
|
+
* - Otherwise the peek inherits from `features.streamAnimation`.
|
|
2158
|
+
*
|
|
2159
|
+
* Per-surface carve-outs:
|
|
2160
|
+
* - `bubbleClass` from a plugin (used by `pop-bubble`) is ignored — the peek
|
|
2161
|
+
* has no bubble analog.
|
|
2162
|
+
* - `containerClass`, `wrap` ("char" | "word"), `useCaret`, `placeholder`
|
|
2163
|
+
* ("skeleton"), `buffer` ("word" | "line"), `speed`, `duration`, and
|
|
2164
|
+
* custom plugins all apply unchanged.
|
|
2165
|
+
*/
|
|
2166
|
+
type AgentWidgetComposerBarPeekConfig = {
|
|
2167
|
+
/**
|
|
2168
|
+
* Reveal animation for the peek's trailing-message preview. Same shape as
|
|
2169
|
+
* `features.streamAnimation`. Omit to inherit from the main stream config.
|
|
2170
|
+
*/
|
|
2171
|
+
streamAnimation?: AgentWidgetStreamAnimationFeature;
|
|
2172
|
+
};
|
|
2173
|
+
type AgentWidgetLauncherConfig = {
|
|
2174
|
+
enabled?: boolean;
|
|
2175
|
+
title?: string;
|
|
2176
|
+
subtitle?: string;
|
|
2177
|
+
textHidden?: boolean;
|
|
2178
|
+
iconUrl?: string;
|
|
2179
|
+
agentIconText?: string;
|
|
2180
|
+
agentIconName?: string;
|
|
2181
|
+
agentIconHidden?: boolean;
|
|
2182
|
+
position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
2183
|
+
/**
|
|
2184
|
+
* Controls how the launcher panel is mounted relative to the host page.
|
|
2185
|
+
* - "floating": default floating launcher / panel behavior
|
|
2186
|
+
* - "docked": wraps the target container and renders as a sibling dock
|
|
2187
|
+
* - "composer-bar": persistent rounded-pill composer fixed to the bottom of
|
|
2188
|
+
* the viewport that morphs into a fullscreen (or modal) chat panel on
|
|
2189
|
+
* submit and minimizes back to the pill on close.
|
|
2190
|
+
*
|
|
2191
|
+
* @default "floating"
|
|
2192
|
+
*/
|
|
2193
|
+
mountMode?: "floating" | "docked" | "composer-bar";
|
|
2194
|
+
/**
|
|
2195
|
+
* Layout configuration for docked mode.
|
|
2196
|
+
*/
|
|
2197
|
+
dock?: AgentWidgetDockConfig;
|
|
2198
|
+
/**
|
|
2199
|
+
* Layout configuration for composer-bar mode.
|
|
2200
|
+
* Only applies when `mountMode === "composer-bar"`.
|
|
2201
|
+
*/
|
|
2202
|
+
composerBar?: AgentWidgetComposerBarConfig;
|
|
2203
|
+
autoExpand?: boolean;
|
|
2204
|
+
width?: string;
|
|
2205
|
+
/**
|
|
2206
|
+
* When true, the widget panel will fill the full height of its container.
|
|
2207
|
+
* Useful for sidebar layouts where the chat should take up the entire viewport height.
|
|
2208
|
+
* The widget will use flex layout to ensure header stays at top, messages scroll in middle,
|
|
2209
|
+
* and composer stays fixed at bottom.
|
|
2210
|
+
*
|
|
2211
|
+
* @default false
|
|
2212
|
+
*/
|
|
2213
|
+
fullHeight?: boolean;
|
|
2214
|
+
/**
|
|
2215
|
+
* When true, the widget panel will be positioned as a sidebar flush with the viewport edges.
|
|
2216
|
+
* The panel will have:
|
|
2217
|
+
* - No border-radius (square corners)
|
|
2218
|
+
* - No margins (flush with top, left/right, and bottom edges)
|
|
2219
|
+
* - Full viewport height
|
|
2220
|
+
* - Subtle shadow on the edge facing the content
|
|
2221
|
+
* - No border between footer and messages
|
|
2222
|
+
*
|
|
2223
|
+
* Use with `position` to control which side ('bottom-left' for left sidebar, 'bottom-right' for right sidebar).
|
|
2224
|
+
* Automatically enables fullHeight when true.
|
|
2225
|
+
*
|
|
2226
|
+
* @default false
|
|
2227
|
+
*/
|
|
2228
|
+
sidebarMode?: boolean;
|
|
2229
|
+
/**
|
|
2230
|
+
* Width of the sidebar panel when sidebarMode is true.
|
|
2231
|
+
* @default "420px"
|
|
2232
|
+
*/
|
|
2233
|
+
sidebarWidth?: string;
|
|
2234
|
+
/**
|
|
2235
|
+
* Offset (in pixels) to subtract from the calculated panel height.
|
|
2236
|
+
* Useful for adjusting the panel height when there are other fixed elements on the page.
|
|
2237
|
+
* Only applies when not in fullHeight or sidebarMode.
|
|
2238
|
+
*
|
|
2239
|
+
* @default 0
|
|
2240
|
+
*/
|
|
2241
|
+
heightOffset?: number;
|
|
2242
|
+
/**
|
|
2243
|
+
* When true, the widget panel expands to fill the full viewport on mobile devices.
|
|
2244
|
+
* Removes border-radius, margins, and shadows for a native app-like experience.
|
|
2245
|
+
* Applies when viewport width is at or below `mobileBreakpoint`.
|
|
2246
|
+
*
|
|
2247
|
+
* @default true
|
|
2248
|
+
*/
|
|
2249
|
+
mobileFullscreen?: boolean;
|
|
2250
|
+
/**
|
|
2251
|
+
* Viewport width (in pixels) at or below which the widget enters mobile fullscreen mode.
|
|
2252
|
+
* Only applies when `mobileFullscreen` is true.
|
|
2253
|
+
*
|
|
2254
|
+
* @default 640
|
|
2255
|
+
*/
|
|
2256
|
+
mobileBreakpoint?: number;
|
|
2257
|
+
/**
|
|
2258
|
+
* CSS z-index applied to the widget wrapper and launcher button in all
|
|
2259
|
+
* positioned modes (floating panel, mobile fullscreen, sidebar, docked
|
|
2260
|
+
* mobile fullscreen). Increase this value if other elements on the host
|
|
2261
|
+
* page appear on top of the widget.
|
|
2262
|
+
*
|
|
2263
|
+
* In viewport-covering modes (sidebar, mobile fullscreen), the widget
|
|
2264
|
+
* also elevates the host element's stacking context and locks
|
|
2265
|
+
* document scroll to prevent background scrolling.
|
|
2266
|
+
*
|
|
2267
|
+
* @default 100000
|
|
2268
|
+
*/
|
|
2269
|
+
zIndex?: number;
|
|
2270
|
+
callToActionIconText?: string;
|
|
2271
|
+
callToActionIconName?: string;
|
|
2272
|
+
callToActionIconColor?: string;
|
|
2273
|
+
callToActionIconBackgroundColor?: string;
|
|
2274
|
+
callToActionIconHidden?: boolean;
|
|
2275
|
+
callToActionIconPadding?: string;
|
|
2276
|
+
agentIconSize?: string;
|
|
2277
|
+
callToActionIconSize?: string;
|
|
2278
|
+
headerIconSize?: string;
|
|
2279
|
+
headerIconName?: string;
|
|
2280
|
+
headerIconHidden?: boolean;
|
|
2281
|
+
closeButtonSize?: string;
|
|
2282
|
+
closeButtonColor?: string;
|
|
2283
|
+
closeButtonBackgroundColor?: string;
|
|
2284
|
+
closeButtonBorderWidth?: string;
|
|
2285
|
+
closeButtonBorderColor?: string;
|
|
2286
|
+
closeButtonBorderRadius?: string;
|
|
2287
|
+
closeButtonPaddingX?: string;
|
|
2288
|
+
closeButtonPaddingY?: string;
|
|
2289
|
+
closeButtonPlacement?: "inline" | "top-right";
|
|
2290
|
+
closeButtonIconName?: string;
|
|
2291
|
+
closeButtonIconText?: string;
|
|
2292
|
+
closeButtonTooltipText?: string;
|
|
2293
|
+
closeButtonShowTooltip?: boolean;
|
|
2294
|
+
clearChat?: AgentWidgetClearChatConfig;
|
|
2295
|
+
/**
|
|
2296
|
+
* Border style for the launcher button.
|
|
2297
|
+
* @example "1px solid #e5e7eb" | "2px solid #3b82f6" | "none"
|
|
2298
|
+
* @default "1px solid #e5e7eb"
|
|
2299
|
+
*/
|
|
2300
|
+
border?: string;
|
|
2301
|
+
/**
|
|
2302
|
+
* Box shadow for the launcher button.
|
|
2303
|
+
* @example "0 10px 15px -3px rgba(0,0,0,0.1)" | "none"
|
|
2304
|
+
* @default "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)"
|
|
2305
|
+
*/
|
|
2306
|
+
shadow?: string;
|
|
2307
|
+
/**
|
|
2308
|
+
* CSS `max-width` for the floating launcher button when the panel is closed.
|
|
2309
|
+
* Title and subtitle each truncate with an ellipsis when space is tight; full strings are available via the native `title` tooltip. Does not affect the open chat panel (`width` / `launcherWidth`).
|
|
2310
|
+
*
|
|
2311
|
+
* @example "min(380px, calc(100vw - 48px))"
|
|
2312
|
+
*/
|
|
2313
|
+
collapsedMaxWidth?: string;
|
|
2314
|
+
};
|
|
2315
|
+
type AgentWidgetSendButtonConfig = {
|
|
2316
|
+
borderWidth?: string;
|
|
2317
|
+
borderColor?: string;
|
|
2318
|
+
paddingX?: string;
|
|
2319
|
+
paddingY?: string;
|
|
2320
|
+
iconText?: string;
|
|
2321
|
+
iconName?: string;
|
|
2322
|
+
useIcon?: boolean;
|
|
2323
|
+
tooltipText?: string;
|
|
2324
|
+
showTooltip?: boolean;
|
|
2325
|
+
backgroundColor?: string;
|
|
2326
|
+
textColor?: string;
|
|
2327
|
+
size?: string;
|
|
2328
|
+
/** Lucide icon name shown while a response is streaming. Clicking the button in this state aborts the stream. Default: "square". */
|
|
2329
|
+
stopIconName?: string;
|
|
2330
|
+
/** Tooltip text shown while streaming. Default: "Stop generating". */
|
|
2331
|
+
stopTooltipText?: string;
|
|
2332
|
+
};
|
|
2333
|
+
/** Optional composer UI state for custom `renderComposer` implementations. */
|
|
2334
|
+
type AgentWidgetComposerConfig = {
|
|
2335
|
+
models?: Array<{
|
|
2336
|
+
id: string;
|
|
2337
|
+
label: string;
|
|
2338
|
+
}>;
|
|
2339
|
+
/** Current selection; host or plugin may update this at runtime. */
|
|
2340
|
+
selectedModelId?: string;
|
|
2341
|
+
};
|
|
2342
|
+
type AgentWidgetClearChatConfig = {
|
|
2343
|
+
enabled?: boolean;
|
|
2344
|
+
placement?: "inline" | "top-right";
|
|
2345
|
+
iconName?: string;
|
|
2346
|
+
iconColor?: string;
|
|
2347
|
+
backgroundColor?: string;
|
|
2348
|
+
borderWidth?: string;
|
|
2349
|
+
borderColor?: string;
|
|
2350
|
+
borderRadius?: string;
|
|
2351
|
+
size?: string;
|
|
2352
|
+
paddingX?: string;
|
|
2353
|
+
paddingY?: string;
|
|
2354
|
+
tooltipText?: string;
|
|
2355
|
+
showTooltip?: boolean;
|
|
2356
|
+
};
|
|
2357
|
+
type AgentWidgetStatusIndicatorConfig = {
|
|
2358
|
+
visible?: boolean;
|
|
2359
|
+
/** Text alignment. Default: 'right'. */
|
|
2360
|
+
align?: 'left' | 'center' | 'right';
|
|
2361
|
+
idleText?: string;
|
|
2362
|
+
/** URL to open in a new tab when the idle text is clicked. */
|
|
2363
|
+
idleLink?: string;
|
|
2364
|
+
connectingText?: string;
|
|
2365
|
+
connectedText?: string;
|
|
2366
|
+
errorText?: string;
|
|
2367
|
+
};
|
|
2368
|
+
type AgentWidgetVoiceRecognitionConfig = {
|
|
2369
|
+
enabled?: boolean;
|
|
2370
|
+
pauseDuration?: number;
|
|
2371
|
+
/** Text shown in the user message placeholder while voice is being processed. Default: "🎤 Processing voice..." */
|
|
2372
|
+
processingText?: string;
|
|
2373
|
+
/** Text shown in the assistant message if voice processing fails. Default: "Voice processing failed. Please try again." */
|
|
2374
|
+
processingErrorText?: string;
|
|
2375
|
+
iconName?: string;
|
|
2376
|
+
iconSize?: string;
|
|
2377
|
+
iconColor?: string;
|
|
2378
|
+
backgroundColor?: string;
|
|
2379
|
+
borderColor?: string;
|
|
2380
|
+
borderWidth?: string;
|
|
2381
|
+
paddingX?: string;
|
|
2382
|
+
paddingY?: string;
|
|
2383
|
+
tooltipText?: string;
|
|
2384
|
+
showTooltip?: boolean;
|
|
2385
|
+
recordingIconColor?: string;
|
|
2386
|
+
recordingBackgroundColor?: string;
|
|
2387
|
+
recordingBorderColor?: string;
|
|
2388
|
+
showRecordingIndicator?: boolean;
|
|
2389
|
+
/** Icon name shown while processing voice input. Default: "loader" */
|
|
2390
|
+
processingIconName?: string;
|
|
2391
|
+
/** Icon color during processing. Inherits idle iconColor if not set */
|
|
2392
|
+
processingIconColor?: string;
|
|
2393
|
+
/** Button background color during processing. Inherits idle backgroundColor if not set */
|
|
2394
|
+
processingBackgroundColor?: string;
|
|
2395
|
+
/** Button border color during processing. Inherits idle borderColor if not set */
|
|
2396
|
+
processingBorderColor?: string;
|
|
2397
|
+
/** Icon name shown while agent is speaking. Default: "volume-2" (or "square" in cancel mode) */
|
|
2398
|
+
speakingIconName?: string;
|
|
2399
|
+
/** Icon color while speaking. Inherits idle iconColor if not set */
|
|
2400
|
+
speakingIconColor?: string;
|
|
2401
|
+
/** Button background color while speaking. Inherits idle backgroundColor if not set */
|
|
2402
|
+
speakingBackgroundColor?: string;
|
|
2403
|
+
/** Button border color while speaking. Inherits idle borderColor if not set */
|
|
2404
|
+
speakingBorderColor?: string;
|
|
2405
|
+
autoResume?: boolean | "assistant";
|
|
2406
|
+
provider?: {
|
|
2407
|
+
type: 'browser' | 'runtype' | 'custom';
|
|
2408
|
+
browser?: {
|
|
2409
|
+
language?: string;
|
|
2410
|
+
continuous?: boolean;
|
|
2411
|
+
};
|
|
2412
|
+
runtype?: {
|
|
2413
|
+
agentId: string;
|
|
2414
|
+
clientToken: string;
|
|
2415
|
+
host?: string;
|
|
2416
|
+
voiceId?: string;
|
|
2417
|
+
/** Duration of silence (ms) before auto-stopping recording. Default: 2000 */
|
|
2418
|
+
pauseDuration?: number;
|
|
2419
|
+
/** RMS volume threshold below which counts as silence. Default: 0.01 */
|
|
2420
|
+
silenceThreshold?: number;
|
|
2421
|
+
};
|
|
2422
|
+
custom?: any;
|
|
2423
|
+
};
|
|
2424
|
+
};
|
|
2425
|
+
/**
|
|
2426
|
+
* Text-to-speech configuration for reading assistant messages aloud.
|
|
2427
|
+
* Currently supports the Web Speech API (`speechSynthesis`).
|
|
2428
|
+
*
|
|
2429
|
+
* @example
|
|
2430
|
+
* ```typescript
|
|
2431
|
+
* textToSpeech: {
|
|
2432
|
+
* enabled: true,
|
|
2433
|
+
* provider: 'browser',
|
|
2434
|
+
* voice: 'Google US English',
|
|
2435
|
+
* rate: 1.2,
|
|
2436
|
+
* pitch: 1.0
|
|
2437
|
+
* }
|
|
2438
|
+
* ```
|
|
2439
|
+
*/
|
|
2440
|
+
type TextToSpeechConfig = {
|
|
2441
|
+
/** Enable text-to-speech for assistant messages */
|
|
2442
|
+
enabled: boolean;
|
|
2443
|
+
/**
|
|
2444
|
+
* TTS provider.
|
|
2445
|
+
* - `'browser'` — Use the Web Speech API for all assistant messages (default).
|
|
2446
|
+
* - `'runtype'` — Server handles TTS for voice interactions.
|
|
2447
|
+
* Set `browserFallback: true` to also speak text-typed responses via the browser.
|
|
2448
|
+
*/
|
|
2449
|
+
provider?: 'browser' | 'runtype';
|
|
2450
|
+
/**
|
|
2451
|
+
* When `provider` is `'runtype'`, fall back to browser TTS for assistant
|
|
2452
|
+
* messages that the server didn't already speak (e.g. text-typed messages).
|
|
2453
|
+
* Has no effect when provider is `'browser'` (browser TTS is always used).
|
|
2454
|
+
* @default false
|
|
2455
|
+
*/
|
|
2456
|
+
browserFallback?: boolean;
|
|
2457
|
+
/** Voice name to use for browser TTS (e.g., 'Google US English'). If not found, uses auto-detect. */
|
|
2458
|
+
voice?: string;
|
|
2459
|
+
/**
|
|
2460
|
+
* Custom voice picker called when `voice` is not set.
|
|
2461
|
+
* Receives the full list of available `SpeechSynthesisVoice` objects and
|
|
2462
|
+
* should return the one to use. If not provided, the SDK auto-detects the
|
|
2463
|
+
* best English voice.
|
|
2464
|
+
*
|
|
2465
|
+
* @example
|
|
2466
|
+
* ```typescript
|
|
2467
|
+
* pickVoice: (voices) => voices.find(v => v.lang === 'fr-FR') ?? voices[0]
|
|
2468
|
+
* ```
|
|
2469
|
+
*/
|
|
2470
|
+
pickVoice?: (voices: SpeechSynthesisVoice[]) => SpeechSynthesisVoice;
|
|
2471
|
+
/** Speech rate (0.1 - 10). Default: 1 */
|
|
2472
|
+
rate?: number;
|
|
2473
|
+
/** Speech pitch (0 - 2). Default: 1 */
|
|
2474
|
+
pitch?: number;
|
|
2475
|
+
};
|
|
2476
|
+
/**
|
|
2477
|
+
* Configuration for tool approval bubbles.
|
|
2478
|
+
* Controls styling, labels, and behavior of the approval UI.
|
|
2479
|
+
*/
|
|
2480
|
+
type AgentWidgetApprovalConfig = {
|
|
2481
|
+
/** Background color of the approval bubble */
|
|
2482
|
+
backgroundColor?: string;
|
|
2483
|
+
/** Border color of the approval bubble */
|
|
2484
|
+
borderColor?: string;
|
|
2485
|
+
/** Color for the title text */
|
|
2486
|
+
titleColor?: string;
|
|
2487
|
+
/** Color for the description text */
|
|
2488
|
+
descriptionColor?: string;
|
|
2489
|
+
/** Background color for the approve button */
|
|
2490
|
+
approveButtonColor?: string;
|
|
2491
|
+
/** Text color for the approve button */
|
|
2492
|
+
approveButtonTextColor?: string;
|
|
2493
|
+
/** Background color for the deny button */
|
|
2494
|
+
denyButtonColor?: string;
|
|
2495
|
+
/** Text color for the deny button */
|
|
2496
|
+
denyButtonTextColor?: string;
|
|
2497
|
+
/** Background color for the parameters block */
|
|
2498
|
+
parameterBackgroundColor?: string;
|
|
2499
|
+
/** Text color for the parameters block */
|
|
2500
|
+
parameterTextColor?: string;
|
|
2501
|
+
/** Title text displayed above the description */
|
|
2502
|
+
title?: string;
|
|
2503
|
+
/** Label for the approve button */
|
|
2504
|
+
approveLabel?: string;
|
|
2505
|
+
/** Label for the deny button */
|
|
2506
|
+
denyLabel?: string;
|
|
2507
|
+
/**
|
|
2508
|
+
* Custom handler for approval decisions.
|
|
2509
|
+
* Return void to let the SDK auto-resolve via the API,
|
|
2510
|
+
* or return a Response/ReadableStream for custom handling.
|
|
2511
|
+
*/
|
|
2512
|
+
onDecision?: (data: {
|
|
2513
|
+
approvalId: string;
|
|
2514
|
+
executionId: string;
|
|
2515
|
+
agentId: string;
|
|
2516
|
+
toolName: string;
|
|
2517
|
+
}, decision: 'approved' | 'denied') => Promise<Response | ReadableStream<Uint8Array> | void>;
|
|
2518
|
+
};
|
|
2519
|
+
type AgentWidgetToolCallConfig = {
|
|
2520
|
+
/** Box-shadow for tool-call bubbles; overrides `theme.toolBubbleShadow` when set. */
|
|
2521
|
+
shadow?: string;
|
|
2522
|
+
/** Background color of the tool call bubble container. */
|
|
2523
|
+
backgroundColor?: string;
|
|
2524
|
+
/** Border color of the tool call bubble container. */
|
|
2525
|
+
borderColor?: string;
|
|
2526
|
+
/** Border width of the tool call bubble container (CSS value, e.g. `"1px"`). */
|
|
2527
|
+
borderWidth?: string;
|
|
2528
|
+
/** Border radius of the tool call bubble container (CSS value, e.g. `"12px"`). */
|
|
2529
|
+
borderRadius?: string;
|
|
2530
|
+
/** Background color of the collapsed header row. */
|
|
2531
|
+
headerBackgroundColor?: string;
|
|
2532
|
+
/** Text color of the collapsed header row (tool name / summary). */
|
|
2533
|
+
headerTextColor?: string;
|
|
2534
|
+
/** Horizontal padding of the collapsed header row (CSS value). */
|
|
2535
|
+
headerPaddingX?: string;
|
|
2536
|
+
/** Vertical padding of the collapsed header row (CSS value). */
|
|
2537
|
+
headerPaddingY?: string;
|
|
2538
|
+
/** Background color of the expanded content area. */
|
|
2539
|
+
contentBackgroundColor?: string;
|
|
2540
|
+
/** Text color of the expanded content area. */
|
|
2541
|
+
contentTextColor?: string;
|
|
2542
|
+
/** Horizontal padding of the expanded content area (CSS value). */
|
|
2543
|
+
contentPaddingX?: string;
|
|
2544
|
+
/** Vertical padding of the expanded content area (CSS value). */
|
|
2545
|
+
contentPaddingY?: string;
|
|
2546
|
+
/** Background color of code blocks (arguments / result) in the expanded area. */
|
|
2547
|
+
codeBlockBackgroundColor?: string;
|
|
2548
|
+
/** Border color of code blocks in the expanded area. */
|
|
2549
|
+
codeBlockBorderColor?: string;
|
|
2550
|
+
/** Text color of code blocks in the expanded area. */
|
|
2551
|
+
codeBlockTextColor?: string;
|
|
2552
|
+
/** Color of the expand/collapse toggle icon. */
|
|
2553
|
+
toggleTextColor?: string;
|
|
2554
|
+
/** Color of section labels ("Arguments", "Result", "Activity") in the expanded area. */
|
|
2555
|
+
labelTextColor?: string;
|
|
2556
|
+
/**
|
|
2557
|
+
* Override the collapsed summary row content for a tool call bubble.
|
|
2558
|
+
* Return `null` to fall back to the built-in summary for the active display mode.
|
|
2559
|
+
*/
|
|
2560
|
+
renderCollapsedSummary?: (context: {
|
|
2561
|
+
message: AgentWidgetMessage;
|
|
2562
|
+
toolCall: AgentWidgetToolCall;
|
|
2563
|
+
defaultSummary: string;
|
|
2564
|
+
previewText: string;
|
|
2565
|
+
collapsedMode: AgentWidgetToolCallCollapsedMode;
|
|
2566
|
+
isActive: boolean;
|
|
2567
|
+
config: AgentWidgetConfig;
|
|
2568
|
+
/** Static elapsed time snapshot, e.g. "2.6s". */
|
|
2569
|
+
elapsed: string;
|
|
2570
|
+
/**
|
|
2571
|
+
* Returns a `<span>` whose text content is automatically updated every
|
|
2572
|
+
* 100ms by the widget's global timer. Place it anywhere in your returned
|
|
2573
|
+
* HTMLElement to get a live-ticking duration display.
|
|
2574
|
+
*/
|
|
2575
|
+
createElapsedElement: () => HTMLElement;
|
|
2576
|
+
}) => HTMLElement | string | null;
|
|
2577
|
+
/**
|
|
2578
|
+
* Override the lightweight collapsed preview content shown for active tool rows.
|
|
2579
|
+
* Return `null` to fall back to the built-in preview text.
|
|
2580
|
+
*/
|
|
2581
|
+
renderCollapsedPreview?: (context: {
|
|
2582
|
+
message: AgentWidgetMessage;
|
|
2583
|
+
toolCall: AgentWidgetToolCall;
|
|
2584
|
+
defaultPreview: string;
|
|
2585
|
+
isActive: boolean;
|
|
2586
|
+
config: AgentWidgetConfig;
|
|
2587
|
+
}) => HTMLElement | string | null;
|
|
2588
|
+
/**
|
|
2589
|
+
* Override the summary content for grouped consecutive tool-call containers.
|
|
2590
|
+
* Return `null` to fall back to the built-in `Called [x] tools` summary.
|
|
2591
|
+
*/
|
|
2592
|
+
renderGroupedSummary?: (context: {
|
|
2593
|
+
messages: AgentWidgetMessage[];
|
|
2594
|
+
toolCalls: AgentWidgetToolCall[];
|
|
2595
|
+
defaultSummary: string;
|
|
2596
|
+
config: AgentWidgetConfig;
|
|
2597
|
+
}) => HTMLElement | string | null;
|
|
2598
|
+
/**
|
|
2599
|
+
* Template string for the header text while a tool call is active (running).
|
|
2600
|
+
*
|
|
2601
|
+
* **Placeholders:** `{toolName}` (tool name), `{duration}` (live-updating elapsed time).
|
|
2602
|
+
*
|
|
2603
|
+
* **Inline formatting:** `~dim~`, `*italic*`, `**bold**` — parsed at render time and
|
|
2604
|
+
* applied as styled `<span>` elements. Works with all animation modes.
|
|
2605
|
+
*
|
|
2606
|
+
* When not set, falls back to the current `collapsedMode` behavior.
|
|
2607
|
+
* @example "Calling {toolName}... ~{duration}~"
|
|
2608
|
+
* @example "**Searching** *{toolName}*..."
|
|
2609
|
+
*/
|
|
2610
|
+
activeTextTemplate?: string;
|
|
2611
|
+
/**
|
|
2612
|
+
* Template string for the header text when a tool call is complete.
|
|
2613
|
+
*
|
|
2614
|
+
* **Placeholders:** `{toolName}` (tool name), `{duration}` (final elapsed time).
|
|
2615
|
+
*
|
|
2616
|
+
* **Inline formatting:** `~dim~`, `*italic*`, `**bold**` — same syntax as `activeTextTemplate`.
|
|
2617
|
+
*
|
|
2618
|
+
* When not set, falls back to the existing "Used tool for X seconds" text.
|
|
2619
|
+
* @example "Finished {toolName} ~{duration}~"
|
|
2620
|
+
*/
|
|
2621
|
+
completeTextTemplate?: string;
|
|
2622
|
+
/**
|
|
2623
|
+
* Primary color for shimmer-color animation mode.
|
|
2624
|
+
* Defaults to the current text color.
|
|
2625
|
+
*/
|
|
2626
|
+
loadingAnimationColor?: string;
|
|
2627
|
+
/**
|
|
2628
|
+
* Secondary/end color for shimmer-color animation mode.
|
|
2629
|
+
* Creates a gradient sweep between `loadingAnimationColor` and this color.
|
|
2630
|
+
* @default "#3b82f6"
|
|
2631
|
+
*/
|
|
2632
|
+
loadingAnimationSecondaryColor?: string;
|
|
2633
|
+
/**
|
|
2634
|
+
* Duration of one full animation cycle in milliseconds.
|
|
2635
|
+
* Applies to pulse, shimmer, shimmer-color, and rainbow modes.
|
|
2636
|
+
* @default 2000
|
|
2637
|
+
*/
|
|
2638
|
+
loadingAnimationDuration?: number;
|
|
2639
|
+
};
|
|
2640
|
+
type AgentWidgetReasoningConfig = {
|
|
2641
|
+
/**
|
|
2642
|
+
* Override the collapsed summary row content for a reasoning bubble.
|
|
2643
|
+
* Return `null` to fall back to the built-in summary.
|
|
2644
|
+
*/
|
|
2645
|
+
renderCollapsedSummary?: (context: {
|
|
2646
|
+
message: AgentWidgetMessage;
|
|
2647
|
+
reasoning: AgentWidgetReasoning;
|
|
2648
|
+
defaultSummary: string;
|
|
2649
|
+
previewText: string;
|
|
2650
|
+
isActive: boolean;
|
|
2651
|
+
config: AgentWidgetConfig;
|
|
2652
|
+
/** Static elapsed time snapshot, e.g. "2.6s". */
|
|
2653
|
+
elapsed: string;
|
|
2654
|
+
/**
|
|
2655
|
+
* Returns a `<span>` whose text content is automatically updated every
|
|
2656
|
+
* 100ms by the widget's global timer. Place it anywhere in your returned
|
|
2657
|
+
* HTMLElement to get a live-ticking duration display.
|
|
2658
|
+
*/
|
|
2659
|
+
createElapsedElement: () => HTMLElement;
|
|
2660
|
+
}) => HTMLElement | string | null;
|
|
2661
|
+
/**
|
|
2662
|
+
* Override the lightweight collapsed preview content shown for active reasoning rows.
|
|
2663
|
+
* Return `null` to fall back to the built-in preview text.
|
|
2664
|
+
*/
|
|
2665
|
+
renderCollapsedPreview?: (context: {
|
|
2666
|
+
message: AgentWidgetMessage;
|
|
2667
|
+
reasoning: AgentWidgetReasoning;
|
|
2668
|
+
defaultPreview: string;
|
|
2669
|
+
isActive: boolean;
|
|
2670
|
+
config: AgentWidgetConfig;
|
|
2671
|
+
}) => HTMLElement | string | null;
|
|
2672
|
+
/**
|
|
2673
|
+
* Template string for the header text while reasoning is active (streaming).
|
|
2674
|
+
*
|
|
2675
|
+
* **Placeholders:** `{duration}` (live-updating elapsed time).
|
|
2676
|
+
*
|
|
2677
|
+
* **Inline formatting:** `~dim~`, `*italic*`, `**bold**` — parsed at render time.
|
|
2678
|
+
*
|
|
2679
|
+
* When not set, falls back to the default "Thinking..." text.
|
|
2680
|
+
* @example "Thinking... ~{duration}~"
|
|
2681
|
+
*/
|
|
2682
|
+
activeTextTemplate?: string;
|
|
2683
|
+
/**
|
|
2684
|
+
* Template string for the header text when reasoning is complete.
|
|
2685
|
+
*
|
|
2686
|
+
* **Placeholders:** `{duration}` (final elapsed time).
|
|
2687
|
+
*
|
|
2688
|
+
* **Inline formatting:** `~dim~`, `*italic*`, `**bold**` — same syntax as `activeTextTemplate`.
|
|
2689
|
+
*
|
|
2690
|
+
* When not set, falls back to the default "Thought for X seconds" text.
|
|
2691
|
+
* @example "Thought for ~{duration}~"
|
|
2692
|
+
*/
|
|
2693
|
+
completeTextTemplate?: string;
|
|
2694
|
+
/**
|
|
2695
|
+
* Primary color for shimmer-color animation mode.
|
|
2696
|
+
* Defaults to the current text color.
|
|
2697
|
+
*/
|
|
2698
|
+
loadingAnimationColor?: string;
|
|
2699
|
+
/**
|
|
2700
|
+
* Secondary/end color for shimmer-color animation mode.
|
|
2701
|
+
* Creates a gradient sweep between `loadingAnimationColor` and this color.
|
|
2702
|
+
* @default "#3b82f6"
|
|
2703
|
+
*/
|
|
2704
|
+
loadingAnimationSecondaryColor?: string;
|
|
2705
|
+
/**
|
|
2706
|
+
* Duration of one full animation cycle in milliseconds.
|
|
2707
|
+
* Applies to pulse, shimmer, shimmer-color, and rainbow modes.
|
|
2708
|
+
* @default 2000
|
|
2709
|
+
*/
|
|
2710
|
+
loadingAnimationDuration?: number;
|
|
2711
|
+
};
|
|
2712
|
+
type AgentWidgetSuggestionChipsConfig = {
|
|
2713
|
+
fontFamily?: "sans-serif" | "serif" | "mono";
|
|
2714
|
+
fontWeight?: string;
|
|
2715
|
+
paddingX?: string;
|
|
2716
|
+
paddingY?: string;
|
|
2717
|
+
};
|
|
2718
|
+
/**
|
|
2719
|
+
* Interface for pluggable stream parsers that extract text from streaming responses.
|
|
2720
|
+
* Parsers handle incremental parsing to extract text values from structured formats (JSON, XML, etc.).
|
|
2721
|
+
*
|
|
2722
|
+
* @example
|
|
2723
|
+
* ```typescript
|
|
2724
|
+
* const jsonParser: AgentWidgetStreamParser = {
|
|
2725
|
+
* processChunk: async (content) => {
|
|
2726
|
+
* // Extract text from JSON - return null if not JSON or text not available yet
|
|
2727
|
+
* if (!content.trim().startsWith('{')) return null;
|
|
2728
|
+
* const match = content.match(/"text"\s*:\s*"([^"]*)"/);
|
|
2729
|
+
* return match ? match[1] : null;
|
|
2730
|
+
* },
|
|
2731
|
+
* getExtractedText: () => extractedText
|
|
2732
|
+
* };
|
|
2733
|
+
* ```
|
|
2734
|
+
*/
|
|
2735
|
+
interface AgentWidgetStreamParserResult {
|
|
2736
|
+
/**
|
|
2737
|
+
* The extracted text to display (may be partial during streaming)
|
|
2738
|
+
*/
|
|
2739
|
+
text: string | null;
|
|
2740
|
+
/**
|
|
2741
|
+
* The raw accumulated content. Built-in parsers always populate this so
|
|
2742
|
+
* downstream middleware (action handlers, logging, etc.) can
|
|
2743
|
+
* inspect/parse the original structured payload.
|
|
2744
|
+
*/
|
|
2745
|
+
raw?: string;
|
|
2746
|
+
}
|
|
2747
|
+
interface AgentWidgetStreamParser {
|
|
2748
|
+
/**
|
|
2749
|
+
* Process a chunk of content and return the extracted text (if available).
|
|
2750
|
+
* This method is called for each chunk as it arrives during streaming.
|
|
2751
|
+
* Return null if the content doesn't match this parser's format or if text is not yet available.
|
|
2752
|
+
*
|
|
2753
|
+
* @param accumulatedContent - The full accumulated content so far (including new chunk)
|
|
2754
|
+
* @returns The extracted text value and optionally raw content, or null if not yet available or format doesn't match
|
|
2755
|
+
*/
|
|
2756
|
+
processChunk(accumulatedContent: string): Promise<AgentWidgetStreamParserResult | string | null> | AgentWidgetStreamParserResult | string | null;
|
|
2757
|
+
/**
|
|
2758
|
+
* Get the currently extracted text value (may be partial).
|
|
2759
|
+
* This is called synchronously to get the latest extracted text without processing.
|
|
2760
|
+
*
|
|
2761
|
+
* @returns The currently extracted text value, or null if not yet available
|
|
2762
|
+
*/
|
|
2763
|
+
getExtractedText(): string | null;
|
|
2764
|
+
/**
|
|
2765
|
+
* Clean up any resources when parsing is complete.
|
|
2766
|
+
*/
|
|
2767
|
+
close?(): Promise<void> | void;
|
|
2768
|
+
}
|
|
2769
|
+
/**
|
|
2770
|
+
* Component renderer function signature for custom components
|
|
2771
|
+
*/
|
|
2772
|
+
type AgentWidgetComponentRenderer = (props: Record<string, unknown>, context: {
|
|
2773
|
+
message: AgentWidgetMessage;
|
|
2774
|
+
config: AgentWidgetConfig;
|
|
2775
|
+
updateProps: (newProps: Record<string, unknown>) => void;
|
|
2776
|
+
}) => HTMLElement;
|
|
2777
|
+
/**
|
|
2778
|
+
* Result from custom SSE event parser
|
|
2779
|
+
*/
|
|
2780
|
+
type AgentWidgetSSEEventResult = {
|
|
2781
|
+
/** Text content to display */
|
|
2782
|
+
text?: string;
|
|
2783
|
+
/** Whether the stream is complete */
|
|
2784
|
+
done?: boolean;
|
|
2785
|
+
/** Error message if an error occurred */
|
|
2786
|
+
error?: string;
|
|
2787
|
+
/** Text segment identity — when this changes, a new assistant message bubble is created */
|
|
2788
|
+
partId?: string;
|
|
2789
|
+
} | null;
|
|
2790
|
+
/**
|
|
2791
|
+
* Custom SSE event parser function
|
|
2792
|
+
* Allows transforming non-standard SSE event formats to persona's expected format
|
|
2793
|
+
*/
|
|
2794
|
+
type AgentWidgetSSEEventParser = (eventData: unknown) => AgentWidgetSSEEventResult | Promise<AgentWidgetSSEEventResult>;
|
|
2795
|
+
/**
|
|
2796
|
+
* Custom fetch function for full control over API requests
|
|
2797
|
+
* Use this for custom authentication, request transformation, etc.
|
|
2798
|
+
*/
|
|
2799
|
+
type AgentWidgetCustomFetch = (url: string, init: RequestInit, payload: AgentWidgetRequestPayload) => Promise<Response>;
|
|
2800
|
+
/**
|
|
2801
|
+
* Dynamic headers function - called before each request
|
|
2802
|
+
*/
|
|
2803
|
+
type AgentWidgetHeadersFunction = () => Record<string, string> | Promise<Record<string, string>>;
|
|
2804
|
+
/**
|
|
2805
|
+
* Session information returned after client token initialization.
|
|
2806
|
+
* Contains session ID, expiry time, flow info, and config from the server.
|
|
2807
|
+
*/
|
|
2808
|
+
type ClientSession = {
|
|
2809
|
+
/** Unique session identifier */
|
|
2810
|
+
sessionId: string;
|
|
2811
|
+
/** When the session expires */
|
|
2812
|
+
expiresAt: Date;
|
|
2813
|
+
/** Flow information */
|
|
2814
|
+
flow: {
|
|
2815
|
+
id: string;
|
|
2816
|
+
name: string;
|
|
2817
|
+
description: string | null;
|
|
2818
|
+
};
|
|
2819
|
+
/** Configuration from the server */
|
|
2820
|
+
config: {
|
|
2821
|
+
welcomeMessage: string | null;
|
|
2822
|
+
placeholder: string;
|
|
2823
|
+
theme: Record<string, unknown> | null;
|
|
2824
|
+
};
|
|
2825
|
+
};
|
|
2826
|
+
/** Icon button in the header title row (minimal layout). */
|
|
2827
|
+
type AgentWidgetHeaderTrailingAction = {
|
|
2828
|
+
id: string;
|
|
2829
|
+
/** Lucide icon name, e.g. `chevron-down` */
|
|
2830
|
+
icon?: string;
|
|
2831
|
+
label?: string;
|
|
2832
|
+
ariaLabel?: string;
|
|
2833
|
+
/**
|
|
2834
|
+
* When set, clicking this action opens a dropdown menu.
|
|
2835
|
+
* Menu item selections fire `onAction(menuItemId)`.
|
|
2836
|
+
*/
|
|
2837
|
+
menuItems?: Array<{
|
|
2838
|
+
id: string;
|
|
2839
|
+
label: string;
|
|
2840
|
+
icon?: string;
|
|
2841
|
+
destructive?: boolean;
|
|
2842
|
+
dividerBefore?: boolean;
|
|
2843
|
+
}>;
|
|
2844
|
+
};
|
|
2845
|
+
/**
|
|
2846
|
+
* Context provided to header render functions
|
|
2847
|
+
*/
|
|
2848
|
+
type HeaderRenderContext = {
|
|
2849
|
+
config: AgentWidgetConfig;
|
|
2850
|
+
onClose?: () => void;
|
|
2851
|
+
onClearChat?: () => void;
|
|
2852
|
+
/** Built from `layout.header.trailingActions` for custom `render` implementations. */
|
|
2853
|
+
trailingActions?: AgentWidgetHeaderTrailingAction[];
|
|
2854
|
+
/** Fired when a built-in trailing action is activated (same as `layout.header.onAction`). */
|
|
2855
|
+
onAction?: (actionId: string) => void;
|
|
2856
|
+
};
|
|
2857
|
+
/**
|
|
2858
|
+
* Context provided to message render functions
|
|
2859
|
+
*/
|
|
2860
|
+
type MessageRenderContext = {
|
|
2861
|
+
message: AgentWidgetMessage;
|
|
2862
|
+
config: AgentWidgetConfig;
|
|
2863
|
+
streaming: boolean;
|
|
2864
|
+
};
|
|
2865
|
+
/**
|
|
2866
|
+
* Context provided to slot render functions
|
|
2867
|
+
*/
|
|
2868
|
+
type SlotRenderContext = {
|
|
2869
|
+
config: AgentWidgetConfig;
|
|
2870
|
+
defaultContent: () => HTMLElement | null;
|
|
2871
|
+
};
|
|
2872
|
+
/**
|
|
2873
|
+
* Header layout configuration
|
|
2874
|
+
* Allows customization of the header section appearance and behavior
|
|
2875
|
+
*/
|
|
2876
|
+
type AgentWidgetHeaderLayoutConfig = {
|
|
2877
|
+
/**
|
|
2878
|
+
* Layout preset: "default" | "minimal"
|
|
2879
|
+
* - default: Standard layout with icon, title, subtitle, and buttons
|
|
2880
|
+
* - minimal: Simplified layout with just title and close button
|
|
2881
|
+
*/
|
|
2882
|
+
layout?: "default" | "minimal";
|
|
2883
|
+
/** Show/hide the header icon */
|
|
2884
|
+
showIcon?: boolean;
|
|
2885
|
+
/** Show/hide the title */
|
|
2886
|
+
showTitle?: boolean;
|
|
2887
|
+
/** Show/hide the subtitle */
|
|
2888
|
+
showSubtitle?: boolean;
|
|
2889
|
+
/** Show/hide the close button */
|
|
2890
|
+
showCloseButton?: boolean;
|
|
2891
|
+
/** Show/hide the clear chat button */
|
|
2892
|
+
showClearChat?: boolean;
|
|
2893
|
+
/**
|
|
2894
|
+
* Custom renderer for complete header override
|
|
2895
|
+
* When provided, replaces the entire header with custom content
|
|
2896
|
+
*/
|
|
2897
|
+
render?: (context: HeaderRenderContext) => HTMLElement;
|
|
2898
|
+
/**
|
|
2899
|
+
* Shown after the title in `minimal` header layout (e.g. chevron menu affordance).
|
|
2900
|
+
*/
|
|
2901
|
+
trailingActions?: AgentWidgetHeaderTrailingAction[];
|
|
2902
|
+
/** Called when a `trailingActions` button is clicked. */
|
|
2903
|
+
onAction?: (actionId: string) => void;
|
|
2904
|
+
/**
|
|
2905
|
+
* Called when the header title row is clicked.
|
|
2906
|
+
* Useful for dropdown menus or navigation triggered from the header.
|
|
2907
|
+
* When set, the title row becomes visually interactive (cursor: pointer).
|
|
2908
|
+
*/
|
|
2909
|
+
onTitleClick?: () => void;
|
|
2910
|
+
/** Style config for the title row hover effect (minimal layout). */
|
|
2911
|
+
titleRowHover?: {
|
|
2912
|
+
/** Hover background color. */
|
|
2913
|
+
background?: string;
|
|
2914
|
+
/** Hover border color. */
|
|
2915
|
+
border?: string;
|
|
2916
|
+
/** Border radius for the pill shape. */
|
|
2917
|
+
borderRadius?: string;
|
|
2918
|
+
/** Padding inside the pill. */
|
|
2919
|
+
padding?: string;
|
|
2920
|
+
};
|
|
2921
|
+
/**
|
|
2922
|
+
* Replaces the title with a combo button (label + chevron + dropdown menu).
|
|
2923
|
+
* When set, `trailingActions`, `onTitleClick`, and `titleRowHover` are ignored
|
|
2924
|
+
* since the combo button handles all of these internally.
|
|
2925
|
+
*/
|
|
2926
|
+
titleMenu?: {
|
|
2927
|
+
/** Dropdown menu items. */
|
|
2928
|
+
menuItems: Array<{
|
|
2929
|
+
id: string;
|
|
2930
|
+
label: string;
|
|
2931
|
+
icon?: string;
|
|
2932
|
+
destructive?: boolean;
|
|
2933
|
+
dividerBefore?: boolean;
|
|
2934
|
+
}>;
|
|
2935
|
+
/** Called when a menu item is selected. */
|
|
2936
|
+
onSelect: (id: string) => void;
|
|
2937
|
+
/** Hover pill style. */
|
|
2938
|
+
hover?: {
|
|
2939
|
+
background?: string;
|
|
2940
|
+
border?: string;
|
|
2941
|
+
borderRadius?: string;
|
|
2942
|
+
padding?: string;
|
|
2943
|
+
};
|
|
2944
|
+
};
|
|
2945
|
+
};
|
|
2946
|
+
/**
|
|
2947
|
+
* Avatar configuration for message bubbles
|
|
2948
|
+
*/
|
|
2949
|
+
type AgentWidgetAvatarConfig = {
|
|
2950
|
+
/** Whether to show avatars */
|
|
2951
|
+
show?: boolean;
|
|
2952
|
+
/** Position of avatar relative to message bubble */
|
|
2953
|
+
position?: "left" | "right";
|
|
2954
|
+
/** URL or emoji for user avatar */
|
|
2955
|
+
userAvatar?: string;
|
|
2956
|
+
/** URL or emoji for assistant avatar */
|
|
2957
|
+
assistantAvatar?: string;
|
|
2958
|
+
};
|
|
2959
|
+
/**
|
|
2960
|
+
* Timestamp configuration for message bubbles
|
|
2961
|
+
*/
|
|
2962
|
+
type AgentWidgetTimestampConfig = {
|
|
2963
|
+
/** Whether to show timestamps */
|
|
2964
|
+
show?: boolean;
|
|
2965
|
+
/** Position of timestamp relative to message */
|
|
2966
|
+
position?: "inline" | "below";
|
|
2967
|
+
/** Custom formatter for timestamp display */
|
|
2968
|
+
format?: (date: Date) => string;
|
|
2969
|
+
};
|
|
2970
|
+
/**
|
|
2971
|
+
* Message layout configuration
|
|
2972
|
+
* Allows customization of how chat messages are displayed
|
|
2973
|
+
*/
|
|
2974
|
+
type AgentWidgetMessageLayoutConfig = {
|
|
2975
|
+
/**
|
|
2976
|
+
* Layout preset: "bubble" | "flat" | "minimal"
|
|
2977
|
+
* - bubble: Standard chat bubble appearance (default)
|
|
2978
|
+
* - flat: Flat messages without bubble styling
|
|
2979
|
+
* - minimal: Minimal styling with reduced padding/borders
|
|
2980
|
+
*/
|
|
2981
|
+
layout?: "bubble" | "flat" | "minimal";
|
|
2982
|
+
/** Avatar configuration */
|
|
2983
|
+
avatar?: AgentWidgetAvatarConfig;
|
|
2984
|
+
/** Timestamp configuration */
|
|
2985
|
+
timestamp?: AgentWidgetTimestampConfig;
|
|
2986
|
+
/** Group consecutive messages from the same role */
|
|
2987
|
+
groupConsecutive?: boolean;
|
|
2988
|
+
/**
|
|
2989
|
+
* Custom renderer for user messages
|
|
2990
|
+
* When provided, replaces the default user message rendering
|
|
2991
|
+
*/
|
|
2992
|
+
renderUserMessage?: (context: MessageRenderContext) => HTMLElement;
|
|
2993
|
+
/**
|
|
2994
|
+
* Custom renderer for assistant messages
|
|
2995
|
+
* When provided, replaces the default assistant message rendering
|
|
2996
|
+
*/
|
|
2997
|
+
renderAssistantMessage?: (context: MessageRenderContext) => HTMLElement;
|
|
2998
|
+
};
|
|
2999
|
+
/**
|
|
3000
|
+
* Available layout slots for content injection
|
|
3001
|
+
*/
|
|
3002
|
+
type WidgetLayoutSlot = "header-left" | "header-center" | "header-right" | "body-top" | "messages" | "body-bottom" | "footer-top" | "composer" | "footer-bottom";
|
|
3003
|
+
/**
|
|
3004
|
+
* Slot renderer function signature
|
|
3005
|
+
* Returns HTMLElement to render in the slot, or null to use default content
|
|
3006
|
+
*/
|
|
3007
|
+
type SlotRenderer = (context: SlotRenderContext) => HTMLElement | null;
|
|
3008
|
+
/**
|
|
3009
|
+
* Main layout configuration
|
|
3010
|
+
* Provides comprehensive control over widget layout and appearance
|
|
3011
|
+
*
|
|
3012
|
+
* @example
|
|
3013
|
+
* ```typescript
|
|
3014
|
+
* config: {
|
|
3015
|
+
* layout: {
|
|
3016
|
+
* header: { layout: "minimal" },
|
|
3017
|
+
* messages: {
|
|
3018
|
+
* avatar: { show: true, assistantAvatar: "/bot.png" },
|
|
3019
|
+
* timestamp: { show: true, position: "below" }
|
|
3020
|
+
* },
|
|
3021
|
+
* slots: {
|
|
3022
|
+
* "footer-top": () => {
|
|
3023
|
+
* const el = document.createElement("div");
|
|
3024
|
+
* el.textContent = "Powered by AI";
|
|
3025
|
+
* return el;
|
|
3026
|
+
* }
|
|
3027
|
+
* }
|
|
3028
|
+
* }
|
|
3029
|
+
* }
|
|
3030
|
+
* ```
|
|
3031
|
+
*/
|
|
3032
|
+
type AgentWidgetLayoutConfig = {
|
|
3033
|
+
/** Header layout configuration */
|
|
3034
|
+
header?: AgentWidgetHeaderLayoutConfig;
|
|
3035
|
+
/** Message layout configuration */
|
|
3036
|
+
messages?: AgentWidgetMessageLayoutConfig;
|
|
3037
|
+
/** Slot renderers for custom content injection */
|
|
3038
|
+
slots?: Partial<Record<WidgetLayoutSlot, SlotRenderer>>;
|
|
3039
|
+
/**
|
|
3040
|
+
* Show/hide the header section entirely.
|
|
3041
|
+
* When false, the header (including icon, title, buttons) is completely hidden.
|
|
3042
|
+
* @default true
|
|
3043
|
+
*/
|
|
3044
|
+
showHeader?: boolean;
|
|
3045
|
+
/**
|
|
3046
|
+
* Show/hide the footer/composer section entirely.
|
|
3047
|
+
* When false, the footer (including input field, send button, suggestions) is completely hidden.
|
|
3048
|
+
* Useful for read-only conversation previews.
|
|
3049
|
+
* @default true
|
|
3050
|
+
*/
|
|
3051
|
+
showFooter?: boolean;
|
|
3052
|
+
/**
|
|
3053
|
+
* Max width for the content area (messages + composer).
|
|
3054
|
+
* Applied with `margin: 0 auto` for centering.
|
|
3055
|
+
* Accepts any CSS width value (e.g. "90ch", "720px", "80%").
|
|
3056
|
+
*/
|
|
3057
|
+
contentMaxWidth?: string;
|
|
3058
|
+
};
|
|
3059
|
+
/**
|
|
3060
|
+
* Token types for marked renderer methods
|
|
3061
|
+
*/
|
|
3062
|
+
type AgentWidgetMarkdownHeadingToken = {
|
|
3063
|
+
type: "heading";
|
|
3064
|
+
raw: string;
|
|
3065
|
+
depth: 1 | 2 | 3 | 4 | 5 | 6;
|
|
3066
|
+
text: string;
|
|
3067
|
+
tokens: unknown[];
|
|
3068
|
+
};
|
|
3069
|
+
type AgentWidgetMarkdownCodeToken = {
|
|
3070
|
+
type: "code";
|
|
3071
|
+
raw: string;
|
|
3072
|
+
text: string;
|
|
3073
|
+
lang?: string;
|
|
3074
|
+
escaped?: boolean;
|
|
3075
|
+
};
|
|
3076
|
+
type AgentWidgetMarkdownBlockquoteToken = {
|
|
3077
|
+
type: "blockquote";
|
|
3078
|
+
raw: string;
|
|
3079
|
+
text: string;
|
|
3080
|
+
tokens: unknown[];
|
|
3081
|
+
};
|
|
3082
|
+
type AgentWidgetMarkdownTableToken = {
|
|
3083
|
+
type: "table";
|
|
3084
|
+
raw: string;
|
|
3085
|
+
header: Array<{
|
|
3086
|
+
text: string;
|
|
3087
|
+
tokens: unknown[];
|
|
3088
|
+
}>;
|
|
3089
|
+
rows: Array<Array<{
|
|
3090
|
+
text: string;
|
|
3091
|
+
tokens: unknown[];
|
|
3092
|
+
}>>;
|
|
3093
|
+
align: Array<"left" | "center" | "right" | null>;
|
|
3094
|
+
};
|
|
3095
|
+
type AgentWidgetMarkdownLinkToken = {
|
|
3096
|
+
type: "link";
|
|
3097
|
+
raw: string;
|
|
3098
|
+
href: string;
|
|
3099
|
+
title: string | null;
|
|
3100
|
+
text: string;
|
|
3101
|
+
tokens: unknown[];
|
|
3102
|
+
};
|
|
3103
|
+
type AgentWidgetMarkdownImageToken = {
|
|
3104
|
+
type: "image";
|
|
3105
|
+
raw: string;
|
|
3106
|
+
href: string;
|
|
3107
|
+
title: string | null;
|
|
3108
|
+
text: string;
|
|
3109
|
+
};
|
|
3110
|
+
type AgentWidgetMarkdownListToken = {
|
|
3111
|
+
type: "list";
|
|
3112
|
+
raw: string;
|
|
3113
|
+
ordered: boolean;
|
|
3114
|
+
start: number | "";
|
|
3115
|
+
loose: boolean;
|
|
3116
|
+
items: unknown[];
|
|
3117
|
+
};
|
|
3118
|
+
type AgentWidgetMarkdownListItemToken = {
|
|
3119
|
+
type: "list_item";
|
|
3120
|
+
raw: string;
|
|
3121
|
+
task: boolean;
|
|
3122
|
+
checked?: boolean;
|
|
3123
|
+
loose: boolean;
|
|
3124
|
+
text: string;
|
|
3125
|
+
tokens: unknown[];
|
|
3126
|
+
};
|
|
3127
|
+
type AgentWidgetMarkdownParagraphToken = {
|
|
3128
|
+
type: "paragraph";
|
|
3129
|
+
raw: string;
|
|
3130
|
+
text: string;
|
|
3131
|
+
tokens: unknown[];
|
|
3132
|
+
};
|
|
3133
|
+
type AgentWidgetMarkdownCodespanToken = {
|
|
3134
|
+
type: "codespan";
|
|
3135
|
+
raw: string;
|
|
3136
|
+
text: string;
|
|
3137
|
+
};
|
|
3138
|
+
type AgentWidgetMarkdownStrongToken = {
|
|
3139
|
+
type: "strong";
|
|
3140
|
+
raw: string;
|
|
3141
|
+
text: string;
|
|
3142
|
+
tokens: unknown[];
|
|
3143
|
+
};
|
|
3144
|
+
type AgentWidgetMarkdownEmToken = {
|
|
3145
|
+
type: "em";
|
|
3146
|
+
raw: string;
|
|
3147
|
+
text: string;
|
|
3148
|
+
tokens: unknown[];
|
|
3149
|
+
};
|
|
3150
|
+
/**
|
|
3151
|
+
* Custom renderer overrides for markdown elements.
|
|
3152
|
+
* Each method receives the token and should return an HTML string.
|
|
3153
|
+
* Return `false` to use the default renderer.
|
|
3154
|
+
*
|
|
3155
|
+
* @example
|
|
3156
|
+
* ```typescript
|
|
3157
|
+
* renderer: {
|
|
3158
|
+
* heading(token) {
|
|
3159
|
+
* return `<h${token.depth} class="custom-heading">${token.text}</h${token.depth}>`;
|
|
3160
|
+
* },
|
|
3161
|
+
* link(token) {
|
|
3162
|
+
* return `<a href="${token.href}" target="_blank" rel="noopener">${token.text}</a>`;
|
|
3163
|
+
* }
|
|
3164
|
+
* }
|
|
3165
|
+
* ```
|
|
3166
|
+
*/
|
|
3167
|
+
type AgentWidgetMarkdownRendererOverrides = {
|
|
3168
|
+
/** Override heading rendering (h1-h6) */
|
|
3169
|
+
heading?: (token: AgentWidgetMarkdownHeadingToken) => string | false;
|
|
3170
|
+
/** Override code block rendering */
|
|
3171
|
+
code?: (token: AgentWidgetMarkdownCodeToken) => string | false;
|
|
3172
|
+
/** Override blockquote rendering */
|
|
3173
|
+
blockquote?: (token: AgentWidgetMarkdownBlockquoteToken) => string | false;
|
|
3174
|
+
/** Override table rendering */
|
|
3175
|
+
table?: (token: AgentWidgetMarkdownTableToken) => string | false;
|
|
3176
|
+
/** Override link rendering */
|
|
3177
|
+
link?: (token: AgentWidgetMarkdownLinkToken) => string | false;
|
|
3178
|
+
/** Override image rendering */
|
|
3179
|
+
image?: (token: AgentWidgetMarkdownImageToken) => string | false;
|
|
3180
|
+
/** Override list rendering (ul/ol) */
|
|
3181
|
+
list?: (token: AgentWidgetMarkdownListToken) => string | false;
|
|
3182
|
+
/** Override list item rendering */
|
|
3183
|
+
listitem?: (token: AgentWidgetMarkdownListItemToken) => string | false;
|
|
3184
|
+
/** Override paragraph rendering */
|
|
3185
|
+
paragraph?: (token: AgentWidgetMarkdownParagraphToken) => string | false;
|
|
3186
|
+
/** Override inline code rendering */
|
|
3187
|
+
codespan?: (token: AgentWidgetMarkdownCodespanToken) => string | false;
|
|
3188
|
+
/** Override strong/bold rendering */
|
|
3189
|
+
strong?: (token: AgentWidgetMarkdownStrongToken) => string | false;
|
|
3190
|
+
/** Override emphasis/italic rendering */
|
|
3191
|
+
em?: (token: AgentWidgetMarkdownEmToken) => string | false;
|
|
3192
|
+
/** Override horizontal rule rendering */
|
|
3193
|
+
hr?: () => string | false;
|
|
3194
|
+
/** Override line break rendering */
|
|
3195
|
+
br?: () => string | false;
|
|
3196
|
+
/** Override deleted/strikethrough rendering */
|
|
3197
|
+
del?: (token: {
|
|
3198
|
+
type: "del";
|
|
3199
|
+
raw: string;
|
|
3200
|
+
text: string;
|
|
3201
|
+
tokens: unknown[];
|
|
3202
|
+
}) => string | false;
|
|
3203
|
+
/** Override checkbox rendering (in task lists) */
|
|
3204
|
+
checkbox?: (token: {
|
|
3205
|
+
checked: boolean;
|
|
3206
|
+
}) => string | false;
|
|
3207
|
+
/** Override HTML passthrough */
|
|
3208
|
+
html?: (token: {
|
|
3209
|
+
type: "html";
|
|
3210
|
+
raw: string;
|
|
3211
|
+
text: string;
|
|
3212
|
+
}) => string | false;
|
|
3213
|
+
/** Override text rendering */
|
|
3214
|
+
text?: (token: {
|
|
3215
|
+
type: "text";
|
|
3216
|
+
raw: string;
|
|
3217
|
+
text: string;
|
|
3218
|
+
}) => string | false;
|
|
3219
|
+
};
|
|
3220
|
+
/**
|
|
3221
|
+
* Markdown parsing options (subset of marked options)
|
|
3222
|
+
*/
|
|
3223
|
+
type AgentWidgetMarkdownOptions = {
|
|
3224
|
+
/**
|
|
3225
|
+
* Enable GitHub Flavored Markdown (tables, strikethrough, autolinks).
|
|
3226
|
+
* @default true
|
|
3227
|
+
*/
|
|
3228
|
+
gfm?: boolean;
|
|
3229
|
+
/**
|
|
3230
|
+
* Convert \n in paragraphs into <br>.
|
|
3231
|
+
* @default true
|
|
3232
|
+
*/
|
|
3233
|
+
breaks?: boolean;
|
|
3234
|
+
/**
|
|
3235
|
+
* Conform to original markdown.pl as much as possible.
|
|
3236
|
+
* @default false
|
|
3237
|
+
*/
|
|
3238
|
+
pedantic?: boolean;
|
|
3239
|
+
/**
|
|
3240
|
+
* Add id attributes to headings.
|
|
3241
|
+
* @default false
|
|
3242
|
+
*/
|
|
3243
|
+
headerIds?: boolean;
|
|
3244
|
+
/**
|
|
3245
|
+
* Prefix for heading id attributes.
|
|
3246
|
+
* @default ""
|
|
3247
|
+
*/
|
|
3248
|
+
headerPrefix?: string;
|
|
3249
|
+
/**
|
|
3250
|
+
* Mangle email addresses for spam protection.
|
|
3251
|
+
* @default true
|
|
3252
|
+
*/
|
|
3253
|
+
mangle?: boolean;
|
|
3254
|
+
/**
|
|
3255
|
+
* Silent mode - don't throw on parse errors.
|
|
3256
|
+
* @default false
|
|
3257
|
+
*/
|
|
3258
|
+
silent?: boolean;
|
|
3259
|
+
};
|
|
3260
|
+
/**
|
|
3261
|
+
* Markdown configuration for customizing how markdown is rendered in chat messages.
|
|
3262
|
+
* Provides three levels of control:
|
|
3263
|
+
*
|
|
3264
|
+
* 1. **CSS Variables** - Override styles via `--cw-md-*` CSS custom properties
|
|
3265
|
+
* 2. **Parsing Options** - Configure marked behavior via `options`
|
|
3266
|
+
* 3. **Custom Renderers** - Full control via `renderer` overrides
|
|
3267
|
+
*
|
|
3268
|
+
* @example
|
|
3269
|
+
* ```typescript
|
|
3270
|
+
* // Level 2: Configure parsing options
|
|
3271
|
+
* config: {
|
|
3272
|
+
* markdown: {
|
|
3273
|
+
* options: {
|
|
3274
|
+
* gfm: true,
|
|
3275
|
+
* breaks: true,
|
|
3276
|
+
* headerIds: true
|
|
3277
|
+
* }
|
|
3278
|
+
* }
|
|
3279
|
+
* }
|
|
3280
|
+
* ```
|
|
3281
|
+
*
|
|
3282
|
+
* @example
|
|
3283
|
+
* ```typescript
|
|
3284
|
+
* // Level 3: Custom renderers
|
|
3285
|
+
* config: {
|
|
3286
|
+
* markdown: {
|
|
3287
|
+
* renderer: {
|
|
3288
|
+
* heading(token) {
|
|
3289
|
+
* return `<h${token.depth} class="custom-h${token.depth}">${token.text}</h${token.depth}>`;
|
|
3290
|
+
* },
|
|
3291
|
+
* link(token) {
|
|
3292
|
+
* return `<a href="${token.href}" target="_blank">${token.text}</a>`;
|
|
3293
|
+
* },
|
|
3294
|
+
* table(token) {
|
|
3295
|
+
* // Wrap tables in a scrollable container
|
|
3296
|
+
* return `<div class="table-scroll">${this.parser.parse(token.tokens)}</div>`;
|
|
3297
|
+
* }
|
|
3298
|
+
* }
|
|
3299
|
+
* }
|
|
3300
|
+
* }
|
|
3301
|
+
* ```
|
|
3302
|
+
*/
|
|
3303
|
+
type AgentWidgetMarkdownConfig = {
|
|
3304
|
+
/**
|
|
3305
|
+
* Markdown parsing options.
|
|
3306
|
+
* These are passed directly to the marked parser.
|
|
3307
|
+
*/
|
|
3308
|
+
options?: AgentWidgetMarkdownOptions;
|
|
3309
|
+
/**
|
|
3310
|
+
* Custom renderer overrides for specific markdown elements.
|
|
3311
|
+
* Each method receives a token object and should return an HTML string.
|
|
3312
|
+
* Return `false` to fall back to the default renderer.
|
|
3313
|
+
*/
|
|
3314
|
+
renderer?: AgentWidgetMarkdownRendererOverrides;
|
|
3315
|
+
/**
|
|
3316
|
+
* Disable default markdown CSS styles.
|
|
3317
|
+
* When true, the widget won't apply any default styles to markdown elements,
|
|
3318
|
+
* allowing you to provide your own CSS.
|
|
3319
|
+
*
|
|
3320
|
+
* @default false
|
|
3321
|
+
*/
|
|
3322
|
+
disableDefaultStyles?: boolean;
|
|
3323
|
+
};
|
|
3324
|
+
/**
|
|
3325
|
+
* Configuration for file attachments in the composer.
|
|
3326
|
+
* Enables users to attach images to their messages.
|
|
3327
|
+
*
|
|
3328
|
+
* @example
|
|
3329
|
+
* ```typescript
|
|
3330
|
+
* config: {
|
|
3331
|
+
* attachments: {
|
|
3332
|
+
* enabled: true,
|
|
3333
|
+
* allowedTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
|
|
3334
|
+
* maxFileSize: 5 * 1024 * 1024, // 5MB
|
|
3335
|
+
* maxFiles: 4
|
|
3336
|
+
* }
|
|
3337
|
+
* }
|
|
3338
|
+
* ```
|
|
3339
|
+
*/
|
|
3340
|
+
type AgentWidgetAttachmentsConfig = {
|
|
3341
|
+
/**
|
|
3342
|
+
* Enable/disable file attachments.
|
|
3343
|
+
* @default false
|
|
3344
|
+
*/
|
|
3345
|
+
enabled?: boolean;
|
|
3346
|
+
/**
|
|
3347
|
+
* Allowed MIME types for attachments.
|
|
3348
|
+
* @default ['image/png', 'image/jpeg', 'image/gif', 'image/webp']
|
|
3349
|
+
*/
|
|
3350
|
+
allowedTypes?: string[];
|
|
3351
|
+
/**
|
|
3352
|
+
* Maximum file size in bytes.
|
|
3353
|
+
* @default 10485760 (10MB)
|
|
3354
|
+
*/
|
|
3355
|
+
maxFileSize?: number;
|
|
3356
|
+
/**
|
|
3357
|
+
* Maximum number of files per message.
|
|
3358
|
+
* @default 4
|
|
3359
|
+
*/
|
|
3360
|
+
maxFiles?: number;
|
|
3361
|
+
/**
|
|
3362
|
+
* Button icon name (from Lucide icons).
|
|
3363
|
+
* @default 'image-plus'
|
|
3364
|
+
*/
|
|
3365
|
+
buttonIconName?: string;
|
|
3366
|
+
/**
|
|
3367
|
+
* Tooltip text for the attachment button.
|
|
3368
|
+
* @default 'Attach image'
|
|
3369
|
+
*/
|
|
3370
|
+
buttonTooltipText?: string;
|
|
3371
|
+
/**
|
|
3372
|
+
* Callback when a file is rejected (wrong type or too large).
|
|
3373
|
+
*/
|
|
3374
|
+
onFileRejected?: (file: File, reason: 'type' | 'size' | 'count') => void;
|
|
3375
|
+
/**
|
|
3376
|
+
* Customize the drag-and-drop overlay that appears when files are dragged over the widget.
|
|
3377
|
+
*/
|
|
3378
|
+
dropOverlay?: {
|
|
3379
|
+
/** Background color/value of the overlay. @default 'rgba(59, 130, 246, 0.08)' */
|
|
3380
|
+
background?: string;
|
|
3381
|
+
/** Backdrop blur applied behind the overlay (CSS value). @default '8px' */
|
|
3382
|
+
backdropBlur?: string;
|
|
3383
|
+
/** Border style shown during drag. @default '2px dashed rgba(59, 130, 246, 0.4)' */
|
|
3384
|
+
border?: string;
|
|
3385
|
+
/** Border radius of the overlay. @default 'inherit' */
|
|
3386
|
+
borderRadius?: string;
|
|
3387
|
+
/** Inset/margin pulling the overlay away from the container edges (CSS value). @default '0' */
|
|
3388
|
+
inset?: string;
|
|
3389
|
+
/** Lucide icon name displayed in the center. @default 'upload' */
|
|
3390
|
+
iconName?: string;
|
|
3391
|
+
/** Icon size (CSS value). @default '48px' */
|
|
3392
|
+
iconSize?: string;
|
|
3393
|
+
/** Icon stroke color. @default 'rgba(59, 130, 246, 0.6)' */
|
|
3394
|
+
iconColor?: string;
|
|
3395
|
+
/** Icon stroke width. @default 0.5 */
|
|
3396
|
+
iconStrokeWidth?: number;
|
|
3397
|
+
/** Optional label text shown below the icon. */
|
|
3398
|
+
label?: string;
|
|
3399
|
+
/** Label font size. @default '0.875rem' */
|
|
3400
|
+
labelSize?: string;
|
|
3401
|
+
/** Label color. @default 'rgba(59, 130, 246, 0.8)' */
|
|
3402
|
+
labelColor?: string;
|
|
3403
|
+
};
|
|
3404
|
+
};
|
|
3405
|
+
/**
|
|
3406
|
+
* Configuration for persisting widget state across page navigations.
|
|
3407
|
+
* Stores open/closed state, voice recognition state, and voice mode in browser storage.
|
|
3408
|
+
*
|
|
3409
|
+
* @example
|
|
3410
|
+
* ```typescript
|
|
3411
|
+
* config: {
|
|
3412
|
+
* persistState: true // Use defaults: sessionStorage, persist open state
|
|
3413
|
+
* }
|
|
3414
|
+
* ```
|
|
3415
|
+
*
|
|
3416
|
+
* @example
|
|
3417
|
+
* ```typescript
|
|
3418
|
+
* config: {
|
|
3419
|
+
* persistState: {
|
|
3420
|
+
* storage: 'local', // Use localStorage instead of sessionStorage
|
|
3421
|
+
* keyPrefix: 'myapp-', // Custom prefix for storage keys
|
|
3422
|
+
* persist: {
|
|
3423
|
+
* openState: true,
|
|
3424
|
+
* voiceState: true,
|
|
3425
|
+
* focusInput: true
|
|
3426
|
+
* },
|
|
3427
|
+
* clearOnChatClear: true
|
|
3428
|
+
* }
|
|
3429
|
+
* }
|
|
3430
|
+
* ```
|
|
3431
|
+
*/
|
|
3432
|
+
type AgentWidgetPersistStateConfig = {
|
|
3433
|
+
/**
|
|
3434
|
+
* Storage type to use.
|
|
3435
|
+
* @default 'session'
|
|
3436
|
+
*/
|
|
3437
|
+
storage?: 'local' | 'session';
|
|
3438
|
+
/**
|
|
3439
|
+
* Prefix for storage keys.
|
|
3440
|
+
* @default 'persona-'
|
|
3441
|
+
*/
|
|
3442
|
+
keyPrefix?: string;
|
|
3443
|
+
/**
|
|
3444
|
+
* What state to persist.
|
|
3445
|
+
*/
|
|
3446
|
+
persist?: {
|
|
3447
|
+
/**
|
|
3448
|
+
* Persist widget open/closed state.
|
|
3449
|
+
* @default true
|
|
3450
|
+
*/
|
|
3451
|
+
openState?: boolean;
|
|
3452
|
+
/**
|
|
3453
|
+
* Persist voice recognition state.
|
|
3454
|
+
* @default true
|
|
3455
|
+
*/
|
|
3456
|
+
voiceState?: boolean;
|
|
3457
|
+
/**
|
|
3458
|
+
* Focus input when restoring open state.
|
|
3459
|
+
* @default true
|
|
3460
|
+
*/
|
|
3461
|
+
focusInput?: boolean;
|
|
3462
|
+
};
|
|
3463
|
+
/**
|
|
3464
|
+
* Clear persisted state when chat is cleared.
|
|
3465
|
+
* @default true
|
|
3466
|
+
*/
|
|
3467
|
+
clearOnChatClear?: boolean;
|
|
3468
|
+
};
|
|
3469
|
+
/**
|
|
3470
|
+
* Context provided to loading indicator render functions.
|
|
3471
|
+
* Used for customizing the loading indicator appearance.
|
|
3472
|
+
*/
|
|
3473
|
+
type LoadingIndicatorRenderContext = {
|
|
3474
|
+
/**
|
|
3475
|
+
* Full widget configuration for accessing theme, etc.
|
|
3476
|
+
*/
|
|
3477
|
+
config: AgentWidgetConfig;
|
|
3478
|
+
/**
|
|
3479
|
+
* Current streaming state (always true when indicator is shown)
|
|
3480
|
+
*/
|
|
3481
|
+
streaming: boolean;
|
|
3482
|
+
/**
|
|
3483
|
+
* Location where the indicator is rendered:
|
|
3484
|
+
* - 'inline': Inside a streaming assistant message bubble (when content is empty)
|
|
3485
|
+
* - 'standalone': Separate bubble while waiting for stream to start
|
|
3486
|
+
*/
|
|
3487
|
+
location: 'inline' | 'standalone';
|
|
3488
|
+
/**
|
|
3489
|
+
* Function to render the default 3-dot bouncing indicator.
|
|
3490
|
+
* Call this if you want to use the default for certain cases.
|
|
3491
|
+
*/
|
|
3492
|
+
defaultRenderer: () => HTMLElement;
|
|
3493
|
+
};
|
|
3494
|
+
/**
|
|
3495
|
+
* Context provided to idle indicator render functions.
|
|
3496
|
+
* Used for customizing the idle state indicator appearance.
|
|
3497
|
+
*/
|
|
3498
|
+
type IdleIndicatorRenderContext = {
|
|
3499
|
+
/**
|
|
3500
|
+
* Full widget configuration for accessing theme, etc.
|
|
3501
|
+
*/
|
|
3502
|
+
config: AgentWidgetConfig;
|
|
3503
|
+
/**
|
|
3504
|
+
* The last message in the conversation (if any).
|
|
3505
|
+
* Useful for conditional rendering based on who spoke last.
|
|
3506
|
+
*/
|
|
3507
|
+
lastMessage: AgentWidgetMessage | undefined;
|
|
3508
|
+
/**
|
|
3509
|
+
* Total number of messages in the conversation.
|
|
3510
|
+
*/
|
|
3511
|
+
messageCount: number;
|
|
3512
|
+
};
|
|
3513
|
+
/**
|
|
3514
|
+
* Configuration for customizing the loading indicator.
|
|
3515
|
+
* The loading indicator is shown while waiting for a response or
|
|
3516
|
+
* when an assistant message is streaming but has no content yet.
|
|
3517
|
+
*
|
|
3518
|
+
* @example
|
|
3519
|
+
* ```typescript
|
|
3520
|
+
* // Custom animated spinner
|
|
3521
|
+
* config: {
|
|
3522
|
+
* loadingIndicator: {
|
|
3523
|
+
* render: ({ location }) => {
|
|
3524
|
+
* const el = document.createElement('div');
|
|
3525
|
+
* el.innerHTML = '<svg class="spinner">...</svg>';
|
|
3526
|
+
* el.setAttribute('data-preserve-animation', 'true');
|
|
3527
|
+
* return el;
|
|
3528
|
+
* }
|
|
3529
|
+
* }
|
|
3530
|
+
* }
|
|
3531
|
+
* ```
|
|
3532
|
+
*
|
|
3533
|
+
* @example
|
|
3534
|
+
* ```typescript
|
|
3535
|
+
* // Different indicators by location
|
|
3536
|
+
* config: {
|
|
3537
|
+
* loadingIndicator: {
|
|
3538
|
+
* render: ({ location, defaultRenderer }) => {
|
|
3539
|
+
* if (location === 'inline') {
|
|
3540
|
+
* return defaultRenderer(); // Use default for inline
|
|
3541
|
+
* }
|
|
3542
|
+
* // Custom for standalone
|
|
3543
|
+
* const el = document.createElement('div');
|
|
3544
|
+
* el.textContent = 'Thinking...';
|
|
3545
|
+
* return el;
|
|
3546
|
+
* }
|
|
3547
|
+
* }
|
|
3548
|
+
* }
|
|
3549
|
+
* ```
|
|
3550
|
+
*
|
|
3551
|
+
* @example
|
|
3552
|
+
* ```typescript
|
|
3553
|
+
* // Hide loading indicator entirely
|
|
3554
|
+
* config: {
|
|
3555
|
+
* loadingIndicator: {
|
|
3556
|
+
* render: () => null
|
|
3557
|
+
* }
|
|
3558
|
+
* }
|
|
3559
|
+
* ```
|
|
3560
|
+
*/
|
|
3561
|
+
type AgentWidgetLoadingIndicatorConfig = {
|
|
3562
|
+
/**
|
|
3563
|
+
* Whether to show the bubble background and border around the standalone loading indicator.
|
|
3564
|
+
* Set to false to render the loading indicator without any bubble styling.
|
|
3565
|
+
* @default true
|
|
3566
|
+
*/
|
|
3567
|
+
showBubble?: boolean;
|
|
3568
|
+
/**
|
|
3569
|
+
* Custom render function for the loading indicator.
|
|
3570
|
+
* Return an HTMLElement to display, or null to hide the indicator.
|
|
3571
|
+
*
|
|
3572
|
+
* For custom animations, add `data-preserve-animation="true"` attribute
|
|
3573
|
+
* to prevent the DOM morpher from interrupting the animation.
|
|
3574
|
+
*/
|
|
3575
|
+
render?: (context: LoadingIndicatorRenderContext) => HTMLElement | null;
|
|
3576
|
+
/**
|
|
3577
|
+
* Render function for the idle state indicator.
|
|
3578
|
+
* Called when the widget is idle (not streaming) and has at least one message.
|
|
3579
|
+
* Return an HTMLElement to display, or null to hide (default).
|
|
3580
|
+
*
|
|
3581
|
+
* For animations, add `data-preserve-animation="true"` attribute
|
|
3582
|
+
* to prevent the DOM morpher from interrupting the animation.
|
|
3583
|
+
*
|
|
3584
|
+
* @example
|
|
3585
|
+
* ```typescript
|
|
3586
|
+
* loadingIndicator: {
|
|
3587
|
+
* renderIdle: ({ lastMessage }) => {
|
|
3588
|
+
* // Only show idle indicator after assistant messages
|
|
3589
|
+
* if (lastMessage?.role !== 'assistant') return null;
|
|
3590
|
+
* const el = document.createElement('div');
|
|
3591
|
+
* el.className = 'pulse-dot';
|
|
3592
|
+
* el.setAttribute('data-preserve-animation', 'true');
|
|
3593
|
+
* return el;
|
|
3594
|
+
* }
|
|
3595
|
+
* }
|
|
3596
|
+
* ```
|
|
3597
|
+
*/
|
|
3598
|
+
renderIdle?: (context: IdleIndicatorRenderContext) => HTMLElement | null;
|
|
3599
|
+
};
|
|
3600
|
+
type AgentWidgetConfig = {
|
|
3601
|
+
apiUrl?: string;
|
|
3602
|
+
flowId?: string;
|
|
3603
|
+
/**
|
|
3604
|
+
* Override the assistant-bubble copy shown when a dispatch fails before any
|
|
3605
|
+
* response streams back (connection refused, CORS, 4xx/5xx, malformed
|
|
3606
|
+
* stream). Provide a static string, or a function of the error so you can
|
|
3607
|
+
* tailor the message per failure and decide whether to surface the raw
|
|
3608
|
+
* reason. When omitted, a default message is shown that includes the
|
|
3609
|
+
* underlying error detail.
|
|
3610
|
+
*
|
|
3611
|
+
* Returning an empty string suppresses the fallback bubble entirely (the
|
|
3612
|
+
* `onError` callback still fires).
|
|
3613
|
+
*
|
|
3614
|
+
* @example
|
|
3615
|
+
* ```typescript
|
|
3616
|
+
* config: {
|
|
3617
|
+
* // Static
|
|
3618
|
+
* errorMessage: "We're having trouble connecting. Please try again."
|
|
3619
|
+
* // Or dynamic
|
|
3620
|
+
* errorMessage: (error) =>
|
|
3621
|
+
* error.message.includes("Failed to fetch")
|
|
3622
|
+
* ? "You appear to be offline."
|
|
3623
|
+
* : "Something went wrong. Please try again."
|
|
3624
|
+
* }
|
|
3625
|
+
* ```
|
|
3626
|
+
*/
|
|
3627
|
+
errorMessage?: string | ((error: Error) => string);
|
|
3628
|
+
/**
|
|
3629
|
+
* Agent configuration for agent execution mode.
|
|
3630
|
+
* When provided, the widget uses agent loop execution instead of flow dispatch.
|
|
3631
|
+
* Mutually exclusive with `flowId`.
|
|
3632
|
+
*
|
|
3633
|
+
* @example
|
|
3634
|
+
* ```typescript
|
|
3635
|
+
* config: {
|
|
3636
|
+
* agent: {
|
|
3637
|
+
* name: 'Assistant',
|
|
3638
|
+
* model: 'openai:gpt-4o-mini',
|
|
3639
|
+
* systemPrompt: 'You are a helpful assistant.',
|
|
3640
|
+
* loopConfig: { maxTurns: 5 }
|
|
3641
|
+
* }
|
|
3642
|
+
* }
|
|
3643
|
+
* ```
|
|
3644
|
+
*/
|
|
3645
|
+
agent?: AgentConfig;
|
|
3646
|
+
/**
|
|
3647
|
+
* Options for agent execution requests.
|
|
3648
|
+
* Only used when `agent` is configured.
|
|
3649
|
+
*
|
|
3650
|
+
* @default { streamResponse: true, recordMode: 'virtual' }
|
|
3651
|
+
*/
|
|
3652
|
+
agentOptions?: AgentRequestOptions;
|
|
3653
|
+
/**
|
|
3654
|
+
* Controls how multiple agent iterations are displayed in the chat UI.
|
|
3655
|
+
* Only used when `agent` is configured.
|
|
3656
|
+
*
|
|
3657
|
+
* - `'separate'`: Each iteration creates a new assistant message bubble
|
|
3658
|
+
* - `'merged'`: All iterations stream into a single assistant message
|
|
3659
|
+
*
|
|
3660
|
+
* @default 'separate'
|
|
3661
|
+
*/
|
|
3662
|
+
iterationDisplay?: 'separate' | 'merged';
|
|
3663
|
+
/**
|
|
3664
|
+
* Client token for direct browser-to-API communication.
|
|
3665
|
+
* When set, the widget uses /v1/client/* endpoints instead of /v1/dispatch.
|
|
3666
|
+
* Mutually exclusive with apiKey/headers authentication.
|
|
3667
|
+
*
|
|
3668
|
+
* @example
|
|
3669
|
+
* ```typescript
|
|
3670
|
+
* config: {
|
|
3671
|
+
* clientToken: 'ct_live_flow01k7_a8b9c0d1e2f3g4h5i6j7k8l9'
|
|
3672
|
+
* }
|
|
3673
|
+
* ```
|
|
3674
|
+
*/
|
|
3675
|
+
clientToken?: string;
|
|
3676
|
+
/**
|
|
3677
|
+
* Callback when session is initialized (client token mode only).
|
|
3678
|
+
* Receives session info including expiry time.
|
|
3679
|
+
*
|
|
3680
|
+
* @example
|
|
3681
|
+
* ```typescript
|
|
3682
|
+
* config: {
|
|
3683
|
+
* onSessionInit: (session) => {
|
|
3684
|
+
* console.log('Session started:', session.sessionId);
|
|
3685
|
+
* }
|
|
3686
|
+
* }
|
|
3687
|
+
* ```
|
|
3688
|
+
*/
|
|
3689
|
+
onSessionInit?: (session: ClientSession) => void;
|
|
3690
|
+
/**
|
|
3691
|
+
* Callback when session expires or errors (client token mode only).
|
|
3692
|
+
* Widget should prompt user to refresh.
|
|
3693
|
+
*
|
|
3694
|
+
* @example
|
|
3695
|
+
* ```typescript
|
|
3696
|
+
* config: {
|
|
3697
|
+
* onSessionExpired: () => {
|
|
3698
|
+
* alert('Your session has expired. Please refresh the page.');
|
|
3699
|
+
* }
|
|
3700
|
+
* }
|
|
3701
|
+
* ```
|
|
3702
|
+
*/
|
|
3703
|
+
onSessionExpired?: () => void;
|
|
3704
|
+
/**
|
|
3705
|
+
* Get stored session ID for session resumption (client token mode only).
|
|
3706
|
+
* Called when initializing a new session to check if there's a previous session_id
|
|
3707
|
+
* that should be passed to /client/init to resume the same conversation record.
|
|
3708
|
+
*
|
|
3709
|
+
* @example
|
|
3710
|
+
* ```typescript
|
|
3711
|
+
* config: {
|
|
3712
|
+
* getStoredSessionId: () => {
|
|
3713
|
+
* const stored = localStorage.getItem('session_id');
|
|
3714
|
+
* return stored || null;
|
|
3715
|
+
* }
|
|
3716
|
+
* }
|
|
3717
|
+
* ```
|
|
3718
|
+
*/
|
|
3719
|
+
getStoredSessionId?: () => string | null;
|
|
3720
|
+
/**
|
|
3721
|
+
* Store session ID for session resumption (client token mode only).
|
|
3722
|
+
* Called when a new session is initialized to persist the session_id
|
|
3723
|
+
* so it can be used to resume the conversation later.
|
|
3724
|
+
*
|
|
3725
|
+
* @example
|
|
3726
|
+
* ```typescript
|
|
3727
|
+
* config: {
|
|
3728
|
+
* setStoredSessionId: (sessionId) => {
|
|
3729
|
+
* localStorage.setItem('session_id', sessionId);
|
|
3730
|
+
* }
|
|
3731
|
+
* }
|
|
3732
|
+
* ```
|
|
3733
|
+
*/
|
|
3734
|
+
setStoredSessionId?: (sessionId: string) => void;
|
|
3735
|
+
/**
|
|
3736
|
+
* Static headers to include with each request.
|
|
3737
|
+
* For dynamic headers (e.g., auth tokens), use `getHeaders` instead.
|
|
3738
|
+
*/
|
|
3739
|
+
headers?: Record<string, string>;
|
|
3740
|
+
/**
|
|
3741
|
+
* Dynamic headers function - called before each request.
|
|
3742
|
+
* Useful for adding auth tokens that may change.
|
|
3743
|
+
* @example
|
|
3744
|
+
* ```typescript
|
|
3745
|
+
* getHeaders: async () => ({
|
|
3746
|
+
* 'Authorization': `Bearer ${await getAuthToken()}`
|
|
3747
|
+
* })
|
|
3748
|
+
* ```
|
|
3749
|
+
*/
|
|
3750
|
+
getHeaders?: AgentWidgetHeadersFunction;
|
|
3751
|
+
copy?: {
|
|
3752
|
+
welcomeTitle?: string;
|
|
3753
|
+
welcomeSubtitle?: string;
|
|
3754
|
+
inputPlaceholder?: string;
|
|
3755
|
+
sendButtonLabel?: string;
|
|
3756
|
+
/** Button label shown in text mode while a response is streaming. Default: "Stop". */
|
|
3757
|
+
stopButtonLabel?: string;
|
|
3758
|
+
/**
|
|
3759
|
+
* When false, the welcome / intro card is not shown above the message list.
|
|
3760
|
+
* @default true
|
|
3761
|
+
*/
|
|
3762
|
+
showWelcomeCard?: boolean;
|
|
3763
|
+
/**
|
|
3764
|
+
* Per-stop-reason copy for the inline notice rendered on assistant
|
|
3765
|
+
* bubbles when the runtime reports a non-natural stop (e.g. the agent
|
|
3766
|
+
* loop hit `max_tool_calls` and was cut off mid-loop). Each key is
|
|
3767
|
+
* optional — keys you omit fall back to the built-in defaults. Set a
|
|
3768
|
+
* key to an empty string to suppress the notice for that reason.
|
|
3769
|
+
*/
|
|
3770
|
+
stopReasonNotice?: Partial<Record<StopReasonKind, string>>;
|
|
3771
|
+
};
|
|
3772
|
+
/**
|
|
3773
|
+
* Semantic design tokens (`palette`, `semantic`, `components`).
|
|
3774
|
+
* Omit for library defaults.
|
|
3775
|
+
*/
|
|
3776
|
+
theme?: DeepPartial<PersonaTheme>;
|
|
3777
|
+
/**
|
|
3778
|
+
* Dark-mode token overrides. Merged over `theme` when the active scheme is dark.
|
|
3779
|
+
*/
|
|
3780
|
+
darkTheme?: DeepPartial<PersonaTheme>;
|
|
3781
|
+
/**
|
|
3782
|
+
* Color scheme mode for the widget.
|
|
3783
|
+
* - 'light': Always use light theme (default)
|
|
3784
|
+
* - 'dark': Always use dark theme
|
|
3785
|
+
* - 'auto': Automatically detect from page (HTML class or prefers-color-scheme)
|
|
3786
|
+
*
|
|
3787
|
+
* When 'auto', detection order:
|
|
3788
|
+
* 1. Check if `<html>` has 'dark' class
|
|
3789
|
+
* 2. Fall back to `prefers-color-scheme: dark` media query
|
|
3790
|
+
*
|
|
3791
|
+
* @default 'light'
|
|
3792
|
+
*/
|
|
3793
|
+
colorScheme?: 'auto' | 'light' | 'dark';
|
|
3794
|
+
features?: AgentWidgetFeatureFlags;
|
|
3795
|
+
/**
|
|
3796
|
+
* When true, focus the chat input after the panel opens and the open animation completes.
|
|
3797
|
+
* Applies to launcher mode (user click, controller.open(), autoExpand) and inline mode (on init).
|
|
3798
|
+
* Skip when voice is active to avoid stealing focus from voice UI.
|
|
3799
|
+
* @default false
|
|
3800
|
+
*/
|
|
3801
|
+
autoFocusInput?: boolean;
|
|
3802
|
+
launcher?: AgentWidgetLauncherConfig;
|
|
3803
|
+
initialMessages?: AgentWidgetMessage[];
|
|
3804
|
+
/**
|
|
3805
|
+
* Artifacts to hydrate into the pane at init. Typically populated from
|
|
3806
|
+
* `storageAdapter.load()` alongside `initialMessages` so the artifact pane
|
|
3807
|
+
* survives a page refresh.
|
|
3808
|
+
*/
|
|
3809
|
+
initialArtifacts?: PersonaArtifactRecord[];
|
|
3810
|
+
/**
|
|
3811
|
+
* Which artifact id (if any) should be selected in the pane at init. Paired
|
|
3812
|
+
* with `initialArtifacts`.
|
|
3813
|
+
*/
|
|
3814
|
+
initialSelectedArtifactId?: string | null;
|
|
3815
|
+
suggestionChips?: string[];
|
|
3816
|
+
suggestionChipsConfig?: AgentWidgetSuggestionChipsConfig;
|
|
3817
|
+
debug?: boolean;
|
|
3818
|
+
formEndpoint?: string;
|
|
3819
|
+
launcherWidth?: string;
|
|
3820
|
+
sendButton?: AgentWidgetSendButtonConfig;
|
|
3821
|
+
statusIndicator?: AgentWidgetStatusIndicatorConfig;
|
|
3822
|
+
voiceRecognition?: AgentWidgetVoiceRecognitionConfig;
|
|
3823
|
+
/**
|
|
3824
|
+
* Text-to-speech configuration for reading assistant messages aloud.
|
|
3825
|
+
* Uses the browser's Web Speech API (`speechSynthesis`).
|
|
3826
|
+
*
|
|
3827
|
+
* @example
|
|
3828
|
+
* ```typescript
|
|
3829
|
+
* config: {
|
|
3830
|
+
* textToSpeech: {
|
|
3831
|
+
* enabled: true,
|
|
3832
|
+
* voice: 'Google US English',
|
|
3833
|
+
* rate: 1.0,
|
|
3834
|
+
* pitch: 1.0
|
|
3835
|
+
* }
|
|
3836
|
+
* }
|
|
3837
|
+
* ```
|
|
3838
|
+
*/
|
|
3839
|
+
textToSpeech?: TextToSpeechConfig;
|
|
3840
|
+
toolCall?: AgentWidgetToolCallConfig;
|
|
3841
|
+
reasoning?: AgentWidgetReasoningConfig;
|
|
3842
|
+
/**
|
|
3843
|
+
* Configuration for tool approval bubbles.
|
|
3844
|
+
* Set to `false` to disable built-in approval handling entirely.
|
|
3845
|
+
*
|
|
3846
|
+
* @example
|
|
3847
|
+
* ```typescript
|
|
3848
|
+
* config: {
|
|
3849
|
+
* approval: {
|
|
3850
|
+
* title: "Permission Required",
|
|
3851
|
+
* approveLabel: "Allow",
|
|
3852
|
+
* denyLabel: "Block",
|
|
3853
|
+
* approveButtonColor: "#16a34a"
|
|
3854
|
+
* }
|
|
3855
|
+
* }
|
|
3856
|
+
* ```
|
|
3857
|
+
*/
|
|
3858
|
+
approval?: AgentWidgetApprovalConfig | false;
|
|
3859
|
+
/**
|
|
3860
|
+
* WebMCP — consume page-registered tools (`document.modelContext.registerTool`).
|
|
3861
|
+
* When `enabled`, the widget installs `@mcp-b/webmcp-polyfill`, snapshots the
|
|
3862
|
+
* registry on every dispatch, ships it as `clientTools[]`, and executes
|
|
3863
|
+
* returned `webmcp:*` tool calls with confirm-by-default gating.
|
|
3864
|
+
*
|
|
3865
|
+
* Server-side policy on the chat surface is the source of truth — these
|
|
3866
|
+
* fields layer on top.
|
|
3867
|
+
*
|
|
3868
|
+
* @example
|
|
3869
|
+
* ```typescript
|
|
3870
|
+
* config: {
|
|
3871
|
+
* webmcp: {
|
|
3872
|
+
* enabled: true,
|
|
3873
|
+
* allowlist: ['search_*', 'list_*'],
|
|
3874
|
+
* }
|
|
3875
|
+
* }
|
|
3876
|
+
* ```
|
|
3877
|
+
*/
|
|
3878
|
+
webmcp?: AgentWidgetWebMcpConfig;
|
|
3879
|
+
postprocessMessage?: (context: {
|
|
3880
|
+
text: string;
|
|
3881
|
+
message: AgentWidgetMessage;
|
|
3882
|
+
streaming: boolean;
|
|
3883
|
+
raw?: string;
|
|
3884
|
+
}) => string;
|
|
3885
|
+
plugins?: AgentWidgetPlugin[];
|
|
3886
|
+
contextProviders?: AgentWidgetContextProvider[];
|
|
3887
|
+
requestMiddleware?: AgentWidgetRequestMiddleware;
|
|
3888
|
+
actionParsers?: AgentWidgetActionParser[];
|
|
3889
|
+
actionHandlers?: AgentWidgetActionHandler[];
|
|
3890
|
+
storageAdapter?: AgentWidgetStorageAdapter;
|
|
3891
|
+
/**
|
|
3892
|
+
* Called after state is loaded from the storage adapter, but before the widget
|
|
3893
|
+
* initializes with that state. Use this to transform or inject messages based
|
|
3894
|
+
* on external state (e.g., navigation flags, checkout returns).
|
|
3895
|
+
*
|
|
3896
|
+
* This hook runs synchronously and must return the (potentially modified) state.
|
|
3897
|
+
*
|
|
3898
|
+
* Returning `{ state, open: true }` also signals that the widget panel should
|
|
3899
|
+
* open after initialization — useful when injecting a post-navigation message
|
|
3900
|
+
* that the user should immediately see.
|
|
3901
|
+
*
|
|
3902
|
+
* @example
|
|
3903
|
+
* ```typescript
|
|
3904
|
+
* // Plain state transform (existing form, still supported)
|
|
3905
|
+
* config: {
|
|
3906
|
+
* onStateLoaded: (state) => {
|
|
3907
|
+
* const navMessage = consumeNavigationFlag();
|
|
3908
|
+
* if (navMessage) {
|
|
3909
|
+
* return {
|
|
3910
|
+
* ...state,
|
|
3911
|
+
* messages: [...(state.messages || []), {
|
|
3912
|
+
* id: `nav-${Date.now()}`,
|
|
3913
|
+
* role: 'assistant',
|
|
3914
|
+
* content: navMessage,
|
|
3915
|
+
* createdAt: new Date().toISOString()
|
|
3916
|
+
* }]
|
|
3917
|
+
* };
|
|
3918
|
+
* }
|
|
3919
|
+
* return state;
|
|
3920
|
+
* }
|
|
3921
|
+
* }
|
|
3922
|
+
* ```
|
|
3923
|
+
*
|
|
3924
|
+
* @example
|
|
3925
|
+
* ```typescript
|
|
3926
|
+
* // Return { state, open: true } to also open the panel
|
|
3927
|
+
* config: {
|
|
3928
|
+
* onStateLoaded: (state) => {
|
|
3929
|
+
* const navMessage = consumeNavigationFlag();
|
|
3930
|
+
* if (navMessage) {
|
|
3931
|
+
* return {
|
|
3932
|
+
* state: {
|
|
3933
|
+
* ...state,
|
|
3934
|
+
* messages: [...(state.messages || []), {
|
|
3935
|
+
* id: `nav-${Date.now()}`,
|
|
3936
|
+
* role: 'assistant',
|
|
3937
|
+
* content: navMessage,
|
|
3938
|
+
* createdAt: new Date().toISOString()
|
|
3939
|
+
* }]
|
|
3940
|
+
* },
|
|
3941
|
+
* open: true
|
|
3942
|
+
* };
|
|
3943
|
+
* }
|
|
3944
|
+
* return state;
|
|
3945
|
+
* }
|
|
3946
|
+
* }
|
|
3947
|
+
* ```
|
|
3948
|
+
*/
|
|
3949
|
+
onStateLoaded?: (state: AgentWidgetStoredState) => AgentWidgetStoredState | {
|
|
3950
|
+
state: AgentWidgetStoredState;
|
|
3951
|
+
open?: boolean;
|
|
3952
|
+
};
|
|
3953
|
+
/**
|
|
3954
|
+
* Registry of custom components that can be rendered from JSON directives.
|
|
3955
|
+
* Components are registered by name and can be invoked via JSON responses
|
|
3956
|
+
* with the format: `{"component": "ComponentName", "props": {...}}`
|
|
3957
|
+
*
|
|
3958
|
+
* @example
|
|
3959
|
+
* ```typescript
|
|
3960
|
+
* config: {
|
|
3961
|
+
* components: {
|
|
3962
|
+
* ProductCard: (props, context) => {
|
|
3963
|
+
* const card = document.createElement("div");
|
|
3964
|
+
* card.innerHTML = `<h3>${props.title}</h3><p>$${props.price}</p>`;
|
|
3965
|
+
* return card;
|
|
3966
|
+
* }
|
|
3967
|
+
* }
|
|
3968
|
+
* }
|
|
3969
|
+
* ```
|
|
3970
|
+
*/
|
|
3971
|
+
components?: Record<string, AgentWidgetComponentRenderer>;
|
|
3972
|
+
/**
|
|
3973
|
+
* Enable component streaming. When true, component props will be updated
|
|
3974
|
+
* incrementally as they stream in from the JSON response.
|
|
3975
|
+
*
|
|
3976
|
+
* @default true
|
|
3977
|
+
*/
|
|
3978
|
+
enableComponentStreaming?: boolean;
|
|
3979
|
+
/**
|
|
3980
|
+
* When false, JSON component directives render without the default bubble chrome
|
|
3981
|
+
* (surface background, border, extra padding). Use for wide custom cards in the transcript.
|
|
3982
|
+
* @default true
|
|
3983
|
+
*/
|
|
3984
|
+
wrapComponentDirectiveInBubble?: boolean;
|
|
3985
|
+
/**
|
|
3986
|
+
* Custom stream parser for extracting text from streaming structured responses.
|
|
3987
|
+
* Handles incremental parsing of JSON, XML, or other formats.
|
|
3988
|
+
* If not provided, uses the default JSON parser.
|
|
3989
|
+
*
|
|
3990
|
+
* @example
|
|
3991
|
+
* ```typescript
|
|
3992
|
+
* streamParser: () => ({
|
|
3993
|
+
* processChunk: async (content) => {
|
|
3994
|
+
* // Return null if not your format, or extracted text if available
|
|
3995
|
+
* if (!content.trim().startsWith('{')) return null;
|
|
3996
|
+
* return extractText(content);
|
|
3997
|
+
* },
|
|
3998
|
+
* getExtractedText: () => extractedText
|
|
3999
|
+
* })
|
|
4000
|
+
* ```
|
|
4001
|
+
*/
|
|
4002
|
+
streamParser?: () => AgentWidgetStreamParser;
|
|
4003
|
+
/**
|
|
4004
|
+
* Additional localStorage key to clear when the clear chat button is clicked.
|
|
4005
|
+
* The widget automatically clears `"persona-chat-history"` by default.
|
|
4006
|
+
* Use this option to clear additional keys (e.g., if you're using a custom storage key).
|
|
4007
|
+
*
|
|
4008
|
+
* @example
|
|
4009
|
+
* ```typescript
|
|
4010
|
+
* config: {
|
|
4011
|
+
* clearChatHistoryStorageKey: "my-custom-chat-history"
|
|
4012
|
+
* }
|
|
4013
|
+
* ```
|
|
4014
|
+
*/
|
|
4015
|
+
clearChatHistoryStorageKey?: string;
|
|
4016
|
+
/**
|
|
4017
|
+
* Built-in parser type selector. Provides an easy way to choose a parser without importing functions.
|
|
4018
|
+
* If both `parserType` and `streamParser` are provided, `streamParser` takes precedence.
|
|
4019
|
+
*
|
|
4020
|
+
* - `"plain"` - Plain text parser (default). Passes through text as-is.
|
|
4021
|
+
* - `"json"` - JSON parser using partial-json. Extracts `text` field from JSON objects incrementally.
|
|
4022
|
+
* - `"regex-json"` - Regex-based JSON parser. Less robust but faster fallback for simple JSON.
|
|
4023
|
+
* - `"xml"` - XML parser. Extracts text content from XML tags.
|
|
4024
|
+
*
|
|
4025
|
+
* @example
|
|
4026
|
+
* ```typescript
|
|
4027
|
+
* config: {
|
|
4028
|
+
* parserType: "json" // Use built-in JSON parser
|
|
4029
|
+
* }
|
|
4030
|
+
* ```
|
|
4031
|
+
*
|
|
4032
|
+
* @example
|
|
4033
|
+
* ```typescript
|
|
4034
|
+
* config: {
|
|
4035
|
+
* parserType: "json",
|
|
4036
|
+
* streamParser: () => customParser() // Custom parser overrides parserType
|
|
4037
|
+
* }
|
|
4038
|
+
* ```
|
|
4039
|
+
*/
|
|
4040
|
+
parserType?: "plain" | "json" | "regex-json" | "xml";
|
|
4041
|
+
/**
|
|
4042
|
+
* Custom fetch function for full control over API requests.
|
|
4043
|
+
* Use this for custom authentication, request/response transformation, etc.
|
|
4044
|
+
*
|
|
4045
|
+
* When provided, this function is called instead of the default fetch.
|
|
4046
|
+
* You receive the URL, RequestInit, and the payload that would be sent.
|
|
4047
|
+
*
|
|
4048
|
+
* @example
|
|
4049
|
+
* ```typescript
|
|
4050
|
+
* config: {
|
|
4051
|
+
* customFetch: async (url, init, payload) => {
|
|
4052
|
+
* // Transform request for your API format
|
|
4053
|
+
* const myPayload = {
|
|
4054
|
+
* flow: { id: 'my-flow-id' },
|
|
4055
|
+
* messages: payload.messages,
|
|
4056
|
+
* options: { stream_response: true }
|
|
4057
|
+
* };
|
|
4058
|
+
*
|
|
4059
|
+
* // Add auth header
|
|
4060
|
+
* const token = await getAuthToken();
|
|
4061
|
+
*
|
|
4062
|
+
* return fetch('/my-api/dispatch', {
|
|
4063
|
+
* method: 'POST',
|
|
4064
|
+
* headers: {
|
|
4065
|
+
* 'Content-Type': 'application/json',
|
|
4066
|
+
* 'Authorization': `Bearer ${token}`
|
|
4067
|
+
* },
|
|
4068
|
+
* body: JSON.stringify(myPayload),
|
|
4069
|
+
* signal: init.signal
|
|
4070
|
+
* });
|
|
4071
|
+
* }
|
|
4072
|
+
* }
|
|
4073
|
+
* ```
|
|
4074
|
+
*/
|
|
4075
|
+
customFetch?: AgentWidgetCustomFetch;
|
|
4076
|
+
/**
|
|
4077
|
+
* Custom SSE event parser for non-standard streaming response formats.
|
|
4078
|
+
*
|
|
4079
|
+
* Use this when your API returns SSE events in a different format than expected.
|
|
4080
|
+
* Return `{ text }` for text chunks, `{ done: true }` for completion,
|
|
4081
|
+
* `{ error }` for errors, or `null` to ignore the event.
|
|
4082
|
+
*
|
|
4083
|
+
* @example
|
|
4084
|
+
* ```typescript
|
|
4085
|
+
* // For Runtype API format
|
|
4086
|
+
* config: {
|
|
4087
|
+
* parseSSEEvent: (data) => {
|
|
4088
|
+
* if ((data.type === 'step_delta' || data.type === 'step_chunk') && (data.delta || data.chunk)) {
|
|
4089
|
+
* return { text: data.delta ?? data.chunk };
|
|
4090
|
+
* }
|
|
4091
|
+
* if (data.type === 'flow_complete') {
|
|
4092
|
+
* return { done: true };
|
|
4093
|
+
* }
|
|
4094
|
+
* if (data.type === 'step_error') {
|
|
4095
|
+
* return { error: data.error };
|
|
4096
|
+
* }
|
|
4097
|
+
* return null; // Ignore other events
|
|
4098
|
+
* }
|
|
4099
|
+
* }
|
|
4100
|
+
* ```
|
|
4101
|
+
*/
|
|
4102
|
+
parseSSEEvent?: AgentWidgetSSEEventParser;
|
|
4103
|
+
/**
|
|
4104
|
+
* Called for every parsed SSE frame (after JSON parse), before native handling.
|
|
4105
|
+
* Use for lightweight side effects (e.g. telemetry). Does not replace native
|
|
4106
|
+
* streaming; pair with {@link parseSSEEvent} only when you need to override text mapping.
|
|
4107
|
+
*
|
|
4108
|
+
* When the event stream inspector is enabled, this runs in the same order as
|
|
4109
|
+
* events are appended to the inspector buffer.
|
|
4110
|
+
*/
|
|
4111
|
+
onSSEEvent?: (eventType: string, payload: unknown) => void;
|
|
4112
|
+
/**
|
|
4113
|
+
* Layout configuration for customizing widget appearance and structure.
|
|
4114
|
+
* Provides control over header, messages, and content slots.
|
|
4115
|
+
*
|
|
4116
|
+
* @example
|
|
4117
|
+
* ```typescript
|
|
4118
|
+
* config: {
|
|
4119
|
+
* layout: {
|
|
4120
|
+
* header: { layout: "minimal" },
|
|
4121
|
+
* messages: { avatar: { show: true } }
|
|
4122
|
+
* }
|
|
4123
|
+
* }
|
|
4124
|
+
* ```
|
|
4125
|
+
*/
|
|
4126
|
+
layout?: AgentWidgetLayoutConfig;
|
|
4127
|
+
/**
|
|
4128
|
+
* Markdown rendering configuration.
|
|
4129
|
+
* Customize how markdown is parsed and rendered in chat messages.
|
|
4130
|
+
*
|
|
4131
|
+
* Override methods:
|
|
4132
|
+
* 1. **CSS Variables** - Override `--cw-md-*` variables in your stylesheet
|
|
4133
|
+
* 2. **Options** - Configure marked parser behavior
|
|
4134
|
+
* 3. **Renderers** - Custom rendering functions for specific elements
|
|
4135
|
+
* 4. **postprocessMessage** - Complete control over message transformation
|
|
4136
|
+
*
|
|
4137
|
+
* @example
|
|
4138
|
+
* ```typescript
|
|
4139
|
+
* config: {
|
|
4140
|
+
* markdown: {
|
|
4141
|
+
* options: { breaks: true, gfm: true },
|
|
4142
|
+
* renderer: {
|
|
4143
|
+
* link(token) {
|
|
4144
|
+
* return `<a href="${token.href}" target="_blank">${token.text}</a>`;
|
|
4145
|
+
* }
|
|
4146
|
+
* }
|
|
4147
|
+
* }
|
|
4148
|
+
* }
|
|
4149
|
+
* ```
|
|
4150
|
+
*/
|
|
4151
|
+
markdown?: AgentWidgetMarkdownConfig;
|
|
4152
|
+
/**
|
|
4153
|
+
* HTML sanitization for rendered message content.
|
|
4154
|
+
*
|
|
4155
|
+
* The widget renders AI-generated markdown as HTML. By default, all HTML
|
|
4156
|
+
* output is sanitized using DOMPurify to prevent XSS attacks.
|
|
4157
|
+
*
|
|
4158
|
+
* - `true` (default): sanitize using built-in DOMPurify
|
|
4159
|
+
* - `false`: disable sanitization (only use with fully trusted content sources)
|
|
4160
|
+
* - `(html: string) => string`: custom sanitizer function
|
|
4161
|
+
*
|
|
4162
|
+
* @default true
|
|
4163
|
+
*/
|
|
4164
|
+
sanitize?: boolean | ((html: string) => string);
|
|
4165
|
+
/**
|
|
4166
|
+
* Configuration for message action buttons (copy, upvote, downvote).
|
|
4167
|
+
* Shows action buttons on assistant messages for user feedback.
|
|
4168
|
+
*
|
|
4169
|
+
* @example
|
|
4170
|
+
* ```typescript
|
|
4171
|
+
* config: {
|
|
4172
|
+
* messageActions: {
|
|
4173
|
+
* enabled: true,
|
|
4174
|
+
* showCopy: true,
|
|
4175
|
+
* showUpvote: true,
|
|
4176
|
+
* showDownvote: true,
|
|
4177
|
+
* visibility: 'hover',
|
|
4178
|
+
* onFeedback: (feedback) => {
|
|
4179
|
+
* console.log('Feedback:', feedback.type, feedback.messageId);
|
|
4180
|
+
* },
|
|
4181
|
+
* onCopy: (message) => {
|
|
4182
|
+
* console.log('Copied message:', message.id);
|
|
4183
|
+
* }
|
|
4184
|
+
* }
|
|
4185
|
+
* }
|
|
4186
|
+
* ```
|
|
4187
|
+
*/
|
|
4188
|
+
messageActions?: AgentWidgetMessageActionsConfig;
|
|
4189
|
+
/**
|
|
4190
|
+
* Configuration for file attachments in the composer.
|
|
4191
|
+
* When enabled, users can attach images to their messages.
|
|
4192
|
+
*
|
|
4193
|
+
* @example
|
|
4194
|
+
* ```typescript
|
|
4195
|
+
* config: {
|
|
4196
|
+
* attachments: {
|
|
4197
|
+
* enabled: true,
|
|
4198
|
+
* maxFileSize: 5 * 1024 * 1024, // 5MB
|
|
4199
|
+
* maxFiles: 4
|
|
4200
|
+
* }
|
|
4201
|
+
* }
|
|
4202
|
+
* ```
|
|
4203
|
+
*/
|
|
4204
|
+
attachments?: AgentWidgetAttachmentsConfig;
|
|
4205
|
+
/**
|
|
4206
|
+
* Composer extras for custom `renderComposer` plugins (model picker, etc.).
|
|
4207
|
+
* `selectedModelId` may be updated at runtime by the host.
|
|
4208
|
+
*/
|
|
4209
|
+
composer?: AgentWidgetComposerConfig;
|
|
4210
|
+
/**
|
|
4211
|
+
* Persist widget state (open/closed, voice mode) across page navigations.
|
|
4212
|
+
* When `true`, uses default settings with sessionStorage.
|
|
4213
|
+
* When an object, allows customizing storage type, key prefix, and what to persist.
|
|
4214
|
+
*
|
|
4215
|
+
* Setting this to `false` is the explicit kill-switch: it disables UI-state
|
|
4216
|
+
* persistence **and** message-history persistence. When `false`, any
|
|
4217
|
+
* `storageAdapter` you configure is ignored and the default localStorage
|
|
4218
|
+
* adapter is not created — no chat history is read or written. Pass `true`
|
|
4219
|
+
* (or omit) to keep the default behavior of persisting messages via the
|
|
4220
|
+
* configured `storageAdapter` (or the built-in localStorage adapter).
|
|
4221
|
+
*
|
|
4222
|
+
* @example
|
|
4223
|
+
* ```typescript
|
|
4224
|
+
* // Simple usage - persist open state in sessionStorage
|
|
4225
|
+
* config: {
|
|
4226
|
+
* persistState: true
|
|
4227
|
+
* }
|
|
4228
|
+
* ```
|
|
4229
|
+
*
|
|
4230
|
+
* @example
|
|
4231
|
+
* ```typescript
|
|
4232
|
+
* // Advanced usage
|
|
4233
|
+
* config: {
|
|
4234
|
+
* persistState: {
|
|
4235
|
+
* storage: 'local', // Use localStorage
|
|
4236
|
+
* persist: {
|
|
4237
|
+
* openState: true,
|
|
4238
|
+
* voiceState: true,
|
|
4239
|
+
* focusInput: true
|
|
4240
|
+
* }
|
|
4241
|
+
* }
|
|
4242
|
+
* }
|
|
4243
|
+
* ```
|
|
4244
|
+
*
|
|
4245
|
+
* @example
|
|
4246
|
+
* ```typescript
|
|
4247
|
+
* // Ephemeral widget — no message history written anywhere
|
|
4248
|
+
* config: {
|
|
4249
|
+
* persistState: false
|
|
4250
|
+
* }
|
|
4251
|
+
* ```
|
|
4252
|
+
*/
|
|
4253
|
+
persistState?: boolean | AgentWidgetPersistStateConfig;
|
|
4254
|
+
/**
|
|
4255
|
+
* Configuration for customizing the loading indicator.
|
|
4256
|
+
* The loading indicator is shown while waiting for a response or
|
|
4257
|
+
* when an assistant message is streaming but has no content yet.
|
|
4258
|
+
*
|
|
4259
|
+
* @example
|
|
4260
|
+
* ```typescript
|
|
4261
|
+
* config: {
|
|
4262
|
+
* loadingIndicator: {
|
|
4263
|
+
* render: ({ location, defaultRenderer }) => {
|
|
4264
|
+
* if (location === 'standalone') {
|
|
4265
|
+
* const el = document.createElement('div');
|
|
4266
|
+
* el.textContent = 'Thinking...';
|
|
4267
|
+
* return el;
|
|
4268
|
+
* }
|
|
4269
|
+
* return defaultRenderer();
|
|
4270
|
+
* }
|
|
4271
|
+
* }
|
|
4272
|
+
* }
|
|
4273
|
+
* ```
|
|
4274
|
+
*/
|
|
4275
|
+
loadingIndicator?: AgentWidgetLoadingIndicatorConfig;
|
|
4276
|
+
};
|
|
4277
|
+
type AgentWidgetMessageRole = "user" | "assistant" | "system";
|
|
4278
|
+
type AgentWidgetReasoning = {
|
|
4279
|
+
id: string;
|
|
4280
|
+
status: "pending" | "streaming" | "complete";
|
|
4281
|
+
chunks: string[];
|
|
4282
|
+
startedAt?: number;
|
|
4283
|
+
completedAt?: number;
|
|
4284
|
+
durationMs?: number;
|
|
4285
|
+
};
|
|
4286
|
+
type AgentWidgetToolCall = {
|
|
4287
|
+
id: string;
|
|
4288
|
+
name?: string;
|
|
4289
|
+
status: "pending" | "running" | "complete";
|
|
4290
|
+
args?: unknown;
|
|
4291
|
+
chunks?: string[];
|
|
4292
|
+
result?: unknown;
|
|
4293
|
+
duration?: number;
|
|
4294
|
+
startedAt?: number;
|
|
4295
|
+
completedAt?: number;
|
|
4296
|
+
durationMs?: number;
|
|
4297
|
+
};
|
|
4298
|
+
/**
|
|
4299
|
+
* Represents a tool approval request in the chat conversation.
|
|
4300
|
+
* Created when the agent requires human approval before executing a tool.
|
|
4301
|
+
*/
|
|
4302
|
+
type AgentWidgetApproval = {
|
|
4303
|
+
id: string;
|
|
4304
|
+
status: "pending" | "approved" | "denied" | "timeout";
|
|
4305
|
+
agentId: string;
|
|
4306
|
+
executionId: string;
|
|
4307
|
+
toolName: string;
|
|
4308
|
+
toolType?: string;
|
|
4309
|
+
description: string;
|
|
4310
|
+
parameters?: unknown;
|
|
4311
|
+
resolvedAt?: number;
|
|
4312
|
+
};
|
|
4313
|
+
type AgentWidgetMessageVariant = "assistant" | "reasoning" | "tool" | "approval";
|
|
4314
|
+
/**
|
|
4315
|
+
* Per-turn / per-step stop reason emitted by the runtime on
|
|
4316
|
+
* `agent_turn_complete` and `step_complete` SSE events. The vocabulary is
|
|
4317
|
+
* owned by the upstream Runtype API — do not extend without coordination.
|
|
4318
|
+
*
|
|
4319
|
+
* - `end_turn` — natural completion (no affordance needed)
|
|
4320
|
+
* - `max_tool_calls` — agent loop tripped the configured tool-call ceiling
|
|
4321
|
+
* - `length` — provider hit max output tokens
|
|
4322
|
+
* - `content_filter` — provider content filter intervened
|
|
4323
|
+
* - `error` — provider/runtime error (prefer existing error rendering)
|
|
4324
|
+
* - `unknown` — explicitly reported but uninformative
|
|
4325
|
+
*
|
|
4326
|
+
* Absent (`undefined`) means "not reported" — distinct from `'unknown'`.
|
|
4327
|
+
*/
|
|
4328
|
+
type StopReasonKind = 'end_turn' | 'max_tool_calls' | 'length' | 'content_filter' | 'error' | 'unknown';
|
|
4329
|
+
/**
|
|
4330
|
+
* Represents a message in the chat conversation.
|
|
4331
|
+
*
|
|
4332
|
+
* @property id - Unique message identifier
|
|
4333
|
+
* @property role - Message role: "user", "assistant", or "system"
|
|
4334
|
+
* @property content - Message text content (for display)
|
|
4335
|
+
* @property contentParts - Original multi-modal content parts (for API requests)
|
|
4336
|
+
* @property createdAt - ISO timestamp when message was created
|
|
4337
|
+
* @property streaming - Whether message is still streaming (for assistant messages)
|
|
4338
|
+
* @property variant - Message variant for assistant messages: "assistant", "reasoning", or "tool"
|
|
4339
|
+
* @property sequence - Message ordering number
|
|
4340
|
+
* @property reasoning - Reasoning data for assistant reasoning messages
|
|
4341
|
+
* @property toolCall - Tool call data for assistant tool messages
|
|
4342
|
+
* @property tools - Array of tool calls
|
|
4343
|
+
* @property viaVoice - Set to `true` when a user message is sent via voice recognition.
|
|
4344
|
+
* Useful for implementing voice-specific behaviors like auto-reactivation.
|
|
4345
|
+
*/
|
|
4346
|
+
type AgentWidgetMessage = {
|
|
4347
|
+
id: string;
|
|
4348
|
+
role: AgentWidgetMessageRole;
|
|
4349
|
+
content: string;
|
|
4350
|
+
createdAt: string;
|
|
4351
|
+
/**
|
|
4352
|
+
* Original multi-modal content parts for this message.
|
|
4353
|
+
* When present, this is sent to the API instead of `content`.
|
|
4354
|
+
* The `content` field contains the text-only representation for display.
|
|
4355
|
+
*/
|
|
4356
|
+
contentParts?: ContentPart[];
|
|
4357
|
+
streaming?: boolean;
|
|
4358
|
+
variant?: AgentWidgetMessageVariant;
|
|
4359
|
+
sequence?: number;
|
|
4360
|
+
reasoning?: AgentWidgetReasoning;
|
|
4361
|
+
toolCall?: AgentWidgetToolCall;
|
|
4362
|
+
tools?: AgentWidgetToolCall[];
|
|
4363
|
+
/** Approval data for messages with variant "approval" */
|
|
4364
|
+
approval?: AgentWidgetApproval;
|
|
4365
|
+
viaVoice?: boolean;
|
|
4366
|
+
/**
|
|
4367
|
+
* Set to `true` on placeholder messages injected during Runtype voice processing.
|
|
4368
|
+
* Use this in `messageTransform` to detect and customize voice processing placeholders.
|
|
4369
|
+
*
|
|
4370
|
+
* @example
|
|
4371
|
+
* messageTransform: ({ text, message }) => {
|
|
4372
|
+
* if (message.voiceProcessing && message.role === 'user') {
|
|
4373
|
+
* return '<div class="my-voice-spinner">Transcribing...</div>';
|
|
4374
|
+
* }
|
|
4375
|
+
* return text;
|
|
4376
|
+
* }
|
|
4377
|
+
*/
|
|
4378
|
+
voiceProcessing?: boolean;
|
|
4379
|
+
/**
|
|
4380
|
+
* Raw structured payload for this message (e.g., JSON action response).
|
|
4381
|
+
* Populated automatically when structured parsers run.
|
|
4382
|
+
*/
|
|
4383
|
+
rawContent?: string;
|
|
4384
|
+
/**
|
|
4385
|
+
* LLM-specific content for API requests.
|
|
4386
|
+
* When present, this is sent to the LLM instead of `content`.
|
|
4387
|
+
*
|
|
4388
|
+
* Priority for API payload:
|
|
4389
|
+
* 1. `contentParts` (if present, used as-is for multi-modal)
|
|
4390
|
+
* 2. `llmContent` (if present, sent as string)
|
|
4391
|
+
* 3. `rawContent` (backward compatibility with structured parsers)
|
|
4392
|
+
* 4. `content` (fallback - display content)
|
|
4393
|
+
*
|
|
4394
|
+
* The `content` field is always used for UI display.
|
|
4395
|
+
*
|
|
4396
|
+
* @example
|
|
4397
|
+
* // Show full details to user, send summary to LLM
|
|
4398
|
+
* {
|
|
4399
|
+
* content: "**Product:** iPhone 15 Pro\n**Price:** $1,199\n**SKU:** IP15P-256",
|
|
4400
|
+
* llmContent: "[Product search: iPhone 15 Pro, $1199]"
|
|
4401
|
+
* }
|
|
4402
|
+
*/
|
|
4403
|
+
llmContent?: string;
|
|
4404
|
+
/**
|
|
4405
|
+
* Text segment identity for chronological ordering.
|
|
4406
|
+
* When present, identifies which text segment this message represents
|
|
4407
|
+
* (e.g., "text_0", "text_1") for messages split at tool boundaries.
|
|
4408
|
+
*/
|
|
4409
|
+
partId?: string;
|
|
4410
|
+
/**
|
|
4411
|
+
* Metadata for messages created during agent loop execution.
|
|
4412
|
+
* Contains execution context like iteration number and turn ID.
|
|
4413
|
+
*/
|
|
4414
|
+
agentMetadata?: AgentMessageMetadata;
|
|
4415
|
+
/**
|
|
4416
|
+
* Per-turn stop reason reported by the runtime on `agent_turn_complete`
|
|
4417
|
+
* (agent-loop path) or the last `step_complete` for a prompt step
|
|
4418
|
+
* (dispatch / flow path). Absent when the API did not report a value.
|
|
4419
|
+
*
|
|
4420
|
+
* When set to a non-natural value (`max_tool_calls`, `length`,
|
|
4421
|
+
* `content_filter`, `error`), the widget renders an inline notice on
|
|
4422
|
+
* the assistant bubble. See `config.copy.stopReasonNotice` to override
|
|
4423
|
+
* the default copy.
|
|
4424
|
+
*/
|
|
4425
|
+
stopReason?: StopReasonKind;
|
|
4426
|
+
};
|
|
4427
|
+
type PersonaArtifactRecord = {
|
|
4428
|
+
id: string;
|
|
4429
|
+
artifactType: PersonaArtifactKind;
|
|
4430
|
+
title?: string;
|
|
4431
|
+
status: "streaming" | "complete";
|
|
4432
|
+
markdown?: string;
|
|
4433
|
+
component?: string;
|
|
4434
|
+
props?: Record<string, unknown>;
|
|
4435
|
+
};
|
|
4436
|
+
|
|
4437
|
+
/**
|
|
4438
|
+
* Optional entry point: `@runtypelabs/persona/smart-dom-reader`.
|
|
4439
|
+
*
|
|
4440
|
+
* Adapts `@mcp-b/smart-dom-reader` into Persona's enriched page-context pipeline and
|
|
4441
|
+
* exposes a ready-made {@link AgentWidgetContextProvider} you can drop into
|
|
4442
|
+
* `config.contextProviders`. This is the ONLY module that imports the smart-dom-reader
|
|
4443
|
+
* runtime value, so the library never reaches the main bundle or the IIFE/CDN build —
|
|
4444
|
+
* importing this subpath is opt-in.
|
|
4445
|
+
*
|
|
4446
|
+
* The library is **vendored** under `src/vendor/smart-dom-reader/` (it is mis-published
|
|
4447
|
+
* on npm and cannot be imported by name — see that directory's README). Vendoring it
|
|
4448
|
+
* here means consumers need no extra install: the code is bundled into this entry, and
|
|
4449
|
+
* consumers who never import this subpath pay nothing.
|
|
4450
|
+
*
|
|
4451
|
+
* What it adds over the default `collectEnrichedPageContext`: Shadow-DOM piercing
|
|
4452
|
+
* (on by default), form grouping, and page landmarks/state.
|
|
4453
|
+
*
|
|
4454
|
+
* ## Actionability caveat
|
|
4455
|
+
*
|
|
4456
|
+
* Persona's click loop (`utils/actions.ts`) drives `document.querySelector`, which
|
|
4457
|
+
* cannot pierce shadow roots or evaluate XPath. The adapter therefore prefers plain-CSS
|
|
4458
|
+
* selectors; elements reachable only via shadow-piercing / XPath selectors are surfaced
|
|
4459
|
+
* to the model as context but are not clickable through the current action handlers.
|
|
4460
|
+
*
|
|
4461
|
+
* @example
|
|
4462
|
+
* ```ts
|
|
4463
|
+
* import initAgentWidget from "@runtypelabs/persona";
|
|
4464
|
+
* import { createSmartDomReaderContextProvider } from "@runtypelabs/persona/smart-dom-reader";
|
|
4465
|
+
*
|
|
4466
|
+
* initAgentWidget({
|
|
4467
|
+
* // ...config
|
|
4468
|
+
* contextProviders: [createSmartDomReaderContextProvider()]
|
|
4469
|
+
* });
|
|
4470
|
+
* ```
|
|
4471
|
+
*/
|
|
4472
|
+
|
|
4473
|
+
/** Options for {@link collectSmartDomContext} and {@link createSmartDomReaderContextProvider}. */
|
|
4474
|
+
interface SmartDomContextOptions extends SmartDomAdapterOptions {
|
|
4475
|
+
/**
|
|
4476
|
+
* `interactive` (default) extracts UI elements only; `full` additionally extracts
|
|
4477
|
+
* semantic content (headings, images, tables, lists, articles).
|
|
4478
|
+
*/
|
|
4479
|
+
mode?: "interactive" | "full";
|
|
4480
|
+
/**
|
|
4481
|
+
* Extraction options passed through to smart-dom-reader (e.g. `includeShadowDOM`,
|
|
4482
|
+
* `maxDepth`, `viewportOnly`). `includeShadowDOM` defaults to true in the library;
|
|
4483
|
+
* the `.persona-host` exclusion guards against the widget reading its own shadow UI.
|
|
4484
|
+
*
|
|
4485
|
+
* Note: the vendored library exposes an `includeIframes` flag but does not actually
|
|
4486
|
+
* traverse iframe content, so iframe piercing is not supported.
|
|
4487
|
+
*/
|
|
4488
|
+
extractionOptions?: Partial<ExtractionOptions>;
|
|
4489
|
+
/** Document to extract from. Default: the global `document`. Ignored when `root` is set. */
|
|
4490
|
+
document?: Document;
|
|
4491
|
+
/**
|
|
4492
|
+
* Scope extraction to this element's subtree instead of the whole document — parity
|
|
4493
|
+
* with `collectEnrichedPageContext`'s `root`. Useful to read only a main-content region
|
|
4494
|
+
* and skip site chrome (nav, sidebars). Shadow DOM inside the subtree is still pierced.
|
|
4495
|
+
* When set, `document` is ignored.
|
|
4496
|
+
*/
|
|
4497
|
+
root?: Element;
|
|
4498
|
+
}
|
|
4499
|
+
/**
|
|
4500
|
+
* Collect enriched page context using smart-dom-reader, mapped into Persona's
|
|
4501
|
+
* {@link EnrichedPageElement}[] shape (parity with `collectEnrichedPageContext`).
|
|
4502
|
+
*
|
|
4503
|
+
* Pass `root` to scope extraction to an element subtree (skipping site chrome);
|
|
4504
|
+
* otherwise the whole `document` (or `opts.document`) is read. Returns an empty array
|
|
4505
|
+
* when neither `root` nor a document is available (e.g. SSR).
|
|
4506
|
+
*/
|
|
4507
|
+
declare function collectSmartDomContext(opts?: SmartDomContextOptions): EnrichedPageElement[];
|
|
4508
|
+
/** Options for {@link createSmartDomReaderContextProvider}. */
|
|
4509
|
+
interface SmartDomReaderProviderOptions extends SmartDomContextOptions {
|
|
4510
|
+
/** Key under which the formatted context is placed in `payload.context`. Default: "pageContext". */
|
|
4511
|
+
contextKey?: string;
|
|
4512
|
+
}
|
|
4513
|
+
/**
|
|
4514
|
+
* Build an {@link AgentWidgetContextProvider} that collects page context with
|
|
4515
|
+
* smart-dom-reader and returns it under `contextKey` (default `"pageContext"`).
|
|
4516
|
+
* Drop into `config.contextProviders`; `buildAgentPayload` merges the result into
|
|
4517
|
+
* `payload.context` on every agent request.
|
|
4518
|
+
*/
|
|
4519
|
+
declare function createSmartDomReaderContextProvider(opts?: SmartDomReaderProviderOptions): AgentWidgetContextProvider;
|
|
4520
|
+
|
|
4521
|
+
export { type SmartDomAdapterOptions, type SmartDomContextOptions, type SmartDomReaderProviderOptions, collectSmartDomContext, createSmartDomReaderContextProvider, smartDomResultToEnriched };
|