@runtypelabs/persona 1.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +1080 -0
  2. package/dist/index.cjs +140 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +2626 -0
  5. package/dist/index.d.ts +2626 -0
  6. package/dist/index.global.js +1843 -0
  7. package/dist/index.global.js.map +1 -0
  8. package/dist/index.js +140 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/install.global.js +2 -0
  11. package/dist/install.global.js.map +1 -0
  12. package/dist/widget.css +1627 -0
  13. package/package.json +79 -0
  14. package/src/@types/idiomorph.d.ts +37 -0
  15. package/src/client.test.ts +387 -0
  16. package/src/client.ts +1589 -0
  17. package/src/components/composer-builder.ts +530 -0
  18. package/src/components/feedback.ts +379 -0
  19. package/src/components/forms.ts +170 -0
  20. package/src/components/header-builder.ts +455 -0
  21. package/src/components/header-layouts.ts +303 -0
  22. package/src/components/launcher.ts +193 -0
  23. package/src/components/message-bubble.ts +528 -0
  24. package/src/components/messages.ts +54 -0
  25. package/src/components/panel.ts +204 -0
  26. package/src/components/reasoning-bubble.ts +144 -0
  27. package/src/components/registry.ts +87 -0
  28. package/src/components/suggestions.ts +97 -0
  29. package/src/components/tool-bubble.ts +288 -0
  30. package/src/defaults.ts +321 -0
  31. package/src/index.ts +175 -0
  32. package/src/install.ts +284 -0
  33. package/src/plugins/registry.ts +77 -0
  34. package/src/plugins/types.ts +95 -0
  35. package/src/postprocessors.ts +194 -0
  36. package/src/runtime/init.ts +162 -0
  37. package/src/session.ts +376 -0
  38. package/src/styles/tailwind.css +20 -0
  39. package/src/styles/widget.css +1627 -0
  40. package/src/types.ts +1635 -0
  41. package/src/ui.ts +3341 -0
  42. package/src/utils/actions.ts +227 -0
  43. package/src/utils/attachment-manager.ts +384 -0
  44. package/src/utils/code-generators.test.ts +500 -0
  45. package/src/utils/code-generators.ts +1806 -0
  46. package/src/utils/component-middleware.ts +137 -0
  47. package/src/utils/component-parser.ts +119 -0
  48. package/src/utils/constants.ts +16 -0
  49. package/src/utils/content.ts +306 -0
  50. package/src/utils/dom.ts +25 -0
  51. package/src/utils/events.ts +41 -0
  52. package/src/utils/formatting.test.ts +166 -0
  53. package/src/utils/formatting.ts +470 -0
  54. package/src/utils/icons.ts +92 -0
  55. package/src/utils/message-id.ts +37 -0
  56. package/src/utils/morph.ts +36 -0
  57. package/src/utils/positioning.ts +17 -0
  58. package/src/utils/storage.ts +72 -0
  59. package/src/utils/theme.ts +105 -0
  60. package/src/widget.css +1 -0
  61. package/widget.css +1 -0
package/src/types.ts ADDED
@@ -0,0 +1,1635 @@
1
+ import type { AgentWidgetPlugin } from "./plugins/types";
2
+
3
+ // ============================================================================
4
+ // Multi-Modal Content Types
5
+ // ============================================================================
6
+
7
+ /**
8
+ * Text content part for multi-modal messages
9
+ */
10
+ export type TextContentPart = {
11
+ type: 'text';
12
+ text: string;
13
+ };
14
+
15
+ /**
16
+ * Image content part for multi-modal messages
17
+ * Supports base64 data URIs or URLs
18
+ */
19
+ export type ImageContentPart = {
20
+ type: 'image';
21
+ image: string; // base64 data URI or URL
22
+ mimeType?: string;
23
+ alt?: string; // optional alt text for accessibility
24
+ };
25
+
26
+ /**
27
+ * File content part for multi-modal messages
28
+ * Supports PDF, TXT, DOCX, and other document types
29
+ */
30
+ export type FileContentPart = {
31
+ type: 'file';
32
+ data: string; // base64 data URI
33
+ mimeType: string;
34
+ filename: string;
35
+ };
36
+
37
+ /**
38
+ * Union type for all content part types
39
+ */
40
+ export type ContentPart = TextContentPart | ImageContentPart | FileContentPart;
41
+
42
+ /**
43
+ * Message content can be a simple string or an array of content parts
44
+ */
45
+ export type MessageContent = string | ContentPart[];
46
+
47
+ // ============================================================================
48
+ // Context and Middleware Types
49
+ // ============================================================================
50
+
51
+ export type AgentWidgetContextProviderContext = {
52
+ messages: AgentWidgetMessage[];
53
+ config: AgentWidgetConfig;
54
+ };
55
+
56
+ export type AgentWidgetContextProvider = (
57
+ context: AgentWidgetContextProviderContext
58
+ ) =>
59
+ | Record<string, unknown>
60
+ | void
61
+ | Promise<Record<string, unknown> | void>;
62
+
63
+ export type AgentWidgetRequestPayloadMessage = {
64
+ role: AgentWidgetMessageRole;
65
+ content: MessageContent;
66
+ createdAt: string;
67
+ };
68
+
69
+ export type AgentWidgetRequestPayload = {
70
+ messages: AgentWidgetRequestPayloadMessage[];
71
+ flowId?: string;
72
+ context?: Record<string, unknown>;
73
+ metadata?: Record<string, unknown>;
74
+ };
75
+
76
+ export type AgentWidgetRequestMiddlewareContext = {
77
+ payload: AgentWidgetRequestPayload;
78
+ config: AgentWidgetConfig;
79
+ };
80
+
81
+ export type AgentWidgetRequestMiddleware = (
82
+ context: AgentWidgetRequestMiddlewareContext
83
+ ) => AgentWidgetRequestPayload | void | Promise<AgentWidgetRequestPayload | void>;
84
+
85
+ export type AgentWidgetParsedAction = {
86
+ type: string;
87
+ payload: Record<string, unknown>;
88
+ raw?: unknown;
89
+ };
90
+
91
+ export type AgentWidgetActionParserInput = {
92
+ text: string;
93
+ message: AgentWidgetMessage;
94
+ };
95
+
96
+ export type AgentWidgetActionParser = (
97
+ input: AgentWidgetActionParserInput
98
+ ) => AgentWidgetParsedAction | null | undefined;
99
+
100
+ export type AgentWidgetActionHandlerResult = {
101
+ handled?: boolean;
102
+ displayText?: string;
103
+ persistMessage?: boolean; // If false, prevents message from being saved to history
104
+ };
105
+
106
+ export type AgentWidgetActionContext = {
107
+ message: AgentWidgetMessage;
108
+ metadata: Record<string, unknown>;
109
+ updateMetadata: (
110
+ updater: (prev: Record<string, unknown>) => Record<string, unknown>
111
+ ) => void;
112
+ document: Document | null;
113
+ };
114
+
115
+ export type AgentWidgetActionHandler = (
116
+ action: AgentWidgetParsedAction,
117
+ context: AgentWidgetActionContext
118
+ ) => AgentWidgetActionHandlerResult | void;
119
+
120
+ export type AgentWidgetStoredState = {
121
+ messages?: AgentWidgetMessage[];
122
+ metadata?: Record<string, unknown>;
123
+ };
124
+
125
+ export interface AgentWidgetStorageAdapter {
126
+ load?: () =>
127
+ | AgentWidgetStoredState
128
+ | null
129
+ | Promise<AgentWidgetStoredState | null>;
130
+ save?: (state: AgentWidgetStoredState) => void | Promise<void>;
131
+ clear?: () => void | Promise<void>;
132
+ }
133
+
134
+ export type AgentWidgetVoiceStateEvent = {
135
+ active: boolean;
136
+ source: "user" | "auto" | "restore" | "system";
137
+ timestamp: number;
138
+ };
139
+
140
+ export type AgentWidgetActionEventPayload = {
141
+ action: AgentWidgetParsedAction;
142
+ message: AgentWidgetMessage;
143
+ };
144
+
145
+ /**
146
+ * Feedback event payload for upvote/downvote actions on messages
147
+ */
148
+ export type AgentWidgetMessageFeedback = {
149
+ type: "upvote" | "downvote";
150
+ messageId: string;
151
+ message: AgentWidgetMessage;
152
+ };
153
+
154
+ /**
155
+ * Configuration for message action buttons (copy, upvote, downvote)
156
+ *
157
+ * **Client Token Mode**: When using `clientToken`, feedback is automatically
158
+ * sent to your Travrse backend. Just enable the buttons and you're done!
159
+ * The `onFeedback` and `onCopy` callbacks are optional for additional local handling.
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * // With clientToken - feedback is automatic!
164
+ * config: {
165
+ * clientToken: 'ct_live_...',
166
+ * messageActions: {
167
+ * showUpvote: true,
168
+ * showDownvote: true,
169
+ * // No onFeedback needed - sent to backend automatically
170
+ * }
171
+ * }
172
+ * ```
173
+ */
174
+ export type AgentWidgetMessageActionsConfig = {
175
+ /**
176
+ * Enable/disable message actions entirely
177
+ * @default true
178
+ */
179
+ enabled?: boolean;
180
+ /**
181
+ * Show copy button
182
+ * @default true
183
+ */
184
+ showCopy?: boolean;
185
+ /**
186
+ * Show upvote button.
187
+ * When using `clientToken`, feedback is sent to the backend automatically.
188
+ * @default false
189
+ */
190
+ showUpvote?: boolean;
191
+ /**
192
+ * Show downvote button.
193
+ * When using `clientToken`, feedback is sent to the backend automatically.
194
+ * @default false
195
+ */
196
+ showDownvote?: boolean;
197
+ /**
198
+ * Visibility mode: 'always' shows buttons always, 'hover' shows on hover only
199
+ * @default 'hover'
200
+ */
201
+ visibility?: "always" | "hover";
202
+ /**
203
+ * Horizontal alignment of action buttons
204
+ * @default 'right'
205
+ */
206
+ align?: "left" | "center" | "right";
207
+ /**
208
+ * Layout style for action buttons
209
+ * - 'pill-inside': Compact floating pill around just the buttons (default for hover)
210
+ * - 'row-inside': Full-width row at the bottom of the message
211
+ * @default 'pill-inside'
212
+ */
213
+ layout?: "pill-inside" | "row-inside";
214
+ /**
215
+ * Callback when user submits feedback (upvote/downvote).
216
+ *
217
+ * **Note**: When using `clientToken`, feedback is AUTOMATICALLY sent to your
218
+ * backend via `/v1/client/feedback`. This callback is called IN ADDITION to
219
+ * the automatic submission, useful for updating local UI or analytics.
220
+ */
221
+ onFeedback?: (feedback: AgentWidgetMessageFeedback) => void;
222
+ /**
223
+ * Callback when user copies a message.
224
+ *
225
+ * **Note**: When using `clientToken`, copy events are AUTOMATICALLY tracked
226
+ * via `/v1/client/feedback`. This callback is called IN ADDITION to the
227
+ * automatic tracking.
228
+ */
229
+ onCopy?: (message: AgentWidgetMessage) => void;
230
+ };
231
+
232
+ export type AgentWidgetStateEvent = {
233
+ open: boolean;
234
+ source: "user" | "auto" | "api" | "system";
235
+ timestamp: number;
236
+ };
237
+
238
+ export type AgentWidgetStateSnapshot = {
239
+ open: boolean;
240
+ launcherEnabled: boolean;
241
+ voiceActive: boolean;
242
+ streaming: boolean;
243
+ };
244
+
245
+ export type AgentWidgetControllerEventMap = {
246
+ "assistant:message": AgentWidgetMessage;
247
+ "assistant:complete": AgentWidgetMessage;
248
+ "voice:state": AgentWidgetVoiceStateEvent;
249
+ "action:detected": AgentWidgetActionEventPayload;
250
+ "widget:opened": AgentWidgetStateEvent;
251
+ "widget:closed": AgentWidgetStateEvent;
252
+ "widget:state": AgentWidgetStateSnapshot;
253
+ "message:feedback": AgentWidgetMessageFeedback;
254
+ "message:copy": AgentWidgetMessage;
255
+ };
256
+
257
+ export type AgentWidgetFeatureFlags = {
258
+ showReasoning?: boolean;
259
+ showToolCalls?: boolean;
260
+ };
261
+
262
+ export type AgentWidgetTheme = {
263
+ primary?: string;
264
+ secondary?: string;
265
+ surface?: string;
266
+ muted?: string;
267
+ accent?: string;
268
+ container?: string;
269
+ border?: string;
270
+ divider?: string;
271
+ messageBorder?: string;
272
+ inputBackground?: string;
273
+ callToAction?: string;
274
+ callToActionBackground?: string;
275
+ sendButtonBackgroundColor?: string;
276
+ sendButtonTextColor?: string;
277
+ sendButtonBorderColor?: string;
278
+ closeButtonColor?: string;
279
+ closeButtonBackgroundColor?: string;
280
+ closeButtonBorderColor?: string;
281
+ clearChatIconColor?: string;
282
+ clearChatBackgroundColor?: string;
283
+ clearChatBorderColor?: string;
284
+ tooltipBackground?: string;
285
+ tooltipForeground?: string;
286
+ micIconColor?: string;
287
+ micBackgroundColor?: string;
288
+ micBorderColor?: string;
289
+ recordingIconColor?: string;
290
+ recordingBackgroundColor?: string;
291
+ recordingBorderColor?: string;
292
+ inputFontFamily?: "sans-serif" | "serif" | "mono";
293
+ inputFontWeight?: string;
294
+ radiusSm?: string;
295
+ radiusMd?: string;
296
+ radiusLg?: string;
297
+ launcherRadius?: string;
298
+ buttonRadius?: string;
299
+ /**
300
+ * Border style for the chat panel container.
301
+ * @example "1px solid #e5e7eb" | "none"
302
+ * @default "1px solid var(--tvw-cw-border)"
303
+ */
304
+ panelBorder?: string;
305
+ /**
306
+ * Box shadow for the chat panel container.
307
+ * @example "0 25px 50px -12px rgba(0,0,0,0.25)" | "none"
308
+ * @default "0 25px 50px -12px rgba(0,0,0,0.25)"
309
+ */
310
+ panelShadow?: string;
311
+ /**
312
+ * Border radius for the chat panel container.
313
+ * @example "16px" | "0"
314
+ * @default "16px"
315
+ */
316
+ panelBorderRadius?: string;
317
+ };
318
+
319
+ export type AgentWidgetLauncherConfig = {
320
+ enabled?: boolean;
321
+ title?: string;
322
+ subtitle?: string;
323
+ textHidden?: boolean;
324
+ iconUrl?: string;
325
+ agentIconText?: string;
326
+ agentIconName?: string;
327
+ agentIconHidden?: boolean;
328
+ position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
329
+ autoExpand?: boolean;
330
+ width?: string;
331
+ /**
332
+ * When true, the widget panel will fill the full height of its container.
333
+ * Useful for sidebar layouts where the chat should take up the entire viewport height.
334
+ * The widget will use flex layout to ensure header stays at top, messages scroll in middle,
335
+ * and composer stays fixed at bottom.
336
+ *
337
+ * @default false
338
+ */
339
+ fullHeight?: boolean;
340
+ /**
341
+ * When true, the widget panel will be positioned as a sidebar flush with the viewport edges.
342
+ * The panel will have:
343
+ * - No border-radius (square corners)
344
+ * - No margins (flush with top, left/right, and bottom edges)
345
+ * - Full viewport height
346
+ * - Subtle shadow on the edge facing the content
347
+ * - No border between footer and messages
348
+ *
349
+ * Use with `position` to control which side ('bottom-left' for left sidebar, 'bottom-right' for right sidebar).
350
+ * Automatically enables fullHeight when true.
351
+ *
352
+ * @default false
353
+ */
354
+ sidebarMode?: boolean;
355
+ /**
356
+ * Width of the sidebar panel when sidebarMode is true.
357
+ * @default "420px"
358
+ */
359
+ sidebarWidth?: string;
360
+ /**
361
+ * Offset (in pixels) to subtract from the calculated panel height.
362
+ * Useful for adjusting the panel height when there are other fixed elements on the page.
363
+ * Only applies when not in fullHeight or sidebarMode.
364
+ *
365
+ * @default 0
366
+ */
367
+ heightOffset?: number;
368
+ callToActionIconText?: string;
369
+ callToActionIconName?: string;
370
+ callToActionIconColor?: string;
371
+ callToActionIconBackgroundColor?: string;
372
+ callToActionIconHidden?: boolean;
373
+ callToActionIconPadding?: string;
374
+ agentIconSize?: string;
375
+ callToActionIconSize?: string;
376
+ headerIconSize?: string;
377
+ headerIconName?: string;
378
+ headerIconHidden?: boolean;
379
+ closeButtonSize?: string;
380
+ closeButtonColor?: string;
381
+ closeButtonBackgroundColor?: string;
382
+ closeButtonBorderWidth?: string;
383
+ closeButtonBorderColor?: string;
384
+ closeButtonBorderRadius?: string;
385
+ closeButtonPaddingX?: string;
386
+ closeButtonPaddingY?: string;
387
+ closeButtonPlacement?: "inline" | "top-right";
388
+ closeButtonIconName?: string;
389
+ closeButtonIconText?: string;
390
+ closeButtonTooltipText?: string;
391
+ closeButtonShowTooltip?: boolean;
392
+ clearChat?: AgentWidgetClearChatConfig;
393
+ /**
394
+ * Border style for the launcher button.
395
+ * @example "1px solid #e5e7eb" | "2px solid #3b82f6" | "none"
396
+ * @default "1px solid #e5e7eb"
397
+ */
398
+ border?: string;
399
+ /**
400
+ * Box shadow for the launcher button.
401
+ * @example "0 10px 15px -3px rgba(0,0,0,0.1)" | "none"
402
+ * @default "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)"
403
+ */
404
+ shadow?: string;
405
+ };
406
+
407
+ export type AgentWidgetSendButtonConfig = {
408
+ borderWidth?: string;
409
+ borderColor?: string;
410
+ paddingX?: string;
411
+ paddingY?: string;
412
+ iconText?: string;
413
+ iconName?: string;
414
+ useIcon?: boolean;
415
+ tooltipText?: string;
416
+ showTooltip?: boolean;
417
+ backgroundColor?: string;
418
+ textColor?: string;
419
+ size?: string;
420
+ };
421
+
422
+ export type AgentWidgetClearChatConfig = {
423
+ enabled?: boolean;
424
+ placement?: "inline" | "top-right";
425
+ iconName?: string;
426
+ iconColor?: string;
427
+ backgroundColor?: string;
428
+ borderWidth?: string;
429
+ borderColor?: string;
430
+ borderRadius?: string;
431
+ size?: string;
432
+ paddingX?: string;
433
+ paddingY?: string;
434
+ tooltipText?: string;
435
+ showTooltip?: boolean;
436
+ };
437
+
438
+ export type AgentWidgetStatusIndicatorConfig = {
439
+ visible?: boolean;
440
+ idleText?: string;
441
+ connectingText?: string;
442
+ connectedText?: string;
443
+ errorText?: string;
444
+ };
445
+
446
+ export type AgentWidgetVoiceRecognitionConfig = {
447
+ enabled?: boolean;
448
+ pauseDuration?: number;
449
+ iconName?: string;
450
+ iconSize?: string;
451
+ iconColor?: string;
452
+ backgroundColor?: string;
453
+ borderColor?: string;
454
+ borderWidth?: string;
455
+ paddingX?: string;
456
+ paddingY?: string;
457
+ tooltipText?: string;
458
+ showTooltip?: boolean;
459
+ recordingIconColor?: string;
460
+ recordingBackgroundColor?: string;
461
+ recordingBorderColor?: string;
462
+ showRecordingIndicator?: boolean;
463
+ autoResume?: boolean | "assistant";
464
+ };
465
+
466
+ export type AgentWidgetToolCallConfig = {
467
+ backgroundColor?: string;
468
+ borderColor?: string;
469
+ borderWidth?: string;
470
+ borderRadius?: string;
471
+ headerBackgroundColor?: string;
472
+ headerTextColor?: string;
473
+ headerPaddingX?: string;
474
+ headerPaddingY?: string;
475
+ contentBackgroundColor?: string;
476
+ contentTextColor?: string;
477
+ contentPaddingX?: string;
478
+ contentPaddingY?: string;
479
+ codeBlockBackgroundColor?: string;
480
+ codeBlockBorderColor?: string;
481
+ codeBlockTextColor?: string;
482
+ toggleTextColor?: string;
483
+ labelTextColor?: string;
484
+ };
485
+
486
+ export type AgentWidgetSuggestionChipsConfig = {
487
+ fontFamily?: "sans-serif" | "serif" | "mono";
488
+ fontWeight?: string;
489
+ paddingX?: string;
490
+ paddingY?: string;
491
+ };
492
+
493
+ /**
494
+ * Interface for pluggable stream parsers that extract text from streaming responses.
495
+ * Parsers handle incremental parsing to extract text values from structured formats (JSON, XML, etc.).
496
+ *
497
+ * @example
498
+ * ```typescript
499
+ * const jsonParser: AgentWidgetStreamParser = {
500
+ * processChunk: async (content) => {
501
+ * // Extract text from JSON - return null if not JSON or text not available yet
502
+ * if (!content.trim().startsWith('{')) return null;
503
+ * const match = content.match(/"text"\s*:\s*"([^"]*)"/);
504
+ * return match ? match[1] : null;
505
+ * },
506
+ * getExtractedText: () => extractedText
507
+ * };
508
+ * ```
509
+ */
510
+ export interface AgentWidgetStreamParserResult {
511
+ /**
512
+ * The extracted text to display (may be partial during streaming)
513
+ */
514
+ text: string | null;
515
+
516
+ /**
517
+ * The raw accumulated content. Built-in parsers always populate this so
518
+ * downstream middleware (action handlers, logging, etc.) can
519
+ * inspect/parse the original structured payload.
520
+ */
521
+ raw?: string;
522
+ }
523
+
524
+ export interface AgentWidgetStreamParser {
525
+ /**
526
+ * Process a chunk of content and return the extracted text (if available).
527
+ * This method is called for each chunk as it arrives during streaming.
528
+ * Return null if the content doesn't match this parser's format or if text is not yet available.
529
+ *
530
+ * @param accumulatedContent - The full accumulated content so far (including new chunk)
531
+ * @returns The extracted text value and optionally raw content, or null if not yet available or format doesn't match
532
+ */
533
+ processChunk(accumulatedContent: string): Promise<AgentWidgetStreamParserResult | string | null> | AgentWidgetStreamParserResult | string | null;
534
+
535
+ /**
536
+ * Get the currently extracted text value (may be partial).
537
+ * This is called synchronously to get the latest extracted text without processing.
538
+ *
539
+ * @returns The currently extracted text value, or null if not yet available
540
+ */
541
+ getExtractedText(): string | null;
542
+
543
+ /**
544
+ * Clean up any resources when parsing is complete.
545
+ */
546
+ close?(): Promise<void> | void;
547
+ }
548
+
549
+
550
+ /**
551
+ * Component renderer function signature for custom components
552
+ */
553
+ export type AgentWidgetComponentRenderer = (
554
+ props: Record<string, unknown>,
555
+ context: {
556
+ message: AgentWidgetMessage;
557
+ config: AgentWidgetConfig;
558
+ updateProps: (newProps: Record<string, unknown>) => void;
559
+ }
560
+ ) => HTMLElement;
561
+
562
+ /**
563
+ * Result from custom SSE event parser
564
+ */
565
+ export type AgentWidgetSSEEventResult = {
566
+ /** Text content to display */
567
+ text?: string;
568
+ /** Whether the stream is complete */
569
+ done?: boolean;
570
+ /** Error message if an error occurred */
571
+ error?: string;
572
+ } | null;
573
+
574
+ /**
575
+ * Custom SSE event parser function
576
+ * Allows transforming non-standard SSE event formats to persona's expected format
577
+ */
578
+ export type AgentWidgetSSEEventParser = (
579
+ eventData: unknown
580
+ ) => AgentWidgetSSEEventResult | Promise<AgentWidgetSSEEventResult>;
581
+
582
+ /**
583
+ * Custom fetch function for full control over API requests
584
+ * Use this for custom authentication, request transformation, etc.
585
+ */
586
+ export type AgentWidgetCustomFetch = (
587
+ url: string,
588
+ init: RequestInit,
589
+ payload: AgentWidgetRequestPayload
590
+ ) => Promise<Response>;
591
+
592
+ /**
593
+ * Dynamic headers function - called before each request
594
+ */
595
+ export type AgentWidgetHeadersFunction = () => Record<string, string> | Promise<Record<string, string>>;
596
+
597
+ // ============================================================================
598
+ // Client Token Types
599
+ // ============================================================================
600
+
601
+ /**
602
+ * Session information returned after client token initialization.
603
+ * Contains session ID, expiry time, flow info, and config from the server.
604
+ */
605
+ export type ClientSession = {
606
+ /** Unique session identifier */
607
+ sessionId: string;
608
+ /** When the session expires */
609
+ expiresAt: Date;
610
+ /** Flow information */
611
+ flow: {
612
+ id: string;
613
+ name: string;
614
+ description: string | null;
615
+ };
616
+ /** Configuration from the server */
617
+ config: {
618
+ welcomeMessage: string | null;
619
+ placeholder: string;
620
+ theme: Record<string, unknown> | null;
621
+ };
622
+ };
623
+
624
+ /**
625
+ * Raw API response from /v1/client/init endpoint
626
+ */
627
+ export type ClientInitResponse = {
628
+ session_id: string;
629
+ expires_at: string;
630
+ flow: {
631
+ id: string;
632
+ name: string;
633
+ description: string | null;
634
+ };
635
+ config: {
636
+ welcome_message: string | null;
637
+ placeholder: string;
638
+ theme: Record<string, unknown> | null;
639
+ };
640
+ };
641
+
642
+ /**
643
+ * Request payload for /v1/client/chat endpoint
644
+ */
645
+ export type ClientChatRequest = {
646
+ session_id: string;
647
+ messages: Array<{
648
+ id?: string;
649
+ role: 'user' | 'assistant' | 'system';
650
+ content: MessageContent;
651
+ }>;
652
+ /** ID for the expected assistant response message */
653
+ assistant_message_id?: string;
654
+ metadata?: Record<string, unknown>;
655
+ context?: Record<string, unknown>;
656
+ };
657
+
658
+ /**
659
+ * Feedback types supported by the API
660
+ */
661
+ export type ClientFeedbackType = 'upvote' | 'downvote' | 'copy' | 'csat' | 'nps';
662
+
663
+ /**
664
+ * Request payload for /v1/client/feedback endpoint
665
+ */
666
+ export type ClientFeedbackRequest = {
667
+ session_id: string;
668
+ /** Required for upvote, downvote, copy feedback types */
669
+ message_id?: string;
670
+ type: ClientFeedbackType;
671
+ /** Required for csat (1-5) and nps (0-10) feedback types */
672
+ rating?: number;
673
+ /** Optional comment for any feedback type */
674
+ comment?: string;
675
+ };
676
+
677
+ // ============================================================================
678
+ // Layout Configuration Types
679
+ // ============================================================================
680
+
681
+ /**
682
+ * Context provided to header render functions
683
+ */
684
+ export type HeaderRenderContext = {
685
+ config: AgentWidgetConfig;
686
+ onClose?: () => void;
687
+ onClearChat?: () => void;
688
+ };
689
+
690
+ /**
691
+ * Context provided to message render functions
692
+ */
693
+ export type MessageRenderContext = {
694
+ message: AgentWidgetMessage;
695
+ config: AgentWidgetConfig;
696
+ streaming: boolean;
697
+ };
698
+
699
+ /**
700
+ * Context provided to slot render functions
701
+ */
702
+ export type SlotRenderContext = {
703
+ config: AgentWidgetConfig;
704
+ defaultContent: () => HTMLElement | null;
705
+ };
706
+
707
+ /**
708
+ * Header layout configuration
709
+ * Allows customization of the header section appearance and behavior
710
+ */
711
+ export type AgentWidgetHeaderLayoutConfig = {
712
+ /**
713
+ * Layout preset: "default" | "minimal" | "expanded"
714
+ * - default: Standard layout with icon, title, subtitle, and buttons
715
+ * - minimal: Simplified layout with just title and close button
716
+ * - expanded: Full branding area with additional content space
717
+ */
718
+ layout?: "default" | "minimal" | "expanded";
719
+ /** Show/hide the header icon */
720
+ showIcon?: boolean;
721
+ /** Show/hide the title */
722
+ showTitle?: boolean;
723
+ /** Show/hide the subtitle */
724
+ showSubtitle?: boolean;
725
+ /** Show/hide the close button */
726
+ showCloseButton?: boolean;
727
+ /** Show/hide the clear chat button */
728
+ showClearChat?: boolean;
729
+ /**
730
+ * Custom renderer for complete header override
731
+ * When provided, replaces the entire header with custom content
732
+ */
733
+ render?: (context: HeaderRenderContext) => HTMLElement;
734
+ };
735
+
736
+ /**
737
+ * Avatar configuration for message bubbles
738
+ */
739
+ export type AgentWidgetAvatarConfig = {
740
+ /** Whether to show avatars */
741
+ show?: boolean;
742
+ /** Position of avatar relative to message bubble */
743
+ position?: "left" | "right";
744
+ /** URL or emoji for user avatar */
745
+ userAvatar?: string;
746
+ /** URL or emoji for assistant avatar */
747
+ assistantAvatar?: string;
748
+ };
749
+
750
+ /**
751
+ * Timestamp configuration for message bubbles
752
+ */
753
+ export type AgentWidgetTimestampConfig = {
754
+ /** Whether to show timestamps */
755
+ show?: boolean;
756
+ /** Position of timestamp relative to message */
757
+ position?: "inline" | "below";
758
+ /** Custom formatter for timestamp display */
759
+ format?: (date: Date) => string;
760
+ };
761
+
762
+ /**
763
+ * Message layout configuration
764
+ * Allows customization of how chat messages are displayed
765
+ */
766
+ export type AgentWidgetMessageLayoutConfig = {
767
+ /**
768
+ * Layout preset: "bubble" | "flat" | "minimal"
769
+ * - bubble: Standard chat bubble appearance (default)
770
+ * - flat: Flat messages without bubble styling
771
+ * - minimal: Minimal styling with reduced padding/borders
772
+ */
773
+ layout?: "bubble" | "flat" | "minimal";
774
+ /** Avatar configuration */
775
+ avatar?: AgentWidgetAvatarConfig;
776
+ /** Timestamp configuration */
777
+ timestamp?: AgentWidgetTimestampConfig;
778
+ /** Group consecutive messages from the same role */
779
+ groupConsecutive?: boolean;
780
+ /**
781
+ * Custom renderer for user messages
782
+ * When provided, replaces the default user message rendering
783
+ */
784
+ renderUserMessage?: (context: MessageRenderContext) => HTMLElement;
785
+ /**
786
+ * Custom renderer for assistant messages
787
+ * When provided, replaces the default assistant message rendering
788
+ */
789
+ renderAssistantMessage?: (context: MessageRenderContext) => HTMLElement;
790
+ };
791
+
792
+ /**
793
+ * Available layout slots for content injection
794
+ */
795
+ export type WidgetLayoutSlot =
796
+ | "header-left"
797
+ | "header-center"
798
+ | "header-right"
799
+ | "body-top"
800
+ | "messages"
801
+ | "body-bottom"
802
+ | "footer-top"
803
+ | "composer"
804
+ | "footer-bottom";
805
+
806
+ /**
807
+ * Slot renderer function signature
808
+ * Returns HTMLElement to render in the slot, or null to use default content
809
+ */
810
+ export type SlotRenderer = (context: SlotRenderContext) => HTMLElement | null;
811
+
812
+ /**
813
+ * Main layout configuration
814
+ * Provides comprehensive control over widget layout and appearance
815
+ *
816
+ * @example
817
+ * ```typescript
818
+ * config: {
819
+ * layout: {
820
+ * header: { layout: "minimal" },
821
+ * messages: {
822
+ * avatar: { show: true, assistantAvatar: "/bot.png" },
823
+ * timestamp: { show: true, position: "below" }
824
+ * },
825
+ * slots: {
826
+ * "footer-top": () => {
827
+ * const el = document.createElement("div");
828
+ * el.textContent = "Powered by AI";
829
+ * return el;
830
+ * }
831
+ * }
832
+ * }
833
+ * }
834
+ * ```
835
+ */
836
+ export type AgentWidgetLayoutConfig = {
837
+ /** Header layout configuration */
838
+ header?: AgentWidgetHeaderLayoutConfig;
839
+ /** Message layout configuration */
840
+ messages?: AgentWidgetMessageLayoutConfig;
841
+ /** Slot renderers for custom content injection */
842
+ slots?: Partial<Record<WidgetLayoutSlot, SlotRenderer>>;
843
+ /**
844
+ * Show/hide the header section entirely.
845
+ * When false, the header (including icon, title, buttons) is completely hidden.
846
+ * @default true
847
+ */
848
+ showHeader?: boolean;
849
+ /**
850
+ * Show/hide the footer/composer section entirely.
851
+ * When false, the footer (including input field, send button, suggestions) is completely hidden.
852
+ * Useful for read-only conversation previews.
853
+ * @default true
854
+ */
855
+ showFooter?: boolean;
856
+ };
857
+
858
+ // ============================================================================
859
+ // Markdown Configuration Types
860
+ // ============================================================================
861
+
862
+ /**
863
+ * Token types for marked renderer methods
864
+ */
865
+ export type AgentWidgetMarkdownHeadingToken = {
866
+ type: "heading";
867
+ raw: string;
868
+ depth: 1 | 2 | 3 | 4 | 5 | 6;
869
+ text: string;
870
+ tokens: unknown[];
871
+ };
872
+
873
+ export type AgentWidgetMarkdownCodeToken = {
874
+ type: "code";
875
+ raw: string;
876
+ text: string;
877
+ lang?: string;
878
+ escaped?: boolean;
879
+ };
880
+
881
+ export type AgentWidgetMarkdownBlockquoteToken = {
882
+ type: "blockquote";
883
+ raw: string;
884
+ text: string;
885
+ tokens: unknown[];
886
+ };
887
+
888
+ export type AgentWidgetMarkdownTableToken = {
889
+ type: "table";
890
+ raw: string;
891
+ header: Array<{ text: string; tokens: unknown[] }>;
892
+ rows: Array<Array<{ text: string; tokens: unknown[] }>>;
893
+ align: Array<"left" | "center" | "right" | null>;
894
+ };
895
+
896
+ export type AgentWidgetMarkdownLinkToken = {
897
+ type: "link";
898
+ raw: string;
899
+ href: string;
900
+ title: string | null;
901
+ text: string;
902
+ tokens: unknown[];
903
+ };
904
+
905
+ export type AgentWidgetMarkdownImageToken = {
906
+ type: "image";
907
+ raw: string;
908
+ href: string;
909
+ title: string | null;
910
+ text: string;
911
+ };
912
+
913
+ export type AgentWidgetMarkdownListToken = {
914
+ type: "list";
915
+ raw: string;
916
+ ordered: boolean;
917
+ start: number | "";
918
+ loose: boolean;
919
+ items: unknown[];
920
+ };
921
+
922
+ export type AgentWidgetMarkdownListItemToken = {
923
+ type: "list_item";
924
+ raw: string;
925
+ task: boolean;
926
+ checked?: boolean;
927
+ loose: boolean;
928
+ text: string;
929
+ tokens: unknown[];
930
+ };
931
+
932
+ export type AgentWidgetMarkdownParagraphToken = {
933
+ type: "paragraph";
934
+ raw: string;
935
+ text: string;
936
+ tokens: unknown[];
937
+ };
938
+
939
+ export type AgentWidgetMarkdownCodespanToken = {
940
+ type: "codespan";
941
+ raw: string;
942
+ text: string;
943
+ };
944
+
945
+ export type AgentWidgetMarkdownStrongToken = {
946
+ type: "strong";
947
+ raw: string;
948
+ text: string;
949
+ tokens: unknown[];
950
+ };
951
+
952
+ export type AgentWidgetMarkdownEmToken = {
953
+ type: "em";
954
+ raw: string;
955
+ text: string;
956
+ tokens: unknown[];
957
+ };
958
+
959
+ /**
960
+ * Custom renderer overrides for markdown elements.
961
+ * Each method receives the token and should return an HTML string.
962
+ * Return `false` to use the default renderer.
963
+ *
964
+ * @example
965
+ * ```typescript
966
+ * renderer: {
967
+ * heading(token) {
968
+ * return `<h${token.depth} class="custom-heading">${token.text}</h${token.depth}>`;
969
+ * },
970
+ * link(token) {
971
+ * return `<a href="${token.href}" target="_blank" rel="noopener">${token.text}</a>`;
972
+ * }
973
+ * }
974
+ * ```
975
+ */
976
+ export type AgentWidgetMarkdownRendererOverrides = {
977
+ /** Override heading rendering (h1-h6) */
978
+ heading?: (token: AgentWidgetMarkdownHeadingToken) => string | false;
979
+ /** Override code block rendering */
980
+ code?: (token: AgentWidgetMarkdownCodeToken) => string | false;
981
+ /** Override blockquote rendering */
982
+ blockquote?: (token: AgentWidgetMarkdownBlockquoteToken) => string | false;
983
+ /** Override table rendering */
984
+ table?: (token: AgentWidgetMarkdownTableToken) => string | false;
985
+ /** Override link rendering */
986
+ link?: (token: AgentWidgetMarkdownLinkToken) => string | false;
987
+ /** Override image rendering */
988
+ image?: (token: AgentWidgetMarkdownImageToken) => string | false;
989
+ /** Override list rendering (ul/ol) */
990
+ list?: (token: AgentWidgetMarkdownListToken) => string | false;
991
+ /** Override list item rendering */
992
+ listitem?: (token: AgentWidgetMarkdownListItemToken) => string | false;
993
+ /** Override paragraph rendering */
994
+ paragraph?: (token: AgentWidgetMarkdownParagraphToken) => string | false;
995
+ /** Override inline code rendering */
996
+ codespan?: (token: AgentWidgetMarkdownCodespanToken) => string | false;
997
+ /** Override strong/bold rendering */
998
+ strong?: (token: AgentWidgetMarkdownStrongToken) => string | false;
999
+ /** Override emphasis/italic rendering */
1000
+ em?: (token: AgentWidgetMarkdownEmToken) => string | false;
1001
+ /** Override horizontal rule rendering */
1002
+ hr?: () => string | false;
1003
+ /** Override line break rendering */
1004
+ br?: () => string | false;
1005
+ /** Override deleted/strikethrough rendering */
1006
+ del?: (token: { type: "del"; raw: string; text: string; tokens: unknown[] }) => string | false;
1007
+ /** Override checkbox rendering (in task lists) */
1008
+ checkbox?: (token: { checked: boolean }) => string | false;
1009
+ /** Override HTML passthrough */
1010
+ html?: (token: { type: "html"; raw: string; text: string }) => string | false;
1011
+ /** Override text rendering */
1012
+ text?: (token: { type: "text"; raw: string; text: string }) => string | false;
1013
+ };
1014
+
1015
+ /**
1016
+ * Markdown parsing options (subset of marked options)
1017
+ */
1018
+ export type AgentWidgetMarkdownOptions = {
1019
+ /**
1020
+ * Enable GitHub Flavored Markdown (tables, strikethrough, autolinks).
1021
+ * @default true
1022
+ */
1023
+ gfm?: boolean;
1024
+ /**
1025
+ * Convert \n in paragraphs into <br>.
1026
+ * @default true
1027
+ */
1028
+ breaks?: boolean;
1029
+ /**
1030
+ * Conform to original markdown.pl as much as possible.
1031
+ * @default false
1032
+ */
1033
+ pedantic?: boolean;
1034
+ /**
1035
+ * Add id attributes to headings.
1036
+ * @default false
1037
+ */
1038
+ headerIds?: boolean;
1039
+ /**
1040
+ * Prefix for heading id attributes.
1041
+ * @default ""
1042
+ */
1043
+ headerPrefix?: string;
1044
+ /**
1045
+ * Mangle email addresses for spam protection.
1046
+ * @default true
1047
+ */
1048
+ mangle?: boolean;
1049
+ /**
1050
+ * Silent mode - don't throw on parse errors.
1051
+ * @default false
1052
+ */
1053
+ silent?: boolean;
1054
+ };
1055
+
1056
+ /**
1057
+ * Markdown configuration for customizing how markdown is rendered in chat messages.
1058
+ * Provides three levels of control:
1059
+ *
1060
+ * 1. **CSS Variables** - Override styles via `--cw-md-*` CSS custom properties
1061
+ * 2. **Parsing Options** - Configure marked behavior via `options`
1062
+ * 3. **Custom Renderers** - Full control via `renderer` overrides
1063
+ *
1064
+ * @example
1065
+ * ```typescript
1066
+ * // Level 2: Configure parsing options
1067
+ * config: {
1068
+ * markdown: {
1069
+ * options: {
1070
+ * gfm: true,
1071
+ * breaks: true,
1072
+ * headerIds: true
1073
+ * }
1074
+ * }
1075
+ * }
1076
+ * ```
1077
+ *
1078
+ * @example
1079
+ * ```typescript
1080
+ * // Level 3: Custom renderers
1081
+ * config: {
1082
+ * markdown: {
1083
+ * renderer: {
1084
+ * heading(token) {
1085
+ * return `<h${token.depth} class="custom-h${token.depth}">${token.text}</h${token.depth}>`;
1086
+ * },
1087
+ * link(token) {
1088
+ * return `<a href="${token.href}" target="_blank">${token.text}</a>`;
1089
+ * },
1090
+ * table(token) {
1091
+ * // Wrap tables in a scrollable container
1092
+ * return `<div class="table-scroll">${this.parser.parse(token.tokens)}</div>`;
1093
+ * }
1094
+ * }
1095
+ * }
1096
+ * }
1097
+ * ```
1098
+ */
1099
+ export type AgentWidgetMarkdownConfig = {
1100
+ /**
1101
+ * Markdown parsing options.
1102
+ * These are passed directly to the marked parser.
1103
+ */
1104
+ options?: AgentWidgetMarkdownOptions;
1105
+
1106
+ /**
1107
+ * Custom renderer overrides for specific markdown elements.
1108
+ * Each method receives a token object and should return an HTML string.
1109
+ * Return `false` to fall back to the default renderer.
1110
+ */
1111
+ renderer?: AgentWidgetMarkdownRendererOverrides;
1112
+
1113
+ /**
1114
+ * Disable default markdown CSS styles.
1115
+ * When true, the widget won't apply any default styles to markdown elements,
1116
+ * allowing you to provide your own CSS.
1117
+ *
1118
+ * @default false
1119
+ */
1120
+ disableDefaultStyles?: boolean;
1121
+ };
1122
+
1123
+ /**
1124
+ * Configuration for file attachments in the composer.
1125
+ * Enables users to attach images to their messages.
1126
+ *
1127
+ * @example
1128
+ * ```typescript
1129
+ * config: {
1130
+ * attachments: {
1131
+ * enabled: true,
1132
+ * allowedTypes: ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],
1133
+ * maxFileSize: 5 * 1024 * 1024, // 5MB
1134
+ * maxFiles: 4
1135
+ * }
1136
+ * }
1137
+ * ```
1138
+ */
1139
+ export type AgentWidgetAttachmentsConfig = {
1140
+ /**
1141
+ * Enable/disable file attachments.
1142
+ * @default false
1143
+ */
1144
+ enabled?: boolean;
1145
+ /**
1146
+ * Allowed MIME types for attachments.
1147
+ * @default ['image/png', 'image/jpeg', 'image/gif', 'image/webp']
1148
+ */
1149
+ allowedTypes?: string[];
1150
+ /**
1151
+ * Maximum file size in bytes.
1152
+ * @default 10485760 (10MB)
1153
+ */
1154
+ maxFileSize?: number;
1155
+ /**
1156
+ * Maximum number of files per message.
1157
+ * @default 4
1158
+ */
1159
+ maxFiles?: number;
1160
+ /**
1161
+ * Button icon name (from Lucide icons).
1162
+ * @default 'image-plus'
1163
+ */
1164
+ buttonIconName?: string;
1165
+ /**
1166
+ * Tooltip text for the attachment button.
1167
+ * @default 'Attach image'
1168
+ */
1169
+ buttonTooltipText?: string;
1170
+ /**
1171
+ * Callback when a file is rejected (wrong type or too large).
1172
+ */
1173
+ onFileRejected?: (file: File, reason: 'type' | 'size' | 'count') => void;
1174
+ };
1175
+
1176
+ export type AgentWidgetConfig = {
1177
+ apiUrl?: string;
1178
+ flowId?: string;
1179
+ /**
1180
+ * Client token for direct browser-to-API communication.
1181
+ * When set, the widget uses /v1/client/* endpoints instead of /v1/dispatch.
1182
+ * Mutually exclusive with apiKey/headers authentication.
1183
+ *
1184
+ * @example
1185
+ * ```typescript
1186
+ * config: {
1187
+ * clientToken: 'ct_live_flow01k7_a8b9c0d1e2f3g4h5i6j7k8l9'
1188
+ * }
1189
+ * ```
1190
+ */
1191
+ clientToken?: string;
1192
+ /**
1193
+ * Callback when session is initialized (client token mode only).
1194
+ * Receives session info including expiry time.
1195
+ *
1196
+ * @example
1197
+ * ```typescript
1198
+ * config: {
1199
+ * onSessionInit: (session) => {
1200
+ * console.log('Session started:', session.sessionId);
1201
+ * }
1202
+ * }
1203
+ * ```
1204
+ */
1205
+ onSessionInit?: (session: ClientSession) => void;
1206
+ /**
1207
+ * Callback when session expires or errors (client token mode only).
1208
+ * Widget should prompt user to refresh.
1209
+ *
1210
+ * @example
1211
+ * ```typescript
1212
+ * config: {
1213
+ * onSessionExpired: () => {
1214
+ * alert('Your session has expired. Please refresh the page.');
1215
+ * }
1216
+ * }
1217
+ * ```
1218
+ */
1219
+ onSessionExpired?: () => void;
1220
+ /**
1221
+ * Get stored session ID for session resumption (client token mode only).
1222
+ * Called when initializing a new session to check if there's a previous session_id
1223
+ * that should be passed to /client/init to resume the same conversation record.
1224
+ *
1225
+ * @example
1226
+ * ```typescript
1227
+ * config: {
1228
+ * getStoredSessionId: () => {
1229
+ * const stored = localStorage.getItem('session_id');
1230
+ * return stored || null;
1231
+ * }
1232
+ * }
1233
+ * ```
1234
+ */
1235
+ getStoredSessionId?: () => string | null;
1236
+ /**
1237
+ * Store session ID for session resumption (client token mode only).
1238
+ * Called when a new session is initialized to persist the session_id
1239
+ * so it can be used to resume the conversation later.
1240
+ *
1241
+ * @example
1242
+ * ```typescript
1243
+ * config: {
1244
+ * setStoredSessionId: (sessionId) => {
1245
+ * localStorage.setItem('session_id', sessionId);
1246
+ * }
1247
+ * }
1248
+ * ```
1249
+ */
1250
+ setStoredSessionId?: (sessionId: string) => void;
1251
+ /**
1252
+ * Static headers to include with each request.
1253
+ * For dynamic headers (e.g., auth tokens), use `getHeaders` instead.
1254
+ */
1255
+ headers?: Record<string, string>;
1256
+ /**
1257
+ * Dynamic headers function - called before each request.
1258
+ * Useful for adding auth tokens that may change.
1259
+ * @example
1260
+ * ```typescript
1261
+ * getHeaders: async () => ({
1262
+ * 'Authorization': `Bearer ${await getAuthToken()}`
1263
+ * })
1264
+ * ```
1265
+ */
1266
+ getHeaders?: AgentWidgetHeadersFunction;
1267
+ copy?: {
1268
+ welcomeTitle?: string;
1269
+ welcomeSubtitle?: string;
1270
+ inputPlaceholder?: string;
1271
+ sendButtonLabel?: string;
1272
+ };
1273
+ theme?: AgentWidgetTheme;
1274
+ /**
1275
+ * Theme colors for dark mode. Applied when dark mode is detected
1276
+ * (when colorScheme is 'dark' or 'auto' with dark mode active).
1277
+ * If not provided, falls back to `theme` colors.
1278
+ *
1279
+ * @example
1280
+ * ```typescript
1281
+ * config: {
1282
+ * theme: { primary: '#111827', surface: '#ffffff' },
1283
+ * darkTheme: { primary: '#f9fafb', surface: '#1f2937' },
1284
+ * colorScheme: 'auto'
1285
+ * }
1286
+ * ```
1287
+ */
1288
+ darkTheme?: AgentWidgetTheme;
1289
+ /**
1290
+ * Color scheme mode for the widget.
1291
+ * - 'light': Always use light theme (default)
1292
+ * - 'dark': Always use dark theme
1293
+ * - 'auto': Automatically detect from page (HTML class or prefers-color-scheme)
1294
+ *
1295
+ * When 'auto', detection order:
1296
+ * 1. Check if `<html>` has 'dark' class
1297
+ * 2. Fall back to `prefers-color-scheme: dark` media query
1298
+ *
1299
+ * @default 'light'
1300
+ */
1301
+ colorScheme?: 'auto' | 'light' | 'dark';
1302
+ features?: AgentWidgetFeatureFlags;
1303
+ launcher?: AgentWidgetLauncherConfig;
1304
+ initialMessages?: AgentWidgetMessage[];
1305
+ suggestionChips?: string[];
1306
+ suggestionChipsConfig?: AgentWidgetSuggestionChipsConfig;
1307
+ debug?: boolean;
1308
+ formEndpoint?: string;
1309
+ launcherWidth?: string;
1310
+ sendButton?: AgentWidgetSendButtonConfig;
1311
+ statusIndicator?: AgentWidgetStatusIndicatorConfig;
1312
+ voiceRecognition?: AgentWidgetVoiceRecognitionConfig;
1313
+ toolCall?: AgentWidgetToolCallConfig;
1314
+ postprocessMessage?: (context: {
1315
+ text: string;
1316
+ message: AgentWidgetMessage;
1317
+ streaming: boolean;
1318
+ raw?: string;
1319
+ }) => string;
1320
+ plugins?: AgentWidgetPlugin[];
1321
+ contextProviders?: AgentWidgetContextProvider[];
1322
+ requestMiddleware?: AgentWidgetRequestMiddleware;
1323
+ actionParsers?: AgentWidgetActionParser[];
1324
+ actionHandlers?: AgentWidgetActionHandler[];
1325
+ storageAdapter?: AgentWidgetStorageAdapter;
1326
+ /**
1327
+ * Registry of custom components that can be rendered from JSON directives.
1328
+ * Components are registered by name and can be invoked via JSON responses
1329
+ * with the format: `{"component": "ComponentName", "props": {...}}`
1330
+ *
1331
+ * @example
1332
+ * ```typescript
1333
+ * config: {
1334
+ * components: {
1335
+ * ProductCard: (props, context) => {
1336
+ * const card = document.createElement("div");
1337
+ * card.innerHTML = `<h3>${props.title}</h3><p>$${props.price}</p>`;
1338
+ * return card;
1339
+ * }
1340
+ * }
1341
+ * }
1342
+ * ```
1343
+ */
1344
+ components?: Record<string, AgentWidgetComponentRenderer>;
1345
+ /**
1346
+ * Enable component streaming. When true, component props will be updated
1347
+ * incrementally as they stream in from the JSON response.
1348
+ *
1349
+ * @default true
1350
+ */
1351
+ enableComponentStreaming?: boolean;
1352
+ /**
1353
+ * Custom stream parser for extracting text from streaming structured responses.
1354
+ * Handles incremental parsing of JSON, XML, or other formats.
1355
+ * If not provided, uses the default JSON parser.
1356
+ *
1357
+ * @example
1358
+ * ```typescript
1359
+ * streamParser: () => ({
1360
+ * processChunk: async (content) => {
1361
+ * // Return null if not your format, or extracted text if available
1362
+ * if (!content.trim().startsWith('{')) return null;
1363
+ * return extractText(content);
1364
+ * },
1365
+ * getExtractedText: () => extractedText
1366
+ * })
1367
+ * ```
1368
+ */
1369
+ streamParser?: () => AgentWidgetStreamParser;
1370
+ /**
1371
+ * Additional localStorage key to clear when the clear chat button is clicked.
1372
+ * The widget automatically clears `"persona-chat-history"` by default.
1373
+ * Use this option to clear additional keys (e.g., if you're using a custom storage key).
1374
+ *
1375
+ * @example
1376
+ * ```typescript
1377
+ * config: {
1378
+ * clearChatHistoryStorageKey: "my-custom-chat-history"
1379
+ * }
1380
+ * ```
1381
+ */
1382
+ clearChatHistoryStorageKey?: string;
1383
+ /**
1384
+ * Built-in parser type selector. Provides an easy way to choose a parser without importing functions.
1385
+ * If both `parserType` and `streamParser` are provided, `streamParser` takes precedence.
1386
+ *
1387
+ * - `"plain"` - Plain text parser (default). Passes through text as-is.
1388
+ * - `"json"` - JSON parser using partial-json. Extracts `text` field from JSON objects incrementally.
1389
+ * - `"regex-json"` - Regex-based JSON parser. Less robust but faster fallback for simple JSON.
1390
+ * - `"xml"` - XML parser. Extracts text content from XML tags.
1391
+ *
1392
+ * @example
1393
+ * ```typescript
1394
+ * config: {
1395
+ * parserType: "json" // Use built-in JSON parser
1396
+ * }
1397
+ * ```
1398
+ *
1399
+ * @example
1400
+ * ```typescript
1401
+ * config: {
1402
+ * parserType: "json",
1403
+ * streamParser: () => customParser() // Custom parser overrides parserType
1404
+ * }
1405
+ * ```
1406
+ */
1407
+ parserType?: "plain" | "json" | "regex-json" | "xml";
1408
+ /**
1409
+ * Custom fetch function for full control over API requests.
1410
+ * Use this for custom authentication, request/response transformation, etc.
1411
+ *
1412
+ * When provided, this function is called instead of the default fetch.
1413
+ * You receive the URL, RequestInit, and the payload that would be sent.
1414
+ *
1415
+ * @example
1416
+ * ```typescript
1417
+ * config: {
1418
+ * customFetch: async (url, init, payload) => {
1419
+ * // Transform request for your API format
1420
+ * const myPayload = {
1421
+ * flow: { id: 'my-flow-id' },
1422
+ * messages: payload.messages,
1423
+ * options: { stream_response: true }
1424
+ * };
1425
+ *
1426
+ * // Add auth header
1427
+ * const token = await getAuthToken();
1428
+ *
1429
+ * return fetch('/my-api/dispatch', {
1430
+ * method: 'POST',
1431
+ * headers: {
1432
+ * 'Content-Type': 'application/json',
1433
+ * 'Authorization': `Bearer ${token}`
1434
+ * },
1435
+ * body: JSON.stringify(myPayload),
1436
+ * signal: init.signal
1437
+ * });
1438
+ * }
1439
+ * }
1440
+ * ```
1441
+ */
1442
+ customFetch?: AgentWidgetCustomFetch;
1443
+ /**
1444
+ * Custom SSE event parser for non-standard streaming response formats.
1445
+ *
1446
+ * Use this when your API returns SSE events in a different format than expected.
1447
+ * Return `{ text }` for text chunks, `{ done: true }` for completion,
1448
+ * `{ error }` for errors, or `null` to ignore the event.
1449
+ *
1450
+ * @example
1451
+ * ```typescript
1452
+ * // For Travrse API format
1453
+ * config: {
1454
+ * parseSSEEvent: (data) => {
1455
+ * if (data.type === 'step_chunk' && data.chunk) {
1456
+ * return { text: data.chunk };
1457
+ * }
1458
+ * if (data.type === 'flow_complete') {
1459
+ * return { done: true };
1460
+ * }
1461
+ * if (data.type === 'step_error') {
1462
+ * return { error: data.error };
1463
+ * }
1464
+ * return null; // Ignore other events
1465
+ * }
1466
+ * }
1467
+ * ```
1468
+ */
1469
+ parseSSEEvent?: AgentWidgetSSEEventParser;
1470
+ /**
1471
+ * Layout configuration for customizing widget appearance and structure.
1472
+ * Provides control over header, messages, and content slots.
1473
+ *
1474
+ * @example
1475
+ * ```typescript
1476
+ * config: {
1477
+ * layout: {
1478
+ * header: { layout: "minimal" },
1479
+ * messages: { avatar: { show: true } }
1480
+ * }
1481
+ * }
1482
+ * ```
1483
+ */
1484
+ layout?: AgentWidgetLayoutConfig;
1485
+
1486
+ /**
1487
+ * Markdown rendering configuration.
1488
+ * Customize how markdown is parsed and rendered in chat messages.
1489
+ *
1490
+ * Override methods:
1491
+ * 1. **CSS Variables** - Override `--cw-md-*` variables in your stylesheet
1492
+ * 2. **Options** - Configure marked parser behavior
1493
+ * 3. **Renderers** - Custom rendering functions for specific elements
1494
+ * 4. **postprocessMessage** - Complete control over message transformation
1495
+ *
1496
+ * @example
1497
+ * ```typescript
1498
+ * config: {
1499
+ * markdown: {
1500
+ * options: { breaks: true, gfm: true },
1501
+ * renderer: {
1502
+ * link(token) {
1503
+ * return `<a href="${token.href}" target="_blank">${token.text}</a>`;
1504
+ * }
1505
+ * }
1506
+ * }
1507
+ * }
1508
+ * ```
1509
+ */
1510
+ markdown?: AgentWidgetMarkdownConfig;
1511
+
1512
+ /**
1513
+ * Configuration for message action buttons (copy, upvote, downvote).
1514
+ * Shows action buttons on assistant messages for user feedback.
1515
+ *
1516
+ * @example
1517
+ * ```typescript
1518
+ * config: {
1519
+ * messageActions: {
1520
+ * enabled: true,
1521
+ * showCopy: true,
1522
+ * showUpvote: true,
1523
+ * showDownvote: true,
1524
+ * visibility: 'hover',
1525
+ * onFeedback: (feedback) => {
1526
+ * console.log('Feedback:', feedback.type, feedback.messageId);
1527
+ * },
1528
+ * onCopy: (message) => {
1529
+ * console.log('Copied message:', message.id);
1530
+ * }
1531
+ * }
1532
+ * }
1533
+ * ```
1534
+ */
1535
+ messageActions?: AgentWidgetMessageActionsConfig;
1536
+
1537
+ /**
1538
+ * Configuration for file attachments in the composer.
1539
+ * When enabled, users can attach images to their messages.
1540
+ *
1541
+ * @example
1542
+ * ```typescript
1543
+ * config: {
1544
+ * attachments: {
1545
+ * enabled: true,
1546
+ * maxFileSize: 5 * 1024 * 1024, // 5MB
1547
+ * maxFiles: 4
1548
+ * }
1549
+ * }
1550
+ * ```
1551
+ */
1552
+ attachments?: AgentWidgetAttachmentsConfig;
1553
+ };
1554
+
1555
+ export type AgentWidgetMessageRole = "user" | "assistant" | "system";
1556
+
1557
+ export type AgentWidgetReasoning = {
1558
+ id: string;
1559
+ status: "pending" | "streaming" | "complete";
1560
+ chunks: string[];
1561
+ startedAt?: number;
1562
+ completedAt?: number;
1563
+ durationMs?: number;
1564
+ };
1565
+
1566
+ export type AgentWidgetToolCall = {
1567
+ id: string;
1568
+ name?: string;
1569
+ status: "pending" | "running" | "complete";
1570
+ args?: unknown;
1571
+ chunks?: string[];
1572
+ result?: unknown;
1573
+ duration?: number;
1574
+ startedAt?: number;
1575
+ completedAt?: number;
1576
+ durationMs?: number;
1577
+ };
1578
+
1579
+ export type AgentWidgetMessageVariant = "assistant" | "reasoning" | "tool";
1580
+
1581
+ /**
1582
+ * Represents a message in the chat conversation.
1583
+ *
1584
+ * @property id - Unique message identifier
1585
+ * @property role - Message role: "user", "assistant", or "system"
1586
+ * @property content - Message text content (for display)
1587
+ * @property contentParts - Original multi-modal content parts (for API requests)
1588
+ * @property createdAt - ISO timestamp when message was created
1589
+ * @property streaming - Whether message is still streaming (for assistant messages)
1590
+ * @property variant - Message variant for assistant messages: "assistant", "reasoning", or "tool"
1591
+ * @property sequence - Message ordering number
1592
+ * @property reasoning - Reasoning data for assistant reasoning messages
1593
+ * @property toolCall - Tool call data for assistant tool messages
1594
+ * @property tools - Array of tool calls
1595
+ * @property viaVoice - Set to `true` when a user message is sent via voice recognition.
1596
+ * Useful for implementing voice-specific behaviors like auto-reactivation.
1597
+ */
1598
+ export type AgentWidgetMessage = {
1599
+ id: string;
1600
+ role: AgentWidgetMessageRole;
1601
+ content: string;
1602
+ createdAt: string;
1603
+ /**
1604
+ * Original multi-modal content parts for this message.
1605
+ * When present, this is sent to the API instead of `content`.
1606
+ * The `content` field contains the text-only representation for display.
1607
+ */
1608
+ contentParts?: ContentPart[];
1609
+ streaming?: boolean;
1610
+ variant?: AgentWidgetMessageVariant;
1611
+ sequence?: number;
1612
+ reasoning?: AgentWidgetReasoning;
1613
+ toolCall?: AgentWidgetToolCall;
1614
+ tools?: AgentWidgetToolCall[];
1615
+ viaVoice?: boolean;
1616
+ /**
1617
+ * Raw structured payload for this message (e.g., JSON action response).
1618
+ * Populated automatically when structured parsers run.
1619
+ */
1620
+ rawContent?: string;
1621
+ };
1622
+
1623
+ export type AgentWidgetEvent =
1624
+ | { type: "message"; message: AgentWidgetMessage }
1625
+ | { type: "status"; status: "connecting" | "connected" | "error" | "idle" }
1626
+ | { type: "error"; error: Error };
1627
+
1628
+ export type AgentWidgetInitOptions = {
1629
+ target: string | HTMLElement;
1630
+ config?: AgentWidgetConfig;
1631
+ useShadowDom?: boolean;
1632
+ onReady?: () => void;
1633
+ windowKey?: string; // If provided, stores the controller on window[windowKey] for global access
1634
+ debugTools?: boolean;
1635
+ };