@runtypelabs/persona 4.32.2 → 4.34.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 (215) hide show
  1. package/dist/activity-ui.d.cts +3 -15
  2. package/dist/activity-ui.d.ts +3 -15
  3. package/dist/animations/glyph-cycle.d.cts +1 -3
  4. package/dist/animations/glyph-cycle.d.ts +1 -3
  5. package/dist/animations/wipe.d.cts +1 -3
  6. package/dist/animations/wipe.d.ts +1 -3
  7. package/dist/approval-ui.cjs +2 -2
  8. package/dist/approval-ui.d.cts +3 -15
  9. package/dist/approval-ui.d.ts +3 -15
  10. package/dist/approval-ui.js +1 -1
  11. package/dist/artifacts-ui.cjs +1 -1
  12. package/dist/artifacts-ui.d.cts +3 -21
  13. package/dist/artifacts-ui.d.ts +3 -21
  14. package/dist/background-runtime.d.cts +2 -3
  15. package/dist/background-runtime.d.ts +2 -3
  16. package/dist/{chunk-BbXc58OA.js → chunk-6CUrH_jm.js} +1 -1
  17. package/dist/{chunk-B_H0l3u2.js → chunk-BRM3bHcJ.js} +1 -1
  18. package/dist/{chunk-DNdeIFJ6.js → chunk-Brdx4NCz.js} +1 -1
  19. package/dist/chunk-C-rux8Q9.js +1 -0
  20. package/dist/{chunk-Ds_twaZn.js → chunk-C4v-HNhS.js} +1 -1
  21. package/dist/{chunk-Bkma5hIU.js → chunk-CE4seJxF.js} +1 -1
  22. package/dist/chunk-CEPM3FOp.js +1 -0
  23. package/dist/{chunk-BpjtKnGI.js → chunk-CV8EPIhT.js} +1 -1
  24. package/dist/{chunk-DVZ8ri1z.js → chunk-CWzHNuVK.js} +1 -1
  25. package/dist/{chunk-B3l7TOOv.js → chunk-CYpt7PsO.js} +1 -1
  26. package/dist/{chunk-2FnmZ9AA.js → chunk-Csr59G5V.js} +1 -1
  27. package/dist/{chunk-B9Zu-wrk.js → chunk-CxNPSY-b.js} +1 -1
  28. package/dist/{chunk-UWMfWW2a.js → chunk-D1G8tgYc.js} +1 -1
  29. package/dist/{chunk-DTGvDx8o.js → chunk-Dj2a9f9w.js} +1 -1
  30. package/dist/chunk-Dqa_GOcj.js +1 -0
  31. package/dist/chunk-DsWv3EGF.js +1 -0
  32. package/dist/{chunk-C7-m0EmI.js → chunk-NUNdcnAZ.js} +1 -1
  33. package/dist/{chunk-D0tcIZJM.js → chunk-YMkMvAsa.js} +2 -2
  34. package/dist/{chunk-C-EHtpbB.js → chunk-h5aOI_Pa.js} +1 -1
  35. package/dist/chunk-i4qGOJAL.js +1 -0
  36. package/dist/chunk-kizhspe5.js +1 -0
  37. package/dist/client-dispatch.js +1 -1
  38. package/dist/client-history.js +1 -1
  39. package/dist/client-stream.js +1 -1
  40. package/dist/{code-generators-DqauYyfX.d.cts → code-generators-DPAqTcAz.d.cts} +0 -2
  41. package/dist/{code-generators-DqauYyfX.d.ts → code-generators-DPAqTcAz.d.ts} +0 -2
  42. package/dist/codegen.cjs +1 -1
  43. package/dist/codegen.d.cts +1 -1
  44. package/dist/codegen.d.ts +1 -1
  45. package/dist/codegen.js +1 -1
  46. package/dist/composer-actions-runtime.cjs +1 -1
  47. package/dist/composer-actions-runtime.d.cts +4 -20
  48. package/dist/composer-actions-runtime.d.ts +4 -20
  49. package/dist/composer-actions-runtime.js +1 -1
  50. package/dist/{composer-input-dwg_o9Z-.d.ts → composer-input-i_FiUp6J.d.cts} +1 -7
  51. package/dist/{composer-input-dwg_o9Z-.d.cts → composer-input-i_FiUp6J.d.ts} +1 -7
  52. package/dist/{composer-store-Dw7Zbqxr.d.ts → composer-store-DhGNgG7Z.d.cts} +1 -3
  53. package/dist/{composer-store-Dw7Zbqxr.d.cts → composer-store-DhGNgG7Z.d.ts} +1 -3
  54. package/dist/config-diagnostics.d.cts +0 -2
  55. package/dist/config-diagnostics.d.ts +0 -2
  56. package/dist/{context-mention-manager-D2p0-JH-.d.ts → context-mention-manager-DL-xwGfL.d.cts} +2 -6
  57. package/dist/{context-mention-manager-D2p0-JH-.d.cts → context-mention-manager-DL-xwGfL.d.ts} +2 -6
  58. package/dist/context-mentions-inline.cjs +1 -1
  59. package/dist/context-mentions-inline.d.cts +2 -4
  60. package/dist/context-mentions-inline.d.ts +2 -4
  61. package/dist/context-mentions-inline.js +1 -1
  62. package/dist/context-mentions.cjs +1 -1
  63. package/dist/context-mentions.d.cts +3 -9
  64. package/dist/context-mentions.d.ts +3 -9
  65. package/dist/{defaults-CjX0kKzb.d.ts → defaults-Bga2Xrsp.d.cts} +2 -5
  66. package/dist/{defaults-CjX0kKzb.d.cts → defaults-Bga2Xrsp.d.ts} +2 -5
  67. package/dist/dictation-runtime.cjs +1 -1
  68. package/dist/dictation-runtime.d.cts +4 -13
  69. package/dist/dictation-runtime.d.ts +4 -13
  70. package/dist/dictation-runtime.js +1 -1
  71. package/dist/{dom-context-DHTabIn7.d.ts → dom-context-Bw-_9Evh.d.cts} +0 -2
  72. package/dist/{dom-context-DHTabIn7.d.cts → dom-context-Bw-_9Evh.d.ts} +0 -2
  73. package/dist/event-stream-view.cjs +1 -1
  74. package/dist/event-stream-view.d.cts +3 -16
  75. package/dist/event-stream-view.d.ts +3 -16
  76. package/dist/{fallback-speech-engine-DmF7MczI.d.ts → fallback-speech-engine-kLdyPcmk.d.cts} +1 -3
  77. package/dist/{fallback-speech-engine-DmF7MczI.d.cts → fallback-speech-engine-kLdyPcmk.d.ts} +1 -3
  78. package/dist/{feature-preferences-CVFDt9Xf.d.ts → feature-preferences-CmwZGKrf.d.cts} +1 -5
  79. package/dist/{feature-preferences-CVFDt9Xf.d.cts → feature-preferences-CmwZGKrf.d.ts} +1 -5
  80. package/dist/forms-ui.d.cts +2 -4
  81. package/dist/forms-ui.d.ts +2 -4
  82. package/dist/{history-confirm-dialog-DJEXsTyE.d.cts → history-confirm-dialog-i46-lPN_.d.cts} +2 -14
  83. package/dist/{history-confirm-dialog-DJEXsTyE.d.ts → history-confirm-dialog-i46-lPN_.d.ts} +2 -14
  84. package/dist/history-shell.cjs +1 -1
  85. package/dist/history-shell.d.cts +8 -27
  86. package/dist/history-shell.d.ts +8 -27
  87. package/dist/history-shell.js +1 -1
  88. package/dist/history-view.d.cts +1 -1
  89. package/dist/history-view.d.ts +1 -1
  90. package/dist/icons-extra.cjs +1 -1
  91. package/dist/icons-extra.d.cts +0 -2
  92. package/dist/icons-extra.d.ts +0 -2
  93. package/dist/index.cjs +44 -44
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.d.cts +11 -11
  96. package/dist/index.d.ts +11 -11
  97. package/dist/index.global.js +1 -1
  98. package/dist/index.global.js.map +1 -1
  99. package/dist/index.js +46 -46
  100. package/dist/index.js.map +1 -1
  101. package/dist/install.global.js +1 -1
  102. package/dist/install.global.js.map +1 -1
  103. package/dist/launcher.global.js +2 -2
  104. package/dist/launcher.global.js.map +1 -1
  105. package/dist/{loading-label-runtime-loader-CPxnoaoi.d.ts → loading-label-runtime-loader-Ccr3ETd3.d.cts} +1 -3
  106. package/dist/{loading-label-runtime-loader-CPxnoaoi.d.cts → loading-label-runtime-loader-Ccr3ETd3.d.ts} +1 -3
  107. package/dist/loading-label-runtime.cjs +1 -1
  108. package/dist/loading-label-runtime.d.cts +1 -1
  109. package/dist/loading-label-runtime.d.ts +1 -1
  110. package/dist/loading-label-runtime.js +1 -1
  111. package/dist/markdown-parsers.js +1 -1
  112. package/dist/{message-bubble-CkZGlCLC.d.ts → message-bubble-C6AVaH7f.d.cts} +21 -4
  113. package/dist/{message-bubble-CkZGlCLC.d.cts → message-bubble-C6AVaH7f.d.ts} +21 -4
  114. package/dist/{plugin-kit-C1mRjLgW.d.cts → plugin-kit-BVggqE3C.d.cts} +0 -2
  115. package/dist/{plugin-kit-C1mRjLgW.d.ts → plugin-kit-BVggqE3C.d.ts} +0 -2
  116. package/dist/plugin-kit.d.cts +1 -1
  117. package/dist/plugin-kit.d.ts +1 -1
  118. package/dist/preferences-runtime.cjs +1 -1
  119. package/dist/preferences-runtime.d.cts +1 -1
  120. package/dist/preferences-runtime.d.ts +1 -1
  121. package/dist/preferences-runtime.js +1 -1
  122. package/dist/{preview-DdUvoLb4.d.cts → preview-BMdRffo4.d.cts} +2 -6
  123. package/dist/{preview-DdUvoLb4.d.ts → preview-BMdRffo4.d.ts} +2 -6
  124. package/dist/{registry-Dz4iHNST.d.ts → registry-B16l8s1b.d.cts} +1 -8
  125. package/dist/{registry-Dz4iHNST.d.cts → registry-B16l8s1b.d.ts} +1 -8
  126. package/dist/{session-Baxp9iRK.d.cts → session-CtZHe7fc.d.cts} +10 -93
  127. package/dist/{session-Baxp9iRK.d.ts → session-CtZHe7fc.d.ts} +10 -93
  128. package/dist/session-reconnect.d.cts +2 -4
  129. package/dist/session-reconnect.d.ts +2 -4
  130. package/dist/smart-dom-reader.d.cts +2 -8
  131. package/dist/smart-dom-reader.d.ts +2 -8
  132. package/dist/testing.d.cts +0 -2
  133. package/dist/testing.d.ts +0 -2
  134. package/dist/theme-editor-preview.cjs +42 -42
  135. package/dist/theme-editor-preview.d.cts +1 -1
  136. package/dist/theme-editor-preview.d.ts +1 -1
  137. package/dist/theme-editor-preview.js +25 -25
  138. package/dist/theme-editor.cjs +25 -5
  139. package/dist/theme-editor.d.cts +2 -22
  140. package/dist/theme-editor.d.ts +2 -22
  141. package/dist/theme-editor.js +1 -1
  142. package/dist/theme-reference.cjs +1 -1
  143. package/dist/theme-reference.d.cts +4 -4
  144. package/dist/theme-reference.d.ts +4 -4
  145. package/dist/theme-reference.js +1 -1
  146. package/dist/{tool-loading-animation-un3kA5Ur.d.ts → tool-loading-animation-D2cyFvmM.d.cts} +1 -7
  147. package/dist/{tool-loading-animation-un3kA5Ur.d.cts → tool-loading-animation-D2cyFvmM.d.ts} +1 -7
  148. package/dist/{tooltip-BkW-8MnD.d.cts → tooltip-DbXkAuJL.d.cts} +0 -2
  149. package/dist/{tooltip-BkW-8MnD.d.ts → tooltip-DbXkAuJL.d.ts} +0 -2
  150. package/dist/{turn-activity-ui-BGNhtMcH.d.cts → turn-activity-ui-Do1Nd-Ow.d.cts} +2 -12
  151. package/dist/{turn-activity-ui-BGNhtMcH.d.ts → turn-activity-ui-Do1Nd-Ow.d.ts} +2 -12
  152. package/dist/turn-activity-ui.d.cts +1 -1
  153. package/dist/turn-activity-ui.d.ts +1 -1
  154. package/dist/{types-BOZHQIk0.d.cts → types-BC7MRxO5.d.cts} +167 -36
  155. package/dist/{types-BOZHQIk0.d.ts → types-BC7MRxO5.d.ts} +167 -36
  156. package/dist/ui-extras.js +3 -3
  157. package/dist/voice-client-tools.d.cts +1 -3
  158. package/dist/voice-client-tools.d.ts +1 -3
  159. package/dist/voice-runtime.cjs +1 -1
  160. package/dist/voice-runtime.d.cts +2 -12
  161. package/dist/voice-runtime.d.ts +2 -12
  162. package/dist/voice-runtime.js +2 -2
  163. package/dist/voice-worklet-player.d.cts +2 -6
  164. package/dist/voice-worklet-player.d.ts +2 -6
  165. package/dist/{webmcp-runtime-entry-DuAG1_yF.d.ts → webmcp-runtime-entry-B8g46vS0.d.cts} +1 -5
  166. package/dist/{webmcp-runtime-entry-DuAG1_yF.d.cts → webmcp-runtime-entry-B8g46vS0.d.ts} +1 -5
  167. package/dist/webmcp-runtime.d.cts +1 -1
  168. package/dist/webmcp-runtime.d.ts +1 -1
  169. package/dist/widget.css +1 -1
  170. package/package.json +4 -4
  171. package/src/client-dispatch.ts +69 -16
  172. package/src/client.operator-role.test.ts +217 -0
  173. package/src/components/event-stream-view.test.ts +5 -0
  174. package/src/components/message-bubble.test.ts +169 -0
  175. package/src/components/message-bubble.ts +123 -34
  176. package/src/components/scroll-to-bottom.test.ts +135 -0
  177. package/src/components/scroll-to-bottom.ts +50 -13
  178. package/src/components/user-message-collapse.ts +117 -0
  179. package/src/defaults-parity.test.ts +14 -0
  180. package/src/defaults.ts +6 -0
  181. package/src/index-core.ts +7 -0
  182. package/src/session.resubmit-from.test.ts +27 -0
  183. package/src/session.test.ts +10 -0
  184. package/src/session.ts +8 -5
  185. package/src/styles/widget.css +79 -1
  186. package/src/theme-reference.ts +5 -3
  187. package/src/types/theme.ts +50 -0
  188. package/src/types.ts +120 -11
  189. package/src/ui.ask-user-question-plugin.test.ts +154 -0
  190. package/src/ui.operator-role.test.ts +336 -0
  191. package/src/ui.scroll-additive.test.ts +219 -117
  192. package/src/ui.scroll.test.ts +176 -13
  193. package/src/ui.ts +267 -119
  194. package/src/ui.turn-activity.test.ts +7 -7
  195. package/src/ui.user-message-collapse.test.ts +540 -0
  196. package/src/utils/__snapshots__/tokens-parity.test.ts.snap +236 -0
  197. package/src/utils/auto-follow.test.ts +0 -38
  198. package/src/utils/auto-follow.ts +0 -35
  199. package/src/utils/brand.test.ts +14 -0
  200. package/src/utils/brand.ts +8 -4
  201. package/src/utils/history-messages.test.ts +30 -0
  202. package/src/utils/history-messages.ts +26 -8
  203. package/src/utils/message-fingerprint.ts +4 -0
  204. package/src/utils/resubmit-turn.test.ts +13 -0
  205. package/src/utils/resubmit-turn.ts +12 -1
  206. package/src/utils/theme.test.ts +23 -0
  207. package/src/utils/tokens.ts +47 -0
  208. package/src/welcome.test.ts +16 -0
  209. package/src/welcome.ts +10 -2
  210. package/dist/chunk-BNE40cEG.js +0 -1
  211. package/dist/chunk-Bz9jAgba.js +0 -1
  212. package/dist/chunk-CVMIncEY.js +0 -1
  213. package/dist/chunk-CdpVP6LZ.js +0 -1
  214. package/dist/chunk-CjkechxR.js +0 -1
  215. package/dist/chunk-DgZZqy14.js +0 -1
package/src/types.ts CHANGED
@@ -616,7 +616,7 @@ export type AgentWidgetReplayedToolResult = {
616
616
  * from its own stored transcript there.
617
617
  */
618
618
  export type AgentWidgetRequestPayloadMessage = {
619
- role: AgentWidgetMessageRole | "tool";
619
+ role: AgentWidgetModelMessageRole | "tool";
620
620
  content: MessageContent;
621
621
  createdAt: string;
622
622
  /** Optional assistant phase for backend adapters that replay provider history. */
@@ -1123,6 +1123,21 @@ export type AgentMessageMetadata = {
1123
1123
  };
1124
1124
  };
1125
1125
 
1126
+ /** Ordinary model context only; the serializer owns tool-call/result pairing. */
1127
+ export type AgentWidgetModelMessageProjection = Pick<AgentWidgetRequestPayloadMessage, "content" | "createdAt" | "phase"> & {
1128
+ role: AgentWidgetModelMessageRole;
1129
+ };
1130
+
1131
+ /** Synchronous, per-message conversion before any dispatch transport serializes history. */
1132
+ export type AgentWidgetModelMessageTransform = (context: {
1133
+ /** Full UI transcript entry, including identity and human provenance. */
1134
+ message: Readonly<AgentWidgetMessage>;
1135
+ /** Resolved model content: contentParts > llmContent > rawContent > content. */
1136
+ content: MessageContent;
1137
+ /** Normal model projection; null for operator messages. Return null to omit. */
1138
+ modelMessage: AgentWidgetModelMessageProjection | null;
1139
+ }) => AgentWidgetModelMessageProjection | null;
1140
+
1126
1141
  export type AgentWidgetRequestMiddlewareContext = {
1127
1142
  payload: AgentWidgetRequestPayload;
1128
1143
  config: AgentWidgetConfig;
@@ -1391,7 +1406,7 @@ export type AgentWidgetMessageActionsConfig = {
1391
1406
  };
1392
1407
 
1393
1408
  /** Which roles a custom message action appears on. */
1394
- export type AgentWidgetCustomMessageActionRole = "user" | "assistant";
1409
+ export type AgentWidgetCustomMessageActionRole = "user" | "assistant" | "operator";
1395
1410
 
1396
1411
  /** One host-contributed message action. */
1397
1412
  export type AgentWidgetCustomMessageAction = {
@@ -2213,10 +2228,13 @@ export type AgentWidgetArtifactsFeature = {
2213
2228
  *
2214
2229
  * - `"anchor-top"` (default): on send, scroll the user's message near the top
2215
2230
  * of the viewport and hold it there while the response streams in beneath it
2216
- * (ChatGPT-style). When a turn has no user send to anchor to (a proactive
2217
- * greeting, an injected assistant message, a resubmit, or first-load
2218
- * streaming), it falls back to `"follow"` for that turn so content never
2219
- * streams in off-screen.
2231
+ * (ChatGPT-style). The anchor holds for the whole reply, even when the
2232
+ * message is taller than the viewport or tool calls push the answer below
2233
+ * the fold, until the reader scrolls away, jumps to the latest content with
2234
+ * the scroll-to-bottom control, or sends again. When a turn has no user send
2235
+ * to anchor to (a proactive greeting, an injected assistant message, a
2236
+ * resubmit, or first-load streaming), it falls back to `"follow"` for that
2237
+ * turn so content never streams in off-screen.
2220
2238
  * - `"follow"`: keep the newest content pinned to the bottom of the viewport,
2221
2239
  * pausing when the user scrolls up and resuming when they return to the
2222
2240
  * bottom. This was the default before 4.x.
@@ -2305,7 +2323,8 @@ export type AgentWidgetScrollBehaviorFeature = {
2305
2323
 
2306
2324
  /** Decorative glyph state. Hidden glyphs should pause host-owned animation. */
2307
2325
  export type AgentWidgetScrollToBottomIconState = {
2308
- activity: "idle" | "generating-below";
2326
+ /** `"approval-below"`: a pending approval is below the fold and the turn waits on it. */
2327
+ activity: "idle" | "generating-below" | "approval-below";
2309
2328
  visible: boolean;
2310
2329
  hovered: boolean;
2311
2330
  focused: boolean;
@@ -2345,6 +2364,15 @@ export type AgentWidgetScrollToBottomFeature = {
2345
2364
  * @default ""
2346
2365
  */
2347
2366
  label?: string;
2367
+ /**
2368
+ * Label shown while a pending approval sits below the fold. The control
2369
+ * widens to a labelled pill with the approval's shield glyph, and the turn
2370
+ * stays put until the reader jumps (anchor-top never scrolls them to it).
2371
+ * Styled by `theme.components.scrollToBottom.approval`. Set to an empty
2372
+ * string to keep the regular control.
2373
+ * @default "Approval needed"
2374
+ */
2375
+ approvalLabel?: string;
2348
2376
  /** Glyph while a live response is arriving below. V4 defaults to arrow; V5 to dots. */
2349
2377
  streamingIndicator?: "dots" | "arrow";
2350
2378
  /** Visibility transition. V4 defaults to none; V5 to fade. Honors reduced motion. */
@@ -2773,6 +2801,31 @@ export type AgentWidgetStreamAnimationFeature = {
2773
2801
  plugins?: Record<string, StreamAnimationPlugin>;
2774
2802
  };
2775
2803
 
2804
+ /**
2805
+ * Collapse long user messages behind a "Show more" / "Show less" toggle.
2806
+ * Only the built-in user bubble collapses; `layout.messages.renderUserMessage`
2807
+ * and plugin `renderMessage` output is left untouched.
2808
+ */
2809
+ export type AgentWidgetUserMessageCollapseFeature = {
2810
+ /**
2811
+ * Clip a user bubble whose rendered content (text, markdown, code,
2812
+ * attachments) is taller than `maxHeight`, with a bottom fade and a real
2813
+ * toggle button. The full message stays in the DOM, so find-in-page, copy,
2814
+ * selection and screen readers still reach it. Interim voice transcripts
2815
+ * stay expanded and collapse once final. A reader's toggle is remembered
2816
+ * per message for the life of the widget.
2817
+ * @default false (`true` under `future.v5Defaults`)
2818
+ */
2819
+ enabled?: boolean;
2820
+ /**
2821
+ * Collapsed height in px. Content within about two lines (48px) of the
2822
+ * limit is left expanded, so the toggle never hides just a line or two.
2823
+ * The default shows about ten lines of bubble text.
2824
+ * @default 240
2825
+ */
2826
+ maxHeight?: number;
2827
+ };
2828
+
2776
2829
  export type AgentWidgetFeatureFlags = {
2777
2830
  showReasoning?: boolean;
2778
2831
  showToolCalls?: boolean;
@@ -2789,6 +2842,8 @@ export type AgentWidgetFeatureFlags = {
2789
2842
  scrollToBottom?: AgentWidgetScrollToBottomFeature;
2790
2843
  /** Transcript scroll behavior during streamed responses. */
2791
2844
  scrollBehavior?: AgentWidgetScrollBehaviorFeature;
2845
+ /** "Show more" collapse for long user messages. Off by default; on under `future.v5Defaults`. */
2846
+ userMessageCollapse?: AgentWidgetUserMessageCollapseFeature;
2792
2847
  /** Collapsed transcript behavior for tool call rows. */
2793
2848
  toolCallDisplay?: AgentWidgetToolCallDisplayFeature;
2794
2849
  /** Collapsed transcript behavior for reasoning rows. */
@@ -7191,6 +7246,11 @@ export type AgentWidgetAvatarConfig = {
7191
7246
  userAvatar?: string;
7192
7247
  /** URL or emoji for assistant avatar */
7193
7248
  assistantAvatar?: string;
7249
+ /**
7250
+ * URL or emoji for operator (human takeover) avatar. Defaults to the letter
7251
+ * "O" on a neutral fill.
7252
+ */
7253
+ operatorAvatar?: string;
7194
7254
  };
7195
7255
 
7196
7256
  /**
@@ -7246,7 +7306,10 @@ export type AgentWidgetMessageLayoutConfig = {
7246
7306
  groupConsecutive?: boolean;
7247
7307
  /** Width behavior for user-authored message rows */
7248
7308
  user?: AgentWidgetMessageRoleLayout;
7249
- /** Width behavior for assistant-authored message rows and assistant UI variants */
7309
+ /**
7310
+ * Width behavior for assistant-authored message rows and assistant UI
7311
+ * variants. Operator rows sit on the same side and use it too.
7312
+ */
7250
7313
  assistant?: AgentWidgetMessageRoleLayout;
7251
7314
  /**
7252
7315
  * Custom renderer for user messages
@@ -8398,6 +8461,8 @@ export type AgentWidgetConfig = {
8398
8461
  */
8399
8462
  getHeaders?: AgentWidgetHeadersFunction;
8400
8463
  copy?: {
8464
+ /** Visible and accessible label for human operator replies. @default "Human operator" */
8465
+ operatorLabel?: string;
8401
8466
  /** @deprecated Use `welcome.title`. */
8402
8467
  welcomeTitle?: string;
8403
8468
  /** @deprecated Use `welcome.subtitle`. */
@@ -8408,6 +8473,10 @@ export type AgentWidgetConfig = {
8408
8473
  sendButtonLabel?: string;
8409
8474
  /** Button label shown in text mode while a response is streaming. Default: "Stop". */
8410
8475
  stopButtonLabel?: string;
8476
+ /** Toggle label on a collapsed long user message (`features.userMessageCollapse`). @default "Show more" */
8477
+ showMoreLabel?: string;
8478
+ /** Toggle label on an expanded long user message (`features.userMessageCollapse`). @default "Show less" */
8479
+ showLessLabel?: string;
8411
8480
  /**
8412
8481
  * When false, the welcome / intro card is not shown above the message list.
8413
8482
  * @deprecated Use `welcome.variant: "none"`.
@@ -8590,6 +8659,16 @@ export type AgentWidgetConfig = {
8590
8659
  * See `docs/context-mentions-plan.md`.
8591
8660
  */
8592
8661
  contextMentions?: AgentWidgetContextMentionConfig;
8662
+ /**
8663
+ * Synchronous model-context conversion for each nonempty conversation entry.
8664
+ * Runs on proxy, agent and client-token dispatch, after local-only notices and
8665
+ * voice captions are excluded. By default operator messages are omitted;
8666
+ * stateless adapters can explicitly map their historical human contributions
8667
+ * into supported model roles. Do not replay contributions already owned by
8668
+ * server history. This does not authorize steering, takeover or tool approval.
8669
+ * Invalid return values fail dispatch. See docs/MESSAGE-INJECTION.md.
8670
+ */
8671
+ modelMessageTransform?: AgentWidgetModelMessageTransform;
8593
8672
  requestMiddleware?: AgentWidgetRequestMiddleware;
8594
8673
  actionParsers?: AgentWidgetActionParser[];
8595
8674
  actionHandlers?: AgentWidgetActionHandler[];
@@ -9093,7 +9172,25 @@ type ConfigPatch<T> = T extends (...args: never[]) => unknown
9093
9172
  ? { [K in keyof T]?: ConfigPatch<T[K]> }
9094
9173
  : T;
9095
9174
 
9096
- export type AgentWidgetMessageRole = "user" | "assistant" | "system";
9175
+ /**
9176
+ * Who authored a message.
9177
+ *
9178
+ * - `user`: the end user.
9179
+ * - `assistant`: the agent (model output).
9180
+ * - `system`: context for the model, rendered like an assistant row.
9181
+ * - `operator`: a human speaking on the agent's side of the conversation, such
9182
+ * as a support agent who has taken over the thread. It renders on the
9183
+ * assistant's side with its own bubble style (`theme.components.message.operator`).
9184
+ * Operator messages are omitted from model requests by default. A stateless
9185
+ * adapter can explicitly convert them with `modelMessageTransform`.
9186
+ */
9187
+ export type AgentWidgetMessageRole = "user" | "assistant" | "system" | "operator";
9188
+
9189
+ /**
9190
+ * Roles that reach a model request. Human operator UI entries must be explicitly
9191
+ * converted to a supported model role by an adapter.
9192
+ */
9193
+ export type AgentWidgetModelMessageRole = Exclude<AgentWidgetMessageRole, "operator">;
9097
9194
 
9098
9195
  export type AgentWidgetReasoning = {
9099
9196
  id: string;
@@ -9216,11 +9313,18 @@ export type AgentWidgetTurnActivityConfig = {
9216
9313
  }) => HTMLElement | string | null | undefined;
9217
9314
  };
9218
9315
 
9316
+ /** Display provenance, supplied by the host; never an authorization credential. */
9317
+ export type AgentWidgetMessageAuthor = {
9318
+ id?: string;
9319
+ displayName?: string;
9320
+ kind?: "human" | "ai" | "system";
9321
+ };
9322
+
9219
9323
  /**
9220
9324
  * Represents a message in the chat conversation.
9221
9325
  *
9222
9326
  * @property id - Unique message identifier
9223
- * @property role - Message role: "user", "assistant", or "system"
9327
+ * @property role - Message role: "user", "assistant", "system", or "operator"
9224
9328
  * @property content - Message text content (for display)
9225
9329
  * @property contentParts - Original multi-modal content parts (for API requests)
9226
9330
  * @property createdAt - ISO timestamp when message was created
@@ -9234,6 +9338,8 @@ export type AgentWidgetTurnActivityConfig = {
9234
9338
  * Useful for implementing voice-specific behaviors like auto-reactivation.
9235
9339
  */
9236
9340
  export type AgentWidgetMessage = {
9341
+ /** Preserved by injection, history and persistence; not sent to models automatically. */
9342
+ author?: AgentWidgetMessageAuthor;
9237
9343
  /** Opt-in whole-turn presentation metadata. Does not change model-visible content. */
9238
9344
  turnActivity?: AgentWidgetTurnActivity;
9239
9345
  id: string;
@@ -9388,10 +9494,13 @@ export type AgentWidgetMessage = {
9388
9494
  * }
9389
9495
  */
9390
9496
  export type InjectMessageOptions = {
9497
+ /** Host-supplied display provenance. Does not grant model or backend authority. */
9498
+ author?: AgentWidgetMessageAuthor;
9391
9499
  /** Mark assistant content as dispatch failure detail for `errorDisplay`. */
9392
9500
  dispatchError?: boolean;
9393
9501
  /**
9394
- * Message role: "assistant", "user", or "system"
9502
+ * Message role: "assistant", "user", "system", or "operator". Operator
9503
+ * messages are omitted from requests unless modelMessageTransform converts them.
9395
9504
  */
9396
9505
  role: AgentWidgetMessageRole;
9397
9506
 
@@ -689,3 +689,157 @@ describe("renderAskUserQuestion plugin hook", () => {
689
689
  controller.destroy();
690
690
  });
691
691
  });
692
+
693
+ describe("typing indicator while ask_user_question arguments stream", () => {
694
+ afterEach(() => {
695
+ document.body.innerHTML = "";
696
+ if (typeof localStorage !== "undefined") localStorage.clear();
697
+ vi.restoreAllMocks();
698
+ vi.unstubAllGlobals();
699
+ });
700
+
701
+ // Streaming transcript updates render once per animation frame; queue the
702
+ // frames so each injection can be rendered synchronously.
703
+ let frames = new Map<number, (time: number) => void>();
704
+ let nextFrameId = 1;
705
+ const renderFrame = () => {
706
+ const pending = [...frames.values()];
707
+ frames.clear();
708
+ pending.forEach((callback) => callback(0));
709
+ };
710
+
711
+ const mountStreaming = async (config: Record<string, unknown> = {}) => {
712
+ frames = new Map();
713
+ vi.stubGlobal("requestAnimationFrame", (callback: (time: number) => void) => {
714
+ const id = nextFrameId++;
715
+ frames.set(id, callback);
716
+ return id;
717
+ });
718
+ vi.stubGlobal("cancelAnimationFrame", (id: number) => frames.delete(id));
719
+ global.fetch = vi.fn().mockImplementation(
720
+ () => new Promise(() => {})
721
+ ) as unknown as typeof fetch;
722
+
723
+ const mount = createMount();
724
+ const controller = createAgentExperience(mount, {
725
+ apiUrl: "https://api.example.com/chat",
726
+ launcher: { enabled: false },
727
+ ...config,
728
+ } as unknown as Parameters<typeof createAgentExperience>[1]);
729
+
730
+ controller.submitMessage("hello");
731
+ await Promise.resolve();
732
+ await Promise.resolve();
733
+ return { mount, controller };
734
+ };
735
+
736
+ // Stamped in the future so the rows sort after the submitted user message.
737
+ const sequence = Date.now() + 60_000;
738
+ const createdAt = new Date(sequence).toISOString();
739
+ const fullArgs = {
740
+ questions: [
741
+ { question: "Which audience?", options: [{ label: "Hobbyists" }, { label: "Pros" }] },
742
+ ],
743
+ };
744
+
745
+ const injectStreamingAsk = (
746
+ controller: ReturnType<typeof createAgentExperience>,
747
+ { chunks = [], args }: { chunks?: string[]; args?: unknown } = {}
748
+ ) => {
749
+ controller.injectTestMessage({
750
+ type: "message",
751
+ message: {
752
+ id: "ask-1",
753
+ role: "assistant",
754
+ content: "",
755
+ createdAt,
756
+ sequence,
757
+ streaming: true,
758
+ variant: "tool",
759
+ toolCall: { id: "ask-1", name: "ask_user_question", status: "running", chunks, args },
760
+ },
761
+ });
762
+ renderFrame();
763
+ };
764
+
765
+ const injectAwaitingAsk = (controller: ReturnType<typeof createAgentExperience>) => {
766
+ controller.injectTestMessage({
767
+ type: "message",
768
+ message: {
769
+ id: "ask-1",
770
+ role: "assistant",
771
+ content: "",
772
+ createdAt,
773
+ sequence,
774
+ streaming: false,
775
+ variant: "tool",
776
+ toolCall: { id: "ask-1", name: "ask_user_question", status: "complete", chunks: [], args: fullArgs },
777
+ agentMetadata: { executionId: "exec_123", awaitingLocalTool: true },
778
+ },
779
+ });
780
+ renderFrame();
781
+ };
782
+
783
+ const typingIndicator = (mount: HTMLElement) =>
784
+ mount.querySelector('[data-typing-indicator="true"]');
785
+ const askSheet = (mount: HTMLElement) => mount.querySelector("[data-persona-ask-sheet-for]");
786
+
787
+ it("keeps the dots until the built-in sheet mounts when no plugin renders the question", async () => {
788
+ const { mount, controller } = await mountStreaming();
789
+
790
+ injectStreamingAsk(controller);
791
+ expect(askSheet(mount)).toBeNull();
792
+ expect(typingIndicator(mount)).not.toBeNull();
793
+
794
+ injectAwaitingAsk(controller);
795
+ expect(askSheet(mount)).not.toBeNull();
796
+ expect(typingIndicator(mount)).toBeNull();
797
+
798
+ controller.destroy();
799
+ });
800
+
801
+ it("keeps the dots while the plugin returns null for a partial payload", async () => {
802
+ const plugin: AgentWidgetPlugin = {
803
+ id: "partial-asker",
804
+ renderAskUserQuestion: ({ payload }) => {
805
+ if (!payload?.questions?.[0]?.options?.length) return null;
806
+ const root = document.createElement("div");
807
+ root.setAttribute("data-test-id", "partial-ask");
808
+ return root;
809
+ },
810
+ };
811
+ const { mount, controller } = await mountStreaming({ plugins: [plugin] });
812
+
813
+ injectStreamingAsk(controller, { chunks: ['{"questions":[{"question":"Which'] });
814
+ expect(mount.querySelector('[data-test-id="partial-ask"]')).toBeNull();
815
+ expect(askSheet(mount)).toBeNull();
816
+ expect(typingIndicator(mount)).not.toBeNull();
817
+
818
+ injectStreamingAsk(controller, { args: fullArgs });
819
+ expect(mount.querySelector('[data-test-id="partial-ask"]')).not.toBeNull();
820
+ expect(typingIndicator(mount)).toBeNull();
821
+
822
+ controller.destroy();
823
+ });
824
+
825
+ it.each([true, false])("hides the dots as soon as the plugin renders a bubble (showToolCalls=%s)", async (showToolCalls) => {
826
+ const plugin: AgentWidgetPlugin = {
827
+ id: "eager-asker",
828
+ renderAskUserQuestion: () => {
829
+ const root = document.createElement("div");
830
+ root.setAttribute("data-test-id", "eager-ask");
831
+ return root;
832
+ },
833
+ };
834
+ const { mount, controller } = await mountStreaming({
835
+ plugins: [plugin],
836
+ features: { showToolCalls },
837
+ });
838
+
839
+ injectStreamingAsk(controller);
840
+ expect(mount.querySelector('[data-test-id="eager-ask"]')).not.toBeNull();
841
+ expect(typingIndicator(mount)).toBeNull();
842
+
843
+ controller.destroy();
844
+ });
845
+ });