@runtypelabs/persona 1.48.0 → 2.1.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 (69) hide show
  1. package/README.md +140 -8
  2. package/dist/index.cjs +90 -39
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +1098 -24
  5. package/dist/index.d.ts +1098 -24
  6. package/dist/index.global.js +134 -83
  7. package/dist/index.global.js.map +1 -1
  8. package/dist/index.js +90 -39
  9. package/dist/index.js.map +1 -1
  10. package/dist/install.global.js +1 -1
  11. package/dist/install.global.js.map +1 -1
  12. package/dist/widget.css +849 -513
  13. package/package.json +1 -1
  14. package/src/artifacts-session.test.ts +80 -0
  15. package/src/client.test.ts +20 -21
  16. package/src/client.ts +153 -4
  17. package/src/components/approval-bubble.ts +45 -42
  18. package/src/components/artifact-card.ts +91 -0
  19. package/src/components/artifact-pane.ts +501 -0
  20. package/src/components/composer-builder.ts +32 -27
  21. package/src/components/event-stream-view.ts +40 -40
  22. package/src/components/feedback.ts +36 -36
  23. package/src/components/forms.ts +11 -11
  24. package/src/components/header-builder.test.ts +32 -0
  25. package/src/components/header-builder.ts +55 -36
  26. package/src/components/header-layouts.ts +58 -125
  27. package/src/components/launcher.ts +36 -21
  28. package/src/components/message-bubble.ts +92 -65
  29. package/src/components/messages.ts +2 -2
  30. package/src/components/panel.ts +42 -11
  31. package/src/components/reasoning-bubble.ts +23 -23
  32. package/src/components/registry.ts +4 -0
  33. package/src/components/suggestions.ts +1 -1
  34. package/src/components/tool-bubble.ts +32 -32
  35. package/src/defaults.ts +30 -4
  36. package/src/index.ts +80 -2
  37. package/src/install.ts +22 -0
  38. package/src/plugins/types.ts +23 -0
  39. package/src/postprocessors.ts +2 -2
  40. package/src/runtime/host-layout.ts +174 -0
  41. package/src/runtime/init.test.ts +236 -0
  42. package/src/runtime/init.ts +114 -55
  43. package/src/session.ts +135 -2
  44. package/src/styles/tailwind.css +1 -1
  45. package/src/styles/widget.css +849 -513
  46. package/src/types/theme.ts +376 -0
  47. package/src/types.ts +338 -15
  48. package/src/ui.docked.test.ts +104 -0
  49. package/src/ui.ts +940 -227
  50. package/src/utils/artifact-gate.test.ts +255 -0
  51. package/src/utils/artifact-gate.ts +142 -0
  52. package/src/utils/artifact-resize.test.ts +64 -0
  53. package/src/utils/artifact-resize.ts +67 -0
  54. package/src/utils/attachment-manager.ts +10 -10
  55. package/src/utils/code-generators.test.ts +52 -0
  56. package/src/utils/code-generators.ts +40 -36
  57. package/src/utils/dock.ts +17 -0
  58. package/src/utils/dom-context.test.ts +504 -0
  59. package/src/utils/dom-context.ts +896 -0
  60. package/src/utils/dom.ts +12 -1
  61. package/src/utils/message-fingerprint.test.ts +187 -0
  62. package/src/utils/message-fingerprint.ts +105 -0
  63. package/src/utils/migration.ts +220 -0
  64. package/src/utils/morph.ts +1 -1
  65. package/src/utils/plugins.ts +175 -0
  66. package/src/utils/positioning.ts +4 -4
  67. package/src/utils/theme.test.ts +157 -0
  68. package/src/utils/theme.ts +224 -60
  69. package/src/utils/tokens.ts +701 -0
package/dist/index.d.cts CHANGED
@@ -45,7 +45,33 @@ interface AgentWidgetPlugin {
45
45
  config: AgentWidgetConfig;
46
46
  defaultRenderer: () => HTMLElement;
47
47
  onSubmit: (text: string) => void;
48
+ /**
49
+ * When true, the assistant stream is active — same moment `session.isStreaming()` becomes true.
50
+ * Prefer wiring controls to `data-persona-composer-disable-when-streaming` plus `setComposerDisabled`
51
+ * in the host, or react to `footer.dataset.personaComposerStreaming === "true"`.
52
+ */
53
+ streaming: boolean;
54
+ /**
55
+ * Legacy alias: host disables the primary submit control while `streaming` is true.
56
+ * @deprecated Use `streaming` for new plugins.
57
+ */
48
58
  disabled: boolean;
59
+ /** Opens the hidden file input when `config.attachments.enabled` is true (no-op otherwise). */
60
+ openAttachmentPicker: () => void;
61
+ /** From `config.composer.models` */
62
+ models?: Array<{
63
+ id: string;
64
+ label: string;
65
+ }>;
66
+ /** From `config.composer.selectedModelId` */
67
+ selectedModelId?: string;
68
+ /** Updates `config.composer.selectedModelId` for the running widget instance. */
69
+ onModelChange?: (modelId: string) => void;
70
+ /**
71
+ * Same behavior as the built-in mic when voice is enabled.
72
+ * Omitted when `config.voiceRecognition.enabled` is not true.
73
+ */
74
+ onVoiceToggle?: () => void;
49
75
  }) => HTMLElement | null;
50
76
  /**
51
77
  * Custom renderer for reasoning bubbles
@@ -189,34 +215,68 @@ type AgentWidgetRequestPayload = {
189
215
  flowId?: string;
190
216
  context?: Record<string, unknown>;
191
217
  metadata?: Record<string, unknown>;
218
+ /** Per-turn template variables for /v1/client/chat (merged as root-level {{var}} in Runtype). */
219
+ inputs?: Record<string, unknown>;
192
220
  };
193
221
  /**
194
222
  * Configuration for agent loop behavior.
195
223
  */
196
224
  type AgentLoopConfig = {
197
- /** Maximum number of reasoning iterations */
198
- maxIterations: number;
199
- /** Stop condition: 'auto' for automatic detection, or a custom JS expression */
200
- stopCondition?: 'auto' | string;
225
+ /** Maximum number of agent turns (1-100). The loop continues while the model calls tools. */
226
+ maxTurns: number;
227
+ /** Maximum cost budget in USD. Agent stops when exceeded. */
228
+ maxCost?: number;
201
229
  /** Enable periodic reflection during execution */
202
230
  enableReflection?: boolean;
203
- /** Number of iterations between reflections */
231
+ /** Number of iterations between reflections (1-50) */
204
232
  reflectionInterval?: number;
205
233
  };
234
+ /**
235
+ * Configuration for agent tools (search, code execution, MCP servers, etc.)
236
+ */
237
+ type AgentToolsConfig = {
238
+ /** Tool IDs to enable (e.g., "builtin:exa", "builtin:dalle", "builtin:openai_web_search") */
239
+ toolIds?: string[];
240
+ /** Per-tool configuration overrides keyed by tool ID */
241
+ toolConfigs?: Record<string, Record<string, unknown>>;
242
+ /** Inline tool definitions for runtime-defined tools */
243
+ runtimeTools?: Array<Record<string, unknown>>;
244
+ /** Custom MCP server connections */
245
+ mcpServers?: Array<Record<string, unknown>>;
246
+ /** Maximum number of tool invocations per execution */
247
+ maxToolCalls?: number;
248
+ /** Tool approval configuration for human-in-the-loop workflows */
249
+ approval?: {
250
+ /** Tool names/patterns to require approval for, or true for all tools */
251
+ require: string[] | boolean;
252
+ /** Approval timeout in milliseconds (default: 300000 / 5 minutes) */
253
+ timeout?: number;
254
+ };
255
+ };
256
+ /** Artifact kinds for the Persona sidebar and dispatch payload */
257
+ type PersonaArtifactKind = "markdown" | "component";
206
258
  /**
207
259
  * Agent configuration for agent execution mode.
208
260
  * When provided in the widget config, enables agent loop execution instead of flow dispatch.
209
261
  */
262
+ type ArtifactConfigPayload = {
263
+ enabled: true;
264
+ types: PersonaArtifactKind[];
265
+ };
210
266
  type AgentConfig = {
211
267
  /** Agent display name */
212
268
  name: string;
213
- /** Model identifier (e.g., 'openai:gpt-4o-mini') */
269
+ /** Model identifier (e.g., 'openai:gpt-4o-mini', 'qwen/qwen3-8b') */
214
270
  model: string;
215
271
  /** System prompt for the agent */
216
272
  systemPrompt: string;
217
273
  /** Temperature for model responses */
218
274
  temperature?: number;
219
- /** Loop configuration for multi-iteration execution */
275
+ /** Tool configuration for the agent */
276
+ tools?: AgentToolsConfig;
277
+ /** Persona artifacts — sibling of tools (virtual agent / API parity) */
278
+ artifacts?: ArtifactConfigPayload;
279
+ /** Loop configuration for multi-turn execution */
220
280
  loopConfig?: AgentLoopConfig;
221
281
  };
222
282
  /**
@@ -251,10 +311,10 @@ type AgentExecutionState = {
251
311
  agentName: string;
252
312
  status: 'running' | 'complete' | 'error';
253
313
  currentIteration: number;
254
- maxIterations: number;
314
+ maxTurns: number;
255
315
  startedAt?: number;
256
316
  completedAt?: number;
257
- stopReason?: 'max_iterations' | 'complete' | 'error' | 'manual';
317
+ stopReason?: 'complete' | 'end_turn' | 'max_turns' | 'max_cost' | 'timeout' | 'error';
258
318
  };
259
319
  /**
260
320
  * Metadata attached to messages created during agent execution.
@@ -449,12 +509,144 @@ type AgentWidgetControllerEventMap = {
449
509
  decision: string;
450
510
  };
451
511
  };
512
+ /**
513
+ * Layout for the artifact split / drawer (CSS lengths unless noted).
514
+ *
515
+ * **Close behavior:** In desktop split mode, the artifact chrome `Close` control uses the same
516
+ * dismiss path as the mobile drawer (`onDismiss` on the artifact pane): the pane is hidden until
517
+ * new artifact content arrives or the host calls `showArtifacts()` on the widget handle.
518
+ */
519
+ type AgentWidgetArtifactsLayoutConfig = {
520
+ /** Flex gap between chat column and artifact pane. @default 0.5rem */
521
+ splitGap?: string;
522
+ /** Artifact column width in split mode. @default 40% */
523
+ paneWidth?: string;
524
+ /** Max width of artifact column. @default 28rem */
525
+ paneMaxWidth?: string;
526
+ /** Min width of artifact column (optional). */
527
+ paneMinWidth?: string;
528
+ /**
529
+ * When the floating panel is at most this wide (px), use in-panel drawer for artifacts
530
+ * instead of a side-by-side split (viewport can still be wide).
531
+ * @default 520
532
+ */
533
+ narrowHostMaxWidth?: number;
534
+ /**
535
+ * When true (default), widen the launcher panel while artifacts are visible and not user-dismissed.
536
+ * No-op for inline embed (`launcher.enabled === false`).
537
+ */
538
+ expandLauncherPanelWhenOpen?: boolean;
539
+ /** Panel width when expanded (launcher + artifacts visible). @default min(720px, calc(100vw - 24px)) */
540
+ expandedPanelWidth?: string;
541
+ /**
542
+ * When true, shows a drag handle between chat and artifact columns in desktop split mode only
543
+ * (hidden in narrow-host drawer and viewport ≤640px). Width is not persisted across reloads.
544
+ */
545
+ resizable?: boolean;
546
+ /** Min artifact column width while resizing. Only `px` strings are supported. @default 200px */
547
+ resizableMinWidth?: string;
548
+ /** Optional max artifact width cap while resizing (`px` only). Layout still bounds by chat min width. */
549
+ resizableMaxWidth?: string;
550
+ /**
551
+ * Visual treatment for the artifact column in split mode.
552
+ * - `'panel'` — bordered sidebar with left border, gap, and shadow (default).
553
+ * - `'seamless'` — flush with chat: no border or shadow, container background, zero gap.
554
+ * @default 'panel'
555
+ */
556
+ paneAppearance?: "panel" | "seamless";
557
+ /** Border radius on the artifact pane (CSS length). Works with any `paneAppearance`. */
558
+ paneBorderRadius?: string;
559
+ /** CSS `box-shadow` on the artifact pane. Set `"none"` to suppress the default shadow. */
560
+ paneShadow?: string;
561
+ /**
562
+ * Full `border` shorthand for the artifact `<aside>` (all sides). Overrides default pane borders.
563
+ * Example: `"1px solid #cccccc"`.
564
+ */
565
+ paneBorder?: string;
566
+ /**
567
+ * `border-left` shorthand only — typical for split view next to chat (with or without resizer).
568
+ * Ignored if `paneBorder` is set. Example: `"1px solid #cccccc"`.
569
+ */
570
+ paneBorderLeft?: string;
571
+ /**
572
+ * Desktop split only (not narrow-host drawer / not ≤640px): square the **main chat card’s**
573
+ * top-right and bottom-right radii, and round the **artifact pane’s** top-right and bottom-right
574
+ * to match `persona-rounded-2xl` (`--persona-radius-lg`) so the two columns read as one shell.
575
+ */
576
+ unifiedSplitChrome?: boolean;
577
+ /**
578
+ * When `unifiedSplitChrome` is true, outer-right corner radius on the artifact column (CSS length).
579
+ * @default matches theme large radius (`--persona-radius-lg`)
580
+ */
581
+ unifiedSplitOuterRadius?: string;
582
+ /**
583
+ * Background color for the artifact column (CSS color). Sets `--persona-artifact-pane-bg` on the widget root.
584
+ */
585
+ paneBackground?: string;
586
+ /**
587
+ * Horizontal padding for artifact toolbar and content (CSS length), e.g. `24px`.
588
+ */
589
+ panePadding?: string;
590
+ /**
591
+ * Toolbar layout preset.
592
+ * - `default` — "Artifacts" title, horizontal tabs, text close.
593
+ * - `document` — view/source toggle, document title, copy / refresh / close; tab strip hidden when only one artifact.
594
+ * @default 'default'
595
+ */
596
+ toolbarPreset?: "default" | "document";
597
+ /**
598
+ * When `toolbarPreset` is `document`, show a visible "Copy" label next to the copy icon.
599
+ */
600
+ documentToolbarShowCopyLabel?: boolean;
601
+ /**
602
+ * When `toolbarPreset` is `document`, show a small chevron after the copy control (e.g. menu affordance).
603
+ */
604
+ documentToolbarShowCopyChevron?: boolean;
605
+ /** Document toolbar icon buttons (view, code, copy, refresh, close) — CSS color. Sets `--persona-artifact-doc-toolbar-icon-color` on the widget root. */
606
+ documentToolbarIconColor?: string;
607
+ /** Active view/source toggle background. Sets `--persona-artifact-doc-toggle-active-bg`. */
608
+ documentToolbarToggleActiveBackground?: string;
609
+ /** Active view/source toggle border color. Sets `--persona-artifact-doc-toggle-active-border`. */
610
+ documentToolbarToggleActiveBorderColor?: string;
611
+ /**
612
+ * Invoked when the document toolbar Refresh control is used (before the pane re-renders).
613
+ * Use to replay `connectStream`, refetch, etc.
614
+ */
615
+ onDocumentToolbarRefresh?: () => void | Promise<void>;
616
+ /**
617
+ * Optional copy dropdown entries (shown when `documentToolbarShowCopyChevron` is true and this array is non-empty).
618
+ * The main Copy control still performs default copy unless `onDocumentToolbarCopyMenuSelect` handles everything.
619
+ */
620
+ documentToolbarCopyMenuItems?: Array<{
621
+ id: string;
622
+ label: string;
623
+ }>;
624
+ /**
625
+ * When set, invoked for the chevron menu (and can override default copy per `actionId`).
626
+ */
627
+ onDocumentToolbarCopyMenuSelect?: (payload: {
628
+ actionId: string;
629
+ artifactId: string | null;
630
+ markdown: string;
631
+ jsonPayload: string;
632
+ }) => void | Promise<void>;
633
+ };
634
+ type AgentWidgetArtifactsFeature = {
635
+ /** When true, Persona shows the artifact pane and handles artifact_* SSE events */
636
+ enabled?: boolean;
637
+ /** If set, artifact events for other types are ignored */
638
+ allowedTypes?: PersonaArtifactKind[];
639
+ /** Split / drawer dimensions and launcher widen behavior */
640
+ layout?: AgentWidgetArtifactsLayoutConfig;
641
+ };
452
642
  type AgentWidgetFeatureFlags = {
453
643
  showReasoning?: boolean;
454
644
  showToolCalls?: boolean;
455
645
  showEventStreamToggle?: boolean;
456
646
  /** Configuration for the Event Stream inspector view */
457
647
  eventStream?: EventStreamConfig;
648
+ /** Optional artifact sidebar (split pane / mobile drawer) */
649
+ artifacts?: AgentWidgetArtifactsFeature;
458
650
  };
459
651
  type SSEEventRecord = {
460
652
  id: string;
@@ -607,7 +799,7 @@ type AgentWidgetTheme = {
607
799
  /**
608
800
  * Border style for the chat panel container.
609
801
  * @example "1px solid #e5e7eb" | "none"
610
- * @default "1px solid var(--tvw-cw-border)"
802
+ * @default "1px solid var(--persona-border)"
611
803
  */
612
804
  panelBorder?: string;
613
805
  /**
@@ -622,6 +814,45 @@ type AgentWidgetTheme = {
622
814
  * @default "16px"
623
815
  */
624
816
  panelBorderRadius?: string;
817
+ /**
818
+ * Box-shadow for user message bubbles (bubble message layout).
819
+ * @example "none" | "0 1px 2px rgba(0,0,0,0.05)"
820
+ */
821
+ messageUserShadow?: string;
822
+ /**
823
+ * Box-shadow for assistant message bubbles (bubble message layout).
824
+ * Overrides the default subtle assistant shadow when set.
825
+ */
826
+ messageAssistantShadow?: string;
827
+ /**
828
+ * Box-shadow for tool-call / function-call rows.
829
+ */
830
+ toolBubbleShadow?: string;
831
+ /**
832
+ * Box-shadow for reasoning (“thinking”) rows.
833
+ */
834
+ reasoningBubbleShadow?: string;
835
+ /**
836
+ * Box-shadow on the composer (input) container.
837
+ */
838
+ composerShadow?: string;
839
+ };
840
+ type AgentWidgetDockConfig = {
841
+ /**
842
+ * Side of the wrapped container where the docked panel should render.
843
+ * @default "right"
844
+ */
845
+ side?: "left" | "right";
846
+ /**
847
+ * Expanded width of the docked panel.
848
+ * @default "420px"
849
+ */
850
+ width?: string;
851
+ /**
852
+ * Width of the collapsed launcher rail when the docked panel is closed.
853
+ * @default "72px"
854
+ */
855
+ collapsedWidth?: string;
625
856
  };
626
857
  type AgentWidgetLauncherConfig = {
627
858
  enabled?: boolean;
@@ -633,6 +864,18 @@ type AgentWidgetLauncherConfig = {
633
864
  agentIconName?: string;
634
865
  agentIconHidden?: boolean;
635
866
  position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
867
+ /**
868
+ * Controls how the launcher panel is mounted relative to the host page.
869
+ * - "floating": default floating launcher / panel behavior
870
+ * - "docked": wraps the target container and renders as a sibling dock
871
+ *
872
+ * @default "floating"
873
+ */
874
+ mountMode?: "floating" | "docked";
875
+ /**
876
+ * Layout configuration for docked mode.
877
+ */
878
+ dock?: AgentWidgetDockConfig;
636
879
  autoExpand?: boolean;
637
880
  width?: string;
638
881
  /**
@@ -672,6 +915,21 @@ type AgentWidgetLauncherConfig = {
672
915
  * @default 0
673
916
  */
674
917
  heightOffset?: number;
918
+ /**
919
+ * When true, the widget panel expands to fill the full viewport on mobile devices.
920
+ * Removes border-radius, margins, and shadows for a native app-like experience.
921
+ * Applies when viewport width is at or below `mobileBreakpoint`.
922
+ *
923
+ * @default true
924
+ */
925
+ mobileFullscreen?: boolean;
926
+ /**
927
+ * Viewport width (in pixels) at or below which the widget enters mobile fullscreen mode.
928
+ * Only applies when `mobileFullscreen` is true.
929
+ *
930
+ * @default 640
931
+ */
932
+ mobileBreakpoint?: number;
675
933
  callToActionIconText?: string;
676
934
  callToActionIconName?: string;
677
935
  callToActionIconColor?: string;
@@ -724,6 +982,15 @@ type AgentWidgetSendButtonConfig = {
724
982
  textColor?: string;
725
983
  size?: string;
726
984
  };
985
+ /** Optional composer UI state for custom `renderComposer` implementations. */
986
+ type AgentWidgetComposerConfig = {
987
+ models?: Array<{
988
+ id: string;
989
+ label: string;
990
+ }>;
991
+ /** Current selection; host or plugin may update this at runtime. */
992
+ selectedModelId?: string;
993
+ };
727
994
  type AgentWidgetClearChatConfig = {
728
995
  enabled?: boolean;
729
996
  placement?: "inline" | "top-right";
@@ -963,6 +1230,8 @@ type AgentWidgetApprovalConfig = {
963
1230
  }, decision: 'approved' | 'denied') => Promise<Response | ReadableStream<Uint8Array> | void>;
964
1231
  };
965
1232
  type AgentWidgetToolCallConfig = {
1233
+ /** Box-shadow for tool-call bubbles; overrides `theme.toolBubbleShadow` when set. */
1234
+ shadow?: string;
966
1235
  backgroundColor?: string;
967
1236
  borderColor?: string;
968
1237
  borderWidth?: string;
@@ -1123,6 +1392,8 @@ type ClientChatRequest = {
1123
1392
  /** ID for the expected assistant response message */
1124
1393
  assistantMessageId?: string;
1125
1394
  metadata?: Record<string, unknown>;
1395
+ /** Per-turn inputs for Runtype prompt templates (e.g. {{page_url}}). */
1396
+ inputs?: Record<string, unknown>;
1126
1397
  context?: Record<string, unknown>;
1127
1398
  };
1128
1399
  /**
@@ -1142,6 +1413,14 @@ type ClientFeedbackRequest = {
1142
1413
  /** Optional comment for any feedback type */
1143
1414
  comment?: string;
1144
1415
  };
1416
+ /** Icon button in the header title row (minimal layout). */
1417
+ type AgentWidgetHeaderTrailingAction = {
1418
+ id: string;
1419
+ /** Lucide icon name, e.g. `chevron-down` */
1420
+ icon?: string;
1421
+ label?: string;
1422
+ ariaLabel?: string;
1423
+ };
1145
1424
  /**
1146
1425
  * Context provided to header render functions
1147
1426
  */
@@ -1149,6 +1428,10 @@ type HeaderRenderContext = {
1149
1428
  config: AgentWidgetConfig;
1150
1429
  onClose?: () => void;
1151
1430
  onClearChat?: () => void;
1431
+ /** Built from `layout.header.trailingActions` for custom `render` implementations. */
1432
+ trailingActions?: AgentWidgetHeaderTrailingAction[];
1433
+ /** Fired when a built-in trailing action is activated (same as `layout.header.onAction`). */
1434
+ onAction?: (actionId: string) => void;
1152
1435
  };
1153
1436
  /**
1154
1437
  * Context provided to message render functions
@@ -1171,12 +1454,11 @@ type SlotRenderContext = {
1171
1454
  */
1172
1455
  type AgentWidgetHeaderLayoutConfig = {
1173
1456
  /**
1174
- * Layout preset: "default" | "minimal" | "expanded"
1457
+ * Layout preset: "default" | "minimal"
1175
1458
  * - default: Standard layout with icon, title, subtitle, and buttons
1176
1459
  * - minimal: Simplified layout with just title and close button
1177
- * - expanded: Full branding area with additional content space
1178
1460
  */
1179
- layout?: "default" | "minimal" | "expanded";
1461
+ layout?: "default" | "minimal";
1180
1462
  /** Show/hide the header icon */
1181
1463
  showIcon?: boolean;
1182
1464
  /** Show/hide the title */
@@ -1192,6 +1474,12 @@ type AgentWidgetHeaderLayoutConfig = {
1192
1474
  * When provided, replaces the entire header with custom content
1193
1475
  */
1194
1476
  render?: (context: HeaderRenderContext) => HTMLElement;
1477
+ /**
1478
+ * Shown after the title in `minimal` header layout (e.g. chevron menu affordance).
1479
+ */
1480
+ trailingActions?: AgentWidgetHeaderTrailingAction[];
1481
+ /** Called when a `trailingActions` button is clicked. */
1482
+ onAction?: (actionId: string) => void;
1195
1483
  };
1196
1484
  /**
1197
1485
  * Avatar configuration for message bubbles
@@ -1299,6 +1587,12 @@ type AgentWidgetLayoutConfig = {
1299
1587
  * @default true
1300
1588
  */
1301
1589
  showFooter?: boolean;
1590
+ /**
1591
+ * Max width for the content area (messages + composer).
1592
+ * Applied with `margin: 0 auto` for centering.
1593
+ * Accepts any CSS width value (e.g. "90ch", "720px", "80%").
1594
+ */
1595
+ contentMaxWidth?: string;
1302
1596
  };
1303
1597
  /**
1304
1598
  * Token types for marked renderer methods
@@ -1827,7 +2121,7 @@ type AgentWidgetConfig = {
1827
2121
  * name: 'Assistant',
1828
2122
  * model: 'openai:gpt-4o-mini',
1829
2123
  * systemPrompt: 'You are a helpful assistant.',
1830
- * loopConfig: { maxIterations: 3, stopCondition: 'auto' }
2124
+ * loopConfig: { maxTurns: 5 }
1831
2125
  * }
1832
2126
  * }
1833
2127
  * ```
@@ -1943,6 +2237,11 @@ type AgentWidgetConfig = {
1943
2237
  welcomeSubtitle?: string;
1944
2238
  inputPlaceholder?: string;
1945
2239
  sendButtonLabel?: string;
2240
+ /**
2241
+ * When false, the welcome / intro card is not shown above the message list.
2242
+ * @default true
2243
+ */
2244
+ showWelcomeCard?: boolean;
1946
2245
  };
1947
2246
  theme?: AgentWidgetTheme;
1948
2247
  /**
@@ -2094,6 +2393,12 @@ type AgentWidgetConfig = {
2094
2393
  * @default true
2095
2394
  */
2096
2395
  enableComponentStreaming?: boolean;
2396
+ /**
2397
+ * When false, JSON component directives render without the default bubble chrome
2398
+ * (surface background, border, extra padding). Use for wide custom cards in the transcript.
2399
+ * @default true
2400
+ */
2401
+ wrapComponentDirectiveInBubble?: boolean;
2097
2402
  /**
2098
2403
  * Custom stream parser for extracting text from streaming structured responses.
2099
2404
  * Handles incremental parsing of JSON, XML, or other formats.
@@ -2292,6 +2597,11 @@ type AgentWidgetConfig = {
2292
2597
  * ```
2293
2598
  */
2294
2599
  attachments?: AgentWidgetAttachmentsConfig;
2600
+ /**
2601
+ * Composer extras for custom `renderComposer` plugins (model picker, etc.).
2602
+ * `selectedModelId` may be updated at runtime by the host.
2603
+ */
2604
+ composer?: AgentWidgetComposerConfig;
2295
2605
  /**
2296
2606
  * Persist widget state (open/closed, voice mode) across page navigations.
2297
2607
  * When `true`, uses default settings with sessionStorage.
@@ -2545,6 +2855,28 @@ type InjectUserMessageOptions = Omit<InjectMessageOptions, "role">;
2545
2855
  * Role defaults to 'system'.
2546
2856
  */
2547
2857
  type InjectSystemMessageOptions = Omit<InjectMessageOptions, "role">;
2858
+ type PersonaArtifactRecord = {
2859
+ id: string;
2860
+ artifactType: PersonaArtifactKind;
2861
+ title?: string;
2862
+ status: "streaming" | "complete";
2863
+ markdown?: string;
2864
+ component?: string;
2865
+ props?: Record<string, unknown>;
2866
+ };
2867
+ /** Programmatic artifact upsert (controller / window API) */
2868
+ type PersonaArtifactManualUpsert = {
2869
+ id?: string;
2870
+ artifactType: "markdown";
2871
+ title?: string;
2872
+ content: string;
2873
+ } | {
2874
+ id?: string;
2875
+ artifactType: "component";
2876
+ title?: string;
2877
+ component: string;
2878
+ props?: Record<string, unknown>;
2879
+ };
2548
2880
  type AgentWidgetEvent = {
2549
2881
  type: "message";
2550
2882
  message: AgentWidgetMessage;
@@ -2554,6 +2886,24 @@ type AgentWidgetEvent = {
2554
2886
  } | {
2555
2887
  type: "error";
2556
2888
  error: Error;
2889
+ } | {
2890
+ type: "artifact_start";
2891
+ id: string;
2892
+ artifactType: PersonaArtifactKind;
2893
+ title?: string;
2894
+ component?: string;
2895
+ } | {
2896
+ type: "artifact_delta";
2897
+ id: string;
2898
+ artDelta: string;
2899
+ } | {
2900
+ type: "artifact_update";
2901
+ id: string;
2902
+ props: Record<string, unknown>;
2903
+ component?: string;
2904
+ } | {
2905
+ type: "artifact_complete";
2906
+ id: string;
2557
2907
  };
2558
2908
  type AgentWidgetInitOptions = {
2559
2909
  target: string | HTMLElement;
@@ -2731,6 +3081,10 @@ type SessionCallbacks = {
2731
3081
  onStreamingChanged: (streaming: boolean) => void;
2732
3082
  onError?: (error: Error) => void;
2733
3083
  onVoiceStatusChanged?: (status: VoiceStatus) => void;
3084
+ onArtifactsState?: (state: {
3085
+ artifacts: PersonaArtifactRecord[];
3086
+ selectedId: string | null;
3087
+ }) => void;
2734
3088
  };
2735
3089
  declare class AgentWidgetSession {
2736
3090
  private config;
@@ -2743,6 +3097,8 @@ declare class AgentWidgetSession {
2743
3097
  private sequenceCounter;
2744
3098
  private clientSession;
2745
3099
  private agentExecution;
3100
+ private artifacts;
3101
+ private selectedArtifactId;
2746
3102
  private voiceProvider;
2747
3103
  private voiceActive;
2748
3104
  private voiceStatus;
@@ -2972,6 +3328,15 @@ declare class AgentWidgetSession {
2972
3328
  resolveApproval(approval: AgentWidgetApproval, decision: 'approved' | 'denied'): Promise<void>;
2973
3329
  cancel(): void;
2974
3330
  clearMessages(): void;
3331
+ getArtifacts(): PersonaArtifactRecord[];
3332
+ getArtifactById(id: string): PersonaArtifactRecord | undefined;
3333
+ getSelectedArtifactId(): string | null;
3334
+ selectArtifact(id: string | null): void;
3335
+ clearArtifacts(): void;
3336
+ upsertArtifact(manual: PersonaArtifactManualUpsert): PersonaArtifactRecord;
3337
+ private clearArtifactState;
3338
+ private emitArtifactsState;
3339
+ private applyArtifactStreamEvent;
2975
3340
  hydrateMessages(messages: AgentWidgetMessage[]): void;
2976
3341
  private handleEvent;
2977
3342
  private setStatus;
@@ -3126,6 +3491,14 @@ type Controller = {
3126
3491
  hideEventStream: () => void;
3127
3492
  /** Returns current visibility state of the event stream panel */
3128
3493
  isEventStreamVisible: () => boolean;
3494
+ /** Show artifact sidebar (no-op if features.artifacts.enabled is false) */
3495
+ showArtifacts: () => void;
3496
+ /** Hide artifact sidebar */
3497
+ hideArtifacts: () => void;
3498
+ /** Upsert an artifact programmatically */
3499
+ upsertArtifact: (manual: PersonaArtifactManualUpsert) => PersonaArtifactRecord | null;
3500
+ selectArtifact: (id: string) => void;
3501
+ clearArtifacts: () => void;
3129
3502
  /**
3130
3503
  * Focus the chat input. Returns true if focus succeeded, false if panel is closed
3131
3504
  * (launcher mode) or textarea is unavailable.
@@ -3148,6 +3521,17 @@ type AgentWidgetInitHandle = AgentWidgetController & {
3148
3521
  };
3149
3522
  declare const initAgentWidget: (options: AgentWidgetInitOptions) => AgentWidgetInitHandle;
3150
3523
 
3524
+ type WidgetHostLayoutMode = "direct" | "docked";
3525
+ type WidgetHostLayout = {
3526
+ mode: WidgetHostLayoutMode;
3527
+ host: HTMLElement;
3528
+ shell: HTMLElement | null;
3529
+ syncWidgetState: (state: Pick<AgentWidgetStateSnapshot, "open" | "launcherEnabled">) => void;
3530
+ updateConfig: (config?: AgentWidgetConfig) => void;
3531
+ destroy: () => void;
3532
+ };
3533
+ declare const createWidgetHostLayout: (target: HTMLElement, config?: AgentWidgetConfig) => WidgetHostLayout;
3534
+
3151
3535
  declare const createLocalStorageAdapter: (key?: string) => AgentWidgetStorageAdapter;
3152
3536
 
3153
3537
  type ActionManagerProcessContext = {
@@ -3339,6 +3723,142 @@ declare function validateImageFile(file: File, acceptedTypes?: string[], maxSize
3339
3723
  error?: string;
3340
3724
  };
3341
3725
 
3726
+ /**
3727
+ * Enriched DOM context collection for providing richer page information to AI.
3728
+ *
3729
+ * Captures interactive elements, stable CSS selectors, ARIA roles, data attributes,
3730
+ * and visibility state — giving the LLM much better context than basic className/innerText.
3731
+ *
3732
+ * ## Modes
3733
+ *
3734
+ * - **structured** (default): collects candidates, scores them with optional {@link ParseRule}
3735
+ * hooks, then applies `maxElements`. Rich containers (e.g. product cards) can surface
3736
+ * before unrelated static noise.
3737
+ * - **simple**: legacy behavior — cap during traversal, interactive-first ordering, no rule
3738
+ * scoring or {@link EnrichedPageElement.formattedSummary}.
3739
+ */
3740
+ interface EnrichedPageElement {
3741
+ /** Stable CSS selector the LLM can use directly */
3742
+ selector: string;
3743
+ /** Lowercase tag name */
3744
+ tagName: string;
3745
+ /** Visible text content, trimmed */
3746
+ text: string;
3747
+ /** ARIA role or null */
3748
+ role: string | null;
3749
+ /** Interactivity classification */
3750
+ interactivity: "clickable" | "input" | "navigable" | "static";
3751
+ /** Relevant attributes: id, data-*, href, aria-label, type, value, name */
3752
+ attributes: Record<string, string>;
3753
+ /**
3754
+ * When set (structured mode + matching rule), {@link formatEnrichedContext} prefers this
3755
+ * markdown-like line instead of raw `text`.
3756
+ */
3757
+ formattedSummary?: string;
3758
+ }
3759
+ /** How DOM context is collected and formatted. */
3760
+ type DomContextMode = "simple" | "structured";
3761
+ /**
3762
+ * Options that control collection limits, visibility, and mode.
3763
+ * Prefer nesting these under {@link DomContextOptions.options}; top-level fields remain
3764
+ * supported for backward compatibility.
3765
+ */
3766
+ interface ParseOptionsConfig {
3767
+ /**
3768
+ * `structured` (default): score candidates with rules, then apply `maxElements`.
3769
+ * `simple`: legacy traversal cap and ordering only — rules are ignored (with a warning
3770
+ * if `rules` was passed on {@link DomContextOptions}).
3771
+ */
3772
+ mode?: DomContextMode;
3773
+ /** Maximum number of elements to return. Default: 80 */
3774
+ maxElements?: number;
3775
+ /** CSS selector for elements to exclude (e.g. the widget host). Default: '.persona-host' */
3776
+ excludeSelector?: string;
3777
+ /** Maximum text length per element. Default: 200 */
3778
+ maxTextLength?: number;
3779
+ /** Only include visible elements. Default: true */
3780
+ visibleOnly?: boolean;
3781
+ /** Root element to walk. Default: document.body */
3782
+ root?: HTMLElement;
3783
+ /**
3784
+ * Maximum candidates gathered before scoring (structured mode only).
3785
+ * Default: `max(500, maxElements * 10)`.
3786
+ */
3787
+ maxCandidates?: number;
3788
+ }
3789
+ interface RuleScoringContext {
3790
+ doc: Document;
3791
+ maxTextLength: number;
3792
+ }
3793
+ /**
3794
+ * Extensible rule for structured DOM context: scoring, descendant suppression, and
3795
+ * optional formatted output.
3796
+ */
3797
+ interface ParseRule {
3798
+ /** Stable id for debugging and tests */
3799
+ id: string;
3800
+ /**
3801
+ * Score bonus when this rule applies to the element (0 when it does not).
3802
+ * Higher scores are kept first when applying `maxElements`.
3803
+ */
3804
+ scoreElement(el: HTMLElement, enriched: EnrichedPageElement, ctx: RuleScoringContext): number;
3805
+ /**
3806
+ * When `owner` is kept in the final set and matched this rule for formatting,
3807
+ * return true to drop `descendant` (redundant price text, CTAs summarized on the card, etc.).
3808
+ */
3809
+ shouldSuppressDescendant?(owner: HTMLElement, descendant: HTMLElement, descendantEnriched: EnrichedPageElement): boolean;
3810
+ /**
3811
+ * Markdown-like summary for the LLM. Only used when `scoreElement` &gt; 0 for this rule.
3812
+ */
3813
+ formatSummary?(el: HTMLElement, enriched: EnrichedPageElement, ctx: RuleScoringContext): string | null;
3814
+ }
3815
+ interface DomContextOptions {
3816
+ /** Nested parse options (mode, limits, root). Merged with legacy top-level fields. */
3817
+ options?: ParseOptionsConfig;
3818
+ /** Custom rules for structured mode. Default: {@link defaultParseRules} */
3819
+ rules?: ParseRule[];
3820
+ /** @inheritdoc ParseOptionsConfig.maxElements */
3821
+ maxElements?: number;
3822
+ /** @inheritdoc ParseOptionsConfig.excludeSelector */
3823
+ excludeSelector?: string;
3824
+ /** @inheritdoc ParseOptionsConfig.maxTextLength */
3825
+ maxTextLength?: number;
3826
+ /** @inheritdoc ParseOptionsConfig.visibleOnly */
3827
+ visibleOnly?: boolean;
3828
+ /** @inheritdoc ParseOptionsConfig.root */
3829
+ root?: HTMLElement;
3830
+ }
3831
+ interface FormatEnrichedContextOptions {
3832
+ /** When `simple`, ignore {@link EnrichedPageElement.formattedSummary}. Default: structured */
3833
+ mode?: DomContextMode;
3834
+ }
3835
+ /** Default structured rules: commerce-style cards and generic search/result rows. */
3836
+ declare const defaultParseRules: ParseRule[];
3837
+ /**
3838
+ * Generate a stable, unique CSS selector for an element.
3839
+ * Priority: #id → [data-testid]/[data-product] → tag.classes with :nth-of-type()
3840
+ */
3841
+ declare function generateStableSelector(el: HTMLElement): string;
3842
+ /**
3843
+ * Collect enriched page context from the DOM.
3844
+ *
3845
+ * - **Default (structured):** walks up to `maxCandidates` nodes, scores with
3846
+ * {@link defaultParseRules} (or `rules`), suppresses redundant descendants when a
3847
+ * formatting rule matches, then keeps the top `maxElements` by score (DOM order tie-break).
3848
+ * - **simple:** legacy path — stops once `maxElements` nodes are collected during traversal
3849
+ * and sorts interactive before static.
3850
+ *
3851
+ * Pass `options: { mode: "simple" }` to disable rules. If `mode` is `simple` and `rules` is
3852
+ * non-empty, rules are ignored and a console warning is emitted.
3853
+ */
3854
+ declare function collectEnrichedPageContext(options?: DomContextOptions): EnrichedPageElement[];
3855
+ /**
3856
+ * Format enriched page elements as a structured string for LLM consumption.
3857
+ * When `mode` is structured (default) and elements include {@link EnrichedPageElement.formattedSummary},
3858
+ * those render under **Structured summaries** before the usual interactivity groups.
3859
+ */
3860
+ declare function formatEnrichedContext(elements: EnrichedPageElement[], options?: FormatEnrichedContextOptions): string;
3861
+
3342
3862
  /**
3343
3863
  * Attachment Manager
3344
3864
  *
@@ -3447,6 +3967,9 @@ declare function generateUserMessageId(): string;
3447
3967
  */
3448
3968
  declare function generateAssistantMessageId(): string;
3449
3969
 
3970
+ declare const isDockedMountMode: (config?: AgentWidgetConfig) => boolean;
3971
+ declare const resolveDockConfig: (config?: AgentWidgetConfig) => Required<AgentWidgetDockConfig>;
3972
+
3450
3973
  type CodeFormat = "esm" | "script-installer" | "script-manual" | "script-advanced" | "react-component" | "react-advanced";
3451
3974
  /**
3452
3975
  * Hook code templates for code generation.
@@ -3615,6 +4138,557 @@ declare class PluginRegistry {
3615
4138
  }
3616
4139
  declare const pluginRegistry: PluginRegistry;
3617
4140
 
4141
+ type TokenType = 'color' | 'spacing' | 'typography' | 'shadow' | 'border' | 'radius';
4142
+ type TokenReference<_T extends TokenType = TokenType> = string;
4143
+ interface ColorShade {
4144
+ 50?: string;
4145
+ 100?: string;
4146
+ 200?: string;
4147
+ 300?: string;
4148
+ 400?: string;
4149
+ 500?: string;
4150
+ 600?: string;
4151
+ 700?: string;
4152
+ 800?: string;
4153
+ 900?: string;
4154
+ 950?: string;
4155
+ [key: string]: string | undefined;
4156
+ }
4157
+ interface ColorPalette {
4158
+ gray: ColorShade;
4159
+ primary: ColorShade;
4160
+ secondary: ColorShade;
4161
+ accent: ColorShade;
4162
+ success: ColorShade;
4163
+ warning: ColorShade;
4164
+ error: ColorShade;
4165
+ [key: string]: ColorShade;
4166
+ }
4167
+ interface SpacingScale {
4168
+ 0: string;
4169
+ 1: string;
4170
+ 2: string;
4171
+ 3: string;
4172
+ 4: string;
4173
+ 5: string;
4174
+ 6: string;
4175
+ 8: string;
4176
+ 10: string;
4177
+ 12: string;
4178
+ 16: string;
4179
+ 20: string;
4180
+ 24: string;
4181
+ 32: string;
4182
+ 40: string;
4183
+ 48: string;
4184
+ 56: string;
4185
+ 64: string;
4186
+ [key: string]: string;
4187
+ }
4188
+ interface ShadowScale {
4189
+ none: string;
4190
+ sm: string;
4191
+ md: string;
4192
+ lg: string;
4193
+ xl: string;
4194
+ '2xl': string;
4195
+ [key: string]: string;
4196
+ }
4197
+ interface BorderScale {
4198
+ none: string;
4199
+ sm: string;
4200
+ md: string;
4201
+ lg: string;
4202
+ [key: string]: string;
4203
+ }
4204
+ interface RadiusScale {
4205
+ none: string;
4206
+ sm: string;
4207
+ md: string;
4208
+ lg: string;
4209
+ xl: string;
4210
+ full: string;
4211
+ [key: string]: string;
4212
+ }
4213
+ interface TypographyScale {
4214
+ fontFamily: {
4215
+ sans: string;
4216
+ serif: string;
4217
+ mono: string;
4218
+ };
4219
+ fontSize: {
4220
+ xs: string;
4221
+ sm: string;
4222
+ base: string;
4223
+ lg: string;
4224
+ xl: string;
4225
+ '2xl': string;
4226
+ '3xl': string;
4227
+ '4xl': string;
4228
+ };
4229
+ fontWeight: {
4230
+ normal: string;
4231
+ medium: string;
4232
+ semibold: string;
4233
+ bold: string;
4234
+ };
4235
+ lineHeight: {
4236
+ tight: string;
4237
+ normal: string;
4238
+ relaxed: string;
4239
+ };
4240
+ }
4241
+ interface SemanticColors {
4242
+ primary: TokenReference<'color'>;
4243
+ secondary: TokenReference<'color'>;
4244
+ accent: TokenReference<'color'>;
4245
+ surface: TokenReference<'color'>;
4246
+ background: TokenReference<'color'>;
4247
+ container: TokenReference<'color'>;
4248
+ text: TokenReference<'color'>;
4249
+ textMuted: TokenReference<'color'>;
4250
+ textInverse: TokenReference<'color'>;
4251
+ border: TokenReference<'color'>;
4252
+ divider: TokenReference<'color'>;
4253
+ interactive: {
4254
+ default: TokenReference<'color'>;
4255
+ hover: TokenReference<'color'>;
4256
+ focus: TokenReference<'color'>;
4257
+ active: TokenReference<'color'>;
4258
+ disabled: TokenReference<'color'>;
4259
+ };
4260
+ feedback: {
4261
+ success: TokenReference<'color'>;
4262
+ warning: TokenReference<'color'>;
4263
+ error: TokenReference<'color'>;
4264
+ info: TokenReference<'color'>;
4265
+ };
4266
+ }
4267
+ interface SemanticSpacing {
4268
+ xs: TokenReference<'spacing'>;
4269
+ sm: TokenReference<'spacing'>;
4270
+ md: TokenReference<'spacing'>;
4271
+ lg: TokenReference<'spacing'>;
4272
+ xl: TokenReference<'spacing'>;
4273
+ '2xl': TokenReference<'spacing'>;
4274
+ }
4275
+ interface SemanticTypography {
4276
+ fontFamily: TokenReference<'typography'>;
4277
+ fontSize: TokenReference<'typography'>;
4278
+ fontWeight: TokenReference<'typography'>;
4279
+ lineHeight: TokenReference<'typography'>;
4280
+ }
4281
+ interface SemanticTokens {
4282
+ colors: SemanticColors;
4283
+ spacing: SemanticSpacing;
4284
+ typography: SemanticTypography;
4285
+ }
4286
+ interface ComponentTokenSet {
4287
+ background?: TokenReference<'color'>;
4288
+ foreground?: TokenReference<'color'>;
4289
+ border?: TokenReference<'color'>;
4290
+ borderRadius?: TokenReference<'radius'>;
4291
+ padding?: TokenReference<'spacing'>;
4292
+ margin?: TokenReference<'spacing'>;
4293
+ shadow?: TokenReference<'shadow'>;
4294
+ opacity?: number;
4295
+ }
4296
+ interface ButtonTokens extends ComponentTokenSet {
4297
+ primary: ComponentTokenSet;
4298
+ secondary: ComponentTokenSet;
4299
+ ghost: ComponentTokenSet;
4300
+ }
4301
+ interface InputTokens extends ComponentTokenSet {
4302
+ background: TokenReference<'color'>;
4303
+ placeholder: TokenReference<'color'>;
4304
+ focus: {
4305
+ border: TokenReference<'color'>;
4306
+ ring: TokenReference<'color'>;
4307
+ };
4308
+ }
4309
+ interface LauncherTokens extends ComponentTokenSet {
4310
+ size: string;
4311
+ iconSize: string;
4312
+ shadow: TokenReference<'shadow'>;
4313
+ }
4314
+ interface PanelTokens extends ComponentTokenSet {
4315
+ width: string;
4316
+ maxWidth: string;
4317
+ height: string;
4318
+ maxHeight: string;
4319
+ }
4320
+ interface HeaderTokens extends ComponentTokenSet {
4321
+ background: TokenReference<'color'>;
4322
+ border: TokenReference<'color'>;
4323
+ borderRadius: TokenReference<'radius'>;
4324
+ }
4325
+ interface MessageTokens {
4326
+ user: {
4327
+ background: TokenReference<'color'>;
4328
+ text: TokenReference<'color'>;
4329
+ borderRadius: TokenReference<'radius'>;
4330
+ /** User bubble box-shadow (token ref or raw CSS, e.g. `none`). */
4331
+ shadow?: string;
4332
+ };
4333
+ assistant: {
4334
+ background: TokenReference<'color'>;
4335
+ text: TokenReference<'color'>;
4336
+ borderRadius: TokenReference<'radius'>;
4337
+ /** Assistant bubble border color (CSS color). */
4338
+ border?: TokenReference<'color'>;
4339
+ /** Assistant bubble box-shadow (token ref or raw CSS, e.g. `none`). */
4340
+ shadow?: string;
4341
+ };
4342
+ }
4343
+ interface MarkdownTokens {
4344
+ inlineCode: {
4345
+ background: TokenReference<'color'>;
4346
+ foreground: TokenReference<'color'>;
4347
+ };
4348
+ /** Foreground for `<a>` in rendered markdown (assistant bubbles + artifact pane). */
4349
+ link?: {
4350
+ foreground: TokenReference<'color'>;
4351
+ };
4352
+ /**
4353
+ * Body font for rendered markdown blocks (artifact pane + markdown bubbles).
4354
+ * Use a raw CSS `font-family` value, e.g. `Georgia, serif`.
4355
+ */
4356
+ prose?: {
4357
+ fontFamily?: string;
4358
+ };
4359
+ /** Optional heading scale overrides (raw CSS or resolvable token paths). */
4360
+ heading?: {
4361
+ h1?: {
4362
+ fontSize?: string;
4363
+ fontWeight?: string;
4364
+ };
4365
+ h2?: {
4366
+ fontSize?: string;
4367
+ fontWeight?: string;
4368
+ };
4369
+ };
4370
+ }
4371
+ interface VoiceTokens {
4372
+ recording: {
4373
+ indicator: TokenReference<'color'>;
4374
+ background: TokenReference<'color'>;
4375
+ border: TokenReference<'color'>;
4376
+ };
4377
+ processing: {
4378
+ icon: TokenReference<'color'>;
4379
+ background: TokenReference<'color'>;
4380
+ };
4381
+ speaking: {
4382
+ icon: TokenReference<'color'>;
4383
+ };
4384
+ }
4385
+ interface ApprovalTokens {
4386
+ requested: {
4387
+ background: TokenReference<'color'>;
4388
+ border: TokenReference<'color'>;
4389
+ text: TokenReference<'color'>;
4390
+ };
4391
+ approve: ComponentTokenSet;
4392
+ deny: ComponentTokenSet;
4393
+ }
4394
+ interface AttachmentTokens {
4395
+ image: {
4396
+ background: TokenReference<'color'>;
4397
+ border: TokenReference<'color'>;
4398
+ };
4399
+ }
4400
+ /** Tool-call row chrome (collapsible tool bubbles). */
4401
+ interface ToolBubbleTokens {
4402
+ /** Box-shadow for tool bubbles (token ref or raw CSS, e.g. `none`). */
4403
+ shadow: string;
4404
+ }
4405
+ /** Reasoning / “thinking” row chrome. */
4406
+ interface ReasoningBubbleTokens {
4407
+ shadow: string;
4408
+ }
4409
+ /** Composer (message input) chrome. */
4410
+ interface ComposerChromeTokens {
4411
+ /** Box-shadow on the composer form (raw CSS, e.g. `none`). */
4412
+ shadow: string;
4413
+ }
4414
+ interface ComponentTokens {
4415
+ button: ButtonTokens;
4416
+ input: InputTokens;
4417
+ launcher: LauncherTokens;
4418
+ panel: PanelTokens;
4419
+ header: HeaderTokens;
4420
+ message: MessageTokens;
4421
+ /** Markdown surfaces (chat + artifact pane). */
4422
+ markdown?: MarkdownTokens;
4423
+ voice: VoiceTokens;
4424
+ approval: ApprovalTokens;
4425
+ attachment: AttachmentTokens;
4426
+ toolBubble: ToolBubbleTokens;
4427
+ reasoningBubble: ReasoningBubbleTokens;
4428
+ composer: ComposerChromeTokens;
4429
+ }
4430
+ interface PaletteExtras {
4431
+ transitions?: Record<string, string>;
4432
+ easings?: Record<string, string>;
4433
+ }
4434
+ interface PersonaThemeBase {
4435
+ palette: {
4436
+ colors: ColorPalette;
4437
+ spacing: SpacingScale;
4438
+ typography: TypographyScale;
4439
+ shadows: ShadowScale;
4440
+ borders: BorderScale;
4441
+ radius: RadiusScale;
4442
+ } & PaletteExtras;
4443
+ }
4444
+ interface PersonaThemeSemantic {
4445
+ semantic: SemanticTokens;
4446
+ }
4447
+ interface PersonaThemeComponents {
4448
+ components: ComponentTokens;
4449
+ }
4450
+ type PersonaTheme = PersonaThemeBase & PersonaThemeSemantic & PersonaThemeComponents;
4451
+ interface ResolvedToken {
4452
+ path: string;
4453
+ value: string;
4454
+ type: TokenType;
4455
+ }
4456
+ interface ThemeValidationError {
4457
+ path: string;
4458
+ message: string;
4459
+ severity: 'error' | 'warning';
4460
+ }
4461
+ interface ThemeValidationResult {
4462
+ valid: boolean;
4463
+ errors: ThemeValidationError[];
4464
+ warnings: ThemeValidationError[];
4465
+ }
4466
+ interface PersonaThemePlugin {
4467
+ name: string;
4468
+ version: string;
4469
+ transform(theme: PersonaTheme): PersonaTheme;
4470
+ cssVariables?: Record<string, string>;
4471
+ afterResolve?(resolved: Record<string, string>): Record<string, string>;
4472
+ }
4473
+ interface CreateThemeOptions {
4474
+ plugins?: PersonaThemePlugin[];
4475
+ validate?: boolean;
4476
+ extend?: PersonaTheme;
4477
+ }
4478
+
4479
+ declare const DEFAULT_PALETTE: {
4480
+ colors: {
4481
+ primary: {
4482
+ 50: string;
4483
+ 100: string;
4484
+ 200: string;
4485
+ 300: string;
4486
+ 400: string;
4487
+ 500: string;
4488
+ 600: string;
4489
+ 700: string;
4490
+ 800: string;
4491
+ 900: string;
4492
+ 950: string;
4493
+ };
4494
+ secondary: {
4495
+ 50: string;
4496
+ 100: string;
4497
+ 200: string;
4498
+ 300: string;
4499
+ 400: string;
4500
+ 500: string;
4501
+ 600: string;
4502
+ 700: string;
4503
+ 800: string;
4504
+ 900: string;
4505
+ 950: string;
4506
+ };
4507
+ accent: {
4508
+ 50: string;
4509
+ 100: string;
4510
+ 200: string;
4511
+ 300: string;
4512
+ 400: string;
4513
+ 500: string;
4514
+ 600: string;
4515
+ 700: string;
4516
+ 800: string;
4517
+ 900: string;
4518
+ 950: string;
4519
+ };
4520
+ gray: {
4521
+ 50: string;
4522
+ 100: string;
4523
+ 200: string;
4524
+ 300: string;
4525
+ 400: string;
4526
+ 500: string;
4527
+ 600: string;
4528
+ 700: string;
4529
+ 800: string;
4530
+ 900: string;
4531
+ 950: string;
4532
+ };
4533
+ success: {
4534
+ 50: string;
4535
+ 100: string;
4536
+ 200: string;
4537
+ 300: string;
4538
+ 400: string;
4539
+ 500: string;
4540
+ 600: string;
4541
+ 700: string;
4542
+ 800: string;
4543
+ 900: string;
4544
+ };
4545
+ warning: {
4546
+ 50: string;
4547
+ 100: string;
4548
+ 200: string;
4549
+ 300: string;
4550
+ 400: string;
4551
+ 500: string;
4552
+ 600: string;
4553
+ 700: string;
4554
+ 800: string;
4555
+ 900: string;
4556
+ };
4557
+ error: {
4558
+ 50: string;
4559
+ 100: string;
4560
+ 200: string;
4561
+ 300: string;
4562
+ 400: string;
4563
+ 500: string;
4564
+ 600: string;
4565
+ 700: string;
4566
+ 800: string;
4567
+ 900: string;
4568
+ };
4569
+ };
4570
+ spacing: {
4571
+ 0: string;
4572
+ 1: string;
4573
+ 2: string;
4574
+ 3: string;
4575
+ 4: string;
4576
+ 5: string;
4577
+ 6: string;
4578
+ 8: string;
4579
+ 10: string;
4580
+ 12: string;
4581
+ 16: string;
4582
+ 20: string;
4583
+ 24: string;
4584
+ 32: string;
4585
+ 40: string;
4586
+ 48: string;
4587
+ 56: string;
4588
+ 64: string;
4589
+ };
4590
+ typography: {
4591
+ fontFamily: {
4592
+ sans: string;
4593
+ serif: string;
4594
+ mono: string;
4595
+ };
4596
+ fontSize: {
4597
+ xs: string;
4598
+ sm: string;
4599
+ base: string;
4600
+ lg: string;
4601
+ xl: string;
4602
+ '2xl': string;
4603
+ '3xl': string;
4604
+ '4xl': string;
4605
+ };
4606
+ fontWeight: {
4607
+ normal: string;
4608
+ medium: string;
4609
+ semibold: string;
4610
+ bold: string;
4611
+ };
4612
+ lineHeight: {
4613
+ tight: string;
4614
+ normal: string;
4615
+ relaxed: string;
4616
+ };
4617
+ };
4618
+ shadows: {
4619
+ none: string;
4620
+ sm: string;
4621
+ md: string;
4622
+ lg: string;
4623
+ xl: string;
4624
+ '2xl': string;
4625
+ };
4626
+ borders: {
4627
+ none: string;
4628
+ sm: string;
4629
+ md: string;
4630
+ lg: string;
4631
+ };
4632
+ radius: {
4633
+ none: string;
4634
+ sm: string;
4635
+ md: string;
4636
+ lg: string;
4637
+ xl: string;
4638
+ '2xl': string;
4639
+ full: string;
4640
+ };
4641
+ };
4642
+ declare const DEFAULT_SEMANTIC: SemanticTokens;
4643
+ declare const DEFAULT_COMPONENTS: ComponentTokens;
4644
+ declare function resolveTokens(theme: PersonaTheme): Record<string, ResolvedToken>;
4645
+ declare function validateTheme(theme: Partial<PersonaTheme>): ThemeValidationResult;
4646
+ declare function createTheme(userConfig?: Partial<PersonaTheme>, options?: CreateThemeOptions): PersonaTheme;
4647
+ declare function themeToCssVariables(theme: PersonaTheme): Record<string, string>;
4648
+
4649
+ type ColorScheme = 'light' | 'dark' | 'auto';
4650
+ interface PersonaWidgetConfig {
4651
+ theme?: Partial<PersonaTheme>;
4652
+ darkTheme?: Partial<PersonaTheme>;
4653
+ colorScheme?: ColorScheme;
4654
+ }
4655
+ type WidgetConfig = PersonaWidgetConfig | AgentWidgetConfig;
4656
+ declare const detectColorScheme: () => "light" | "dark";
4657
+ declare const getColorScheme: (config?: WidgetConfig) => "light" | "dark";
4658
+ declare const getActiveTheme: (config?: WidgetConfig) => PersonaTheme;
4659
+ declare const applyThemeVariables: (element: HTMLElement, config?: WidgetConfig) => void;
4660
+ declare const createThemeObserver: (callback: (scheme: "light" | "dark") => void) => (() => void);
4661
+
4662
+ declare function accessibilityPlugin(): PersonaThemePlugin;
4663
+ declare function animationsPlugin(): PersonaThemePlugin;
4664
+ declare function brandPlugin(brandConfig: {
4665
+ colors?: {
4666
+ primary?: string;
4667
+ secondary?: string;
4668
+ accent?: string;
4669
+ };
4670
+ logo?: string;
4671
+ }): PersonaThemePlugin;
4672
+ declare function reducedMotionPlugin(): PersonaThemePlugin;
4673
+ declare function highContrastPlugin(): PersonaThemePlugin;
4674
+ declare function createPlugin(config: {
4675
+ name: string;
4676
+ version: string;
4677
+ transform?: (theme: PersonaTheme) => PersonaTheme;
4678
+ cssVariables?: Record<string, string>;
4679
+ afterResolve?: (resolved: Record<string, string>) => Record<string, string>;
4680
+ }): PersonaThemePlugin;
4681
+
4682
+ interface V1ToV2MigrationOptions {
4683
+ warn?: boolean;
4684
+ colorScheme?: 'light' | 'dark' | 'auto';
4685
+ }
4686
+ declare function migrateV1Theme(v1Theme: AgentWidgetTheme | undefined, options?: V1ToV2MigrationOptions): Partial<PersonaTheme>;
4687
+ declare function validateV1Theme(v1Theme: unknown): {
4688
+ valid: boolean;
4689
+ warnings: string[];
4690
+ };
4691
+
3618
4692
  /**
3619
4693
  * Context provided to component renderers
3620
4694
  */
@@ -3721,6 +4795,12 @@ declare const createTypingIndicator: () => HTMLElement;
3721
4795
  declare const renderLoadingIndicatorWithFallback: (location: "inline" | "standalone", customRenderer?: LoadingIndicatorRenderer, widgetConfig?: AgentWidgetConfig) => HTMLElement | null;
3722
4796
  /**
3723
4797
  * Create message action buttons (copy, upvote, downvote)
4798
+ *
4799
+ * This is a pure rendering function. It creates button elements with the
4800
+ * correct `data-action` attributes, icons, and CSS classes. All click
4801
+ * handling, vote state management, clipboard logic, and callback dispatch
4802
+ * is handled via event delegation in `ui.ts` so that handlers survive
4803
+ * idiomorph DOM morphing.
3724
4804
  */
3725
4805
  declare const createMessageActions: (message: AgentWidgetMessage, actionsConfig: AgentWidgetMessageActionsConfig, _callbacks?: MessageActionCallbacks) => HTMLElement;
3726
4806
  /**
@@ -3865,6 +4945,9 @@ interface HeaderLayoutContext {
3865
4945
  showClose?: boolean;
3866
4946
  onClose?: () => void;
3867
4947
  onClearChat?: () => void;
4948
+ /** Passed from `buildHeaderWithLayout` for minimal/default chrome extensions */
4949
+ layoutHeaderConfig?: AgentWidgetHeaderLayoutConfig;
4950
+ onHeaderAction?: (actionId: string) => void;
3868
4951
  }
3869
4952
  type HeaderLayoutRenderer = (context: HeaderLayoutContext) => HeaderElements;
3870
4953
  /**
@@ -3872,16 +4955,7 @@ type HeaderLayoutRenderer = (context: HeaderLayoutContext) => HeaderElements;
3872
4955
  * Full header with icon, title, subtitle, clear chat, and close button
3873
4956
  */
3874
4957
  declare const buildDefaultHeader: HeaderLayoutRenderer;
3875
- /**
3876
- * Build minimal header layout
3877
- * Simplified layout with just title and close button
3878
- */
3879
4958
  declare const buildMinimalHeader: HeaderLayoutRenderer;
3880
- /**
3881
- * Build expanded header layout
3882
- * Full branding area with additional space for custom content
3883
- */
3884
- declare const buildExpandedHeader: HeaderLayoutRenderer;
3885
4959
  /**
3886
4960
  * Header layout registry
3887
4961
  * Maps layout names to their renderer functions
@@ -3901,4 +4975,4 @@ declare function createVoiceProvider(config: VoiceConfig): VoiceProvider;
3901
4975
  declare function createBestAvailableVoiceProvider(config?: Partial<VoiceConfig>): VoiceProvider;
3902
4976
  declare function isVoiceSupported(config?: Partial<VoiceConfig>): boolean;
3903
4977
 
3904
- export { type AgentConfig, type AgentExecutionState, type AgentLoopConfig, type AgentMessageMetadata, type AgentRequestOptions, type AgentWidgetAgentRequestPayload, type AgentWidgetApproval, type AgentWidgetApprovalConfig, type AgentWidgetAttachmentsConfig, type AgentWidgetAvatarConfig, AgentWidgetClient, type AgentWidgetConfig, type AgentWidgetController, type AgentWidgetControllerEventMap, type AgentWidgetCustomFetch, type AgentWidgetEvent, type AgentWidgetFeatureFlags, type AgentWidgetHeaderLayoutConfig, type AgentWidgetHeadersFunction, type AgentWidgetInitHandle, type AgentWidgetInitOptions, type AgentWidgetLauncherConfig, type AgentWidgetLayoutConfig, type AgentWidgetLoadingIndicatorConfig, type AgentWidgetMarkdownConfig, type AgentWidgetMarkdownOptions, type AgentWidgetMarkdownRendererOverrides, type AgentWidgetMessage, type AgentWidgetMessageActionsConfig, type AgentWidgetMessageFeedback, type AgentWidgetMessageLayoutConfig, type AgentWidgetPlugin, type AgentWidgetRequestPayload, type AgentWidgetSSEEventParser, type AgentWidgetSSEEventResult, AgentWidgetSession, type AgentWidgetSessionStatus, type AgentWidgetStreamParser, type AgentWidgetStreamParserResult, type AgentWidgetTheme, type AgentWidgetTimestampConfig, AttachmentManager, type AttachmentManagerConfig, type CSATFeedbackOptions, type ClientChatRequest, type ClientFeedbackRequest, type ClientFeedbackType, type ClientInitResponse, type ClientSession, type CodeFormat, type CodeGeneratorHooks, type CodeGeneratorOptions, type ComponentContext, type ComponentDirective, type ComponentRenderer, type ComposerBuildContext, type ComposerElements, type ContentPart, type CreateStandardBubbleOptions, DEFAULT_DARK_THEME, DEFAULT_LIGHT_THEME, DEFAULT_WIDGET_CONFIG, type EventStreamBadgeColor, type EventStreamConfig, type EventStreamPayloadRenderContext, type EventStreamRowRenderContext, type EventStreamToolbarRenderContext, type EventStreamViewRenderContext, type HeaderBuildContext, type HeaderElements, type HeaderLayoutContext, type HeaderLayoutRenderer, type HeaderRenderContext, type IdleIndicatorRenderContext, type ImageContentPart, type InjectAssistantMessageOptions, type InjectMessageOptions, type InjectSystemMessageOptions, type InjectUserMessageOptions, type LoadingIndicatorRenderContext, type LoadingIndicatorRenderer, type MarkdownProcessorOptions, type MessageActionCallbacks, type MessageContent, type MessageRenderContext, type MessageTransform, type NPSFeedbackOptions, type PendingAttachment, type SSEEventCallback, type SSEEventRecord, type SlotRenderContext, type SlotRenderer, type TextContentPart, VERSION, type VoiceConfig, type VoiceProvider, type VoiceResult, type VoiceStatus, type WidgetLayoutSlot, attachHeaderToContainer, buildComposer, buildDefaultHeader, buildExpandedHeader, buildHeader, buildHeaderWithLayout, buildMinimalHeader, componentRegistry, createActionManager, createAgentExperience, createBestAvailableVoiceProvider, createBubbleWithLayout, createCSATFeedback, createComponentMiddleware, createComponentStreamParser, createDirectivePostprocessor, createFlexibleJsonStreamParser, createImagePart, createJsonStreamParser, createLocalStorageAdapter, createMarkdownProcessor, createMarkdownProcessorFromConfig, createMessageActions, createNPSFeedback, createPlainTextParser, createRegexJsonParser, createStandardBubble, createTextPart, createTypingIndicator, createVoiceProvider, createXmlParser, initAgentWidget as default, defaultActionHandlers, defaultJsonActionParser, directivePostprocessor, escapeHtml, extractComponentDirectiveFromMessage, fileToImagePart, generateAssistantMessageId, generateCodeSnippet, generateMessageId, generateUserMessageId, getDisplayText, getHeaderLayout, getImageParts, hasComponentDirective, hasImages, headerLayouts, initAgentWidget, isComponentDirectiveType, isVoiceSupported, markdownPostprocessor, mergeWithDefaults, normalizeContent, pluginRegistry, renderComponentDirective, renderLoadingIndicatorWithFallback, validateImageFile };
4978
+ export { type AgentConfig, type AgentExecutionState, type AgentLoopConfig, type AgentMessageMetadata, type AgentRequestOptions, type AgentToolsConfig, type AgentWidgetAgentRequestPayload, type AgentWidgetApproval, type AgentWidgetApprovalConfig, type AgentWidgetArtifactsFeature, type AgentWidgetArtifactsLayoutConfig, type AgentWidgetAttachmentsConfig, type AgentWidgetAvatarConfig, AgentWidgetClient, type AgentWidgetComposerConfig, type AgentWidgetConfig, type AgentWidgetController, type AgentWidgetControllerEventMap, type AgentWidgetCustomFetch, type AgentWidgetDockConfig, type AgentWidgetEvent, type AgentWidgetFeatureFlags, type AgentWidgetHeaderLayoutConfig, type AgentWidgetHeadersFunction, type AgentWidgetInitHandle, type AgentWidgetInitOptions, type AgentWidgetLauncherConfig, type AgentWidgetLayoutConfig, type AgentWidgetLoadingIndicatorConfig, type AgentWidgetMarkdownConfig, type AgentWidgetMarkdownOptions, type AgentWidgetMarkdownRendererOverrides, type AgentWidgetMessage, type AgentWidgetMessageActionsConfig, type AgentWidgetMessageFeedback, type AgentWidgetMessageLayoutConfig, type AgentWidgetPlugin, type AgentWidgetRequestPayload, type AgentWidgetSSEEventParser, type AgentWidgetSSEEventResult, AgentWidgetSession, type AgentWidgetSessionStatus, type AgentWidgetStreamParser, type AgentWidgetStreamParserResult, type AgentWidgetTheme, type AgentWidgetTimestampConfig, type ArtifactConfigPayload, AttachmentManager, type AttachmentManagerConfig, type BorderScale, type CSATFeedbackOptions, type ClientChatRequest, type ClientFeedbackRequest, type ClientFeedbackType, type ClientInitResponse, type ClientSession, type CodeFormat, type CodeGeneratorHooks, type CodeGeneratorOptions, type ColorPalette, type ColorShade, type ComponentContext, type ComponentDirective, type ComponentRenderer, type ComponentTokens, type ComposerBuildContext, type ComposerElements, type ContentPart, type CreateStandardBubbleOptions, type CreateThemeOptions, DEFAULT_COMPONENTS, DEFAULT_DARK_THEME, DEFAULT_LIGHT_THEME, DEFAULT_PALETTE, DEFAULT_SEMANTIC, DEFAULT_WIDGET_CONFIG, type DomContextMode, type DomContextOptions, type EnrichedPageElement, type EventStreamBadgeColor, type EventStreamConfig, type EventStreamPayloadRenderContext, type EventStreamRowRenderContext, type EventStreamToolbarRenderContext, type EventStreamViewRenderContext, type FormatEnrichedContextOptions, type HeaderBuildContext, type HeaderElements, type HeaderLayoutContext, type HeaderLayoutRenderer, type HeaderRenderContext, type IdleIndicatorRenderContext, type ImageContentPart, type InjectAssistantMessageOptions, type InjectMessageOptions, type InjectSystemMessageOptions, type InjectUserMessageOptions, type LoadingIndicatorRenderContext, type LoadingIndicatorRenderer, type MarkdownProcessorOptions, type MessageActionCallbacks, type MessageContent, type MessageRenderContext, type MessageTransform, type NPSFeedbackOptions, type ParseOptionsConfig, type ParseRule, type PendingAttachment, type PersonaArtifactKind, type PersonaArtifactManualUpsert, type PersonaArtifactRecord, type PersonaTheme, type PersonaThemePlugin, type RadiusScale, type RuleScoringContext, type SSEEventCallback, type SSEEventRecord, type SemanticColors, type SemanticSpacing, type SemanticTypography, type ShadowScale, type SlotRenderContext, type SlotRenderer, type SpacingScale, type TextContentPart, type ThemeValidationError, type ThemeValidationResult, type TokenReference, type TypographyScale, VERSION, type VoiceConfig, type VoiceProvider, type VoiceResult, type VoiceStatus, type WidgetHostLayout, type WidgetHostLayoutMode, type WidgetLayoutSlot, accessibilityPlugin, animationsPlugin, applyThemeVariables, attachHeaderToContainer, brandPlugin, buildComposer, buildDefaultHeader, buildHeader, buildHeaderWithLayout, buildMinimalHeader, collectEnrichedPageContext, componentRegistry, createActionManager, createAgentExperience, createBestAvailableVoiceProvider, createBubbleWithLayout, createCSATFeedback, createComponentMiddleware, createComponentStreamParser, createDirectivePostprocessor, createFlexibleJsonStreamParser, createImagePart, createJsonStreamParser, createLocalStorageAdapter, createMarkdownProcessor, createMarkdownProcessorFromConfig, createMessageActions, createNPSFeedback, createPlainTextParser, createPlugin, createRegexJsonParser, createStandardBubble, createTextPart, createTheme, createThemeObserver, createTypingIndicator, createVoiceProvider, createWidgetHostLayout, createXmlParser, initAgentWidget as default, defaultActionHandlers, defaultJsonActionParser, defaultParseRules, detectColorScheme, directivePostprocessor, escapeHtml, extractComponentDirectiveFromMessage, fileToImagePart, formatEnrichedContext, generateAssistantMessageId, generateCodeSnippet, generateMessageId, generateStableSelector, generateUserMessageId, getActiveTheme, getColorScheme, getDisplayText, getHeaderLayout, getImageParts, hasComponentDirective, hasImages, headerLayouts, highContrastPlugin, initAgentWidget, isComponentDirectiveType, isDockedMountMode, isVoiceSupported, markdownPostprocessor, mergeWithDefaults, migrateV1Theme, normalizeContent, pluginRegistry, reducedMotionPlugin, renderComponentDirective, renderLoadingIndicatorWithFallback, resolveDockConfig, resolveTokens, themeToCssVariables, validateImageFile, validateTheme, validateV1Theme };