@runtypelabs/persona 4.17.0 → 4.18.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 (165) hide show
  1. package/dist/animations/glyph-cycle.d.cts +1 -1
  2. package/dist/animations/glyph-cycle.d.ts +1 -1
  3. package/dist/animations/{types-BpWGm7_6.d.cts → types-DsmTD8TO.d.cts} +12 -0
  4. package/dist/animations/{types-BpWGm7_6.d.ts → types-DsmTD8TO.d.ts} +12 -0
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/chunk-3I2S3I5P.js +1 -0
  8. package/dist/chunk-76IELU7F.js +1 -0
  9. package/dist/chunk-EFJBUH5F.js +1 -0
  10. package/dist/chunk-PDPVORUF.js +1 -0
  11. package/dist/chunk-Q735PSZL.js +1 -0
  12. package/dist/chunk-VK6JOIYJ.js +1 -0
  13. package/dist/codegen.cjs +4 -4
  14. package/dist/codegen.js +4 -4
  15. package/dist/{context-mentions-BTSTIBFC.js → context-mentions-MJL6S6P2.js} +1 -1
  16. package/dist/context-mentions.cjs +9 -8
  17. package/dist/context-mentions.d.cts +1224 -14
  18. package/dist/context-mentions.d.ts +1224 -14
  19. package/dist/context-mentions.js +9 -8
  20. package/dist/event-stream-view-4EZ7BGFK.js +1 -0
  21. package/dist/event-stream-view.cjs +129 -0
  22. package/dist/event-stream-view.d.cts +8384 -0
  23. package/dist/event-stream-view.d.ts +8384 -0
  24. package/dist/event-stream-view.js +129 -0
  25. package/dist/history-view-BD6OXLNA.js +898 -0
  26. package/dist/history-view.cjs +898 -0
  27. package/dist/history-view.d.cts +1461 -0
  28. package/dist/history-view.d.ts +1461 -0
  29. package/dist/history-view.js +898 -0
  30. package/dist/index.cjs +77 -77
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +2040 -21
  33. package/dist/index.d.ts +2040 -21
  34. package/dist/index.global.js +67 -66
  35. package/dist/index.global.js.map +1 -1
  36. package/dist/index.js +77 -77
  37. package/dist/index.js.map +1 -1
  38. package/dist/launcher.global.js +5 -4
  39. package/dist/launcher.global.js.map +1 -1
  40. package/dist/plugin-kit.cjs +1 -1
  41. package/dist/plugin-kit.d.cts +0 -8
  42. package/dist/plugin-kit.d.ts +0 -8
  43. package/dist/plugin-kit.js +1 -1
  44. package/dist/smart-dom-reader.d.cts +1224 -14
  45. package/dist/smart-dom-reader.d.ts +1224 -14
  46. package/dist/theme-editor-preview.cjs +970 -73
  47. package/dist/theme-editor-preview.d.cts +1343 -15
  48. package/dist/theme-editor-preview.d.ts +1343 -15
  49. package/dist/theme-editor-preview.js +81 -81
  50. package/dist/theme-editor.cjs +7 -7
  51. package/dist/theme-editor.d.cts +1227 -15
  52. package/dist/theme-editor.d.ts +1227 -15
  53. package/dist/theme-editor.js +9 -9
  54. package/dist/theme-reference.cjs +1 -1
  55. package/dist/theme-reference.d.cts +226 -7
  56. package/dist/theme-reference.d.ts +226 -7
  57. package/dist/theme-reference.js +1 -1
  58. package/dist/webmcp-polyfill.js +2 -2
  59. package/dist/widget.css +1 -1
  60. package/package.json +18 -6
  61. package/src/client-history-rest.test.ts +1034 -0
  62. package/src/client-visitor-history.test.ts +596 -0
  63. package/src/client.ts +1040 -24
  64. package/src/components/artifact-inline.ts +8 -1
  65. package/src/components/artifact-pane.test.ts +278 -1
  66. package/src/components/artifact-pane.ts +135 -67
  67. package/src/components/artifact-preview.test.ts +14 -1
  68. package/src/components/artifact-preview.ts +9 -0
  69. package/src/components/header-builder.ts +3 -0
  70. package/src/components/header-layouts.test.ts +171 -7
  71. package/src/components/header-layouts.ts +83 -53
  72. package/src/components/header-parts.test.ts +154 -1
  73. package/src/components/header-parts.ts +266 -138
  74. package/src/components/history-confirm-dialog.ts +177 -0
  75. package/src/components/history-shell-copy.ts +99 -0
  76. package/src/components/history-view/announcer.ts +40 -0
  77. package/src/components/history-view/copy.ts +105 -0
  78. package/src/components/history-view/css.ts +1078 -0
  79. package/src/components/history-view/grouping.test.ts +142 -0
  80. package/src/components/history-view/grouping.ts +142 -0
  81. package/src/components/history-view/icons.ts +73 -0
  82. package/src/components/history-view/rows.ts +341 -0
  83. package/src/components/history-view/state.ts +309 -0
  84. package/src/components/history-view.test.ts +2660 -0
  85. package/src/components/history-view.ts +1891 -0
  86. package/src/components/launcher-surface.test.ts +133 -0
  87. package/src/components/launcher.test.ts +18 -0
  88. package/src/components/launcher.ts +7 -7
  89. package/src/components/panel.ts +17 -7
  90. package/src/components/welcome.ts +72 -2
  91. package/src/defaults.ts +36 -3
  92. package/src/event-stream-view-bundle.test.ts +69 -0
  93. package/src/event-stream-view-entry.ts +19 -0
  94. package/src/event-stream-view-loader.ts +31 -0
  95. package/src/event-stream-view.ts +19 -0
  96. package/src/generated/runtype-openapi-contract.ts +54 -4
  97. package/src/history-render.ts +325 -0
  98. package/src/history-view-bundle.test.ts +78 -0
  99. package/src/history-view-entry.ts +41 -0
  100. package/src/history-view-loader.test.ts +30 -0
  101. package/src/history-view-loader.ts +32 -0
  102. package/src/history-view.ts +28 -0
  103. package/src/index-core.ts +50 -2
  104. package/src/index-global.ts +55 -0
  105. package/src/internal/demo-history-provider.test.ts +402 -0
  106. package/src/internal/demo-history-provider.ts +689 -0
  107. package/src/internal/history-provider-registry.ts +25 -0
  108. package/src/internal/history-provider.ts +150 -0
  109. package/src/internal/runtype-history-provider.test.ts +313 -0
  110. package/src/internal/runtype-history-provider.ts +240 -0
  111. package/src/plugin-kit.test.ts +16 -0
  112. package/src/plugin-kit.ts +13 -5
  113. package/src/plugins/types.ts +177 -1
  114. package/src/session-history.test.ts +1397 -0
  115. package/src/session.ts +1200 -26
  116. package/src/styles/widget-styles.test.ts +133 -0
  117. package/src/styles/widget.css +227 -8
  118. package/src/theme-editor/preview-utils.test.ts +19 -0
  119. package/src/theme-editor/preview-utils.ts +15 -1
  120. package/src/theme-editor/preview.ts +7 -1
  121. package/src/theme-editor/role-mappings.ts +4 -1
  122. package/src/theme-editor/sections.test.ts +168 -1
  123. package/src/theme-editor/sections.ts +136 -7
  124. package/src/theme-editor/webmcp/summary.ts +16 -2
  125. package/src/theme-reference.ts +20 -6
  126. package/src/types/theme.ts +213 -7
  127. package/src/types.ts +927 -7
  128. package/src/ui.artifact-rail-resize.test.ts +235 -0
  129. package/src/ui.attachments-drop.test.ts +1 -0
  130. package/src/ui.header-update-stability.test.ts +2 -2
  131. package/src/ui.history-chunk.test.ts +72 -0
  132. package/src/ui.history-config-provider.test.ts +318 -0
  133. package/src/ui.history-controller.test.ts +711 -0
  134. package/src/ui.history-lifecycle.test.ts +456 -0
  135. package/src/ui.history-render-hooks.test.ts +904 -0
  136. package/src/ui.history-shell.test.ts +1887 -0
  137. package/src/ui.rail-overlay.test.ts +573 -0
  138. package/src/ui.rail-resize.test.ts +284 -0
  139. package/src/ui.rebuild-composer.test.ts +21 -0
  140. package/src/ui.render-welcome.test.ts +42 -0
  141. package/src/ui.shortcuts.test.ts +166 -0
  142. package/src/ui.ts +3071 -240
  143. package/src/utils/__fixtures__/client-conversation-history-wire.json +108 -0
  144. package/src/utils/artifact-file.ts +15 -0
  145. package/src/utils/artifact-gate.test.ts +4 -0
  146. package/src/utils/artifact-gate.ts +13 -17
  147. package/src/utils/artifact-resize.test.ts +28 -0
  148. package/src/utils/artifact-resize.ts +9 -2
  149. package/src/utils/buttons.ts +188 -1
  150. package/src/utils/config-merge.test.ts +91 -0
  151. package/src/utils/dropdown.ts +10 -1
  152. package/src/utils/history-messages.test.ts +284 -0
  153. package/src/utils/history-messages.ts +284 -0
  154. package/src/utils/icons.ts +3 -0
  155. package/src/utils/shortcuts.test.ts +288 -0
  156. package/src/utils/shortcuts.ts +221 -0
  157. package/src/utils/theme.test.ts +501 -4
  158. package/src/utils/theme.ts +42 -3
  159. package/src/utils/tokens.ts +153 -15
  160. package/src/utils/tooltip-timing.ts +4 -0
  161. package/src/utils/tooltip.test.ts +249 -7
  162. package/src/utils/tooltip.ts +146 -4
  163. package/src/utils/visitor-store.test.ts +425 -0
  164. package/src/utils/visitor-store.ts +337 -0
  165. package/dist/chunk-QJ4LV2KW.js +0 -1
package/src/types.ts CHANGED
@@ -6,6 +6,10 @@ import type {
6
6
  RuntypeStopReasonKind,
7
7
  } from "./generated/runtype-openapi-contract";
8
8
  import type { TargetResolver } from "./utils/target";
9
+ import type { VisitorStore } from "./utils/visitor-store";
10
+ import type { HistoryWireMessage } from "./utils/history-messages";
11
+ // Internal seam (not exported from any entry); type-only, so the cycle is erased.
12
+ import type { HistoryProvider } from "./internal/history-provider";
9
13
  import type { IconName } from "./utils/icons";
10
14
 
11
15
  export type { TargetResolver, ResolvedTarget } from "./utils/target";
@@ -1221,6 +1225,56 @@ export type AgentWidgetControllerEventMap = {
1221
1225
  "stream:resuming": { executionId: string; after: string; attempt: number };
1222
1226
  /** The durable turn resumed and reached its terminal after a reconnect. */
1223
1227
  "stream:resumed": { executionId: string; after: string };
1228
+ /**
1229
+ * History (Messages) surface events. Payloads are sanitized: never a visitor
1230
+ * token, identity proof, visitor/end-user id, or backend diagnostic string.
1231
+ */
1232
+ "history:opened": {
1233
+ presentation: ResolvedHistoryPresentation;
1234
+ returnSurface: HistoryReturnSurface;
1235
+ timestamp: number;
1236
+ };
1237
+ "history:closed": { returnSurface: HistoryReturnSurface; timestamp: number };
1238
+ "history:conversationOpened": {
1239
+ conversationId: string;
1240
+ /** List title of the opened conversation; null when not yet known. */
1241
+ title: string | null;
1242
+ scope: HistoryScope;
1243
+ timestamp: number;
1244
+ };
1245
+ /**
1246
+ * A replacement conversation was committed (header action, in-panel "New
1247
+ * conversation", or `startNewConversation()`). `history:closed` alone cannot
1248
+ * distinguish this commit from a plain close.
1249
+ */
1250
+ "history:conversationStarted": {
1251
+ /** `null` when the provider committed without returning a record id. */
1252
+ conversationId: string | null;
1253
+ timestamp: number;
1254
+ };
1255
+ "history:conversationDeleted": {
1256
+ conversationId: string;
1257
+ scope: HistoryScope;
1258
+ /** The deleted record was the open one, so the transcript was replaced. */
1259
+ wasActive: boolean;
1260
+ timestamp: number;
1261
+ };
1262
+ "history:cleared": {
1263
+ deleted: number;
1264
+ scope: HistoryScope;
1265
+ /** `null` is a deliberate headless client-token-wide delete. */
1266
+ targetId: string | null;
1267
+ timestamp: number;
1268
+ };
1269
+ "history:identityReset": {
1270
+ remoteRevocationConfirmed: boolean;
1271
+ timestamp: number;
1272
+ };
1273
+ /** Deduped: emitted only when the discriminated state or reason changes. */
1274
+ "history:identityStatusChanged": {
1275
+ status: HistoryIdentityStatus;
1276
+ timestamp: number;
1277
+ };
1224
1278
  };
1225
1279
 
1226
1280
  /**
@@ -1362,6 +1416,8 @@ export type AgentWidgetArtifactsLayoutConfig = {
1362
1416
  documentToolbarToggleActiveBackground?: string;
1363
1417
  /** Active view/source toggle border color. Sets `--persona-artifact-doc-toggle-active-border`. */
1364
1418
  documentToolbarToggleActiveBorderColor?: string;
1419
+ /** Active view/source toggle icon color (defaults to `documentToolbarIconColor`). Sets `--persona-artifact-doc-toggle-active-color`. */
1420
+ documentToolbarToggleActiveColor?: string;
1365
1421
  /** Show an expand/collapse toggle in the artifact toolbar (both presets). Expanded fills the widget with the pane and hides the chat column. Default: false. */
1366
1422
  showExpandToggle?: boolean;
1367
1423
  /** Show the copy control in the `default` toolbar preset (the `document` preset always shows it). Default: false. */
@@ -1371,24 +1427,42 @@ export type AgentWidgetArtifactsLayoutConfig = {
1371
1427
  /** Tab fade width (CSS length). @default 24px */
1372
1428
  tabFadeSize?: string;
1373
1429
  /**
1374
- * Invoked when the document toolbar Refresh control is used (before the pane re-renders).
1375
- * Use to replay `connectStream`, refetch, etc.
1430
+ * Invoked when the document toolbar Refresh control is used (before the pane
1431
+ * re-renders). The button renders only while this handler is set: the refresh
1432
+ * glyph universally reads "reload this preview" (Claude artifacts, v0), so
1433
+ * keep the handler scoped to refreshing the rendered artifact — regenerating
1434
+ * content or resetting the conversation belongs to message-level or
1435
+ * conversation-level controls, not artifact chrome.
1376
1436
  */
1377
1437
  onDocumentToolbarRefresh?: () => void | Promise<void>;
1378
1438
  /**
1379
1439
  * Optional copy dropdown entries (shown when `documentToolbarShowCopyChevron` is true and this array is non-empty).
1380
1440
  * The main Copy control still performs default copy unless `onDocumentToolbarCopyMenuSelect` handles everything.
1441
+ * Without a handler, built-in ids work out of the box: `download` saves the
1442
+ * artifact (real filename/MIME for file artifacts, `<title>.md` otherwise);
1443
+ * `markdown`/`md` and `json`/`source` copy that form to the clipboard.
1381
1444
  */
1382
1445
  documentToolbarCopyMenuItems?: Array<{ id: string; label: string }>;
1383
1446
  /**
1384
1447
  * When set, invoked for the chevron menu (and can override default copy per `actionId`).
1448
+ * Return `false` to fall through to the widget's built-in behavior for that
1449
+ * action id (`download` saves the artifact; copy ids copy); any other return
1450
+ * value keeps the handler's outcome as final.
1385
1451
  */
1386
1452
  onDocumentToolbarCopyMenuSelect?: (payload: {
1387
1453
  actionId: string;
1388
1454
  artifactId: string | null;
1389
1455
  markdown: string;
1390
1456
  jsonPayload: string;
1391
- }) => void | Promise<void>;
1457
+ /** File metadata when the selected artifact is a previewable file. */
1458
+ file?: PersonaArtifactFileMeta;
1459
+ /** Filename the built-in download would use (file basename, or `<title>.md`). */
1460
+ suggestedFilename: string;
1461
+ /** MIME type paired with `suggestedFilename`. */
1462
+ mime: string;
1463
+ /** Raw content the built-in download would write (unfenced source for file artifacts). */
1464
+ content: string;
1465
+ }) => void | boolean | Promise<void | boolean>;
1392
1466
  };
1393
1467
 
1394
1468
  /**
@@ -2283,6 +2357,11 @@ export type AgentWidgetFeatureFlags = {
2283
2357
  * pills + optional free-text input.
2284
2358
  */
2285
2359
  askUserQuestion?: AgentWidgetAskUserQuestionFeature;
2360
+ /**
2361
+ * Per-visitor conversation history. Client token mode only; disabled by
2362
+ * default so no capability flag is sent on init and no UI mounts.
2363
+ */
2364
+ history?: AgentWidgetHistoryFeature;
2286
2365
  /**
2287
2366
  * Built-in `suggest_replies` follow-ups. When the assistant invokes the
2288
2367
  * tool, the widget shows the suggestions using `suggestions.followUps` and
@@ -2508,6 +2587,628 @@ export type AgentWidgetAskUserQuestionFeature = {
2508
2587
  styles?: AgentWidgetAskUserQuestionStyles;
2509
2588
  };
2510
2589
 
2590
+ /**
2591
+ * Copy overrides for the visitor-facing conversation history surface. These are
2592
+ * the widget's only localization mechanism for history, so every user-visible
2593
+ * history string is listed here.
2594
+ */
2595
+ export interface AgentWidgetHistoryCopy {
2596
+ /** Title of the history view. Defaults to "Messages". */
2597
+ viewTitle?: string;
2598
+ /** Heading shown when the visitor has no stored conversations. */
2599
+ emptyTitle?: string;
2600
+ /** Body text shown when the visitor has no stored conversations. */
2601
+ emptyDescription?: string;
2602
+ /** Heading shown when a history request fails. */
2603
+ errorTitle?: string;
2604
+ /** Body text shown when a history request fails. */
2605
+ errorDescription?: string;
2606
+ /** Label for the retry action on the error state. */
2607
+ retryLabel?: string;
2608
+ /** Heading shown after a 429 rate-limited history response. */
2609
+ rateLimitedTitle?: string;
2610
+ /** Body text shown after a 429 rate-limited history response. */
2611
+ rateLimitedDescription?: string;
2612
+ /** Confirmation prompt before deleting a single conversation. */
2613
+ deleteConversationConfirm?: string;
2614
+ /** Confirmation prompt before deleting every conversation in the active scope. */
2615
+ clearHistoryConfirm?: string;
2616
+ /** Confirmation prompt before detaching this browser from its history identity. */
2617
+ resetIdentityConfirm?: string;
2618
+ /** Label for the action that starts a new server conversation. */
2619
+ newConversationLabel?: string;
2620
+ /** Label for the action that prepends the previous transcript page. */
2621
+ showEarlierMessagesLabel?: string;
2622
+ /** Accessible label for the transcript skeleton while a conversation loads. */
2623
+ openConversationLoadingLabel?: string;
2624
+ /** Body text when a selected conversation failed to load. */
2625
+ openConversationErrorTitle?: string;
2626
+ /** Label for the retry action after a conversation failed to load. */
2627
+ openConversationRetryLabel?: string;
2628
+ /** Label for the return-to-messages action after a conversation failed to load. */
2629
+ openConversationBackLabel?: string;
2630
+ /** Status notice after a deleted conversation was recovered into a fresh one. */
2631
+ conversationDeletedNotice?: string;
2632
+ /** Status notice after this browser was detached from its history identity. */
2633
+ identityResetNotice?: string;
2634
+ /** Status notice when a local reset succeeded but server revocation was unconfirmed. */
2635
+ identityResetUnconfirmedNotice?: string;
2636
+ /** Pinned group heading for starred conversations. */
2637
+ groupStarred?: string;
2638
+ /** Time-group heading for conversations updated today. */
2639
+ groupToday?: string;
2640
+ /** Time-group heading for conversations updated yesterday. */
2641
+ groupYesterday?: string;
2642
+ /** Time-group heading for the trailing 7-day window. */
2643
+ groupPrevious7Days?: string;
2644
+ /** Time-group heading for the trailing 30-day window. */
2645
+ groupPrevious30Days?: string;
2646
+ /** Time-group heading for older conversations, grouped by month and year. */
2647
+ groupMonthYear?: string;
2648
+ /** Scope title when history is limited to this browser. */
2649
+ browserOnlyTitle?: string;
2650
+ /** Scope description when history is limited to this browser. */
2651
+ browserOnlyDescription?: string;
2652
+ /** Scope title while an identity proof is being resolved. */
2653
+ verifyingTitle?: string;
2654
+ /** Scope description while an identity proof is being resolved. */
2655
+ verifyingDescription?: string;
2656
+ /** Scope title when history is available across signed-in devices. */
2657
+ verifiedTitle?: string;
2658
+ /** Scope description when history is available across signed-in devices. */
2659
+ verifiedDescription?: string;
2660
+ /** Scope title when the visitor must sign in again to view account history. */
2661
+ authenticationRequiredTitle?: string;
2662
+ /** Scope description when the visitor must sign in again to view account history. */
2663
+ authenticationRequiredDescription?: string;
2664
+ /** Scope title when the host identity provider threw or rejected. */
2665
+ identityProviderFailedTitle?: string;
2666
+ /** Scope description when the host identity provider threw or rejected. */
2667
+ identityProviderFailedDescription?: string;
2668
+ /** Scope title when the server did not admit a supplied identity proof. */
2669
+ proofNotAdmittedTitle?: string;
2670
+ /** Scope description when the server did not admit a supplied identity proof. */
2671
+ proofNotAdmittedDescription?: string;
2672
+ /** Label for the action that retries identity resolution. */
2673
+ retryIdentityLabel?: string;
2674
+
2675
+ // --- shell chrome (rendered before/around the lazy Messages chunk) ---------
2676
+
2677
+ /** Accessible name of the header control that opens history. */
2678
+ openHistoryLabel?: string;
2679
+ /** Accessible name of that control while a turn is streaming or resuming. */
2680
+ openHistoryBusyLabel?: string;
2681
+ /** Least-destructive action in every history confirmation dialog. */
2682
+ confirmCancelLabel?: string;
2683
+ /** Heading of the delete-one confirmation. */
2684
+ deleteConversationConfirmTitle?: string;
2685
+ /** Confirming action of the delete-one confirmation. */
2686
+ deleteConversationConfirmLabel?: string;
2687
+ /** Heading of the delete-all confirmation. */
2688
+ clearHistoryConfirmTitle?: string;
2689
+ /** Confirming action of the delete-all confirmation. */
2690
+ clearHistoryConfirmLabel?: string;
2691
+ /** Delete-all body used when the resolved scope is the signed-in user. */
2692
+ clearHistoryVerifiedConfirm?: string;
2693
+ /** Heading of the forget-this-device confirmation. */
2694
+ resetIdentityConfirmTitle?: string;
2695
+ /** Confirming action of the forget-this-device confirmation. */
2696
+ resetIdentityConfirmLabel?: string;
2697
+
2698
+ // --- Messages view (resolved inside the lazy chunk) ------------------------
2699
+
2700
+ /** Panel back control; returns to the surface that opened Messages. */
2701
+ backLabel?: string;
2702
+ /** Rail close control, shown only when the rail is not collapsible. */
2703
+ closeLabel?: string;
2704
+ /** Rail collapse toggle while the rail is expanded. */
2705
+ collapseLabel?: string;
2706
+ /** Rail collapse toggle while the rail is collapsed to icons. */
2707
+ expandLabel?: string;
2708
+ /** Accessible name of the docked rail's drag-resize handle. */
2709
+ resizeLabel?: string;
2710
+ /** Accessible status while the first page loads. */
2711
+ loadingLabel?: string;
2712
+ /** Label of the paging action below the last group. */
2713
+ loadMoreLabel?: string;
2714
+ /** Label of that action while its page is in flight. */
2715
+ loadingMoreLabel?: string;
2716
+ /**
2717
+ * Heading over the conversation list. Visible in rail presentation, where it
2718
+ * carries the list overflow trigger; sr-only in panel presentation.
2719
+ */
2720
+ conversationsTitle?: string;
2721
+ /** Accessible name of the per-row overflow trigger and its menu. */
2722
+ rowActionsLabel?: string;
2723
+ /** Accessible name of the list-level overflow trigger and its menu. */
2724
+ listOptionsLabel?: string;
2725
+ /** Delete item inside the per-row overflow menu. */
2726
+ deleteConversationLabel?: string;
2727
+ /** Delete-all item inside the list overflow menu. */
2728
+ clearHistoryLabel?: string;
2729
+ /** Forget-this-device item inside the list overflow menu. */
2730
+ resetIdentityLabel?: string;
2731
+ /** Row message count. `{count}` placeholder. */
2732
+ messageCountLabel?: string;
2733
+ /** Row message count for exactly one message. */
2734
+ messageCountLabelOne?: string;
2735
+ /** Announced after a single conversation was deleted. */
2736
+ conversationRemovedNotice?: string;
2737
+ /** Announced after every conversation in scope was deleted. */
2738
+ historyClearedNotice?: string;
2739
+ /** Heading shown when history is unavailable for this surface. */
2740
+ unavailableTitle?: string;
2741
+ /** Body text shown when history is unavailable for this surface. */
2742
+ unavailableDescription?: string;
2743
+ /** Heading of the post-deletion replacement-init recovery state. */
2744
+ newConversationRequiredTitle?: string;
2745
+ /** Body text of the post-deletion replacement-init recovery state. */
2746
+ newConversationRequiredDescription?: string;
2747
+ /** Rate-limited wait guidance. `{seconds}` placeholder. */
2748
+ rateLimitedWaitDescription?: string;
2749
+ /** Row-adjacent error after a failed open. */
2750
+ openFailedLabel?: string;
2751
+ /** Row-adjacent error after a failed delete. */
2752
+ deleteFailedLabel?: string;
2753
+ /** Relative time for the most recent bucket. */
2754
+ relativeNow?: string;
2755
+ /** Relative time in minutes. `{value}` placeholder. */
2756
+ relativeMinutes?: string;
2757
+ /** Relative time in hours. `{value}` placeholder. */
2758
+ relativeHours?: string;
2759
+ /** Relative time in days. `{value}` placeholder. */
2760
+ relativeDays?: string;
2761
+ /** Relative time in weeks. `{value}` placeholder. */
2762
+ relativeWeeks?: string;
2763
+ /** Relative time in years. `{value}` placeholder. */
2764
+ relativeYears?: string;
2765
+ }
2766
+
2767
+ /**
2768
+ * One row in a `features.history.rail.sections` navigation section. Icon
2769
+ * precedence, highest first: `renderIcon`, then `iconUrl`, then `icon`.
2770
+ * Whichever resolves first is built once and reused for the item's lifetime.
2771
+ */
2772
+ export interface AgentWidgetHistoryRailSectionItem {
2773
+ /** Stable identity, stamped as `data-persona-rail-item`. */
2774
+ id: string;
2775
+ /** Visible text, and the default `aria-label` of the button. */
2776
+ label: string;
2777
+ /**
2778
+ * Lucide name from the widget's built-in icon registry (see `IconName`). A
2779
+ * name outside the registry warns once and the row renders label-only.
2780
+ */
2781
+ icon?: IconName | (string & {});
2782
+ /** Image URL or data URI, rendered as a decorative `img` in the icon slot. */
2783
+ iconUrl?: string;
2784
+ /** Full custom icon node. Returning null renders the row label-only. */
2785
+ renderIcon?: () => Element | null;
2786
+ /** Small trailing text chip, for a count or a short status word. */
2787
+ badge?: string;
2788
+ /** Invoked on click. A throw is caught and warned once per section. */
2789
+ onSelect: () => void;
2790
+ }
2791
+
2792
+ /** One navigation section in the history rail, above or below the list. */
2793
+ export interface AgentWidgetHistoryRailSection {
2794
+ /** Stable identity, stamped as `data-persona-rail-section`. */
2795
+ id: string;
2796
+ /** Optional heading, styled like a conversation date-group heading. */
2797
+ title?: string;
2798
+ /** Default `"above-conversations"`. */
2799
+ placement?: "above-conversations" | "below-conversations" | "footer";
2800
+ items: AgentWidgetHistoryRailSectionItem[];
2801
+ }
2802
+
2803
+ /**
2804
+ * Host-defined item in the list overflow menu (the `…` beside the
2805
+ * conversation-list heading). Rendered above the built-in destructive items.
2806
+ */
2807
+ export interface AgentWidgetHistoryListAction {
2808
+ /** Stable identity; keeps menu focus keys stable across re-renders. */
2809
+ id: string;
2810
+ label: string;
2811
+ /** Destructive (red) styling like the built-in delete-all. Default false. */
2812
+ danger?: boolean;
2813
+ /** Receives the conversation summaries loaded at selection time. */
2814
+ onSelect: (context: {
2815
+ conversations: HistoryConversationSummary[];
2816
+ }) => void | Promise<void>;
2817
+ }
2818
+
2819
+ /**
2820
+ * Feature config for per-visitor conversation history. Without a `provider`,
2821
+ * client token mode only: the built-in Runtype backend needs one, so the UI
2822
+ * never renders for proxy/agent sessions. A `provider` lifts that restriction.
2823
+ */
2824
+ export interface AgentWidgetHistoryFeature {
2825
+ /** Master switch. Default false: no capability flag on init, no UI. */
2826
+ enabled?: boolean;
2827
+ /**
2828
+ * Custom conversation-history backend. Supply one to run the history UX
2829
+ * (list, open, delete, delete-all, header title binding) against your own
2830
+ * storage in ANY session mode, including a custom `apiUrl` backend with no
2831
+ * client token. Omitted, history falls back to the built-in Runtype backend,
2832
+ * which requires client token mode.
2833
+ *
2834
+ * Pass either an instance or a factory. The factory form is called once per
2835
+ * widget instance, so a single script-tag config object (assembled before
2836
+ * install) can back several widgets without sharing provider state. Passing a
2837
+ * new factory identity through `update()` rebuilds the provider.
2838
+ *
2839
+ * Failures are signalled by throwing `HistoryProviderError` with a `code` from
2840
+ * `HistoryProviderErrorCode`. The view branches on that code alone: it never
2841
+ * reads message text, and an untyped throw degrades to a generic error state.
2842
+ *
2843
+ * `prepareOpen`/`prepareStartNew` resolve BEFORE anything visible changes:
2844
+ * they return a `PreparedHistoryActivation` whose `commit()` applies the
2845
+ * already-authorized binding and whose `discard()` idempotently abandons a
2846
+ * superseded activation, leaving the active chat untouched. The widget calls
2847
+ * exactly one of the two per prepared activation.
2848
+ *
2849
+ * Optional capabilities hide their affordances when absent: no `update`
2850
+ * removes the rename/star actions (including the built-in title-menu `star`),
2851
+ * and no `resetDevice` removes "forget this device".
2852
+ *
2853
+ * The provider's `capabilities.scopes` is authoritative: the widget derives a
2854
+ * default scope (`"verified-user"` when `getIdentityProof` is configured,
2855
+ * otherwise `"browser"`) and narrows it to what the provider advertises, so a
2856
+ * browser-only provider is never asked for a scope it cannot serve. An
2857
+ * explicit `features.history.scope` is a hard request and still fails closed
2858
+ * with `unsupported_scope`. `getIdentityProof` is never called for a supplied
2859
+ * provider, and `HistoryOperationContext` carries only the resolved scope;
2860
+ * report your own identity through `getIdentityStatus()` /
2861
+ * `subscribeIdentityStatus()`, which is what the scope banner and its retry
2862
+ * affordance read.
2863
+ *
2864
+ * Reopening the last conversation after a page reload is the host's job: the
2865
+ * widget's boot resume runs only on the built-in Runtype path, so with a
2866
+ * supplied provider the transcript is restored by the storage adapter while
2867
+ * `getActiveConversationId()` starts null (no active row, no
2868
+ * conversation-bound header title) until something opens a conversation.
2869
+ * Persist the id yourself and call `controller.openConversation(id)` on ready
2870
+ * if you want the last conversation to come back automatically.
2871
+ *
2872
+ * @example
2873
+ * ```js
2874
+ * features: { history: { enabled: true, provider: () => myProvider } }
2875
+ * ```
2876
+ */
2877
+ provider?: HistoryProvider | (() => HistoryProvider);
2878
+ /** Page size for the conversation list (default 25, server max 100). */
2879
+ pageSize?: number;
2880
+ /**
2881
+ * Default history placement. "panel" replaces the conversation surface;
2882
+ * "rail" requests a side navigation host whenever it is at least 720px wide
2883
+ * and collapses to panel below that; "auto" uses rail only for a wide inline
2884
+ * or docked shell and keeps floating launchers panel-based at every width.
2885
+ * Default "panel".
2886
+ */
2887
+ presentation?: "panel" | "rail" | "auto";
2888
+ /**
2889
+ * Geometry of the rail navigation column. `side` picks the edge it docks to
2890
+ * (default "left", beside the conversation); `width` is its fixed width in
2891
+ * px, clamped to 200-400 (default 260). `collapsible` (default true) puts a
2892
+ * collapse toggle in the rail header that shrinks it to a 52px icon rail, and
2893
+ * `defaultCollapsed` (default false) picks the state a visitor who has never
2894
+ * toggled it starts in. The chosen state is remembered per visitor alongside
2895
+ * the other `persistState` keys. Ignored in panel presentation.
2896
+ */
2897
+ rail?: {
2898
+ side?: "left" | "right";
2899
+ width?: number;
2900
+ collapsible?: boolean;
2901
+ defaultCollapsed?: boolean;
2902
+ /**
2903
+ * Drag handle on the docked rail's divider edge, resizing it live within
2904
+ * the same 200 to 400 clamp `width` takes. Default false. The chosen width
2905
+ * is remembered per visitor alongside the other `persistState` keys and
2906
+ * outranks a later `width` update, the way the collapsed state does; the
2907
+ * floating overlay rail inherits it but is never itself resizable, and a
2908
+ * collapsed icon column ignores it until it expands again.
2909
+ */
2910
+ resizable?: boolean;
2911
+ /**
2912
+ * Shape of the COLLAPSED rail. `"icon-rail"` (default) keeps the 52px icon
2913
+ * column beside the conversation. `"overlay"` removes the column entirely:
2914
+ * a trigger takes the leading edge of the conversation header, hovering or
2915
+ * tapping it floats the expanded rail over the conversation, and clicking
2916
+ * it pins the rail back open. The floating rail is transient, so leaving
2917
+ * it, Escape, a click outside, or selecting a conversation all dismiss it;
2918
+ * its own header toggle docks it instead, being the control the pointer
2919
+ * finds there. Geometry follows `width` and `side`; the collapsed rail's
2920
+ * chunk is not loaded until the first hover. Ignored when `collapsible` is
2921
+ * false or in panel presentation.
2922
+ */
2923
+ collapsedBehavior?: "icon-rail" | "overlay";
2924
+ /**
2925
+ * One brand declaration, placed in both of the rail's identity spots: the
2926
+ * header's heading area when expanded (the mark leading, `copy.viewTitle`
2927
+ * beside it as the wordmark), and the collapse toggle's rest face when the
2928
+ * rail is collapsed, where hover and keyboard focus reveal the panel glyph
2929
+ * under it. A coarse pointer keeps the glyph, having no hover to reveal it
2930
+ * with. Icon precedence, highest first: `render`, then `iconUrl`, then
2931
+ * `icon`. `render` receives the placement through `ctx.collapsed` (`true`
2932
+ * is the collapsed toggle face) and may return null to skip that spot.
2933
+ * `renderHeader` outranks this for the heading area; the collapsed face is
2934
+ * this slot's alone. Decorative either way: the view's accessible name
2935
+ * still comes from `copy.viewTitle`, and the toggle keeps its own label.
2936
+ * Ignored in panel presentation.
2937
+ */
2938
+ brand?: {
2939
+ /** Lucide name from the built-in registry; an unknown one warns once. */
2940
+ icon?: IconName | (string & {});
2941
+ /** Image URL or data URI, rendered as a decorative `img`. */
2942
+ iconUrl?: string;
2943
+ /** Full custom node. A throw warns once and drops the brand. */
2944
+ render?: (context: { collapsed: boolean }) => Element | null;
2945
+ };
2946
+ /**
2947
+ * Full override of the rail header's identity area, outranking `brand`
2948
+ * there, re-invoked whenever the collapsed state changes. Return null to
2949
+ * leave the area empty, which leaves the collapse toggle standing alone.
2950
+ * The accessible name of the
2951
+ * view comes from `copy.viewTitle` either way: the heading stays in the
2952
+ * DOM, visually hidden, whenever this renders. Ignored in panel
2953
+ * presentation.
2954
+ */
2955
+ renderHeader?: (context: {
2956
+ collapsed: boolean;
2957
+ defaultTitle: string;
2958
+ }) => Element | null;
2959
+ /**
2960
+ * Extra navigation sections stacked around the conversation list, in array
2961
+ * order within each `placement` bucket. Rail-only in this release: a move
2962
+ * into panel presentation removes them and a move back re-renders them.
2963
+ */
2964
+ sections?: AgentWidgetHistoryRailSection[];
2965
+ /**
2966
+ * Keyboard shortcut for the collapse toggle, e.g. `"mod+b"` ("mod" is
2967
+ * Command on Apple platforms, Control elsewhere). Undefined by default, so
2968
+ * an embedded widget never claims a host page's keys. The same declaration
2969
+ * drives the binding, the toggle's tooltip hint chip, and its
2970
+ * `aria-keyshortcuts`.
2971
+ */
2972
+ collapseShortcut?: string | false;
2973
+ /**
2974
+ * Where `collapseShortcut` listens. Default `"widget"`: only while focus is
2975
+ * inside the widget. `"page"` claims the combo document-wide, which suits a
2976
+ * fullscreen widget that IS the app.
2977
+ */
2978
+ collapseShortcutScope?: "widget" | "page";
2979
+ };
2980
+ /**
2981
+ * History scope. Defaults to "verified-user" when getIdentityProof exists,
2982
+ * otherwise "browser". Before this browser has been identity-bound, a null
2983
+ * proof falls back to browser scope for that operation. Once visitor.endUserId
2984
+ * is non-null, a null proof fails closed until the host resets history identity.
2985
+ *
2986
+ * With a `provider`, this is a hard request: the provider must advertise it
2987
+ * in `capabilities.scopes` or every history operation fails with
2988
+ * `unsupported_scope`. Leave it unset to let the provider's advertised scope
2989
+ * win.
2990
+ */
2991
+ scope?: HistoryScope;
2992
+ /** Copy overrides. Every user-visible history string must be here. */
2993
+ copy?: AgentWidgetHistoryCopy;
2994
+ /**
2995
+ * Date bucketing of the conversation list. `"time"` (default) groups rows
2996
+ * under Today, Yesterday, Previous 7 days, Previous 30 days, and month
2997
+ * headings. `"none"` renders one flat list in server order instead, the
2998
+ * recents-style sidebar. Starred conversations keep their pinned group
2999
+ * either way.
3000
+ */
3001
+ grouping?: "time" | "none";
3002
+ /**
3003
+ * Leading avatar on each conversation row. Unset, rows show the launcher's
3004
+ * image mark (`launcher.iconUrl`) when one exists and are text-only
3005
+ * otherwise, the assistant-list default. Pass an image URL or a glyph to
3006
+ * always show a mark, or false to force text-only rows.
3007
+ */
3008
+ rowAvatar?: boolean | string;
3009
+ /** Show the evidence-based history scope/status beneath the Messages heading. Default true. */
3010
+ showScopeStatus?: boolean;
3011
+ /**
3012
+ * Visitor-facing delete of a single conversation via each row's overflow
3013
+ * menu. Default true. False hides the control only: it changes nothing about
3014
+ * what is stored or for how long, and custom row slots still receive
3015
+ * `requestDelete`. Turn off for deployments that treat conversations as
3016
+ * business records.
3017
+ */
3018
+ showDelete?: boolean;
3019
+ /**
3020
+ * The "Delete all conversations" item in the Messages overflow menu. Default
3021
+ * true. False hides the control only; storage and retention are unchanged.
3022
+ * The identity reset item is separate and unaffected.
3023
+ */
3024
+ showDeleteAll?: boolean;
3025
+ /**
3026
+ * Host items appended to the list overflow menu, above the built-in
3027
+ * destructive actions. The trigger renders whenever any item exists, so
3028
+ * these keep the menu alive even with `showDeleteAll: false`.
3029
+ */
3030
+ listActions?: AgentWidgetHistoryListAction[];
3031
+ }
3032
+
3033
+ /** Resolved scope for a logical history operation. */
3034
+ export type HistoryScope = "browser" | "verified-user";
3035
+
3036
+ /** `presentation` after it has been resolved against the host container width. */
3037
+ export type ResolvedHistoryPresentation = "panel" | "rail";
3038
+
3039
+ /**
3040
+ * The surface Messages returns to on back/Escape. `"home"` only when a Home
3041
+ * Screen composition opened it; the core header button always records
3042
+ * `"conversation"`.
3043
+ */
3044
+ export type HistoryReturnSurface = "home" | "conversation";
3045
+
3046
+ /**
3047
+ * Evidence-based history identity state. Never inferred from the mere presence
3048
+ * of `getIdentityProof`, and never carries token, visitor, or end-user values.
3049
+ */
3050
+ export type HistoryIdentityStatus =
3051
+ | {
3052
+ state: "browser_only";
3053
+ reason:
3054
+ | "configured_browser_scope"
3055
+ | "no_identity_provider"
3056
+ | "proof_unavailable_before_binding";
3057
+ }
3058
+ | { state: "verifying" }
3059
+ | { state: "verified" }
3060
+ | {
3061
+ state: "authentication_required";
3062
+ reason: "proof_unavailable_after_binding" | "invalid_identity_proof";
3063
+ }
3064
+ | { state: "identity_provider_failed" }
3065
+ | { state: "configuration_error"; reason: "proof_not_admitted" }
3066
+ | { state: "resetting" }
3067
+ | {
3068
+ state: "unavailable";
3069
+ reason: "history_disabled" | "ineligible_mode";
3070
+ };
3071
+
3072
+ // ----------------------------------------------------------------------------
3073
+ // History rendering customization (plan D7, "Public rendering customization
3074
+ // contract"). Types only: the implementation stays in the lazy view chunk and
3075
+ // the core arbitration layer.
3076
+ // ----------------------------------------------------------------------------
3077
+
3078
+ /** Fully defaulted history copy. Every user-visible history string. */
3079
+ export type ResolvedHistoryCopy = Required<AgentWidgetHistoryCopy>;
3080
+
3081
+ /** List-region state handed to render hooks. */
3082
+ export type HistoryViewState =
3083
+ | { kind: "loading"; phase: "initial" | "refresh" | "load-more" }
3084
+ | { kind: "ready" }
3085
+ | { kind: "empty" }
3086
+ | {
3087
+ kind: "error";
3088
+ reason:
3089
+ | "unavailable"
3090
+ | "authentication_failed"
3091
+ | "authentication_required"
3092
+ | "identity_provider_failed"
3093
+ | "proof_not_admitted"
3094
+ | "unsupported_scope"
3095
+ | "unknown";
3096
+ retryable: boolean;
3097
+ }
3098
+ | { kind: "rate_limited"; retryAfterSeconds: number }
3099
+ | { kind: "new_conversation_required" };
3100
+
3101
+ /** The single in-flight history operation, or `null` when idle. */
3102
+ export type HistoryPendingAction =
3103
+ | { kind: "refresh" | "load-more" | "start-new" | "clear" | "reset" }
3104
+ | { kind: "open" | "delete"; conversationId: string }
3105
+ | null;
3106
+
3107
+ /**
3108
+ * Every history operation a render hook may invoke. Each routes through the
3109
+ * same controller/session path as the default renderer, so busy gating,
3110
+ * selection epochs, and confirmations behave identically.
3111
+ */
3112
+ export type AgentWidgetHistoryRenderActions = {
3113
+ refresh(): Promise<void>;
3114
+ loadMore(): Promise<void>;
3115
+ openConversation(conversationId: string): Promise<void>;
3116
+ startNewConversation(): Promise<void>;
3117
+ /** Each request method opens Persona's accessible confirmation first. */
3118
+ requestDeleteConversation(
3119
+ conversationId: string
3120
+ ): Promise<"deleted" | "cancelled">;
3121
+ requestClearConversationHistory(): Promise<"cleared" | "cancelled">;
3122
+ requestResetHistoryIdentity(): Promise<
3123
+ | { outcome: "cancelled" }
3124
+ | { outcome: "reset"; remoteRevocationConfirmed: boolean }
3125
+ >;
3126
+ close(): void;
3127
+ };
3128
+
3129
+ /** Context for `renderHistoryView`. Frozen snapshot; no provider or credentials. */
3130
+ export type AgentWidgetRenderHistoryViewContext = {
3131
+ conversations: readonly HistoryConversationSummary[];
3132
+ activeConversationId: string | null;
3133
+ state: HistoryViewState;
3134
+ pendingAction: HistoryPendingAction;
3135
+ identityStatus: HistoryIdentityStatus;
3136
+ nextCursor: string | null;
3137
+ presentation: ResolvedHistoryPresentation;
3138
+ returnSurface: HistoryReturnSurface;
3139
+ config: AgentWidgetConfig;
3140
+ copy: ResolvedHistoryCopy;
3141
+ actions: AgentWidgetHistoryRenderActions;
3142
+ /** The default full view. Bypasses this hook only; lower slots still apply. */
3143
+ defaultRenderer(): HTMLElement;
3144
+ /** Re-run arbitration with the current snapshot. Inert after close/destroy. */
3145
+ requestRender(): void;
3146
+ /** Teardown for this render; runs before the next one and on close. */
3147
+ onCleanup(callback: () => void): void;
3148
+ };
3149
+
3150
+ /** Context for `renderHistoryConversation` (one list row). */
3151
+ export type AgentWidgetRenderHistoryConversationContext = {
3152
+ conversation: HistoryConversationSummary;
3153
+ active: boolean;
3154
+ pending: "opening" | "deleting" | null;
3155
+ presentation: ResolvedHistoryPresentation;
3156
+ config: AgentWidgetConfig;
3157
+ open(): Promise<void>;
3158
+ requestDelete(): Promise<"deleted" | "cancelled">;
3159
+ defaultRenderer(): HTMLElement;
3160
+ };
3161
+
3162
+ /** Context for `renderHistoryHeader` (the Messages top bar). */
3163
+ export type AgentWidgetRenderHistoryHeaderContext = {
3164
+ presentation: ResolvedHistoryPresentation;
3165
+ returnSurface: HistoryReturnSurface;
3166
+ identityStatus: HistoryIdentityStatus;
3167
+ pendingAction: HistoryPendingAction;
3168
+ config: AgentWidgetConfig;
3169
+ copy: ResolvedHistoryCopy;
3170
+ actions: Pick<
3171
+ AgentWidgetHistoryRenderActions,
3172
+ "close" | "startNewConversation"
3173
+ >;
3174
+ defaultRenderer(): HTMLElement;
3175
+ };
3176
+
3177
+ /**
3178
+ * Context for `renderHistoryState`. Invoked only for non-ready list states;
3179
+ * `retry`/`startNewConversation` are present only where they are safe, so
3180
+ * custom DOM never infers recovery behavior.
3181
+ */
3182
+ export type AgentWidgetRenderHistoryStateContext = {
3183
+ state: Exclude<HistoryViewState, { kind: "ready" }>;
3184
+ identityStatus: HistoryIdentityStatus;
3185
+ presentation: ResolvedHistoryPresentation;
3186
+ config: AgentWidgetConfig;
3187
+ copy: ResolvedHistoryCopy;
3188
+ retry?: () => Promise<void>;
3189
+ startNewConversation?: () => Promise<void>;
3190
+ defaultRenderer(): HTMLElement;
3191
+ };
3192
+
3193
+ /**
3194
+ * Context for `renderHistoryOpenError` (the transcript-level surface shown
3195
+ * when opening a conversation from the Messages list fails). `retry` and
3196
+ * `back` are the only exits: both route through the token-guarded open flow,
3197
+ * so custom DOM must not fetch on its own. `back` is present only when the
3198
+ * Messages surface is available to return to.
3199
+ */
3200
+ export type AgentWidgetRenderHistoryOpenErrorContext = {
3201
+ conversationId: string;
3202
+ config: AgentWidgetConfig;
3203
+ /** Resolved shell copy the default surface renders. */
3204
+ copy: { title: string; retryLabel: string; backLabel: string };
3205
+ /** Re-runs the open through Persona's token-guarded flow. */
3206
+ retry: () => void;
3207
+ /** Returns to the Messages list. Absent when history is unavailable. */
3208
+ back?: () => void;
3209
+ defaultRenderer(): HTMLElement;
3210
+ };
3211
+
2511
3212
  export type SSEEventRecord = {
2512
3213
  id: string;
2513
3214
  type: string;
@@ -2996,6 +3697,25 @@ export type AgentWidgetLauncherTeaserConfig = {
2996
3697
  dismissLabel?: string;
2997
3698
  };
2998
3699
 
3700
+ /**
3701
+ * Timing for the portaled icon-control tooltip (header, composer, rail).
3702
+ * Keyboard focus always opens immediately. Hover waits `delayMs` unless
3703
+ * another tooltip closed within `skipDelayMs` (toolbar scan).
3704
+ */
3705
+ export type AgentWidgetTooltipConfig = {
3706
+ /**
3707
+ * Milliseconds to wait on hover before the first tooltip appears.
3708
+ * `0` restores the previous instant-open behavior.
3709
+ * @default 200
3710
+ */
3711
+ delayMs?: number;
3712
+ /**
3713
+ * After a tooltip closes, later hovers in this window skip `delayMs`.
3714
+ * @default 300
3715
+ */
3716
+ skipDelayMs?: number;
3717
+ };
3718
+
2999
3719
  export type AgentWidgetSendButtonConfig = {
3000
3720
  borderWidth?: string;
3001
3721
  borderColor?: string;
@@ -4128,6 +4848,21 @@ export type ClientSession = {
4128
4848
  name: string;
4129
4849
  description: string | null;
4130
4850
  };
4851
+ /**
4852
+ * Active conversation record for this session. Optional only for rolling-deploy
4853
+ * compatibility with servers that predate the visitor history contract.
4854
+ */
4855
+ conversationId?: string;
4856
+ /**
4857
+ * Canonical resolved chat target (flow or agent). Normalized from the response
4858
+ * `targetId`, falling back to `flow.id` during rolling deployment. History code
4859
+ * reads this, never widget config.
4860
+ */
4861
+ targetId?: string;
4862
+ /** Opaque change token; differs whenever the conversation transcript mutated. */
4863
+ conversationRevision?: string;
4864
+ /** Visitor grant backing history. `token` appears only at mint. */
4865
+ visitor?: ClientVisitorGrant;
4131
4866
  /** Configuration from the server */
4132
4867
  config: {
4133
4868
  welcomeMessage: string | null;
@@ -4136,6 +4871,21 @@ export type ClientSession = {
4136
4871
  };
4137
4872
  };
4138
4873
 
4874
+ /**
4875
+ * Visitor credential and identity acknowledgement returned by /v1/client/init.
4876
+ * `expiresAt` stays the wire string: it is a server-slid idle window the widget
4877
+ * never evaluates locally.
4878
+ */
4879
+ export type ClientVisitorGrant = {
4880
+ id: string;
4881
+ /** Replacement secret. Present only at mint; persist it before anything else. */
4882
+ token?: string;
4883
+ expiresAt: string;
4884
+ endUserId: string | null;
4885
+ /** Server acknowledgement of the supplied identity proof. Status metadata only. */
4886
+ identityStatus?: "not_provided" | "admitted" | "ignored";
4887
+ };
4888
+
4139
4889
  /**
4140
4890
  * Raw API response from /v1/client/init endpoint
4141
4891
  */
@@ -4147,6 +4897,11 @@ export type ClientInitResponse = {
4147
4897
  name: string;
4148
4898
  description: string | null;
4149
4899
  };
4900
+ /** Additive visitor history fields; optional for rolling-deploy compatibility. */
4901
+ conversationId?: string;
4902
+ targetId?: string;
4903
+ conversationRevision?: string;
4904
+ visitor?: ClientVisitorGrant;
4150
4905
  config: {
4151
4906
  welcomeMessage: string | null;
4152
4907
  placeholder: string;
@@ -4154,6 +4909,109 @@ export type ClientInitResponse = {
4154
4909
  };
4155
4910
  };
4156
4911
 
4912
+ /**
4913
+ * Transactional init handle. `commit()` installs only the client's session
4914
+ * cache; `discard()` is an idempotent no-op. Neither writes persisted ids.
4915
+ */
4916
+ export type PreparedClientSession = {
4917
+ session: ClientSession;
4918
+ commit(): void;
4919
+ discard(): void;
4920
+ };
4921
+
4922
+ /**
4923
+ * One conversation row, normalized at the Runtype boundary. The deprecated wire
4924
+ * `flowId` alias never escapes that boundary: history code reads `targetId`.
4925
+ */
4926
+ export type HistoryConversationSummary = {
4927
+ id: string;
4928
+ /** Server-generated; rendered verbatim, never derived locally. */
4929
+ title: string;
4930
+ /** Resolved chat target (flow or agent); `null` when the server omits it. */
4931
+ targetId: string | null;
4932
+ /** Server-produced plain-text preview; `null` when unavailable. */
4933
+ preview: string | null;
4934
+ messageCount: number;
4935
+ createdAt: string;
4936
+ updatedAt: string;
4937
+ /** Visitor-pinned flag. Absent when the provider has no update capability. */
4938
+ starred?: boolean;
4939
+ };
4940
+
4941
+ /** Visitor-editable conversation metadata. Absent fields are left unchanged. */
4942
+ export type HistoryConversationPatch = {
4943
+ title?: string;
4944
+ starred?: boolean;
4945
+ };
4946
+
4947
+ export type HistoryConversationPage = {
4948
+ data: HistoryConversationSummary[];
4949
+ nextCursor: string | null;
4950
+ };
4951
+
4952
+ /**
4953
+ * Conversation detail. Messages stay on the wire shape: mapping to widget
4954
+ * messages belongs to `utils/history-messages.ts`.
4955
+ */
4956
+ export type HistoryConversationDetail = {
4957
+ summary: HistoryConversationSummary;
4958
+ messages: HistoryWireMessage[];
4959
+ nextMessageCursor: string | null;
4960
+ conversationRevision: string | null;
4961
+ };
4962
+
4963
+ /** One finalized visitor-visible projection for an already-stored message. */
4964
+ export type HistoryDisplayProjection = {
4965
+ id: string;
4966
+ displayContent: string;
4967
+ };
4968
+
4969
+ /**
4970
+ * Pending projection marker. Only ids are stored: the display strings already
4971
+ * live in the persisted messages and are never duplicated.
4972
+ */
4973
+ export type PendingDisplayProjections = {
4974
+ conversationId: string;
4975
+ messageIds: string[];
4976
+ };
4977
+
4978
+ /**
4979
+ * Internal history dependencies the controller injects into the session and
4980
+ * every client it builds. Not part of the public config surface.
4981
+ */
4982
+ export interface WidgetHistoryInternals {
4983
+ visitorStore?: VisitorStore;
4984
+ /** Resolves once stored state has been applied; gates history-capable init. */
4985
+ historyBootstrapReady?: Promise<void>;
4986
+ /**
4987
+ * The one production provider is built by the shell from client-token config
4988
+ * (or taken from the internal demo registry). Session/UI history code depends
4989
+ * on this seam, never on `AgentWidgetClient`.
4990
+ */
4991
+ historyProvider?: HistoryProvider;
4992
+ /** Persist the revision beside the active conversation id (internal only). */
4993
+ setStoredConversationRevision?: (revision: string | null) => void;
4994
+ getStoredConversationRevision?: () => string | null;
4995
+ /** "Show earlier messages" cursor for the persisted transcript. */
4996
+ setStoredMessageCursor?: (cursor: string | null) => void;
4997
+ getStoredMessageCursor?: () => string | null;
4998
+ /**
4999
+ * Display projections finalized in the browser but not yet acknowledged by
5000
+ * the server. Absent callbacks mean memory-only (persistence disabled).
5001
+ */
5002
+ setPendingDisplayProjections?: (
5003
+ pending: PendingDisplayProjections | null
5004
+ ) => void;
5005
+ getPendingDisplayProjections?: () => PendingDisplayProjections | null;
5006
+ onHistoryAvailabilityChanged?: (available: boolean) => void;
5007
+ /** Deduped status changes; carries no token, proof, or identity value. */
5008
+ onHistoryIdentityStatusChanged?: (status: HistoryIdentityStatus) => void;
5009
+ onHistoryContinuityChanged?: (info: {
5010
+ previousConversationId: string | null;
5011
+ conversationId: string;
5012
+ }) => void;
5013
+ }
5014
+
4157
5015
  /**
4158
5016
  * Request payload for /v1/client/chat endpoint
4159
5017
  */
@@ -4163,6 +5021,11 @@ export type ClientChatRequest = {
4163
5021
  id?: string;
4164
5022
  role: 'user' | 'assistant' | 'system';
4165
5023
  content: MessageContent;
5024
+ /**
5025
+ * Visitor-visible projection, sent only where it diverges from the model
5026
+ * channel above (contract fact #15). Never model input or merge authority.
5027
+ */
5028
+ displayContent?: string;
4166
5029
  }>;
4167
5030
  /** ID for the expected assistant response message */
4168
5031
  assistantMessageId?: string;
@@ -4236,6 +5099,13 @@ export type AgentWidgetHeaderTrailingAction = {
4236
5099
  destructive?: boolean;
4237
5100
  dividerBefore?: boolean;
4238
5101
  }>;
5102
+ /**
5103
+ * Keyboard shortcut that activates this action, e.g. `"mod+shift+h"` ("mod"
5104
+ * is Command on Apple platforms, Control elsewhere). Fires only while focus
5105
+ * is inside the widget, and also drives the button's tooltip hint chip and
5106
+ * its `aria-keyshortcuts`.
5107
+ */
5108
+ shortcut?: string;
4239
5109
  };
4240
5110
 
4241
5111
  /**
@@ -4283,6 +5153,15 @@ export type AgentWidgetHeaderLayoutConfig = {
4283
5153
  showIcon?: boolean;
4284
5154
  /** Show/hide the title */
4285
5155
  showTitle?: boolean;
5156
+ /**
5157
+ * What the header title (or `titleMenu` label) displays. `"static"`
5158
+ * (default) always shows `launcher.title`. `"conversation"` shows the active
5159
+ * conversation's title — the Claude-style pairing for a `titleMenu` holding
5160
+ * conversation actions — falling back to `launcher.title` while no titled
5161
+ * conversation is open (fresh chat, cleared history, or before the history
5162
+ * view has loaded titles). Requires `features.history` for titles to exist.
5163
+ */
5164
+ titleSource?: "static" | "conversation";
4286
5165
  /** Show/hide the subtitle */
4287
5166
  showSubtitle?: boolean;
4288
5167
  /** Show/hide the close button */
@@ -4333,8 +5212,16 @@ export type AgentWidgetHeaderLayoutConfig = {
4333
5212
  destructive?: boolean;
4334
5213
  dividerBefore?: boolean;
4335
5214
  }>;
4336
- /** Called when a menu item is selected. */
4337
- onSelect: (id: string) => void;
5215
+ /**
5216
+ * Called when a menu item is selected. Returning `false` (sync or via
5217
+ * promise) falls through to the widget built-ins, mirroring the artifact
5218
+ * copy-menu contract: id `star` toggles the active conversation's star and
5219
+ * id `delete` runs the shell's confirm-then-delete on it (both require
5220
+ * `features.history` and, for star, a provider with the update
5221
+ * capability). `rename` has no built-in: hosts own the rename UI and call
5222
+ * `renameConversation()`.
5223
+ */
5224
+ onSelect: (id: string) => void | boolean | Promise<void | boolean>;
4338
5225
  /** Hover pill style. */
4339
5226
  hover?: {
4340
5227
  background?: string;
@@ -4928,15 +5815,20 @@ export type LoadingIndicatorRenderContext = {
4928
5815
  */
4929
5816
  config: AgentWidgetConfig;
4930
5817
  /**
4931
- * Current streaming state (always true when indicator is shown)
5818
+ * Current streaming state. Always true for 'inline' and 'standalone';
5819
+ * false for 'conversation-open' (a history fetch, not a stream).
4932
5820
  */
4933
5821
  streaming: boolean;
4934
5822
  /**
4935
5823
  * Location where the indicator is rendered:
4936
5824
  * - 'inline': Inside a streaming assistant message bubble (when content is empty)
4937
5825
  * - 'standalone': Separate bubble while waiting for stream to start
5826
+ * - 'conversation-open': Transcript stand-in while a conversation opened
5827
+ * from the Messages list is being fetched. The returned element is mounted
5828
+ * inside Persona's centered transcript column, which keeps the status
5829
+ * role, announcement label, and composer gating.
4938
5830
  */
4939
- location: 'inline' | 'standalone';
5831
+ location: 'inline' | 'standalone' | 'conversation-open';
4940
5832
  /**
4941
5833
  * Function to render the default 3-dot bouncing indicator.
4942
5834
  * Call this if you want to use the default for certain cases.
@@ -5240,6 +6132,28 @@ export type AgentWidgetConfig = {
5240
6132
  * ```
5241
6133
  */
5242
6134
  setStoredSessionId?: (sessionId: string) => void;
6135
+ /**
6136
+ * Called immediately before any cross-device history request and before a
6137
+ * conversationId resume of a conversation discovered on another device.
6138
+ * Return a fresh end-user access token (rt_eu_… / JWT). Null uses exact-browser
6139
+ * scope only for a never-bound visitor; a previously bound visitor fails closed
6140
+ * until resetHistoryIdentity() runs. Never cached by the widget.
6141
+ */
6142
+ getIdentityProof?: () => string | null | Promise<string | null>;
6143
+ /**
6144
+ * Read the persisted active conversation id (client token mode only).
6145
+ * Called only after the internal stored-state bootstrap gate resolves.
6146
+ */
6147
+ getStoredConversationId?: () => string | null;
6148
+ /** Persist the active conversation id so a later boot reopens the same record. */
6149
+ setStoredConversationId?: (conversationId: string) => void;
6150
+ /** Drop the persisted active conversation id (new conversation, deletion, reset). */
6151
+ clearStoredConversationId?: () => void;
6152
+ /**
6153
+ * Drop the persisted session id. Dedicated clear seam: it avoids widening
6154
+ * `setStoredSessionId` and its existing host call sites.
6155
+ */
6156
+ clearStoredSessionId?: () => void;
5243
6157
  /**
5244
6158
  * Static headers to include with each request.
5245
6159
  * For dynamic headers (e.g., auth tokens), use `getHeaders` instead.
@@ -5319,6 +6233,12 @@ export type AgentWidgetConfig = {
5319
6233
  * @default false
5320
6234
  */
5321
6235
  autoFocusInput?: boolean;
6236
+ /**
6237
+ * Hover timing for the portaled control tooltip. Keyboard focus is
6238
+ * always immediate. Unset keys keep the ChatGPT/Claude defaults
6239
+ * (`delayMs: 200`, `skipDelayMs: 300`).
6240
+ */
6241
+ tooltip?: AgentWidgetTooltipConfig;
5322
6242
  launcher?: AgentWidgetLauncherConfig;
5323
6243
  initialMessages?: AgentWidgetMessage[];
5324
6244
  /**