@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/src/types.ts CHANGED
@@ -71,6 +71,8 @@ export type AgentWidgetRequestPayload = {
71
71
  flowId?: string;
72
72
  context?: Record<string, unknown>;
73
73
  metadata?: Record<string, unknown>;
74
+ /** Per-turn template variables for /v1/client/chat (merged as root-level {{var}} in Runtype). */
75
+ inputs?: Record<string, unknown>;
74
76
  };
75
77
 
76
78
  // ============================================================================
@@ -81,30 +83,65 @@ export type AgentWidgetRequestPayload = {
81
83
  * Configuration for agent loop behavior.
82
84
  */
83
85
  export type AgentLoopConfig = {
84
- /** Maximum number of reasoning iterations */
85
- maxIterations: number;
86
- /** Stop condition: 'auto' for automatic detection, or a custom JS expression */
87
- stopCondition?: 'auto' | string;
86
+ /** Maximum number of agent turns (1-100). The loop continues while the model calls tools. */
87
+ maxTurns: number;
88
+ /** Maximum cost budget in USD. Agent stops when exceeded. */
89
+ maxCost?: number;
88
90
  /** Enable periodic reflection during execution */
89
91
  enableReflection?: boolean;
90
- /** Number of iterations between reflections */
92
+ /** Number of iterations between reflections (1-50) */
91
93
  reflectionInterval?: number;
92
94
  };
93
95
 
96
+ /**
97
+ * Configuration for agent tools (search, code execution, MCP servers, etc.)
98
+ */
99
+ export type AgentToolsConfig = {
100
+ /** Tool IDs to enable (e.g., "builtin:exa", "builtin:dalle", "builtin:openai_web_search") */
101
+ toolIds?: string[];
102
+ /** Per-tool configuration overrides keyed by tool ID */
103
+ toolConfigs?: Record<string, Record<string, unknown>>;
104
+ /** Inline tool definitions for runtime-defined tools */
105
+ runtimeTools?: Array<Record<string, unknown>>;
106
+ /** Custom MCP server connections */
107
+ mcpServers?: Array<Record<string, unknown>>;
108
+ /** Maximum number of tool invocations per execution */
109
+ maxToolCalls?: number;
110
+ /** Tool approval configuration for human-in-the-loop workflows */
111
+ approval?: {
112
+ /** Tool names/patterns to require approval for, or true for all tools */
113
+ require: string[] | boolean;
114
+ /** Approval timeout in milliseconds (default: 300000 / 5 minutes) */
115
+ timeout?: number;
116
+ };
117
+ };
118
+
119
+ /** Artifact kinds for the Persona sidebar and dispatch payload */
120
+ export type PersonaArtifactKind = "markdown" | "component";
121
+
94
122
  /**
95
123
  * Agent configuration for agent execution mode.
96
124
  * When provided in the widget config, enables agent loop execution instead of flow dispatch.
97
125
  */
126
+ export type ArtifactConfigPayload = {
127
+ enabled: true;
128
+ types: PersonaArtifactKind[];
129
+ };
130
+
98
131
  export type AgentConfig = {
99
132
  /** Agent display name */
100
133
  name: string;
101
- /** Model identifier (e.g., 'openai:gpt-4o-mini') */
134
+ /** Model identifier (e.g., 'openai:gpt-4o-mini', 'qwen/qwen3-8b') */
102
135
  model: string;
103
136
  /** System prompt for the agent */
104
137
  systemPrompt: string;
105
138
  /** Temperature for model responses */
106
139
  temperature?: number;
107
- /** Loop configuration for multi-iteration execution */
140
+ /** Tool configuration for the agent */
141
+ tools?: AgentToolsConfig;
142
+ /** Persona artifacts — sibling of tools (virtual agent / API parity) */
143
+ artifacts?: ArtifactConfigPayload;
144
+ /** Loop configuration for multi-turn execution */
108
145
  loopConfig?: AgentLoopConfig;
109
146
  };
110
147
 
@@ -142,10 +179,10 @@ export type AgentExecutionState = {
142
179
  agentName: string;
143
180
  status: 'running' | 'complete' | 'error';
144
181
  currentIteration: number;
145
- maxIterations: number;
182
+ maxTurns: number;
146
183
  startedAt?: number;
147
184
  completedAt?: number;
148
- stopReason?: 'max_iterations' | 'complete' | 'error' | 'manual';
185
+ stopReason?: 'complete' | 'end_turn' | 'max_turns' | 'max_cost' | 'timeout' | 'error';
149
186
  };
150
187
 
151
188
  /**
@@ -361,12 +398,143 @@ export type AgentWidgetControllerEventMap = {
361
398
  "approval:resolved": { approval: AgentWidgetApproval; decision: string };
362
399
  };
363
400
 
401
+ /**
402
+ * Layout for the artifact split / drawer (CSS lengths unless noted).
403
+ *
404
+ * **Close behavior:** In desktop split mode, the artifact chrome `Close` control uses the same
405
+ * dismiss path as the mobile drawer (`onDismiss` on the artifact pane): the pane is hidden until
406
+ * new artifact content arrives or the host calls `showArtifacts()` on the widget handle.
407
+ */
408
+ export type AgentWidgetArtifactsLayoutConfig = {
409
+ /** Flex gap between chat column and artifact pane. @default 0.5rem */
410
+ splitGap?: string;
411
+ /** Artifact column width in split mode. @default 40% */
412
+ paneWidth?: string;
413
+ /** Max width of artifact column. @default 28rem */
414
+ paneMaxWidth?: string;
415
+ /** Min width of artifact column (optional). */
416
+ paneMinWidth?: string;
417
+ /**
418
+ * When the floating panel is at most this wide (px), use in-panel drawer for artifacts
419
+ * instead of a side-by-side split (viewport can still be wide).
420
+ * @default 520
421
+ */
422
+ narrowHostMaxWidth?: number;
423
+ /**
424
+ * When true (default), widen the launcher panel while artifacts are visible and not user-dismissed.
425
+ * No-op for inline embed (`launcher.enabled === false`).
426
+ */
427
+ expandLauncherPanelWhenOpen?: boolean;
428
+ /** Panel width when expanded (launcher + artifacts visible). @default min(720px, calc(100vw - 24px)) */
429
+ expandedPanelWidth?: string;
430
+ /**
431
+ * When true, shows a drag handle between chat and artifact columns in desktop split mode only
432
+ * (hidden in narrow-host drawer and viewport ≤640px). Width is not persisted across reloads.
433
+ */
434
+ resizable?: boolean;
435
+ /** Min artifact column width while resizing. Only `px` strings are supported. @default 200px */
436
+ resizableMinWidth?: string;
437
+ /** Optional max artifact width cap while resizing (`px` only). Layout still bounds by chat min width. */
438
+ resizableMaxWidth?: string;
439
+ /**
440
+ * Visual treatment for the artifact column in split mode.
441
+ * - `'panel'` — bordered sidebar with left border, gap, and shadow (default).
442
+ * - `'seamless'` — flush with chat: no border or shadow, container background, zero gap.
443
+ * @default 'panel'
444
+ */
445
+ paneAppearance?: "panel" | "seamless";
446
+ /** Border radius on the artifact pane (CSS length). Works with any `paneAppearance`. */
447
+ paneBorderRadius?: string;
448
+ /** CSS `box-shadow` on the artifact pane. Set `"none"` to suppress the default shadow. */
449
+ paneShadow?: string;
450
+ /**
451
+ * Full `border` shorthand for the artifact `<aside>` (all sides). Overrides default pane borders.
452
+ * Example: `"1px solid #cccccc"`.
453
+ */
454
+ paneBorder?: string;
455
+ /**
456
+ * `border-left` shorthand only — typical for split view next to chat (with or without resizer).
457
+ * Ignored if `paneBorder` is set. Example: `"1px solid #cccccc"`.
458
+ */
459
+ paneBorderLeft?: string;
460
+ /**
461
+ * Desktop split only (not narrow-host drawer / not ≤640px): square the **main chat card’s**
462
+ * top-right and bottom-right radii, and round the **artifact pane’s** top-right and bottom-right
463
+ * to match `persona-rounded-2xl` (`--persona-radius-lg`) so the two columns read as one shell.
464
+ */
465
+ unifiedSplitChrome?: boolean;
466
+ /**
467
+ * When `unifiedSplitChrome` is true, outer-right corner radius on the artifact column (CSS length).
468
+ * @default matches theme large radius (`--persona-radius-lg`)
469
+ */
470
+ unifiedSplitOuterRadius?: string;
471
+ /**
472
+ * Background color for the artifact column (CSS color). Sets `--persona-artifact-pane-bg` on the widget root.
473
+ */
474
+ paneBackground?: string;
475
+ /**
476
+ * Horizontal padding for artifact toolbar and content (CSS length), e.g. `24px`.
477
+ */
478
+ panePadding?: string;
479
+ /**
480
+ * Toolbar layout preset.
481
+ * - `default` — "Artifacts" title, horizontal tabs, text close.
482
+ * - `document` — view/source toggle, document title, copy / refresh / close; tab strip hidden when only one artifact.
483
+ * @default 'default'
484
+ */
485
+ toolbarPreset?: "default" | "document";
486
+ /**
487
+ * When `toolbarPreset` is `document`, show a visible "Copy" label next to the copy icon.
488
+ */
489
+ documentToolbarShowCopyLabel?: boolean;
490
+ /**
491
+ * When `toolbarPreset` is `document`, show a small chevron after the copy control (e.g. menu affordance).
492
+ */
493
+ documentToolbarShowCopyChevron?: boolean;
494
+ /** Document toolbar icon buttons (view, code, copy, refresh, close) — CSS color. Sets `--persona-artifact-doc-toolbar-icon-color` on the widget root. */
495
+ documentToolbarIconColor?: string;
496
+ /** Active view/source toggle background. Sets `--persona-artifact-doc-toggle-active-bg`. */
497
+ documentToolbarToggleActiveBackground?: string;
498
+ /** Active view/source toggle border color. Sets `--persona-artifact-doc-toggle-active-border`. */
499
+ documentToolbarToggleActiveBorderColor?: string;
500
+ /**
501
+ * Invoked when the document toolbar Refresh control is used (before the pane re-renders).
502
+ * Use to replay `connectStream`, refetch, etc.
503
+ */
504
+ onDocumentToolbarRefresh?: () => void | Promise<void>;
505
+ /**
506
+ * Optional copy dropdown entries (shown when `documentToolbarShowCopyChevron` is true and this array is non-empty).
507
+ * The main Copy control still performs default copy unless `onDocumentToolbarCopyMenuSelect` handles everything.
508
+ */
509
+ documentToolbarCopyMenuItems?: Array<{ id: string; label: string }>;
510
+ /**
511
+ * When set, invoked for the chevron menu (and can override default copy per `actionId`).
512
+ */
513
+ onDocumentToolbarCopyMenuSelect?: (payload: {
514
+ actionId: string;
515
+ artifactId: string | null;
516
+ markdown: string;
517
+ jsonPayload: string;
518
+ }) => void | Promise<void>;
519
+ };
520
+
521
+ export type AgentWidgetArtifactsFeature = {
522
+ /** When true, Persona shows the artifact pane and handles artifact_* SSE events */
523
+ enabled?: boolean;
524
+ /** If set, artifact events for other types are ignored */
525
+ allowedTypes?: PersonaArtifactKind[];
526
+ /** Split / drawer dimensions and launcher widen behavior */
527
+ layout?: AgentWidgetArtifactsLayoutConfig;
528
+ };
529
+
364
530
  export type AgentWidgetFeatureFlags = {
365
531
  showReasoning?: boolean;
366
532
  showToolCalls?: boolean;
367
533
  showEventStreamToggle?: boolean;
368
534
  /** Configuration for the Event Stream inspector view */
369
535
  eventStream?: EventStreamConfig;
536
+ /** Optional artifact sidebar (split pane / mobile drawer) */
537
+ artifacts?: AgentWidgetArtifactsFeature;
370
538
  };
371
539
 
372
540
  export type SSEEventRecord = {
@@ -531,7 +699,7 @@ export type AgentWidgetTheme = {
531
699
  /**
532
700
  * Border style for the chat panel container.
533
701
  * @example "1px solid #e5e7eb" | "none"
534
- * @default "1px solid var(--tvw-cw-border)"
702
+ * @default "1px solid var(--persona-border)"
535
703
  */
536
704
  panelBorder?: string;
537
705
  /**
@@ -546,6 +714,46 @@ export type AgentWidgetTheme = {
546
714
  * @default "16px"
547
715
  */
548
716
  panelBorderRadius?: string;
717
+ /**
718
+ * Box-shadow for user message bubbles (bubble message layout).
719
+ * @example "none" | "0 1px 2px rgba(0,0,0,0.05)"
720
+ */
721
+ messageUserShadow?: string;
722
+ /**
723
+ * Box-shadow for assistant message bubbles (bubble message layout).
724
+ * Overrides the default subtle assistant shadow when set.
725
+ */
726
+ messageAssistantShadow?: string;
727
+ /**
728
+ * Box-shadow for tool-call / function-call rows.
729
+ */
730
+ toolBubbleShadow?: string;
731
+ /**
732
+ * Box-shadow for reasoning (“thinking”) rows.
733
+ */
734
+ reasoningBubbleShadow?: string;
735
+ /**
736
+ * Box-shadow on the composer (input) container.
737
+ */
738
+ composerShadow?: string;
739
+ };
740
+
741
+ export type AgentWidgetDockConfig = {
742
+ /**
743
+ * Side of the wrapped container where the docked panel should render.
744
+ * @default "right"
745
+ */
746
+ side?: "left" | "right";
747
+ /**
748
+ * Expanded width of the docked panel.
749
+ * @default "420px"
750
+ */
751
+ width?: string;
752
+ /**
753
+ * Width of the collapsed launcher rail when the docked panel is closed.
754
+ * @default "72px"
755
+ */
756
+ collapsedWidth?: string;
549
757
  };
550
758
 
551
759
  export type AgentWidgetLauncherConfig = {
@@ -558,6 +766,18 @@ export type AgentWidgetLauncherConfig = {
558
766
  agentIconName?: string;
559
767
  agentIconHidden?: boolean;
560
768
  position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
769
+ /**
770
+ * Controls how the launcher panel is mounted relative to the host page.
771
+ * - "floating": default floating launcher / panel behavior
772
+ * - "docked": wraps the target container and renders as a sibling dock
773
+ *
774
+ * @default "floating"
775
+ */
776
+ mountMode?: "floating" | "docked";
777
+ /**
778
+ * Layout configuration for docked mode.
779
+ */
780
+ dock?: AgentWidgetDockConfig;
561
781
  autoExpand?: boolean;
562
782
  width?: string;
563
783
  /**
@@ -597,6 +817,21 @@ export type AgentWidgetLauncherConfig = {
597
817
  * @default 0
598
818
  */
599
819
  heightOffset?: number;
820
+ /**
821
+ * When true, the widget panel expands to fill the full viewport on mobile devices.
822
+ * Removes border-radius, margins, and shadows for a native app-like experience.
823
+ * Applies when viewport width is at or below `mobileBreakpoint`.
824
+ *
825
+ * @default true
826
+ */
827
+ mobileFullscreen?: boolean;
828
+ /**
829
+ * Viewport width (in pixels) at or below which the widget enters mobile fullscreen mode.
830
+ * Only applies when `mobileFullscreen` is true.
831
+ *
832
+ * @default 640
833
+ */
834
+ mobileBreakpoint?: number;
600
835
  callToActionIconText?: string;
601
836
  callToActionIconName?: string;
602
837
  callToActionIconColor?: string;
@@ -651,6 +886,13 @@ export type AgentWidgetSendButtonConfig = {
651
886
  size?: string;
652
887
  };
653
888
 
889
+ /** Optional composer UI state for custom `renderComposer` implementations. */
890
+ export type AgentWidgetComposerConfig = {
891
+ models?: Array<{ id: string; label: string }>;
892
+ /** Current selection; host or plugin may update this at runtime. */
893
+ selectedModelId?: string;
894
+ };
895
+
654
896
  export type AgentWidgetClearChatConfig = {
655
897
  enabled?: boolean;
656
898
  placement?: "inline" | "top-right";
@@ -921,6 +1163,8 @@ export type AgentWidgetApprovalConfig = {
921
1163
  };
922
1164
 
923
1165
  export type AgentWidgetToolCallConfig = {
1166
+ /** Box-shadow for tool-call bubbles; overrides `theme.toolBubbleShadow` when set. */
1167
+ shadow?: string;
924
1168
  backgroundColor?: string;
925
1169
  borderColor?: string;
926
1170
  borderWidth?: string;
@@ -1109,6 +1353,8 @@ export type ClientChatRequest = {
1109
1353
  /** ID for the expected assistant response message */
1110
1354
  assistantMessageId?: string;
1111
1355
  metadata?: Record<string, unknown>;
1356
+ /** Per-turn inputs for Runtype prompt templates (e.g. {{page_url}}). */
1357
+ inputs?: Record<string, unknown>;
1112
1358
  context?: Record<string, unknown>;
1113
1359
  };
1114
1360
 
@@ -1135,6 +1381,15 @@ export type ClientFeedbackRequest = {
1135
1381
  // Layout Configuration Types
1136
1382
  // ============================================================================
1137
1383
 
1384
+ /** Icon button in the header title row (minimal layout). */
1385
+ export type AgentWidgetHeaderTrailingAction = {
1386
+ id: string;
1387
+ /** Lucide icon name, e.g. `chevron-down` */
1388
+ icon?: string;
1389
+ label?: string;
1390
+ ariaLabel?: string;
1391
+ };
1392
+
1138
1393
  /**
1139
1394
  * Context provided to header render functions
1140
1395
  */
@@ -1142,6 +1397,10 @@ export type HeaderRenderContext = {
1142
1397
  config: AgentWidgetConfig;
1143
1398
  onClose?: () => void;
1144
1399
  onClearChat?: () => void;
1400
+ /** Built from `layout.header.trailingActions` for custom `render` implementations. */
1401
+ trailingActions?: AgentWidgetHeaderTrailingAction[];
1402
+ /** Fired when a built-in trailing action is activated (same as `layout.header.onAction`). */
1403
+ onAction?: (actionId: string) => void;
1145
1404
  };
1146
1405
 
1147
1406
  /**
@@ -1167,12 +1426,11 @@ export type SlotRenderContext = {
1167
1426
  */
1168
1427
  export type AgentWidgetHeaderLayoutConfig = {
1169
1428
  /**
1170
- * Layout preset: "default" | "minimal" | "expanded"
1429
+ * Layout preset: "default" | "minimal"
1171
1430
  * - default: Standard layout with icon, title, subtitle, and buttons
1172
1431
  * - minimal: Simplified layout with just title and close button
1173
- * - expanded: Full branding area with additional content space
1174
1432
  */
1175
- layout?: "default" | "minimal" | "expanded";
1433
+ layout?: "default" | "minimal";
1176
1434
  /** Show/hide the header icon */
1177
1435
  showIcon?: boolean;
1178
1436
  /** Show/hide the title */
@@ -1188,6 +1446,12 @@ export type AgentWidgetHeaderLayoutConfig = {
1188
1446
  * When provided, replaces the entire header with custom content
1189
1447
  */
1190
1448
  render?: (context: HeaderRenderContext) => HTMLElement;
1449
+ /**
1450
+ * Shown after the title in `minimal` header layout (e.g. chevron menu affordance).
1451
+ */
1452
+ trailingActions?: AgentWidgetHeaderTrailingAction[];
1453
+ /** Called when a `trailingActions` button is clicked. */
1454
+ onAction?: (actionId: string) => void;
1191
1455
  };
1192
1456
 
1193
1457
  /**
@@ -1310,6 +1574,12 @@ export type AgentWidgetLayoutConfig = {
1310
1574
  * @default true
1311
1575
  */
1312
1576
  showFooter?: boolean;
1577
+ /**
1578
+ * Max width for the content area (messages + composer).
1579
+ * Applied with `margin: 0 auto` for centering.
1580
+ * Accepts any CSS width value (e.g. "90ch", "720px", "80%").
1581
+ */
1582
+ contentMaxWidth?: string;
1313
1583
  };
1314
1584
 
1315
1585
  // ============================================================================
@@ -1850,7 +2120,7 @@ export type AgentWidgetConfig = {
1850
2120
  * name: 'Assistant',
1851
2121
  * model: 'openai:gpt-4o-mini',
1852
2122
  * systemPrompt: 'You are a helpful assistant.',
1853
- * loopConfig: { maxIterations: 3, stopCondition: 'auto' }
2123
+ * loopConfig: { maxTurns: 5 }
1854
2124
  * }
1855
2125
  * }
1856
2126
  * ```
@@ -1966,6 +2236,11 @@ export type AgentWidgetConfig = {
1966
2236
  welcomeSubtitle?: string;
1967
2237
  inputPlaceholder?: string;
1968
2238
  sendButtonLabel?: string;
2239
+ /**
2240
+ * When false, the welcome / intro card is not shown above the message list.
2241
+ * @default true
2242
+ */
2243
+ showWelcomeCard?: boolean;
1969
2244
  };
1970
2245
  theme?: AgentWidgetTheme;
1971
2246
  /**
@@ -2117,6 +2392,12 @@ export type AgentWidgetConfig = {
2117
2392
  * @default true
2118
2393
  */
2119
2394
  enableComponentStreaming?: boolean;
2395
+ /**
2396
+ * When false, JSON component directives render without the default bubble chrome
2397
+ * (surface background, border, extra padding). Use for wide custom cards in the transcript.
2398
+ * @default true
2399
+ */
2400
+ wrapComponentDirectiveInBubble?: boolean;
2120
2401
  /**
2121
2402
  * Custom stream parser for extracting text from streaming structured responses.
2122
2403
  * Handles incremental parsing of JSON, XML, or other formats.
@@ -2319,6 +2600,12 @@ export type AgentWidgetConfig = {
2319
2600
  */
2320
2601
  attachments?: AgentWidgetAttachmentsConfig;
2321
2602
 
2603
+ /**
2604
+ * Composer extras for custom `renderComposer` plugins (model picker, etc.).
2605
+ * `selectedModelId` may be updated at runtime by the host.
2606
+ */
2607
+ composer?: AgentWidgetComposerConfig;
2608
+
2322
2609
  /**
2323
2610
  * Persist widget state (open/closed, voice mode) across page navigations.
2324
2611
  * When `true`, uses default settings with sessionStorage.
@@ -2596,10 +2883,46 @@ export type InjectUserMessageOptions = Omit<InjectMessageOptions, "role">;
2596
2883
  */
2597
2884
  export type InjectSystemMessageOptions = Omit<InjectMessageOptions, "role">;
2598
2885
 
2886
+ export type PersonaArtifactRecord = {
2887
+ id: string;
2888
+ artifactType: PersonaArtifactKind;
2889
+ title?: string;
2890
+ status: "streaming" | "complete";
2891
+ markdown?: string;
2892
+ component?: string;
2893
+ props?: Record<string, unknown>;
2894
+ };
2895
+
2896
+ /** Programmatic artifact upsert (controller / window API) */
2897
+ export type PersonaArtifactManualUpsert =
2898
+ | { id?: string; artifactType: "markdown"; title?: string; content: string }
2899
+ | {
2900
+ id?: string;
2901
+ artifactType: "component";
2902
+ title?: string;
2903
+ component: string;
2904
+ props?: Record<string, unknown>;
2905
+ };
2906
+
2599
2907
  export type AgentWidgetEvent =
2600
2908
  | { type: "message"; message: AgentWidgetMessage }
2601
2909
  | { type: "status"; status: "connecting" | "connected" | "error" | "idle" }
2602
- | { type: "error"; error: Error };
2910
+ | { type: "error"; error: Error }
2911
+ | {
2912
+ type: "artifact_start";
2913
+ id: string;
2914
+ artifactType: PersonaArtifactKind;
2915
+ title?: string;
2916
+ component?: string;
2917
+ }
2918
+ | { type: "artifact_delta"; id: string; artDelta: string }
2919
+ | {
2920
+ type: "artifact_update";
2921
+ id: string;
2922
+ props: Record<string, unknown>;
2923
+ component?: string;
2924
+ }
2925
+ | { type: "artifact_complete"; id: string };
2603
2926
 
2604
2927
  export type AgentWidgetInitOptions = {
2605
2928
  target: string | HTMLElement;
@@ -0,0 +1,104 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+
5
+ import { createWidgetHostLayout } from "./runtime/host-layout";
6
+ import { createAgentExperience } from "./ui";
7
+
8
+ describe("createAgentExperience docked mode", () => {
9
+ afterEach(() => {
10
+ document.body.innerHTML = "";
11
+ });
12
+
13
+ it("collapses the docked panel wrapper and keeps the rail trigger visible when closed", () => {
14
+ const mount = document.createElement("div");
15
+ document.body.appendChild(mount);
16
+
17
+ const controller = createAgentExperience(mount, {
18
+ apiUrl: "https://api.example.com/chat",
19
+ launcher: {
20
+ mountMode: "docked",
21
+ autoExpand: true,
22
+ dock: {
23
+ side: "right",
24
+ width: "420px",
25
+ collapsedWidth: "72px",
26
+ },
27
+ },
28
+ });
29
+
30
+ const wrapper = mount.firstElementChild as HTMLElement | null;
31
+ const launcherButton = mount.lastElementChild as HTMLButtonElement | null;
32
+
33
+ expect(wrapper).not.toBeNull();
34
+ expect(wrapper?.style.display).toBe("flex");
35
+ expect(launcherButton).not.toBeNull();
36
+ expect(launcherButton?.tagName).toBe("BUTTON");
37
+ expect(launcherButton?.style.display).toBe("none");
38
+ expect(launcherButton?.className).toContain("persona-mt-4");
39
+
40
+ controller.close();
41
+
42
+ expect(wrapper?.style.display).toBe("none");
43
+ expect(launcherButton?.style.display).toBe("");
44
+
45
+ controller.open();
46
+
47
+ expect(wrapper?.style.display).toBe("flex");
48
+ expect(launcherButton?.style.display).toBe("none");
49
+
50
+ controller.destroy();
51
+ });
52
+
53
+ it("collapses the dock width when the header close button is clicked", () => {
54
+ const target = document.createElement("div");
55
+ document.body.appendChild(target);
56
+
57
+ const hostLayout = createWidgetHostLayout(target, {
58
+ launcher: {
59
+ mountMode: "docked",
60
+ autoExpand: true,
61
+ dock: {
62
+ side: "right",
63
+ width: "420px",
64
+ collapsedWidth: "72px",
65
+ },
66
+ },
67
+ });
68
+ const mount = document.createElement("div");
69
+ hostLayout.host.appendChild(mount);
70
+
71
+ const controller = createAgentExperience(mount, {
72
+ apiUrl: "https://api.example.com/chat",
73
+ launcher: {
74
+ mountMode: "docked",
75
+ autoExpand: true,
76
+ dock: {
77
+ side: "right",
78
+ width: "420px",
79
+ collapsedWidth: "72px",
80
+ },
81
+ },
82
+ });
83
+
84
+ const syncDockState = () => hostLayout.syncWidgetState(controller.getState());
85
+ const openUnsub = controller.on("widget:opened", syncDockState);
86
+ const closeUnsub = controller.on("widget:closed", syncDockState);
87
+ syncDockState();
88
+
89
+ const dockSlot = hostLayout.shell?.querySelector<HTMLElement>('[data-persona-dock-role="panel"]');
90
+ const closeButton = mount.querySelector<HTMLButtonElement>('[aria-label="Close chat"]');
91
+
92
+ expect(dockSlot?.style.width).toBe("420px");
93
+ expect(closeButton).not.toBeNull();
94
+
95
+ closeButton!.click();
96
+
97
+ expect(dockSlot?.style.width).toBe("72px");
98
+
99
+ openUnsub();
100
+ closeUnsub();
101
+ controller.destroy();
102
+ hostLayout.destroy();
103
+ });
104
+ });