@runtypelabs/persona 4.14.0 → 4.16.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 (119) hide show
  1. package/README.md +4 -4
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/{types-DveIaNx6.d.cts → types-BpWGm7_6.d.cts} +17 -0
  5. package/dist/animations/{types-DveIaNx6.d.ts → types-BpWGm7_6.d.ts} +17 -0
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/chunk-QJ4LV2KW.js +1 -0
  9. package/dist/codegen.cjs +12 -11
  10. package/dist/codegen.js +14 -13
  11. package/dist/{context-mentions-ONG7A7M6.js → context-mentions-BTSTIBFC.js} +1 -1
  12. package/dist/context-mentions.cjs +11 -8
  13. package/dist/context-mentions.d.cts +520 -21
  14. package/dist/context-mentions.d.ts +520 -21
  15. package/dist/context-mentions.js +11 -8
  16. package/dist/index.cjs +69 -68
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +595 -46
  19. package/dist/index.d.ts +595 -46
  20. package/dist/index.global.js +60 -57
  21. package/dist/index.global.js.map +1 -1
  22. package/dist/index.js +69 -68
  23. package/dist/index.js.map +1 -1
  24. package/dist/launcher.global.js +5 -2
  25. package/dist/launcher.global.js.map +1 -1
  26. package/dist/smart-dom-reader.d.cts +520 -21
  27. package/dist/smart-dom-reader.d.ts +520 -21
  28. package/dist/testing.cjs +3 -3
  29. package/dist/testing.d.cts +7 -0
  30. package/dist/testing.d.ts +7 -0
  31. package/dist/testing.js +3 -3
  32. package/dist/theme-editor-preview.cjs +67 -67
  33. package/dist/theme-editor-preview.d.cts +534 -22
  34. package/dist/theme-editor-preview.d.ts +534 -22
  35. package/dist/theme-editor-preview.js +57 -57
  36. package/dist/theme-editor.cjs +10 -10
  37. package/dist/theme-editor.d.cts +521 -23
  38. package/dist/theme-editor.d.ts +521 -23
  39. package/dist/theme-editor.js +10 -10
  40. package/dist/theme-reference.cjs +1 -1
  41. package/dist/theme-reference.d.cts +104 -3
  42. package/dist/theme-reference.d.ts +104 -3
  43. package/dist/theme-reference.js +1 -1
  44. package/dist/widget.css +1 -1
  45. package/package.json +1 -1
  46. package/src/ask-user-question-tool.ts +10 -7
  47. package/src/components/artifact-pane.test.ts +27 -0
  48. package/src/components/artifact-pane.ts +7 -3
  49. package/src/components/artifact-preview.ts +1 -1
  50. package/src/components/composer-parts.test.ts +103 -4
  51. package/src/components/composer-parts.ts +53 -15
  52. package/src/components/header-builder.ts +27 -2
  53. package/src/components/header-layouts.test.ts +56 -0
  54. package/src/components/header-layouts.ts +29 -9
  55. package/src/components/launcher-surface.test.ts +403 -0
  56. package/src/components/launcher.ts +297 -2
  57. package/src/components/messages.ts +5 -2
  58. package/src/components/panel.ts +92 -71
  59. package/src/components/pill-composer-builder.test.ts +18 -0
  60. package/src/components/suggestions.test.ts +273 -0
  61. package/src/components/suggestions.ts +528 -86
  62. package/src/components/welcome.test.ts +121 -0
  63. package/src/components/welcome.ts +305 -0
  64. package/src/components/widget-view.test.ts +5 -3
  65. package/src/components/widget-view.ts +18 -7
  66. package/src/defaults.ts +7 -5
  67. package/src/generated/runtype-openapi-contract.ts +17 -0
  68. package/src/index-core.ts +29 -2
  69. package/src/launcher-global.ts +11 -8
  70. package/src/plugins/types.ts +136 -3
  71. package/src/presets.ts +3 -3
  72. package/src/session.ts +10 -8
  73. package/src/styles/widget-styles.test.ts +382 -0
  74. package/src/styles/widget.css +778 -4
  75. package/src/suggest-replies-tool.test.ts +233 -2
  76. package/src/suggest-replies-tool.ts +115 -39
  77. package/src/testing/mock-stream.test.ts +19 -0
  78. package/src/testing/mock-stream.ts +20 -0
  79. package/src/theme-editor/preview-utils.test.ts +34 -0
  80. package/src/theme-editor/preview-utils.ts +77 -2
  81. package/src/theme-editor/sections.test.ts +33 -0
  82. package/src/theme-editor/sections.ts +83 -6
  83. package/src/theme-reference.ts +34 -5
  84. package/src/types/theme.ts +72 -3
  85. package/src/types.ts +312 -18
  86. package/src/ui.body-top-slot.test.ts +95 -0
  87. package/src/ui.content-width.test.ts +91 -0
  88. package/src/ui.detached-panel.test.ts +1 -1
  89. package/src/ui.docked.test.ts +7 -4
  90. package/src/ui.launcher-teaser.test.ts +162 -0
  91. package/src/ui.rebuild-composer.test.ts +491 -0
  92. package/src/ui.render-welcome.test.ts +505 -0
  93. package/src/ui.scroll-additive.test.ts +91 -21
  94. package/src/ui.scroll.test.ts +302 -14
  95. package/src/ui.suggest-replies.test.ts +1112 -1
  96. package/src/ui.ts +1008 -149
  97. package/src/ui.welcome.test.ts +504 -0
  98. package/src/utils/artifact-file.test.ts +1 -1
  99. package/src/utils/artifact-gate.test.ts +27 -0
  100. package/src/utils/artifact-gate.ts +9 -0
  101. package/src/utils/attachment-manager.test.ts +37 -0
  102. package/src/utils/attachment-manager.ts +15 -0
  103. package/src/utils/auto-follow.test.ts +23 -0
  104. package/src/utils/auto-follow.ts +11 -1
  105. package/src/utils/code-generators.test.ts +40 -0
  106. package/src/utils/code-generators.ts +22 -0
  107. package/src/utils/config-merge.test.ts +24 -0
  108. package/src/utils/config-merge.ts +2 -0
  109. package/src/utils/content-width.test.ts +37 -0
  110. package/src/utils/content-width.ts +17 -0
  111. package/src/utils/icons.test.ts +26 -0
  112. package/src/utils/icons.ts +8 -0
  113. package/src/utils/plugin-storage.test.ts +94 -0
  114. package/src/utils/plugin-storage.ts +98 -0
  115. package/src/utils/theme.test.ts +216 -0
  116. package/src/utils/tokens.ts +113 -3
  117. package/src/welcome.test.ts +213 -0
  118. package/src/welcome.ts +130 -0
  119. package/dist/chunk-MMUPR2JW.js +0 -1
@@ -269,6 +269,82 @@ interface SmartDomAdapterOptions {
269
269
  */
270
270
  declare function smartDomResultToEnriched(result: SmartDOMResult, opts?: SmartDomAdapterOptions): EnrichedPageElement[];
271
271
 
272
+ interface ResolvedWelcomeConfig {
273
+ title: string;
274
+ subtitle: string;
275
+ icon?: AgentWidgetWelcomeIcon;
276
+ variant: AgentWidgetWelcomeVariant;
277
+ dismiss: AgentWidgetWelcomeDismiss;
278
+ /** Undefined under `variant: "hero"`, which suppresses the greeting. */
279
+ message?: string;
280
+ }
281
+
282
+ /**
283
+ * Synchronous key-value store handed to plugin render hooks. Backed by
284
+ * `localStorage`, namespaced per plugin id. The async `storageAdapter` cannot
285
+ * back a synchronous API and `persistState` is not a general key-value surface.
286
+ */
287
+ interface AgentWidgetPluginStorage {
288
+ get: (key: string) => string | null;
289
+ set: (key: string, value: string) => void;
290
+ remove: (key: string) => void;
291
+ }
292
+
293
+ type AgentWidgetTransformSuggestionsContext = {
294
+ /**
295
+ * Fresh copy of the suggestions entering this plugin, already normalized:
296
+ * shorthand strings expanded and the effective send/fill behavior resolved.
297
+ */
298
+ suggestions: AgentWidgetResolvedSuggestion[];
299
+ surface: AgentWidgetSuggestionSurface;
300
+ source: AgentWidgetSuggestionSource;
301
+ config: AgentWidgetConfig;
302
+ };
303
+ type AgentWidgetRenderSuggestionContext = {
304
+ /** Fully normalized suggestion, including the effective send/fill behavior. */
305
+ suggestion: AgentWidgetResolvedSuggestion;
306
+ index: number;
307
+ surface: AgentWidgetSuggestionSurface;
308
+ source: AgentWidgetSuggestionSource;
309
+ variant: AgentWidgetSuggestionVariant;
310
+ streaming: boolean;
311
+ config: AgentWidgetConfig;
312
+ /** Build Persona's standard chip, card, or list item. */
313
+ defaultRenderer: () => HTMLElement;
314
+ /**
315
+ * Run the normal selection pipeline: DOM event, plugin selection hooks, then
316
+ * send or fill. Custom renderers should call this instead of sending directly.
317
+ */
318
+ select: () => void;
319
+ };
320
+ type AgentWidgetRenderWelcomeContext = {
321
+ /** Alias-resolved welcome config; never the raw `copy.welcome*` shape. */
322
+ config: ResolvedWelcomeConfig;
323
+ variant: AgentWidgetWelcomeVariant;
324
+ /** Derived visibility for this render; governs the default renderer only. */
325
+ visible: boolean;
326
+ /** The core-owned welcome card, live-updated by `controller.update()`. */
327
+ defaultRenderer: () => HTMLElement;
328
+ sendMessage: (text: string) => void;
329
+ /** Re-run welcome arbitration: cleanups run, previous content is removed. */
330
+ requestRender: () => void;
331
+ /**
332
+ * Persona's starter renderer, wired through the full select pipeline:
333
+ * `onSuggestionSelect`, cancelable `persona:suggestion:*` events, send/fill.
334
+ */
335
+ renderStarter: (suggestion: AgentWidgetSuggestion) => HTMLElement;
336
+ /** Sync store namespaced by `persistState.keyPrefix` plus the plugin id. */
337
+ storage: AgentWidgetPluginStorage;
338
+ /** Teardown for this render; runs before the next one and on destroy. */
339
+ onCleanup: (fn: () => void) => void;
340
+ };
341
+ type AgentWidgetSuggestionSelectContext = {
342
+ suggestion: AgentWidgetResolvedSuggestion;
343
+ surface: AgentWidgetSuggestionSurface;
344
+ source: AgentWidgetSuggestionSource;
345
+ variant: AgentWidgetSuggestionVariant;
346
+ config: AgentWidgetConfig;
347
+ };
272
348
  /**
273
349
  * Plugin interface for customizing widget components
274
350
  */
@@ -281,6 +357,25 @@ interface AgentWidgetPlugin {
281
357
  * Optional priority (higher = runs first). Default: 0
282
358
  */
283
359
  priority?: number;
360
+ /**
361
+ * Transform, filter, enrich, or reorder suggestions. Hooks receive normalized
362
+ * items and compose in plugin priority order; the return value may use the
363
+ * loose shape (string shorthand included) and is re-normalized before the next
364
+ * hook. A per-item `behavior` set here survives. `maxItems` caps the list
365
+ * after the full chain. Return an empty array to hide the surface.
366
+ */
367
+ transformSuggestions?: (context: AgentWidgetTransformSuggestionsContext) => AgentWidgetSuggestion[];
368
+ /**
369
+ * Render one starter or follow-up suggestion. Return null to use Persona's
370
+ * default chip/card/list renderer. Call `select()` from custom interaction
371
+ * controls to preserve events, selection hooks, and send/fill behavior.
372
+ */
373
+ renderSuggestion?: (context: AgentWidgetRenderSuggestionContext) => HTMLElement | null;
374
+ /**
375
+ * Observe or intercept a suggestion selection. Return false to cancel the
376
+ * built-in send/fill action. Hooks run in plugin priority order.
377
+ */
378
+ onSuggestionSelect?: (context: AgentWidgetSuggestionSelectContext) => void | boolean;
284
379
  /**
285
380
  * Custom renderer for message bubbles
286
381
  * Return null to use default renderer
@@ -299,6 +394,34 @@ interface AgentWidgetPlugin {
299
394
  defaultRenderer: () => HTMLElement;
300
395
  onToggle: () => void;
301
396
  }) => HTMLElement | null;
397
+ /**
398
+ * Custom renderer for the welcome surface (card, hero, greeting stack).
399
+ * First plugin returning an element wins; null falls through to the default,
400
+ * same contract as `renderSuggestion`.
401
+ *
402
+ * The core owns the welcome host: it stays mounted for the panel's lifetime
403
+ * and content is swapped inside it. A plugin element renders regardless of
404
+ * `ctx.visible` (derived visibility governs the default renderer only) and
405
+ * the host overlays the transcript while that element is active, so a home
406
+ * screen can return over an existing conversation. Return null again to
407
+ * clear the overlay.
408
+ *
409
+ * The ctx carries no controller: plugins are host-instantiated, so close a
410
+ * factory over the init handle (`plugin.attach(controller)`).
411
+ *
412
+ * @example
413
+ * ```typescript
414
+ * renderWelcome: ({ config, renderStarter, requestRender, onCleanup }) => {
415
+ * const root = document.createElement("div");
416
+ * root.textContent = config.title;
417
+ * root.appendChild(renderStarter({ label: "Track my order" }));
418
+ * const timer = setInterval(requestRender, 60_000);
419
+ * onCleanup(() => clearInterval(timer));
420
+ * return root;
421
+ * }
422
+ * ```
423
+ */
424
+ renderWelcome?: (context: AgentWidgetRenderWelcomeContext) => HTMLElement | null;
302
425
  /**
303
426
  * Custom renderer for panel header
304
427
  * Return null to use default renderer
@@ -343,6 +466,15 @@ interface AgentWidgetPlugin {
343
466
  * Omitted when `config.voiceRecognition.enabled` is not true.
344
467
  */
345
468
  onVoiceToggle?: () => void;
469
+ /**
470
+ * Re-run composer arbitration and swap the footer in place. The hook runs
471
+ * once at panel construction, so this is how a composer plugin unlocks
472
+ * (returns null) or re-renders later. Pending attachments, the mention
473
+ * affordances, and the composer listener registry are re-wired by the core.
474
+ */
475
+ requestRender: () => void;
476
+ /** Same sync store as the `renderWelcome` ctx, namespaced per plugin id. */
477
+ storage: AgentWidgetPluginStorage;
346
478
  }) => HTMLElement | null;
347
479
  /**
348
480
  * Custom renderer for reasoning bubbles
@@ -723,6 +855,15 @@ interface PanelTokens extends ComponentTokenSet {
723
855
  /** Background of the region revealed behind a detached panel. */
724
856
  canvasBackground?: string;
725
857
  }
858
+ /** Per-element text styling shared by themable text surfaces. */
859
+ interface TextStyleTokens {
860
+ fontFamily?: string;
861
+ fontSize?: string;
862
+ fontWeight?: string;
863
+ lineHeight?: string;
864
+ letterSpacing?: string;
865
+ color?: TokenReference<'color'>;
866
+ }
726
867
  interface HeaderTokens extends ComponentTokenSet {
727
868
  background: TokenReference<'color'>;
728
869
  border: TokenReference<'color'>;
@@ -731,10 +872,14 @@ interface HeaderTokens extends ComponentTokenSet {
731
872
  iconBackground: TokenReference<'color'>;
732
873
  /** Foreground (glyph stroke or emoji text) on the header avatar tile. */
733
874
  iconForeground: TokenReference<'color'>;
734
- /** Header title line (next to the icon, or minimal layout title). */
875
+ /** Legacy alias of `title.color`; `title.color` wins when both are set. */
735
876
  titleForeground: TokenReference<'color'>;
736
- /** Header subtitle line under the title. */
877
+ /** Legacy alias of `subtitle.color`; `subtitle.color` wins when both are set. */
737
878
  subtitleForeground: TokenReference<'color'>;
879
+ /** Header title typography (next to the icon, or minimal layout title). */
880
+ title?: TextStyleTokens;
881
+ /** Header subtitle typography, for the line under the title. */
882
+ subtitle?: TextStyleTokens;
738
883
  /** Default color for clear / close icon buttons when launcher overrides are unset. */
739
884
  actionIconForeground: TokenReference<'color'>;
740
885
  /** Box-shadow on the header (e.g., a fade shadow to replace the default border). */
@@ -764,7 +909,7 @@ interface MessageTokens {
764
909
  }
765
910
  /**
766
911
  * Welcome / intro card rendered above the message list when no messages exist.
767
- * Set `copy.showWelcomeCard: false` to hide it; use `layout.slots["body-top"]`
912
+ * Set `welcome.variant: "none"` to hide it; use `layout.slots["body-top"]`
768
913
  * to replace it wholesale.
769
914
  */
770
915
  interface IntroCardTokens extends ComponentTokenSet {
@@ -773,6 +918,12 @@ interface IntroCardTokens extends ComponentTokenSet {
773
918
  padding?: TokenReference<'spacing'>;
774
919
  /** Box-shadow on the intro card (token ref or raw CSS, e.g. `none`). */
775
920
  shadow?: string;
921
+ /** Full border shorthand on the intro card (raw CSS, e.g. `"1px solid rgba(0,0,0,0.1)"`). @default "none" */
922
+ border?: string;
923
+ /** Welcome title typography. */
924
+ title?: TextStyleTokens;
925
+ /** Welcome subtitle typography. */
926
+ subtitle?: TextStyleTokens;
776
927
  }
777
928
  /** Collapsible widget chrome (tool bubbles, reasoning bubbles, approval bubbles). */
778
929
  interface CollapsibleWidgetTokens {
@@ -815,6 +966,8 @@ interface MarkdownTokens {
815
966
  background?: TokenReference<'color'>;
816
967
  borderColor?: TokenReference<'color'>;
817
968
  textColor?: TokenReference<'color'>;
969
+ /** Corner radius; follows `palette.radius.md` by default so square-corner themes get square code blocks. */
970
+ borderRadius?: TokenReference<'radius'>;
818
971
  };
819
972
  /** Table styling. */
820
973
  table?: {
@@ -872,10 +1025,27 @@ interface ToolBubbleTokens {
872
1025
  interface ReasoningBubbleTokens {
873
1026
  shadow: string;
874
1027
  }
1028
+ /** Scrollbar appearance shared by every scroller in the widget. */
1029
+ interface ScrollbarTokens {
1030
+ /** Thumb color. @default semantic.colors.border */
1031
+ thumb?: TokenReference<'color'>;
1032
+ /** Track color. @default "transparent" */
1033
+ track?: TokenReference<'color'>;
1034
+ }
875
1035
  /** Composer (message input) chrome. */
876
1036
  interface ComposerChromeTokens {
877
1037
  /** Box-shadow on the composer form (raw CSS, e.g. `none`). */
878
1038
  shadow: string;
1039
+ /** Border color of the composer form. @default semantic.colors.border */
1040
+ borderColor?: TokenReference<'color'>;
1041
+ /** Inner padding of the composer form (raw CSS shorthand). @default "0.75rem 1rem" */
1042
+ padding?: string;
1043
+ /** Gap between the textarea row and the actions row. @default "0.5rem" */
1044
+ gap?: string;
1045
+ /** Composer textarea font-size. @default "0.875rem" */
1046
+ fontSize?: string;
1047
+ /** Composer textarea line-height. @default "1.25rem" */
1048
+ lineHeight?: string;
879
1049
  }
880
1050
  /** Artifact toolbar chrome. */
881
1051
  interface ArtifactToolbarTokens {
@@ -1025,6 +1195,29 @@ interface ScrollToBottomTokens extends ComponentTokenSet {
1025
1195
  fontSize?: string;
1026
1196
  iconSize?: string;
1027
1197
  }
1198
+ /** Visual tokens shared by one suggestion presentation variant. */
1199
+ interface SuggestionVariantTokens extends ComponentTokenSet {
1200
+ /** Space inside one item, between its icon and its copy. */
1201
+ gap?: string;
1202
+ /** Space between suggestion items in the container. */
1203
+ itemGap?: string;
1204
+ minHeight?: string;
1205
+ fontSize?: string;
1206
+ lineHeight?: string;
1207
+ iconSize?: string;
1208
+ hoverBackground?: TokenReference<'color'>;
1209
+ hoverForeground?: TokenReference<'color'>;
1210
+ hoverBorder?: TokenReference<'color'>;
1211
+ pressedBackground?: TokenReference<'color'>;
1212
+ focusRing?: TokenReference<'color'>;
1213
+ disabledOpacity?: string;
1214
+ }
1215
+ /** Starter and follow-up suggestion chrome. */
1216
+ interface SuggestionTokens {
1217
+ chip: SuggestionVariantTokens;
1218
+ card: SuggestionVariantTokens;
1219
+ list: SuggestionVariantTokens;
1220
+ }
1028
1221
  /** Toggle group chrome (used by createToggleGroup). */
1029
1222
  interface ToggleGroupTokens {
1030
1223
  /** Gap between toggle buttons. Default: 0 (connected). */
@@ -1049,12 +1242,16 @@ interface ComponentTokens {
1049
1242
  toolBubble: ToolBubbleTokens;
1050
1243
  reasoningBubble: ReasoningBubbleTokens;
1051
1244
  composer: ComposerChromeTokens;
1245
+ /** Scrollbar appearance for every scroller in the widget. */
1246
+ scrollbar?: ScrollbarTokens;
1052
1247
  /** Icon button styling tokens. */
1053
1248
  iconButton?: IconButtonTokens;
1054
1249
  /** Label button styling tokens. */
1055
1250
  labelButton?: LabelButtonTokens;
1056
1251
  /** Scroll-to-bottom indicator styling tokens. */
1057
1252
  scrollToBottom?: ScrollToBottomTokens;
1253
+ /** Starter prompt and follow-up suggestion styling tokens. */
1254
+ suggestion?: SuggestionTokens;
1058
1255
  /** Toggle group styling tokens. */
1059
1256
  toggleGroup?: ToggleGroupTokens;
1060
1257
  /** Artifact toolbar, tab strip, and pane chrome. */
@@ -1349,7 +1546,24 @@ type RuntypeExecutionStreamEvent = ({
1349
1546
  type: "source";
1350
1547
  url?: string;
1351
1548
  [key: string]: unknown;
1549
+ } | {
1550
+ executionId: string;
1551
+ iteration?: number;
1552
+ seq: number;
1553
+ state: Record<string, unknown>;
1554
+ type: "state_snapshot";
1352
1555
  } | ({
1556
+ executionId: string;
1557
+ iteration?: number;
1558
+ patch: Array<{
1559
+ from?: string;
1560
+ op: "add" | "remove" | "replace" | "move" | "copy" | "test";
1561
+ path: string;
1562
+ value?: unknown;
1563
+ }>;
1564
+ seq: number;
1565
+ type: "state_delta";
1566
+ }) | ({
1353
1567
  executionId: string;
1354
1568
  hiddenParameterNames?: Array<string>;
1355
1569
  iteration?: number;
@@ -1591,6 +1805,7 @@ declare const LUCIDE_ICONS: {
1591
1805
  readonly plus: IconNode;
1592
1806
  readonly minus: IconNode;
1593
1807
  readonly pencil: IconNode;
1808
+ readonly "pen-line": IconNode;
1594
1809
  readonly trash: IconNode;
1595
1810
  readonly "trash-2": IconNode;
1596
1811
  readonly save: IconNode;
@@ -1633,11 +1848,13 @@ declare const LUCIDE_ICONS: {
1633
1848
  readonly folder: IconNode;
1634
1849
  readonly "folder-open": IconNode;
1635
1850
  readonly files: IconNode;
1851
+ readonly "chart-column": IconNode;
1636
1852
  readonly sparkles: IconNode;
1637
1853
  readonly zap: IconNode;
1638
1854
  readonly sun: IconNode;
1639
1855
  readonly moon: IconNode;
1640
1856
  readonly flag: IconNode;
1857
+ readonly lightbulb: IconNode;
1641
1858
  readonly monitor: IconNode;
1642
1859
  readonly smartphone: IconNode;
1643
1860
  };
@@ -2635,6 +2852,13 @@ type AgentWidgetArtifactsLayoutConfig = {
2635
2852
  * @default 520
2636
2853
  */
2637
2854
  narrowHostMaxWidth?: number;
2855
+ /**
2856
+ * Width of the slide-over artifact drawer (the narrow-host and mobile
2857
+ * presentations; `paneWidth`/`paneMaxWidth` only size the desktop split).
2858
+ * CSS length; `"100%"` makes the drawer cover the whole panel.
2859
+ * @default min(calc(100% - 1rem), 22rem)
2860
+ */
2861
+ drawerWidth?: string;
2638
2862
  /**
2639
2863
  * When true (default), widen the launcher panel while artifacts are visible and not user-dismissed.
2640
2864
  * No-op for inline embed (`launcher.enabled === false`).
@@ -3263,6 +3487,18 @@ type AgentWidgetScrollMode = "follow" | "anchor-top" | "none";
3263
3487
  * restored thread opens at a readable place rather than at the very bottom.
3264
3488
  */
3265
3489
  type AgentWidgetScrollRestorePosition = "bottom" | "last-user-turn";
3490
+ /**
3491
+ * Scrollbar visibility policy for every scroll surface in the widget.
3492
+ *
3493
+ * - `"on-scroll"` hides bars at rest and reveals them on genuine reader
3494
+ * input, keeping them visible while the reader is away from the resting
3495
+ * position: overlay-scrollbar semantics on every platform.
3496
+ * - `"auto"` keeps the themed bar under native visibility semantics: overlay
3497
+ * platforms still auto-hide, classic-scrollbar platforms (Windows,
3498
+ * always-show macOS) show it persistently.
3499
+ * - `"hidden"` never shows an indicator. Scrolling itself is unaffected.
3500
+ */
3501
+ type AgentWidgetScrollbarPolicy = "on-scroll" | "auto" | "hidden";
3266
3502
  type AgentWidgetScrollBehaviorFeature = {
3267
3503
  /** Scroll behavior during streamed responses. @default "anchor-top" */
3268
3504
  mode?: AgentWidgetScrollMode;
@@ -3307,6 +3543,13 @@ type AgentWidgetScrollBehaviorFeature = {
3307
3543
  * @default false
3308
3544
  */
3309
3545
  announce?: boolean;
3546
+ /**
3547
+ * Scrollbar visibility policy, applied to the transcript and every inner
3548
+ * scroller through one root attribute. Themable via
3549
+ * `--persona-scrollbar-thumb` and `--persona-scrollbar-track`.
3550
+ * @default "on-scroll"
3551
+ */
3552
+ scrollbar?: AgentWidgetScrollbarPolicy;
3310
3553
  };
3311
3554
  type AgentWidgetScrollToBottomFeature = {
3312
3555
  /**
@@ -3596,10 +3839,13 @@ type AgentWidgetFeatureFlags = {
3596
3839
  */
3597
3840
  askUserQuestion?: AgentWidgetAskUserQuestionFeature;
3598
3841
  /**
3599
- * Built-in `suggest_replies` quick-reply chips. When the assistant invokes
3600
- * the tool, the widget shows the suggestions as tappable chips above the
3601
- * composer (reusing the suggestion-chips surface) and immediately resumes
3602
- * the execution: fire-and-forget, no user input awaited.
3842
+ * Built-in `suggest_replies` follow-ups. When the assistant invokes the
3843
+ * tool, the widget shows the suggestions using `suggestions.followUps` and
3844
+ * immediately resumes the execution: fire-and-forget, no user input awaited.
3845
+ *
3846
+ * @deprecated Both keys moved to `suggestions.followUps`: use
3847
+ * `suggestions.followUps.enabled` and `suggestions.followUps.expose`. This
3848
+ * alias keeps working per key, with `suggestions.followUps` winning.
3603
3849
  */
3604
3850
  suggestReplies?: AgentWidgetSuggestRepliesFeature;
3605
3851
  };
@@ -3639,11 +3885,12 @@ type WidgetPreferenceSlice = {
3639
3885
  };
3640
3886
  };
3641
3887
  /**
3642
- * Feature config for the built-in `suggest_replies` quick-reply chips.
3643
- * Chips render in the existing suggestions slot above the composer and are
3644
- * styled by the widget-level `suggestionChipsConfig`. A tapped chip is sent
3645
- * verbatim as the user's next message; chips clear once any user message
3646
- * follows them.
3888
+ * Feature config for the built-in `suggest_replies` follow-ups.
3889
+ *
3890
+ * @deprecated Both keys moved to `suggestions.followUps`
3891
+ * (`suggestions.followUps.enabled` / `suggestions.followUps.expose`), which
3892
+ * also owns presentation, placement, and send/fill behavior. Resolution is
3893
+ * per key with `suggestions.followUps` winning; removal waits for 5.0.
3647
3894
  */
3648
3895
  type AgentWidgetSuggestRepliesFeature = {
3649
3896
  /**
@@ -3651,14 +3898,18 @@ type AgentWidgetSuggestRepliesFeature = {
3651
3898
  * renders as a regular tool bubble and is NOT auto-resumed: only set this
3652
3899
  * with no server-side `suggest_replies` declaration, or the execution
3653
3900
  * parks awaiting a resume that never comes.
3901
+ * @deprecated Use `suggestions.followUps.enabled`.
3654
3902
  */
3655
3903
  enabled?: boolean;
3656
3904
  /**
3657
3905
  * Advertise the built-in `suggest_replies` tool to the agent on every
3658
3906
  * dispatch via `clientTools[]`: no server-side `runtimeTools` declaration
3659
- * needed. Defaults to `false`: flows that already declare the tool via
3660
- * `runtimeTools` would otherwise present it to the model twice. Ignored
3661
- * when `enabled` is `false`.
3907
+ * needed. Defaults to `false`. Declare the tool in one place: the API
3908
+ * dedupes a double declaration by name with `clientTools[]` winning, so the
3909
+ * model still sees exactly one tool, but the widget's description and schema
3910
+ * win and the effective tool type becomes `local` (the execution pauses and
3911
+ * the widget auto-resumes it). Ignored when `enabled` is `false`.
3912
+ * @deprecated Use `suggestions.followUps.expose`.
3662
3913
  */
3663
3914
  expose?: boolean;
3664
3915
  };
@@ -3728,10 +3979,13 @@ type AgentWidgetAskUserQuestionFeature = {
3728
3979
  * matching {@link AskUserQuestionPayload}; when the model calls it, the
3729
3980
  * existing answer-pill sheet renders and the answer resumes the execution.
3730
3981
  *
3731
- * Defaults to `false`: flows that already declare `ask_user_question` via
3732
- * `runtimeTools` would otherwise present the tool to the model twice.
3733
- * Ignored when `enabled` is `false`: never offer the agent a question
3734
- * tool the widget can't render an answer UI for.
3982
+ * Defaults to `false`. Declare the tool in one place: the API dedupes a
3983
+ * double declaration by name with `clientTools[]` winning, so the model
3984
+ * still sees exactly one tool, but the widget's description and schema win
3985
+ * and the effective tool type becomes `local` (the execution pauses and the
3986
+ * widget resumes it with the user's answer). Ignored when `enabled` is
3987
+ * `false`: never offer the agent a question tool the widget can't render an
3988
+ * answer UI for.
3735
3989
  */
3736
3990
  expose?: boolean;
3737
3991
  /** Slide-in animation duration in ms. Defaults to 180. */
@@ -4196,6 +4450,57 @@ type AgentWidgetLauncherConfig = {
4196
4450
  * @example "min(380px, calc(100vw - 48px))"
4197
4451
  */
4198
4452
  collapsedMaxWidth?: string;
4453
+ /**
4454
+ * Proactive nudge bubble anchored above the collapsed floating launcher.
4455
+ * Omit it (the default) and no teaser renders.
4456
+ */
4457
+ teaser?: AgentWidgetLauncherTeaserConfig;
4458
+ };
4459
+ /**
4460
+ * Launcher teaser: a small bubble above the collapsed launcher. Clicking it
4461
+ * opens the panel and consumes the teaser exactly like an explicit dismissal.
4462
+ * Within a single page load the teaser appears at most once under either
4463
+ * frequency.
4464
+ *
4465
+ * @example
4466
+ * ```typescript
4467
+ * launcher: {
4468
+ * teaser: { text: "Need a hand picking a plan?", delayMs: 3000 }
4469
+ * }
4470
+ * ```
4471
+ */
4472
+ type AgentWidgetLauncherTeaserConfig = {
4473
+ /** Bubble copy. An empty string renders no teaser. */
4474
+ text: string;
4475
+ /**
4476
+ * Delay before the bubble appears, in milliseconds.
4477
+ * @default 0
4478
+ */
4479
+ delayMs?: number;
4480
+ /**
4481
+ * How often the teaser may return.
4482
+ *
4483
+ * `"once"`: a dismissal or a click-through is remembered in `localStorage`
4484
+ * under `` `${persistState.keyPrefix ?? "persona-"}teaser-dismissed` `` and
4485
+ * the teaser never returns on that browser. `"always"`: shows on every page
4486
+ * load, tracked in memory only. `persistState: false` downgrades `"once"` to
4487
+ * in-memory too, so the teaser reappears each load.
4488
+ *
4489
+ * Custom `storageAdapter` implementations do not participate in teaser state.
4490
+ *
4491
+ * @default "once"
4492
+ */
4493
+ frequency?: "once" | "always";
4494
+ /**
4495
+ * Render an x affordance that dismisses the teaser without opening the panel.
4496
+ * @default true
4497
+ */
4498
+ dismissible?: boolean;
4499
+ /**
4500
+ * Accessible label for the dismiss affordance.
4501
+ * @default "Dismiss message"
4502
+ */
4503
+ dismissLabel?: string;
4199
4504
  };
4200
4505
  type AgentWidgetSendButtonConfig = {
4201
4506
  borderWidth?: string;
@@ -4204,6 +4509,10 @@ type AgentWidgetSendButtonConfig = {
4204
4509
  paddingY?: string;
4205
4510
  iconText?: string;
4206
4511
  iconName?: string;
4512
+ /** Icon glyph box in icon mode, e.g. "20px". Defaults to half of `size`. */
4513
+ iconSize?: string;
4514
+ /** Stroke width for the icon glyph, in viewBox units. Default 2. */
4515
+ iconStrokeWidth?: number;
4207
4516
  useIcon?: boolean;
4208
4517
  tooltipText?: string;
4209
4518
  showTooltip?: boolean;
@@ -4927,6 +5236,177 @@ type AgentWidgetSuggestionChipsConfig = {
4927
5236
  paddingX?: string;
4928
5237
  paddingY?: string;
4929
5238
  };
5239
+ /** A suggested next action shown to the user. Strings remain supported as shorthand. */
5240
+ type AgentWidgetSuggestion = string | {
5241
+ /** Stable analytics / DOM identity. Falls back to the prompt or label. */
5242
+ id?: string;
5243
+ /** Concise visible action label. */
5244
+ label: string;
5245
+ /** Text placed in the composer or sent. Defaults to `label`. */
5246
+ prompt?: string;
5247
+ /**
5248
+ * Optional supporting copy, most useful with the `card` variant. The
5249
+ * `chip` variant ignores it and renders the label alone.
5250
+ */
5251
+ description?: string;
5252
+ /** Optional icon from Persona's bundled Lucide registry. */
5253
+ icon?: IconName;
5254
+ /**
5255
+ * CSS color for this item's glyph. Tints the icon only: label, border,
5256
+ * and background stay neutral. Ignored without an `icon`.
5257
+ */
5258
+ iconColor?: string;
5259
+ /** Override the surface's click behavior for this item. */
5260
+ behavior?: AgentWidgetSuggestionBehavior;
5261
+ /** Visually promote one high-confidence action. */
5262
+ emphasis?: "default" | "primary";
5263
+ };
5264
+ type AgentWidgetSuggestionBehavior = "send" | "fill";
5265
+ type AgentWidgetSuggestionVariant = "chip" | "card" | "list";
5266
+ type AgentWidgetSuggestionSurface = "starter" | "followUp";
5267
+ /**
5268
+ * Who produced the rendered suggestions: static config, the agent's
5269
+ * `suggest_replies` tool, or `controller.setFollowUpSuggestions`.
5270
+ */
5271
+ type AgentWidgetSuggestionSource = "config" | "agent" | "host";
5272
+ /**
5273
+ * A suggestion after Persona has normalized shorthand strings and resolved the
5274
+ * effective click behavior. Plugin hooks receive this stable shape.
5275
+ */
5276
+ type AgentWidgetResolvedSuggestion = {
5277
+ id: string;
5278
+ label: string;
5279
+ prompt: string;
5280
+ description?: string;
5281
+ icon?: IconName;
5282
+ /** CSS color for the glyph only; never the label, border, or background. */
5283
+ iconColor?: string;
5284
+ behavior: AgentWidgetSuggestionBehavior;
5285
+ emphasis: "default" | "primary";
5286
+ };
5287
+ /** Presentation keys shared by every suggestion surface. */
5288
+ type AgentWidgetSuggestionSurfaceConfig = {
5289
+ /** Item density. Defaults to `card` for starters and `chip` for follow-ups. */
5290
+ variant?: AgentWidgetSuggestionVariant;
5291
+ /** Default click behavior, overridable per item. Defaults to `send`. */
5292
+ behavior?: AgentWidgetSuggestionBehavior;
5293
+ /**
5294
+ * Layout for items past the surface width. Defaults to `wrap` on every
5295
+ * surface; `scroll` opts into a horizontal strip for large sets. Applies
5296
+ * to the `chip` variant only; the `card` and `list` layouts manage their
5297
+ * own stacking.
5298
+ */
5299
+ overflow?: "scroll" | "wrap";
5300
+ /** Maximum visible items. Defaults to 4. */
5301
+ maxItems?: number;
5302
+ };
5303
+ type AgentWidgetStarterSuggestionsConfig = AgentWidgetSuggestionSurfaceConfig & {
5304
+ /** Suggestions shown before the first user message. */
5305
+ items?: AgentWidgetSuggestion[];
5306
+ /**
5307
+ * `auto` uses the welcome surface when the welcome card renders and the
5308
+ * composer otherwise. Explicit values are literal: `welcome` renders
5309
+ * nothing when the welcome card is hidden. Defaults to `auto`.
5310
+ */
5311
+ placement?: "auto" | "welcome" | "composer";
5312
+ };
5313
+ type AgentWidgetFollowUpSuggestionsConfig = AgentWidgetSuggestionSurfaceConfig & {
5314
+ /**
5315
+ * Enable the built-in `suggest_replies` follow-ups. Defaults to true. When
5316
+ * false, `suggest_replies` renders as a regular tool bubble and is NOT
5317
+ * auto-resumed: only set this with no server-side `suggest_replies`
5318
+ * declaration, or the execution parks awaiting a resume that never comes.
5319
+ */
5320
+ enabled?: boolean;
5321
+ /**
5322
+ * Advertise the built-in `suggest_replies` tool to the agent on every
5323
+ * dispatch via `clientTools[]`: no server-side `runtimeTools` declaration
5324
+ * needed. Defaults to `false`. Declare the tool in one place: the API
5325
+ * dedupes a double declaration by name with `clientTools[]` winning, so
5326
+ * the model still sees exactly one tool, but the widget's description and
5327
+ * schema win and the effective tool type becomes `local` (the execution
5328
+ * pauses and the widget auto-resumes it). Ignored when `enabled` is
5329
+ * `false`.
5330
+ */
5331
+ expose?: boolean;
5332
+ /**
5333
+ * `auto` places follow-ups after the transcript in regular panels and above
5334
+ * the composer in composer-bar mode. Defaults to `auto`.
5335
+ */
5336
+ placement?: "auto" | "after-message" | "composer";
5337
+ };
5338
+ /**
5339
+ * Structured suggestions configuration. The legacy `suggestionChips` and
5340
+ * `suggestionChipsConfig` fields remain supported for backwards compatibility.
5341
+ */
5342
+ type AgentWidgetSuggestionsConfig = {
5343
+ starters?: AgentWidgetStarterSuggestionsConfig;
5344
+ followUps?: AgentWidgetFollowUpSuggestionsConfig;
5345
+ };
5346
+ /**
5347
+ * Which welcome surface renders.
5348
+ * `"card"` is the top-of-scroll intro card (default, back-compatible),
5349
+ * `"hero"` centers it in the empty conversation, `"none"` renders no surface.
5350
+ */
5351
+ type AgentWidgetWelcomeVariant = "card" | "hero" | "none";
5352
+ /**
5353
+ * When the welcome surface goes away. `"never"` is the default for `"card"`;
5354
+ * `"on-first-message"` is the default for `"hero"` and cannot be overridden
5355
+ * there.
5356
+ */
5357
+ type AgentWidgetWelcomeDismiss = "never" | "on-first-message";
5358
+ /**
5359
+ * Avatar or logo shown above the welcome title. Discriminated union; the
5360
+ * function form is the escape hatch for custom markup. There is deliberately
5361
+ * no raw HTML string variant (sanitization surface plus accessibility hole).
5362
+ */
5363
+ type AgentWidgetWelcomeIcon = {
5364
+ type: "lucide";
5365
+ name: IconName;
5366
+ }
5367
+ /** `alt` is required, and may be `""` for a decorative logo. */
5368
+ | {
5369
+ type: "image";
5370
+ url: string;
5371
+ alt: string;
5372
+ }
5373
+ /** Emoji or short text glyph. */
5374
+ | {
5375
+ type: "text";
5376
+ text: string;
5377
+ } | (() => HTMLElement | SVGElement);
5378
+ /**
5379
+ * First-open welcome surface. Consolidates the deprecated
5380
+ * `copy.welcomeTitle` / `copy.welcomeSubtitle` / `copy.showWelcomeCard`
5381
+ * options; a field present here wins over its legacy alias.
5382
+ *
5383
+ * All defaults live in the resolver (`resolveWelcomeConfig`), never in
5384
+ * `DEFAULT_WIDGET_CONFIG`: presence on this object is what distinguishes a
5385
+ * host-set value from a filled-in default.
5386
+ */
5387
+ interface AgentWidgetWelcomeConfig {
5388
+ /** Card or hero title. Defaults to "Hello 👋". */
5389
+ title?: string;
5390
+ /** Scope statement in the assistant's voice. Empty string omits it. */
5391
+ subtitle?: string;
5392
+ /** Avatar or logo shown above the title. */
5393
+ icon?: AgentWidgetWelcomeIcon;
5394
+ /** @default "card" */
5395
+ variant?: AgentWidgetWelcomeVariant;
5396
+ /** @default "never" for `card`, always `"on-first-message"` for `hero`. */
5397
+ dismiss?: AgentWidgetWelcomeDismiss;
5398
+ /**
5399
+ * Display-only greeting bubble pinned at transcript position zero. It is UI
5400
+ * chrome derived from config, never a session message: it does not appear in
5401
+ * `getMessages()`, is never persisted or sent to the model, `onMessage` does
5402
+ * not fire for it, and it survives `clearChat()`. Use an assistant
5403
+ * `initialMessages` entry instead when the model should see the greeting.
5404
+ *
5405
+ * Ignored (with a debug-mode warning) when `variant` is `"hero"`: the hero
5406
+ * is the greeting.
5407
+ */
5408
+ message?: string;
5409
+ }
4930
5410
  /**
4931
5411
  * Interface for pluggable stream parsers that extract text from streaming responses.
4932
5412
  * Parsers handle incremental parsing to extract text values from structured formats (JSON, XML, etc.).
@@ -5108,7 +5588,9 @@ type AgentWidgetHeaderLayoutConfig = {
5108
5588
  */
5109
5589
  render?: (context: HeaderRenderContext) => HTMLElement;
5110
5590
  /**
5111
- * Shown after the title in `minimal` header layout (e.g. chevron menu affordance).
5591
+ * Icon action buttons at the trailing edge of the `minimal` header layout,
5592
+ * clustered with the close button and sharing its chrome. For a menu
5593
+ * affordance next to the title, use `titleMenu` instead.
5112
5594
  */
5113
5595
  trailingActions?: AgentWidgetHeaderTrailingAction[];
5114
5596
  /** Called when a `trailingActions` button is clicked. */
@@ -5288,7 +5770,11 @@ type AgentWidgetLayoutConfig = {
5288
5770
  /**
5289
5771
  * Max width for the content area (messages + composer).
5290
5772
  * Applied with `margin: 0 auto` for centering.
5291
- * Accepts any CSS width value (e.g. "90ch", "720px", "80%").
5773
+ * Accepts any CSS width value (e.g. "90ch", "720px", "80%"), or "none"
5774
+ * for a full-width column. Only engages on panels wider than the cap,
5775
+ * so launcher-width panels are unaffected.
5776
+ * @default "768px" ("720px" in composer-bar mode via
5777
+ * `launcher.composerBar.contentMaxWidth`)
5292
5778
  */
5293
5779
  contentMaxWidth?: string;
5294
5780
  };
@@ -6036,7 +6522,9 @@ type AgentWidgetConfig = {
6036
6522
  */
6037
6523
  getHeaders?: AgentWidgetHeadersFunction;
6038
6524
  copy?: {
6525
+ /** @deprecated Use `welcome.title`. */
6039
6526
  welcomeTitle?: string;
6527
+ /** @deprecated Use `welcome.subtitle`. */
6040
6528
  welcomeSubtitle?: string;
6041
6529
  inputPlaceholder?: string;
6042
6530
  sendButtonLabel?: string;
@@ -6044,6 +6532,7 @@ type AgentWidgetConfig = {
6044
6532
  stopButtonLabel?: string;
6045
6533
  /**
6046
6534
  * When false, the welcome / intro card is not shown above the message list.
6535
+ * @deprecated Use `welcome.variant: "none"`.
6047
6536
  * @default true
6048
6537
  */
6049
6538
  showWelcomeCard?: boolean;
@@ -6108,7 +6597,17 @@ type AgentWidgetConfig = {
6108
6597
  * with `initialArtifacts`.
6109
6598
  */
6110
6599
  initialSelectedArtifactId?: string | null;
6600
+ /** Rich starter and follow-up suggestion surfaces. */
6601
+ suggestions?: AgentWidgetSuggestionsConfig;
6602
+ /**
6603
+ * First-open welcome surface (card, hero, or none) plus the optional
6604
+ * display-only greeting bubble. Supersedes `copy.welcomeTitle`,
6605
+ * `copy.welcomeSubtitle`, and `copy.showWelcomeCard`.
6606
+ */
6607
+ welcome?: AgentWidgetWelcomeConfig;
6608
+ /** @deprecated Prefer `suggestions.starters.items`. */
6111
6609
  suggestionChips?: string[];
6610
+ /** @deprecated Prefer semantic tokens under `theme.components.suggestion`. */
6112
6611
  suggestionChipsConfig?: AgentWidgetSuggestionChipsConfig;
6113
6612
  debug?: boolean;
6114
6613
  formEndpoint?: string;