@runtypelabs/persona 4.24.0 → 4.26.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 (237) 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-Db61aI9J.d.cts → types-BwKTZhg9.d.cts} +14 -0
  4. package/dist/animations/{types-Db61aI9J.d.ts → types-BwKTZhg9.d.ts} +14 -0
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/animations-extra.d.cts +14 -0
  8. package/dist/animations-extra.d.ts +14 -0
  9. package/dist/approval-ui-QAKWNFNM.js +1 -0
  10. package/dist/approval-ui.cjs +2 -2
  11. package/dist/approval-ui.d.cts +243 -8
  12. package/dist/approval-ui.d.ts +243 -8
  13. package/dist/approval-ui.js +2 -2
  14. package/dist/{artifacts-ui-IGB5YKLO.js → artifacts-ui-NJGMM27I.js} +1 -1
  15. package/dist/artifacts-ui.cjs +4 -4
  16. package/dist/artifacts-ui.d.cts +243 -8
  17. package/dist/artifacts-ui.d.ts +243 -8
  18. package/dist/artifacts-ui.js +5 -5
  19. package/dist/chunk-2ANTRKTV.js +1 -0
  20. package/dist/chunk-4HPTPUC6.js +1 -0
  21. package/dist/chunk-6V3MNEKB.js +1 -0
  22. package/dist/chunk-HGUCGM3K.js +9 -0
  23. package/dist/chunk-IO5VVUKP.js +3 -0
  24. package/dist/chunk-J4J3Y2P2.js +1 -0
  25. package/dist/chunk-K6HKIP5K.js +1 -0
  26. package/dist/chunk-K7NA7G7J.js +1 -0
  27. package/dist/chunk-MXUTS3LB.js +1 -0
  28. package/dist/chunk-PDPVORUF.js +1 -0
  29. package/dist/chunk-QPPJUSZH.js +1 -0
  30. package/dist/chunk-QRKGQPU7.js +1 -0
  31. package/dist/chunk-S6PBWE75.js +1 -0
  32. package/dist/chunk-T7QM7YMF.js +1 -0
  33. package/dist/chunk-TR2H475E.js +1 -0
  34. package/dist/chunk-UPO4GUFC.js +1 -0
  35. package/dist/chunk-ZC66NAIR.js +1 -0
  36. package/dist/client-history-YPEG45Q4.js +1 -0
  37. package/dist/client-history.js +1 -0
  38. package/dist/client-stream-42FOI3EC.js +4 -0
  39. package/dist/client-stream.js +5 -0
  40. package/dist/codegen.cjs +1 -1
  41. package/dist/codegen.js +1 -1
  42. package/dist/context-mentions-E7HJYWO6.js +161 -0
  43. package/dist/{context-mentions-inline-I2RDEBAK.js → context-mentions-inline-QVESLW55.js} +1 -1
  44. package/dist/context-mentions.cjs +7 -7
  45. package/dist/context-mentions.d.cts +243 -8
  46. package/dist/context-mentions.d.ts +243 -8
  47. package/dist/context-mentions.js +8 -8
  48. package/dist/event-stream-view-FOIJHFQO.js +1 -0
  49. package/dist/event-stream-view.cjs +1 -1
  50. package/dist/event-stream-view.d.cts +246 -31
  51. package/dist/event-stream-view.d.ts +246 -31
  52. package/dist/event-stream-view.js +1 -1
  53. package/dist/forms-ui.d.cts +295 -667
  54. package/dist/forms-ui.d.ts +295 -667
  55. package/dist/history-shell-QDQ44PSE.js +3 -0
  56. package/dist/history-shell.js +3 -0
  57. package/dist/{history-view-BD6OXLNA.js → history-view-NCFBI5GR.js} +2 -2
  58. package/dist/history-view.cjs +2 -2
  59. package/dist/history-view.d.cts +35 -1
  60. package/dist/history-view.d.ts +35 -1
  61. package/dist/history-view.js +2 -2
  62. package/dist/index.cjs +80 -74
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +301 -738
  65. package/dist/index.d.ts +301 -738
  66. package/dist/index.global.js +3 -215
  67. package/dist/index.global.js.map +1 -1
  68. package/dist/index.js +81 -75
  69. package/dist/index.js.map +1 -1
  70. package/dist/install.global.js +1 -1
  71. package/dist/install.global.js.map +1 -1
  72. package/dist/launcher.global.js +3 -67
  73. package/dist/launcher.global.js.map +1 -1
  74. package/dist/markdown-parsers.js +22 -22
  75. package/dist/runtype-tts-entry-NFBVWR7N.js +1 -0
  76. package/dist/runtype-tts.js +1 -1
  77. package/dist/session-actions-ISSKSI66.js +1 -0
  78. package/dist/session-actions.js +1 -0
  79. package/dist/session-reconnect.d.cts +243 -8
  80. package/dist/session-reconnect.d.ts +243 -8
  81. package/dist/smart-dom-reader.cjs +13 -13
  82. package/dist/smart-dom-reader.d.cts +243 -8
  83. package/dist/smart-dom-reader.d.ts +243 -8
  84. package/dist/smart-dom-reader.js +11 -11
  85. package/dist/theme-editor-preview.cjs +116 -98
  86. package/dist/theme-editor-preview.d.cts +245 -8
  87. package/dist/theme-editor-preview.d.ts +245 -8
  88. package/dist/theme-editor-preview.js +49 -54
  89. package/dist/theme-editor.cjs +2 -2
  90. package/dist/theme-editor.d.cts +244 -23
  91. package/dist/theme-editor.d.ts +244 -23
  92. package/dist/theme-editor.js +2 -2
  93. package/dist/theme-reference.d.cts +14 -0
  94. package/dist/theme-reference.d.ts +14 -0
  95. package/dist/ui-extras-entry-WAYFKVNW.js +3 -0
  96. package/dist/ui-extras.js +72 -0
  97. package/dist/voice-runtime-UOQOFAFY.js +17 -0
  98. package/dist/voice-runtime.cjs +17 -1
  99. package/dist/voice-runtime.d.cts +11643 -192
  100. package/dist/voice-runtime.d.ts +11643 -192
  101. package/dist/voice-runtime.js +17 -1
  102. package/dist/voice-worklet-player.cjs +19 -4
  103. package/dist/voice-worklet-player.d.cts +9 -10
  104. package/dist/voice-worklet-player.d.ts +9 -10
  105. package/dist/voice-worklet-player.js +19 -4
  106. package/dist/webmcp-polyfill.js +1 -1
  107. package/dist/webmcp-runtime-V6ZWW3PI.js +6 -0
  108. package/dist/webmcp-runtime.cjs +2 -2
  109. package/dist/webmcp-runtime.d.cts +1 -33
  110. package/dist/webmcp-runtime.d.ts +1 -33
  111. package/dist/webmcp-runtime.js +2 -2
  112. package/dist/widget.css +1 -1
  113. package/package.json +11 -5
  114. package/src/approval-paused-tool-settle.test.ts +221 -0
  115. package/src/client-approve.test.ts +311 -0
  116. package/src/client-history-eager.ts +9 -0
  117. package/src/client-history-loader.ts +23 -0
  118. package/src/client-history.ts +579 -0
  119. package/src/client-resume.ts +177 -0
  120. package/src/client-stream-eager.ts +12 -0
  121. package/src/client-stream-loader.ts +25 -0
  122. package/src/client-stream.ts +1880 -0
  123. package/src/client.test.ts +31 -1
  124. package/src/client.ts +293 -2607
  125. package/src/components/approval-actions.test.ts +27 -0
  126. package/src/components/approval-actions.ts +27 -3
  127. package/src/components/message-bubble.test.ts +19 -0
  128. package/src/components/message-bubble.ts +2 -0
  129. package/src/components/registry.ts +6 -6
  130. package/src/components/tool-bubble.test.ts +9 -0
  131. package/src/components/tool-bubble.ts +1 -0
  132. package/src/history-shell-bundle.test.ts +54 -0
  133. package/src/history-shell-eager.ts +12 -0
  134. package/src/history-shell-loader.ts +27 -0
  135. package/src/history-shell.ts +2362 -0
  136. package/src/history-view-loader.ts +2 -0
  137. package/src/history-view.ts +3 -0
  138. package/src/index-core.ts +8 -0
  139. package/src/index-global.ts +90 -248
  140. package/src/index.ts +13 -0
  141. package/src/install.ts +6 -4
  142. package/src/internal/runtype-history-provider.test.ts +1 -0
  143. package/src/internal/runtype-history-provider.ts +28 -13
  144. package/src/plugins/registry.ts +8 -8
  145. package/src/session-actions-chunk.test.ts +55 -0
  146. package/src/session-actions-eager.ts +11 -0
  147. package/src/session-actions-loader.ts +37 -0
  148. package/src/session-actions.ts +909 -0
  149. package/src/session-history.test.ts +3 -1
  150. package/src/session.ts +875 -1530
  151. package/src/session.voice.test.ts +1241 -5
  152. package/src/styles/widget.css +58 -0
  153. package/src/theme-editor/state.ts +91 -91
  154. package/src/types.ts +269 -5
  155. package/src/ui-ask-user-sheet.ts +382 -0
  156. package/src/ui-extras-eager.ts +10 -0
  157. package/src/ui-extras-entry.ts +9 -0
  158. package/src/ui-extras-loader.ts +26 -0
  159. package/src/ui.artifact-rail-resize.test.ts +2 -1
  160. package/src/ui.ask-user-chunk.test.ts +93 -0
  161. package/src/ui.history-chunk.test.ts +2 -1
  162. package/src/ui.history-config-provider.test.ts +2 -1
  163. package/src/ui.history-confirm-fallback.test.ts +142 -0
  164. package/src/ui.history-controller.test.ts +2 -1
  165. package/src/ui.history-lifecycle.test.ts +2 -1
  166. package/src/ui.history-render-hooks.test.ts +2 -1
  167. package/src/ui.history-shell.test.ts +2 -1
  168. package/src/ui.rail-overlay.test.ts +2 -1
  169. package/src/ui.rail-resize.test.ts +2 -1
  170. package/src/ui.rail-trigger-pending.test.ts +2 -1
  171. package/src/ui.ts +399 -2770
  172. package/src/ui.voice-prewarm.test.ts +222 -0
  173. package/src/utils/attachment-manager.ts +67 -67
  174. package/src/utils/constants.ts +3 -0
  175. package/src/utils/context-mention-controller.ts +323 -323
  176. package/src/utils/context-mention-manager.ts +40 -40
  177. package/src/utils/context-mention-orchestrator.ts +28 -0
  178. package/src/utils/event-stream-buffer.ts +50 -50
  179. package/src/utils/event-stream-store.ts +43 -43
  180. package/src/utils/throughput-tracker.ts +34 -34
  181. package/src/utils/virtual-scroller.ts +67 -67
  182. package/src/utils/visitor-store.test.ts +50 -0
  183. package/src/utils/visitor-store.ts +23 -5
  184. package/src/voice/__fixtures__/voice-wire/README.md +1 -0
  185. package/src/voice/__fixtures__/voice-wire/client/cancel.json +3 -0
  186. package/src/voice/__fixtures__/voice-wire/client/context.json +4 -0
  187. package/src/voice/__fixtures__/voice-wire/client/delegation_result.json +6 -0
  188. package/src/voice/__fixtures__/voice-wire/client/delegation_update.json +6 -0
  189. package/src/voice/__fixtures__/voice-wire/client/ping.json +3 -0
  190. package/src/voice/__fixtures__/voice-wire/client/playback_progress.json +5 -0
  191. package/src/voice/__fixtures__/voice-wire/client/start.json +3 -0
  192. package/src/voice/__fixtures__/voice-wire/server/artifact.json +11 -0
  193. package/src/voice/__fixtures__/voice-wire/server/attached.json +4 -0
  194. package/src/voice/__fixtures__/voice-wire/server/audio_clear.json +4 -0
  195. package/src/voice/__fixtures__/voice-wire/server/audio_end.json +3 -0
  196. package/src/voice/__fixtures__/voice-wire/server/delegation_cancelled.json +5 -0
  197. package/src/voice/__fixtures__/voice-wire/server/delegation_completed.json +8 -0
  198. package/src/voice/__fixtures__/voice-wire/server/delegation_started.json +15 -0
  199. package/src/voice/__fixtures__/voice-wire/server/error.json +6 -0
  200. package/src/voice/__fixtures__/voice-wire/server/metrics.json +10 -0
  201. package/src/voice/__fixtures__/voice-wire/server/pong.json +3 -0
  202. package/src/voice/__fixtures__/voice-wire/server/session_config.json +16 -0
  203. package/src/voice/__fixtures__/voice-wire/server/transcript_final.json +7 -0
  204. package/src/voice/__fixtures__/voice-wire/server/transcript_interim.json +4 -0
  205. package/src/voice/__fixtures__/voice-wire/server/transcript_partial.json +6 -0
  206. package/src/voice/__fixtures__/voice-wire/server/transcript_update.json +10 -0
  207. package/src/voice/__fixtures__/voice-wire/server/warning.json +5 -0
  208. package/src/voice/audio-playback-manager.test.ts +57 -0
  209. package/src/voice/audio-playback-manager.ts +120 -98
  210. package/src/voice/browser-voice-provider.ts +37 -37
  211. package/src/voice/fallback-speech-engine.ts +22 -16
  212. package/src/voice/keyed-voice-transcript.ts +314 -0
  213. package/src/voice/mic-state-styles.ts +182 -0
  214. package/src/voice/read-aloud-controller.ts +46 -44
  215. package/src/voice/runtype-speech-engine.ts +35 -33
  216. package/src/voice/runtype-voice-provider.ts +856 -164
  217. package/src/voice/session-voice-wiring.ts +266 -0
  218. package/src/voice/voice-delegation.test.ts +71 -0
  219. package/src/voice/voice-delegation.ts +364 -0
  220. package/src/voice/voice-turn-tracker.ts +6 -6
  221. package/src/voice/voice.test.ts +1250 -7
  222. package/src/voice/worklet-playback-engine.test.ts +81 -0
  223. package/src/voice/worklet-playback-engine.ts +21 -3
  224. package/src/voice-runtime-loader.ts +4 -1
  225. package/src/voice-runtime.ts +7 -0
  226. package/src/webmcp-runtime-entry.ts +41 -39
  227. package/dist/approval-ui-3NMYNXBD.js +0 -1
  228. package/dist/chunk-5EIIHQLQ.js +0 -1
  229. package/dist/chunk-7CO4T5KB.js +0 -1
  230. package/dist/chunk-EFJBUH5F.js +0 -1
  231. package/dist/chunk-R2TMIMDI.js +0 -3
  232. package/dist/chunk-UTVLOTWB.js +0 -9
  233. package/dist/context-mentions-45MODWOY.js +0 -161
  234. package/dist/event-stream-view-GL3ADRY2.js +0 -1
  235. package/dist/runtype-tts-entry-UJAEF7NZ.js +0 -1
  236. package/dist/voice-runtime-EFR3R5ES.js +0 -1
  237. package/dist/webmcp-runtime-JI7T4QYF.js +0 -6
@@ -6689,6 +6689,22 @@ type AgentWidgetStatusIndicatorConfig = {
6689
6689
  /** Status text while a reconnect attempt is in flight. */
6690
6690
  resumingText?: string;
6691
6691
  };
6692
+ /** Built-in `voiceRecognition.prewarm` triggers. See {@link AgentWidgetVoiceRecognitionConfig.prewarm}. */
6693
+ type AgentWidgetVoicePrewarmTrigger = 'hover';
6694
+ /**
6695
+ * Custom `voiceRecognition.prewarm` trigger. Runs while the composer has a mic
6696
+ * button, and again when the button is rebuilt or `update()` swaps in a
6697
+ * different function. Call `warm()` whenever the visitor shows intent to talk
6698
+ * (it is safe to call repeatedly). Return a cleanup to run on replacement.
6699
+ */
6700
+ type AgentWidgetVoicePrewarmHook = (context: {
6701
+ /** Fire-and-forget voice warm-up; latched and error-swallowing. */
6702
+ warm: () => void;
6703
+ /** The composer's current mic button. */
6704
+ micButton: HTMLButtonElement;
6705
+ /** The widget's mount element. */
6706
+ mount: HTMLElement;
6707
+ }) => void | (() => void);
6692
6708
  type AgentWidgetVoiceRecognitionConfig = {
6693
6709
  /**
6694
6710
  * Enable the mic button and voice input. The mic still only renders when the
@@ -6754,6 +6770,29 @@ type AgentWidgetVoiceRecognitionConfig = {
6754
6770
  * Fires once per turn after the reply completes.
6755
6771
  */
6756
6772
  onMetrics?: (metrics: VoiceMetrics) => void;
6773
+ /**
6774
+ * Warm the voice path when the visitor shows intent to talk, before the mic
6775
+ * click, so the call starts faster.
6776
+ *
6777
+ * - `'hover'`: when the pointer enters the mic button, it receives focus, or
6778
+ * (on touch) the first pointer-down on it.
6779
+ * - `false`: never.
6780
+ * - A function: custom trigger. Called with `{ warm, micButton, mount }`
6781
+ * while the mic button exists (and again when the button is rebuilt or
6782
+ * `update()` swaps in a different function); call `warm()` whenever you
6783
+ * decide, e.g. when the panel opens. May return a cleanup that runs on
6784
+ * destroy, rebuild, or replacement. Replaces the built-in trigger.
6785
+ *
6786
+ * What `warm()` does depends on the provider: `runtype` fires its prewarm
6787
+ * (see `provider.runtype.prewarmMode`), a `custom` provider's optional
6788
+ * {@link VoiceProvider.prewarm} runs, and the browser (Web Speech) path
6789
+ * starts the client-token session init (as `sessionInit` does), since speech
6790
+ * recognition itself cannot start before the click. Warming is
6791
+ * fire-and-forget, latched, and never surfaces an error or UI state.
6792
+ *
6793
+ * @default 'hover' in client token mode, otherwise `false`
6794
+ */
6795
+ prewarm?: AgentWidgetVoicePrewarmTrigger | AgentWidgetVoicePrewarmHook | false;
6757
6796
  provider?: {
6758
6797
  type: 'browser' | 'runtype' | 'custom';
6759
6798
  browser?: {
@@ -6775,6 +6814,62 @@ type AgentWidgetVoiceRecognitionConfig = {
6775
6814
  * `@runtypelabs/persona/voice-worklet-player` for a jitter-buffered engine.
6776
6815
  */
6777
6816
  createPlaybackEngine?: () => VoicePlaybackEngine | Promise<VoicePlaybackEngine>;
6817
+ /**
6818
+ * How `prewarm()` warms the voice path (see `voiceRecognition.prewarm`).
6819
+ *
6820
+ * - `'request'` (default): a fire-and-forget
6821
+ * `POST {host}/v1/client/agents/{agentId}/voice/prewarm`.
6822
+ * - `'attach'`: open the voice WebSocket ahead of the click without
6823
+ * starting a call; the click then starts the call on that socket. It can
6824
+ * create server work before the visitor clicks, so it is opt-in.
6825
+ * @default 'request'
6826
+ */
6827
+ prewarmMode?: 'request' | 'attach';
6828
+ /**
6829
+ * `'attach'` mode: how long (ms) an attached socket waits for the click
6830
+ * before it closes. The server clamps it to 30000..600000.
6831
+ * @default 30000
6832
+ */
6833
+ attachIdleMs?: number;
6834
+ /**
6835
+ * Full-duplex (speech-to-speech, e.g. GPT-Live) calls: run the turns the
6836
+ * voice model hands off to the agent through this widget's normal chat
6837
+ * pipeline. The spoken request becomes the user message, and the answer
6838
+ * renders as a regular assistant message (streaming, tool and approval
6839
+ * UI, WebMCP page tools, persistence) that the voice model then reads
6840
+ * aloud. Only takes effect when the server supports it (it confirms in
6841
+ * `session_config`); otherwise the agent turn runs on the server and its
6842
+ * spoken reply is transcribed, as before. Set `false` to always keep the
6843
+ * agent turn on the server.
6844
+ * @default true
6845
+ */
6846
+ clientDelegation?: boolean;
6847
+ /**
6848
+ * Full-duplex calls: extra context for the voice model (for example the
6849
+ * page the visitor is on), sent after the chat history as of call start.
6850
+ * The frame goes out once, when the visitor finishes their first
6851
+ * utterance (or at the first agent hand-off). A function is
6852
+ * called at call start and may be async. This text is capped at 4000 characters and the whole frame
6853
+ * (history included) at 8000. Sent only to servers that accept call
6854
+ * context (they announce it in `session_config`).
6855
+ */
6856
+ callContext?: string | (() => string | Promise<string>);
6857
+ /**
6858
+ * Full-duplex calls with client delegation: how long a tool approval
6859
+ * that a spoken request raised may wait before it is declined as
6860
+ * expired (and the voice model says so). Server-side approval gates
6861
+ * keep their own timeout too. Hanging up never declines it: the
6862
+ * approval card in the chat stays usable. Capped at 540000 (9 minutes),
6863
+ * under the server's 10-minute delegation deadline.
6864
+ * @default 300000
6865
+ */
6866
+ approvalTimeoutMs?: number;
6867
+ /**
6868
+ * Speech-to-speech calls: the one-line notice shown in the composer
6869
+ * status area when the call starts. `false` hides it.
6870
+ * @default "You're talking to an AI assistant. Voice is processed by OpenAI."
6871
+ */
6872
+ disclosureText?: string | false;
6778
6873
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
6779
6874
  pauseDuration?: number;
6780
6875
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
@@ -7013,6 +7108,13 @@ interface VoicePlaybackEngine {
7013
7108
  onFinished(callback: () => void): void;
7014
7109
  /** Release all audio resources. */
7015
7110
  destroy(): Promise<void> | void;
7111
+ /**
7112
+ * Optional. Speech-to-speech calls stream reply audio with no end-of-stream
7113
+ * marker, so an engine that holds audio below a prebuffer waterline must, while
7114
+ * continuous, release that held tail on its own (e.g. after a short input gap)
7115
+ * instead of waiting for {@link markStreamEnd}. Engines without a prebuffer can omit it.
7116
+ */
7117
+ setContinuousMode?(enabled: boolean): void;
7016
7118
  }
7017
7119
  /**
7018
7120
  * A {@link VoicePlaybackEngine} that also supports pause/resume. Returned by
@@ -7060,6 +7162,18 @@ type VoiceConfig = {
7060
7162
  * `@runtypelabs/persona/voice-worklet-player` for a jitter-buffered engine.
7061
7163
  */
7062
7164
  createPlaybackEngine?: () => VoicePlaybackEngine | Promise<VoicePlaybackEngine>;
7165
+ /** See `voiceRecognition.provider.runtype.prewarmMode`. @default 'request' */
7166
+ prewarmMode?: 'request' | 'attach';
7167
+ /** See `voiceRecognition.provider.runtype.attachIdleMs`. @default 30000 */
7168
+ attachIdleMs?: number;
7169
+ /** See `voiceRecognition.provider.runtype.clientDelegation`. @default true */
7170
+ clientDelegation?: boolean;
7171
+ /** See `voiceRecognition.provider.runtype.callContext`. */
7172
+ callContext?: string | (() => string | Promise<string>);
7173
+ /** See `voiceRecognition.provider.runtype.approvalTimeoutMs`. @default 300000 (capped at 540000) */
7174
+ approvalTimeoutMs?: number;
7175
+ /** See `voiceRecognition.provider.runtype.disclosureText`. */
7176
+ disclosureText?: string | false;
7063
7177
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
7064
7178
  pauseDuration?: number;
7065
7179
  /** @deprecated No-op on the realtime path: the server's STT owns turn-taking. */
@@ -7092,14 +7206,23 @@ interface VoiceProvider {
7092
7206
  * Register a callback for incremental transcript updates during a voice turn.
7093
7207
  * `isFinal=false` is a live interim update (user partials, or assistant deltas
7094
7208
  * on providers that stream them); `isFinal=true` finalizes that role's text.
7095
- * On the realtime `runtype` path, interim updates fire for the `user` only and
7096
- * the `assistant` arrives as a single final. Providers with overlapping turns
7097
- * must include the same turnId on user and assistant finals; without IDs,
7098
- * providers must discard cancelled output before emitting the next user final.
7099
- */
7100
- onTranscript?(callback: (role: 'user' | 'assistant', text: string, isFinal: boolean, metadata?: {
7101
- turnId?: string;
7102
- }) => void): void;
7209
+ * On the classic realtime `runtype` path, interim updates fire for the `user`
7210
+ * only and the `assistant` arrives as a single final; without a turnId the
7211
+ * session assumes strictly alternating turns, and providers must discard
7212
+ * cancelled output before emitting the next user final.
7213
+ *
7214
+ * When `metadata.turnId` is present (full-duplex / speech-to-speech, e.g.
7215
+ * GPT-Live), the session reconciles by `(turnId, role)`: each pair owns one
7216
+ * bubble that later updates replace in place, turns may overlap or arrive in
7217
+ * any order, and no empty assistant placeholder is injected. User and
7218
+ * assistant may share a turnId (a late user bubble then renders above its
7219
+ * reply) or use distinct ones. `metadata.startMs` (call-relative ms when the
7220
+ * utterance began) orders a new bubble among the call's other keyed bubbles;
7221
+ * without it bubbles order by first arrival. After a stop, keyed providers
7222
+ * must drop the cancelled reply's output until the server acknowledges the
7223
+ * cancel: the session renders every reply it receives.
7224
+ */
7225
+ onTranscript?(callback: (role: 'user' | 'assistant', text: string, isFinal: boolean, metadata?: VoiceTranscriptMetadata) => void): void;
7103
7226
  /** Register a callback for per-turn latency metrics (realtime path). */
7104
7227
  onMetrics?(callback: (metrics: VoiceMetrics) => void): void;
7105
7228
  /**
@@ -7120,6 +7243,116 @@ interface VoiceProvider {
7120
7243
  deactivateBargeIn?(): Promise<void>;
7121
7244
  /** Stop playback / cancel in-flight request without starting recording */
7122
7245
  stopPlayback?(): void;
7246
+ /**
7247
+ * Warm the provider ahead of `startListening()` (the widget calls it on mic
7248
+ * intent, see `voiceRecognition.prewarm`). Must be fire-and-forget: never
7249
+ * throw, emit errors or status, or acquire the microphone. May be called
7250
+ * repeatedly; throttle as needed.
7251
+ */
7252
+ prewarm?(): void;
7253
+ /**
7254
+ * Called by the session once after construction with the chat-side surface
7255
+ * a full-duplex provider uses to hand agent turns to the chat pipeline
7256
+ * ({@link VoiceSessionBridge}). Providers without client delegation omit it.
7257
+ */
7258
+ setSessionBridge?(bridge: VoiceSessionBridge): void;
7259
+ /** The AI-disclosure notice for the live call, if one should show (speech-to-speech). */
7260
+ getDisclosure?(): string | null;
7261
+ }
7262
+ /** Metadata on a {@link VoiceProvider.onTranscript} update. */
7263
+ type VoiceTranscriptMetadata = {
7264
+ /** Groups updates that belong to one utterance bubble (full duplex). */
7265
+ turnId?: string;
7266
+ /** Call-relative ms when the utterance began; orders late bubbles. */
7267
+ startMs?: number;
7268
+ /** Call-relative ms of the latest audio this update covers. */
7269
+ endMs?: number;
7270
+ /**
7271
+ * The bubble only captions speech (see `AgentWidgetMessage.voiceCaption`).
7272
+ * The chat pipeline owns the conversation, as with client delegation.
7273
+ */
7274
+ caption?: boolean;
7275
+ };
7276
+ /** An agent turn a full-duplex voice model handed to the widget. */
7277
+ type VoiceDelegationRequest = {
7278
+ /** The voice model's delegation id (`delegation_started.delegationId`). */
7279
+ delegationId: string;
7280
+ /** What the visitor asked, as the voice model heard it. */
7281
+ userText: string;
7282
+ /**
7283
+ * The user transcript utterances `userText` joins, oldest first (the last
7284
+ * is the one it came from). May be empty.
7285
+ */
7286
+ userUtteranceIds: string[];
7287
+ /** The voice model's view of the conversation it delegated from. */
7288
+ messages: Array<{
7289
+ role: string;
7290
+ content: unknown;
7291
+ }>;
7292
+ };
7293
+ /**
7294
+ * How a delegated turn ended, or that it waits on an approval (contract
7295
+ * Amendment 5). `pending_approval` is the only non-terminal status. Open:
7296
+ * a receiver treats an unknown terminal status as `failed`.
7297
+ * - `completed`: the work ran.
7298
+ * - `denied`: the visitor said no (Deny tap, or a spoken decline).
7299
+ * - `timeout`: the approval lapsed (`approvalTimeoutMs`, or the server's own timeout).
7300
+ * - `cancelled`: replaced by a newer request, or abandoned by the system.
7301
+ * - `failed`: an error.
7302
+ */
7303
+ type VoiceDelegationStatus = 'completed' | 'pending_approval' | 'denied' | 'timeout' | 'cancelled' | 'failed' | (string & {});
7304
+ /** The terminal result of a turn that parked on approvals. */
7305
+ type VoiceDelegationFollowUp = {
7306
+ status: VoiceDelegationStatus;
7307
+ /** What to read aloud (Markdown allowed). May be empty: nothing more to say. */
7308
+ text: string;
7309
+ };
7310
+ /** The delegated turn's answer, sent back for the voice model to read aloud. */
7311
+ type VoiceDelegationResult = {
7312
+ /** `pending_approval` when `followUp` is set; otherwise terminal. */
7313
+ status: VoiceDelegationStatus;
7314
+ /** The assistant text (Markdown allowed). */
7315
+ text: string;
7316
+ /**
7317
+ * Set when the turn parked on approvals (`text` then asks for the decision).
7318
+ * Resolves with the terminal result once the visitor decides (or the
7319
+ * approval is replaced, declined by voice, or unanswered for
7320
+ * `approvalTimeoutMs`) and the resumed turn finishes; `null` only when
7321
+ * `signal` aborts first (hang-up, or the server cancelled the delegation).
7322
+ */
7323
+ followUp?: (options: {
7324
+ signal: AbortSignal;
7325
+ approvalTimeoutMs?: number;
7326
+ /** `false`: run only the approval bookkeeping (expiry, supersede); nothing will be read back. @default true */
7327
+ readBack?: boolean;
7328
+ /** The resumed turn stopped on another approval: ask for it too (`text` is its approval script). */
7329
+ onUpdate?: (text: string) => void;
7330
+ }) => Promise<VoiceDelegationFollowUp | null>;
7331
+ };
7332
+ /**
7333
+ * Chat-side surface the session hands a full-duplex provider: the visible
7334
+ * history (for the call-start context) and a way to run a delegated turn
7335
+ * through the normal chat pipeline.
7336
+ */
7337
+ interface VoiceSessionBridge {
7338
+ /** Visible user/assistant messages, oldest first. */
7339
+ getHistory(): Array<{
7340
+ role: 'user' | 'assistant';
7341
+ content: string;
7342
+ }>;
7343
+ /**
7344
+ * Submit the turn as the visitor's message (reusing its transcript bubble)
7345
+ * and resolve with the assistant's final answer once the turn settles.
7346
+ * Waits for any chat turn already in flight first. Never rejects.
7347
+ */
7348
+ runDelegatedTurn(request: VoiceDelegationRequest): Promise<VoiceDelegationResult>;
7349
+ /**
7350
+ * The server dropped this delegation's result (cancelled it, or refused a
7351
+ * late result): its chat answer is no longer spoken, so browser TTS may read it.
7352
+ * `expired`: the server's deadline passed, so its pending approvals are
7353
+ * declined too, as the approval TTL would.
7354
+ */
7355
+ dropDelegation?(delegationId: string, expired?: boolean): void;
7123
7356
  }
7124
7357
  /**
7125
7358
  * Extra context for an approval decision, surfaced to `onDecision` and to the
@@ -9814,6 +10047,11 @@ type AgentWidgetToolCall = {
9814
10047
  startedAt?: number;
9815
10048
  completedAt?: number;
9816
10049
  durationMs?: number;
10050
+ /**
10051
+ * The call paused for an approval that was granted, and the agent re-ran it
10052
+ * as a new tool call: that later call carries the result, this one has none.
10053
+ */
10054
+ superseded?: boolean;
9817
10055
  };
9818
10056
  /**
9819
10057
  * Represents a tool approval request in the chat conversation.
@@ -9835,6 +10073,8 @@ type AgentWidgetApproval = {
9835
10073
  */
9836
10074
  reason?: string;
9837
10075
  parameters?: unknown;
10076
+ /** The paused tool call this approval gates (`approval_start.toolCallId`). */
10077
+ toolCallId?: string;
9838
10078
  resolvedAt?: number;
9839
10079
  };
9840
10080
  type AgentWidgetMessageVariant = "assistant" | "reasoning" | "tool" | "approval";
@@ -9934,6 +10174,13 @@ type AgentWidgetMessage = {
9934
10174
  * }
9935
10175
  */
9936
10176
  voiceProcessing?: boolean;
10177
+ /**
10178
+ * Display-only caption of full-duplex voice speech, such as the voice
10179
+ * model's own words, or something the visitor said that wasn't submitted
10180
+ * as a chat turn. It renders in the transcript but is never sent to the
10181
+ * model as conversation.
10182
+ */
10183
+ voiceCaption?: boolean;
9937
10184
  /**
9938
10185
  * Raw structured payload for this message (e.g., JSON action response).
9939
10186
  * Populated automatically when structured parsers run.
@@ -10049,6 +10296,8 @@ type InjectMessageOptions = {
10049
10296
  * Consumers can detect this in `messageTransform` to render custom UI.
10050
10297
  */
10051
10298
  voiceProcessing?: boolean;
10299
+ /** See {@link AgentWidgetMessage.voiceCaption}. */
10300
+ voiceCaption?: boolean;
10052
10301
  /**
10053
10302
  * Raw structured payload (typically a JSON string) representing the
10054
10303
  * full directive that produced this message: e.g. `{ "text": "...",
@@ -10251,51 +10500,15 @@ type DispatchOptions = {
10251
10500
  type SSEHandler = (event: AgentWidgetEvent) => void;
10252
10501
  type SSEEventCallback = (eventType: string, payload: unknown) => void;
10253
10502
  declare class AgentWidgetClient {
10503
+ #private;
10254
10504
  private config;
10255
10505
  private historyInternals;
10256
- private readonly apiUrl;
10257
- private readonly headers;
10258
10506
  private readonly debug;
10259
- private readonly createStreamParser;
10260
- private readonly contextProviders;
10261
- private readonly requestMiddleware?;
10262
- private readonly customFetch?;
10263
- private readonly parseSSEEvent?;
10264
- private readonly getHeaders?;
10265
- private onSSEEvent?;
10266
- /**
10267
- * Turn id of the newest client-token dispatch. A dispatch whose id no longer
10268
- * matches is superseded, and every SSE frame it still receives is dropped
10269
- */
10270
- private currentClientTurnId;
10271
10507
  private clientSession;
10272
- private sessionInitPromise;
10273
- /**
10274
- * Early-init latch for `warmSession()`. `false` = armed. Otherwise it holds
10275
- * the session value (`null` or an expired session) the one early attempt was
10276
- * made against, so a failed attempt is not retried on every keystroke. It is
10277
- * re-armed when a session is installed or cleared, so each session lifetime
10278
- * gets one early init.
10279
- */
10280
- private sessionWarmLatch;
10281
10508
  private lastSentClientToolsFingerprint;
10282
10509
  private clientToolsFingerprintSessionId;
10283
- private sentNonEmptyClientToolsSessionId;
10284
- private historyUnavailable;
10285
- private historyUnavailableWarned;
10286
- private claimInFlight;
10287
- private historyIdentityStatus;
10288
- private historyIdentitySubscribers;
10289
- private historyAvailabilitySubscribers;
10290
10510
  private webMcpBridge;
10291
- private webMcpBridgePromise;
10292
- /** undefined = never set; null = explicitly cleared. Applied at adoption. */
10293
- private pendingWebMcpConfirmHandler;
10294
10511
  constructor(config?: AgentWidgetConfig, historyInternals?: WidgetHistoryInternals);
10295
- /** Load the lazy runtime chunk and construct the bridge with core-owned deps. */
10296
- private createWebMcpBridge;
10297
- /** Resolve the bridge, loading the runtime chunk on first use. */
10298
- private getWebMcpBridge;
10299
10512
  /**
10300
10513
  * Refresh config in place WITHOUT tearing down the live connection or the
10301
10514
  * WebMCP bridge. `AgentWidgetSession.updateConfig` calls this when only
@@ -10353,29 +10566,10 @@ declare class AgentWidgetClient {
10353
10566
  * Check if running in client token mode
10354
10567
  */
10355
10568
  isClientTokenMode(): boolean;
10356
- /**
10357
- * Resolve the effective backend routing for the current config. Combines the
10358
- * explicit `agentId`/`flowId` fields with the normalized `target` string
10359
- * (resolved via `resolveTarget`). Computed on demand so it stays correct
10360
- * across `update()`; the `target`/explicit-field conflict is rejected in the
10361
- * constructor, so at most one source is set here.
10362
- */
10363
- private routing;
10364
10569
  /**
10365
10570
  * Check if operating in agent execution mode
10366
10571
  */
10367
10572
  isAgentMode(): boolean;
10368
- /**
10369
- * Base URL for Runtype client-surface routes (`/v1/client/*`,
10370
- * `/v1/agents/*`). Strips a configured `/v1/dispatch` suffix so an apiUrl
10371
- * pointed at the dispatch endpoint still resolves its sibling routes. Not
10372
- * used for proxy-mode URLs, whose resume route is `${apiUrl}/resume`.
10373
- */
10374
- private clientApiBase;
10375
- /**
10376
- * Get the appropriate API URL based on mode
10377
- */
10378
- private getClientApiUrl;
10379
10573
  /**
10380
10574
  * Get the current client session (if any)
10381
10575
  */
@@ -10409,56 +10603,13 @@ declare class AgentWidgetClient {
10409
10603
  * `/v1/client/init`.
10410
10604
  */
10411
10605
  initSession(): Promise<ClientSession>;
10412
- /** Visitor history rides on client-token init only, and latches off after a 403 degrade. */
10413
- private isHistoryCapable;
10414
10606
  private readVisitorToken;
10415
- /** One-way, client-lifetime latch: chat keeps working without history. */
10416
- private markHistoryUnavailable;
10417
10607
  /** Public, secret-free view of the current identity state. */
10418
10608
  getHistoryIdentityStatus(): HistoryIdentityStatus;
10419
10609
  /** Deduped status notifications; carries no token, proof, or identity value. */
10420
10610
  subscribeHistoryIdentityStatus(callback: (status: HistoryIdentityStatus) => void): () => void;
10421
10611
  /** Fires only on the one-way 403 degrade; availability never returns. */
10422
10612
  subscribeHistoryAvailability(callback: (available: boolean) => void): () => void;
10423
- /** Config-derived state before any per-operation evidence exists. */
10424
- private restingIdentityStatus;
10425
- /** Keeps an already-observed browser_only reason; otherwise derives one. */
10426
- private browserOnlyStatus;
10427
- private derivedBrowserOnly;
10428
- /** Identical consecutive states are not re-announced. */
10429
- private setHistoryIdentityStatus;
10430
- /**
10431
- * The single uncached init primitive: builds one body, performs one fetch.
10432
- * `conversationId` and `sessionId` are mutually exclusive on the wire (strict
10433
- * server union), so a resume never carries the stored session id.
10434
- */
10435
- private createClientSession;
10436
- private _doInitSession;
10437
- /**
10438
- * An empty store means this init may mint a visitor, so it runs under the
10439
- * cross-tab first-init lock: a waiter re-reads inside the lock and joins the
10440
- * winner's visitor instead of minting a second one.
10441
- */
10442
- private ordinaryInit;
10443
- private doOrdinaryInit;
10444
- /**
10445
- * Ordinary init carrying an identity proof: binds/rebinds the visitor,
10446
- * persists any replacement token (inside `createClientSession`), and installs
10447
- * the result as the live session.
10448
- */
10449
- private reinitWithProof;
10450
- /**
10451
- * A minted visitor means the record this session just created is still
10452
- * unowned; one immediate re-init with `{visitorToken, sessionId}` claims it.
10453
- */
10454
- private claimFirstConversation;
10455
- /**
10456
- * Continuity guard + id persistence. A different record than the persisted
10457
- * one is a privacy transition, announced before the new id is written.
10458
- */
10459
- private finishInit;
10460
- /** Wrap an uncached init so nothing installs until the winner commits. */
10461
- private prepared;
10462
10613
  /**
10463
10614
  * Transactional reopen of a known conversation. Always bypasses the session
10464
10615
  * cache, never sends the stored session id, and needs a credential the server
@@ -10485,10 +10636,6 @@ declare class AgentWidgetClient {
10485
10636
  * when the conversation is reset (`WidgetSession.clearMessages`).
10486
10637
  */
10487
10638
  resetClientToolsFingerprint(): void;
10488
- /**
10489
- * Get the feedback API URL
10490
- */
10491
- private getFeedbackApiUrl;
10492
10639
  /**
10493
10640
  * Send feedback for a message (client token mode only).
10494
10641
  * Supports upvote, downvote, copy, csat, and nps feedback types.
@@ -10547,37 +10694,6 @@ declare class AgentWidgetClient {
10547
10694
  * @param comment - Optional comment
10548
10695
  */
10549
10696
  submitNPSFeedback(rating: number, comment?: string): Promise<void>;
10550
- /** `/v1/client/<path>` with a query string; credentials ride in headers. */
10551
- private historyUrl;
10552
- /** Explicit per-operation scope wins; otherwise config, then evidence. */
10553
- private resolveHistoryScope;
10554
- private resolveChatIdentityProof;
10555
- /**
10556
- * Resolve the proof for one logical request. `null` is an intentional
10557
- * browser-scope fallback only while the visitor has never been bound.
10558
- */
10559
- private resolveIdentityProof;
10560
- /** Bind the visitor before the first verified request; admitted proofs only. */
10561
- private bindIdentity;
10562
- private readErrorCode;
10563
- /**
10564
- * Selective one-shot 401 recovery. Returns the session to retry under;
10565
- * anything unrecoverable throws typed and is never retried.
10566
- */
10567
- private recoverFromUnauthorized;
10568
- private historyErrorFor;
10569
- /**
10570
- * Validate `X-History-Identity-Status` before any body is committed: the
10571
- * per-operation acknowledgement, not a prior init, is what proves scope.
10572
- */
10573
- private commitIdentityAcknowledgement;
10574
- /**
10575
- * Shared history transport: live session, `sessionId` query param,
10576
- * `X-Visitor-Token` header, one resolved proof, one-shot 401 recovery, and a
10577
- * credential-revision guard that discards a response the store outran.
10578
- */
10579
- private historyFetch;
10580
- private assertHistoryUsable;
10581
10697
  /** One page of the visitor's conversations, newest first. */
10582
10698
  listConversations(opts?: {
10583
10699
  cursor?: string;
@@ -10629,18 +10745,6 @@ declare class AgentWidgetClient {
10629
10745
  * Send a message - handles both proxy and client token modes
10630
10746
  */
10631
10747
  dispatch(options: DispatchOptions, onEvent: SSEHandler): Promise<void>;
10632
- /**
10633
- * Client token mode dispatch
10634
- */
10635
- private dispatchClientToken;
10636
- /**
10637
- * Proxy mode dispatch (original implementation)
10638
- */
10639
- private dispatchProxy;
10640
- /**
10641
- * Agent mode dispatch
10642
- */
10643
- private dispatchAgent;
10644
10748
  /**
10645
10749
  * Process an external SSE stream through the SDK's event pipeline.
10646
10750
  * This allows piping responses from endpoints like agent approval
@@ -10650,114 +10754,25 @@ declare class AgentWidgetClient {
10650
10754
  /**
10651
10755
  * Send an approval decision to the API and return the response
10652
10756
  * for streaming continuation.
10757
+ *
10758
+ * Routes by mode:
10759
+ * - **client-token mode**: POST `${apiBase}/v1/client/approve` with the
10760
+ * active `sessionId` and no Bearer key (runtypelabs/core#9518). Answers a
10761
+ * gate authored `tools.approval.approver: 'end-user'`; the SSE body has
10762
+ * the same shape as `/v1/client/resume`.
10763
+ * - **dispatch / proxy mode**: POST `${apiBase}/v1/agents/{agentId}/approve`
10764
+ * with the host's headers.
10653
10765
  */
10654
10766
  resolveApproval(approval: {
10655
10767
  agentId: string;
10656
10768
  executionId: string;
10657
10769
  approvalId: string;
10658
10770
  }, decision: 'approved' | 'denied'): Promise<Response>;
10659
- /**
10660
- * Resume a paused flow execution by supplying outputs for LOCAL
10661
- * (client-executed) tools. Used by the built-in `ask_user_question`
10662
- * answer-pill sheet, but generic enough for any LOCAL tool.
10663
- *
10664
- * Routes by mode:
10665
- * - **client-token mode**: POST `${apiBase}/v1/client/resume` (the
10666
- * session-authenticated sibling of `/v1/client/chat`; runtypelabs/core#3889),
10667
- * with the active `sessionId` in the body and no Bearer key: a browser
10668
- * client-token page holds no secret. The page's tool registry is
10669
- * re-snapshotted and sent alongside `toolOutputs` via the same diff-only
10670
- * `clientTools` / `clientToolsFingerprint` protocol as `/v1/client/chat`
10671
- * (runtypelabs/core#5361), so tools registered by a mid-run page
10672
- * navigation replace the run's dispatch-time set and become callable on
10673
- * the next model turn. Old servers strip the unknown fields and keep the
10674
- * frozen-at-dispatch behavior.
10675
- * - **dispatch / proxy mode**: POST `${apiUrl}/resume`: Runtype mounts
10676
- * resume as a child of `/v1/dispatch`, so the URL is `${apiUrl}/resume`,
10677
- * and proxies follow the same shape (`/api/chat/dispatch/resume`).
10678
- *
10679
- * Returns the raw Response so the caller can pipe its SSE body through
10680
- * `connectStream()`.
10681
- *
10682
- * @param executionId - The paused execution id carried on `await`.
10683
- * @param toolOutputs - Map keyed by per-call `toolCallId` (core#3878),
10684
- * falling back to tool name for legacy servers → the tool's result value.
10685
- */
10686
- /**
10687
- * Diff-only / send-once WebMCP clientTools transport, shared by the
10688
- * client-token chat (`/v1/client/chat`) and resume (`/v1/client/resume`)
10689
- * paths — both routes speak the same protocol (runtypelabs/core#5361).
10690
- *
10691
- * Decides, against the shared fingerprint cache, whether this request ships
10692
- * the full `clientTools[]` + fingerprint (first send under this session, or
10693
- * a changed set) or the fingerprint alone (unchanged set; the server reuses
10694
- * its stored copy). Runs `doFetch` with the chosen fields and retries
10695
- * EXACTLY once with the full array on a
10696
- * `409 { error: 'client_tools_resend_required' }` registry miss — the retry
10697
- * is 409-*triggered*, never 409-*expected*, so servers predating the
10698
- * protocol (which strip the unknown fields and never 409) work unchanged.
10699
- * The 409 body is probed on a `clone()` so the original response body stays
10700
- * readable by the caller's error handling.
10701
- *
10702
- * The cache is NOT committed here: callers invoke the returned `commit()`
10703
- * only after the server has accepted the request (response OK / stream
10704
- * started), so a failed request can never record a fingerprint the server
10705
- * never stored. A resend-required miss invalidates the cached fingerprint
10706
- * immediately so later turns keep resending in full until a clean success
10707
- * commits a fresh one.
10708
- *
10709
- * `emptyMeansReplace` (resume only): when the live snapshot is empty but a
10710
- * non-empty set was committed under this session and never explicitly
10711
- * cleared (the paused tool navigated to a page with no tool registry), ship
10712
- * an explicit `clientTools: []` so the server REPLACES the persisted
10713
- * dispatch-time set with nothing and clears its stored registry. Chat keeps
10714
- * its omit-when-empty behavior: on `/chat`, absent fields already mean "no
10715
- * tools this turn", whereas on `/resume` absence means "keep the frozen
10716
- * dispatch-time set".
10717
- */
10718
- private sendWithClientToolsDiff;
10719
10771
  resumeFlow(executionId: string, toolOutputs: Record<string, unknown>, options?: {
10720
10772
  streamResponse?: boolean;
10721
10773
  signal?: AbortSignal;
10722
10774
  after?: string;
10723
10775
  }): Promise<Response>;
10724
- /**
10725
- * The opt-in structured mention channel: the MOST RECENT user turn's
10726
- * `mentionContext` (set by `session.applyMentionBundle`) namespaced under
10727
- * `mentions`. Only the latest user message is consulted — otherwise an older
10728
- * turn's mentions would re-attach to `context.mentions` on every later send.
10729
- * Returns null when the latest user turn carried no structured mention context.
10730
- * The default model-visible path (`llmAppend`) already rode into the message's
10731
- * `llmContent`/`contentParts`, so it needs nothing here.
10732
- */
10733
- private latestMentionContext;
10734
- /**
10735
- * Aggregate request `context`: merge every context provider's result with the
10736
- * opt-in mention context from the latest user turn. Returns null when nothing
10737
- * contributed, so callers can skip setting `payload.context`. Shared by the
10738
- * agent and flow payload builders.
10739
- */
10740
- private buildContextAggregate;
10741
- /**
10742
- * Non-empty composer selections only. An empty snapshot leaves the field off
10743
- * the wire entirely, so a widget with no picker and no modes sends exactly
10744
- * what it sent before.
10745
- */
10746
- private normalizeComposerOptions;
10747
- /**
10748
- * Rule 4: an inline client-defined agent may take the turn's model from the
10749
- * composer, but only for an id the host actually declared in
10750
- * `composer.models`. Config is untouched; the mapping is per request.
10751
- */
10752
- private applyInlineAgentModel;
10753
- private buildAgentPayload;
10754
- private buildPayload;
10755
- /**
10756
- * Handle custom SSE event parsing via parseSSEEvent callback
10757
- * Returns true if event was handled, false otherwise
10758
- */
10759
- private handleCustomSSEEvent;
10760
- private streamResponse;
10761
10776
  }
10762
10777
 
10763
10778
  /** The resolved bundle gathered at submit, merged into the user message. */
@@ -10831,6 +10846,7 @@ type SessionHistoryNotice = {
10831
10846
  message: string;
10832
10847
  };
10833
10848
  declare class AgentWidgetSession {
10849
+ #private;
10834
10850
  private config;
10835
10851
  private callbacks;
10836
10852
  private historyInternals;
@@ -10839,79 +10855,16 @@ declare class AgentWidgetSession {
10839
10855
  private status;
10840
10856
  private streaming;
10841
10857
  private abortController;
10842
- private sequenceCounter;
10843
10858
  private clientSession;
10844
10859
  private agentExecution;
10845
- private resumable;
10846
- private activeAssistantMessageId;
10847
- private reconnecting;
10848
- private reconnectController;
10849
- private reconnectControllerPromise;
10850
- private executionStateTimer;
10851
10860
  private artifacts;
10852
- private selectedArtifactId;
10853
10861
  private webMcpInflightKeys;
10854
10862
  private webMcpResolvedKeys;
10855
10863
  private webMcpResolveControllers;
10856
- private webMcpEpoch;
10857
10864
  private webMcpApprovalResolvers;
10858
- private webMcpApprovalSeq;
10865
+ private approvalTokens;
10859
10866
  private webMcpAwaitBatches;
10860
- private voiceProvider;
10861
- /** In-flight setupVoice chunk load; toggleVoice awaits it when racing setup. */
10862
- private voiceSetupPromise;
10863
- /** Bumped by setupVoice/cleanupVoice so a stale chunk load can't install. */
10864
- private voiceSetupGeneration;
10865
- /** Latest 0..1 capture amplitude pushed by a provider that reports one. */
10866
- private voiceLevel;
10867
- private voiceActive;
10868
- private voiceStatus;
10869
- private activeConversationId;
10870
- private activeConversationRevision;
10871
- private historyNextMessageCursor;
10872
- /** Monotonic: a later selection supersedes every earlier fetch/prepare. */
10873
- private historyOpenEpoch;
10874
- /** Blocks `setClientSession`'s welcome branch across a reopen re-init. */
10875
- private suppressWelcomeInjection;
10876
- private historyRecovery;
10877
- private historySendBlocked;
10878
- /** Continuity wipe / boot reconciliation; dispatch waits on it. */
10879
- private historyGate;
10880
- /** Deduped by `(id, cursor)` so repeated clicks issue one request. */
10881
- private olderPageRequests;
10882
- /** Serializes projection finalization against every other operation. */
10883
- private projectionFinalizationPromise;
10884
- /** Message id → projection the server has acknowledged (sent or finalized). */
10885
- private acknowledgedProjections;
10886
- private pendingProjections;
10887
- /** Persisted revision as it stood before boot init overwrote it. */
10888
- private bootConversationRevision;
10889
- private bootRevisionCaptured;
10890
- /** Exactly one visitor-store subscription per instance; re-keyed with the store. */
10891
- private visitorStoreUnsubscribe;
10892
- private subscribedVisitorStore;
10893
- /** An external credential change happened; the next dispatch must re-init. */
10894
- private credentialReinitPending;
10895
- private credentialReinitPromise;
10896
10867
  constructor(config: AgentWidgetConfig | undefined, callbacks: SessionCallbacks, historyInternals?: WidgetHistoryInternals);
10897
- /**
10898
- * Warm the deferred voice-runtime chunk (provider factory + providers) at
10899
- * init when a voice provider is configured, so setupVoice / the first mic
10900
- * click resolve against a warm module. Module fetch only — WebSocket and
10901
- * AudioContext still wait for their own triggers. Errors are swallowed: the
10902
- * setupVoice path retries via the chunk loader's rejection-retry.
10903
- */
10904
- private prefetchVoiceRuntime;
10905
- /**
10906
- * Warm the deferred Runtype TTS engine chunk at init so it's resolved before
10907
- * the first "Read aloud" click. Module fetch only — no AudioContext (that
10908
- * still waits for the user gesture on first playback). In the IIFE/CDN build
10909
- * this fetches the standalone `runtype-tts.js`; in bundler builds the module
10910
- * is already inlined, so this is a cheap no-op. Errors are swallowed: a failed
10911
- * prefetch just means the chunk loads on first click instead (or, on a hard
10912
- * failure, the browser-voice fallback kicks in).
10913
- */
10914
- private prefetchRuntypeTts;
10915
10868
  /**
10916
10869
  * Set callback for capturing raw SSE events (forwards to client)
10917
10870
  */
@@ -10944,6 +10897,8 @@ declare class AgentWidgetSession {
10944
10897
  * Get current voice status
10945
10898
  */
10946
10899
  getVoiceStatus(): VoiceStatus;
10900
+ /** The live call's AI-disclosure notice, if it shows one. */
10901
+ getVoiceDisclosure(): string | null;
10947
10902
  /**
10948
10903
  * Get the voice interruption mode from the provider (none/cancel/barge-in)
10949
10904
  */
@@ -10963,40 +10918,27 @@ declare class AgentWidgetSession {
10963
10918
  isBargeInActive(): boolean;
10964
10919
  /** Tear down the barge-in mic pipeline: "hang up" the always-on mic */
10965
10920
  deactivateBargeIn(): Promise<void>;
10966
- private pendingVoiceUserMessageId;
10967
- private pendingVoiceAssistantMessageId;
10968
- private voiceTurns;
10969
- private voiceDisconnectPromise;
10970
10921
  private ttsSpokenMessageIds;
10971
10922
  private readAloud;
10972
- /**
10973
- * Resolve the speech engine behind read-aloud (and auto-speak):
10974
- * 1. An explicit `textToSpeech.createEngine` always wins (full BYO control).
10975
- * 2. `provider: 'runtype'` builds the hosted {@link RuntypeSpeechEngine}
10976
- * from the widget config (host = `apiUrl`, `agentId`, `clientToken`),
10977
- * wrapped in a browser {@link FallbackSpeechEngine} unless
10978
- * `browserFallback: false` — so a missing endpoint or transient failure
10979
- * degrades to the browser voice instead of erroring, and auto-upgrades to
10980
- * Runtype voices the day the endpoint ships.
10981
- * 3. Otherwise the browser Web Speech API engine.
10982
- */
10983
- private createSpeechEngine;
10984
10923
  /**
10985
10924
  * Setup voice recognition with the given configuration
10986
10925
  */
10987
10926
  setupVoice(config?: VoiceConfig): void;
10988
- /** Wire callbacks onto a freshly constructed provider and connect it. */
10989
- private wireVoiceProvider;
10990
10927
  /**
10991
10928
  * Toggle voice recognition on/off
10992
10929
  */
10993
10930
  toggleVoice(): Promise<void>;
10931
+ /**
10932
+ * Warm the voice path ahead of the mic click (`voiceRecognition.prewarm`).
10933
+ * Session-owned providers (`runtype`, `custom`) get `provider.prewarm()` once
10934
+ * the lazy voice runtime has installed them; the browser (Web Speech) path
10935
+ * warms the client-token session instead. Fire-and-forget and silent.
10936
+ */
10937
+ prewarmVoice(): void;
10994
10938
  /**
10995
10939
  * Cleanup voice resources
10996
10940
  */
10997
10941
  cleanupVoice(): void;
10998
- private disconnectVoice;
10999
- private settlePendingVoiceTurn;
11000
10942
  /**
11001
10943
  * Extract voice configuration from widget config
11002
10944
  */
@@ -11017,12 +10959,6 @@ declare class AgentWidgetSession {
11017
10959
  * Set the client session after initialization
11018
10960
  */
11019
10961
  setClientSession(session: ClientSession, pristine?: boolean): void;
11020
- /**
11021
- * Welcome injection is skipped while `suppressWelcomeInjection` is latched:
11022
- * reopening a stored conversation is not a new thread, and the hydrated
11023
- * transcript would otherwise race a welcome bubble.
11024
- */
11025
- private injectWelcomeMessage;
11026
10962
  /**
11027
10963
  * Get current client session
11028
10964
  */
@@ -11071,26 +11007,10 @@ declare class AgentWidgetSession {
11071
11007
  setHistoryInternals(internals: WidgetHistoryInternals): void;
11072
11008
  /** Widget teardown: release the visitor-store subscription. */
11073
11009
  destroy(): void;
11074
- /**
11075
- * The session always owns the continuity transition, whether or not the shell
11076
- * supplied its own observer: a credential/record break must wipe local state
11077
- * before the replacement session becomes sendable.
11078
- */
11079
- private composeHistoryInternals;
11080
- private get historyProvider();
11081
- private requireHistoryProvider;
11082
- /**
11083
- * One resolved scope per logical action. An explicitly requested scope the
11084
- * provider cannot serve fails locally rather than silently downgrading.
11085
- */
11086
- private historyContext;
11087
11010
  getHistoryState(): SessionHistoryState;
11088
11011
  getActiveConversationId(): string | null;
11089
- private emitHistoryState;
11090
- private notifyHistory;
11091
11012
  /** Pending destructive/reconciliation work every other operation waits on. */
11092
11013
  awaitHistorySettled(): Promise<void>;
11093
- private assertHistoryIdle;
11094
11014
  listConversations(opts?: {
11095
11015
  cursor?: string;
11096
11016
  limit?: number;
@@ -11115,7 +11035,6 @@ declare class AgentWidgetSession {
11115
11035
  loadOlderMessages(id: string, cursor?: string | null, opts?: {
11116
11036
  scope?: HistoryScope;
11117
11037
  }): Promise<HistoryOlderPage>;
11118
- private fetchOlderPage;
11119
11038
  /**
11120
11039
  * The canonical new-thread operation. A failed prepare leaves the current
11121
11040
  * conversation completely intact.
@@ -11123,11 +11042,6 @@ declare class AgentWidgetSession {
11123
11042
  startNewConversation(opts?: {
11124
11043
  scope?: HistoryScope;
11125
11044
  }): Promise<void>;
11126
- /**
11127
- * Clear local + persisted conversation state, then commit the replacement.
11128
- * Welcome injection is deliberately allowed once the transcript is empty.
11129
- */
11130
- private installFreshConversation;
11131
11045
  /**
11132
11046
  * Deleting the active record (or everything) is already irreversible: the
11133
11047
  * transcript is destroyed first, then a replacement session is prepared.
@@ -11154,60 +11068,12 @@ declare class AgentWidgetSession {
11154
11068
  resetHistoryDevice(): Promise<{
11155
11069
  remoteRevocationConfirmed: boolean;
11156
11070
  }>;
11157
- private recoverFromDestroyedConversation;
11158
- /**
11159
- * Never reinstalls the deleted session. A failed preparation leaves an
11160
- * explicit recoverable state instead of the old transcript.
11161
- */
11162
- private prepareReplacementConversation;
11163
- /** The next explicit action retries a failed replacement preparation. */
11164
- private retryRequiredConversation;
11165
- /**
11166
- * Install a prepared activation. The provider's commit also invokes the
11167
- * session binding (`bindActivatedSession`); the fallback covers a shell that
11168
- * has not wired that callback yet.
11169
- */
11170
- private commitActivation;
11171
11071
  /**
11172
11072
  * Session-owned binding for a winning prepared activation. Installs the
11173
11073
  * client session WITHOUT the ordinary welcome branch while the reopen latch
11174
11074
  * is set, and persists the replacement ids through the normal storage path.
11175
11075
  */
11176
11076
  bindActivatedSession(session: ClientSession): void;
11177
- private persistConversationMetadata;
11178
- /** Conversation-scoped caches that must not leak across records. */
11179
- private resetConversationScopedState;
11180
- /**
11181
- * Irreversible local + persisted clear. Used by new-conversation, active
11182
- * deletion, 410 recovery, reset, and the continuity wipe.
11183
- */
11184
- private discardConversationState;
11185
- /**
11186
- * D6 continuity wipe (session side): a replacement visitor, identity rebind,
11187
- * cross-tab convergence, or credential expiry selected a different record.
11188
- * Invalidate synchronously; dispatch stays blocked until cleanup completes.
11189
- */
11190
- private handleHistoryContinuityChanged;
11191
- /**
11192
- * One subscription per widget instance, re-keyed when the controller swaps
11193
- * the store. Owned here so the transition sits beside the continuity wipe it
11194
- * reuses.
11195
- */
11196
- private syncVisitorStoreSubscription;
11197
- /**
11198
- * D3 privacy boundary: a sibling tab cleared or replaced the shared visitor
11199
- * credential. Privacy wins over turn continuity, so a live turn is cancelled.
11200
- * A "local" change is this tab's own mint/write and owns its init path; a
11201
- * change out of an empty store is first-init convergence, not a break.
11202
- */
11203
- private handleExternalCredentialChange;
11204
- /**
11205
- * Dispatch may not proceed until a session exists under the NEW credential.
11206
- * Deliberately lazy: re-initializing on the storage event itself would hand
11207
- * an idle tab a fresh credential the visitor just deleted elsewhere.
11208
- */
11209
- private completeCredentialReinit;
11210
- private runCredentialReinit;
11211
11077
  /**
11212
11078
  * Authoritative boot reconciliation (D4). Runs after a `conversationId` boot
11213
11079
  * resume and before chat becomes sendable: an unchanged revision needs no
@@ -11217,47 +11083,11 @@ declare class AgentWidgetSession {
11217
11083
  reconcileBootConversation(opts?: {
11218
11084
  scope?: HistoryScope;
11219
11085
  }): Promise<void>;
11220
- private runBootReconciliation;
11221
- /** Runtype transport op: active session + browser scope, never a proof. */
11222
- private projectionFinalizationEnabled;
11223
- private setPendingProjections;
11224
- private readPendingProjections;
11225
- /** Record what the dispatch payload carried as each message's projection. */
11226
- private recordDispatchedProjections;
11227
- /**
11228
- * After a terminal assistant message, finalize any browser-derived projection
11229
- * the server cannot have. Painting is never delayed by this.
11230
- */
11231
- private scheduleProjectionFinalization;
11232
- /** Serialized: operations await this chain instead of racing it. */
11233
- private enqueueProjectionFinalization;
11234
- private finalizeProjections;
11235
- /** Transport-shaped failures only; a contract failure is not retried. */
11236
- private isTransientProjectionError;
11237
- private clearFinalizedMarker;
11238
11086
  /**
11239
11087
  * Boot replay: rebuild the batch from the persisted ids plus the local
11240
11088
  * message content, then finalize before display reconciliation completes.
11241
11089
  */
11242
11090
  replayPendingProjections(): Promise<void>;
11243
- /**
11244
- * A local transcript mutation whose response carries no new revision: clear
11245
- * the persisted one rather than guessing, so the next boot refreshes safely.
11246
- */
11247
- private invalidateConversationRevision;
11248
- /** Blocks a send until the owning history transition finishes. */
11249
- private awaitHistorySendable;
11250
- private static isConversationDeleted;
11251
- /**
11252
- * Safe 410 recovery (D5). The old payload would recreate the transcript that
11253
- * was just deleted, so the retry carries ONLY the just-submitted user turn.
11254
- */
11255
- private dispatchWithDeletedRecovery;
11256
- /**
11257
- * Capture only the newly submitted user message, destroy the deleted record's
11258
- * local state, and initialize a fresh owned conversation to retry into.
11259
- */
11260
- private recoverFromDeletedConversation;
11261
11091
  updateConfig(next: AgentWidgetConfig): void;
11262
11092
  getMessages(): AgentWidgetMessage[];
11263
11093
  getStatus(): AgentWidgetSessionStatus;
@@ -11354,15 +11184,6 @@ declare class AgentWidgetSession {
11354
11184
  * });
11355
11185
  */
11356
11186
  injectComponentDirective(options: InjectComponentDirectiveOptions): AgentWidgetMessage;
11357
- /**
11358
- * Resolve the gathered mentions and merge them into the user message's
11359
- * model-visible content. Default path is `llmAppend` → `llmContent` (or a text
11360
- * `contentPart` when parts are present, since `contentParts` wins the priority
11361
- * chain); the opt-in structured `context` lands on `mentionContext` for the
11362
- * client to namespace into the request `context`. Failures were already
11363
- * dropped by `finalize()`.
11364
- */
11365
- private applyMentionBundle;
11366
11187
  sendMessage(rawInput: string, options?: {
11367
11188
  viaVoice?: boolean;
11368
11189
  /** Multi-modal content parts (e.g., images) to include with the message */
@@ -11407,6 +11228,15 @@ declare class AgentWidgetSession {
11407
11228
  * (`composer.streamingSubmitBehavior: "interrupt"`).
11408
11229
  */
11409
11230
  interrupt?: boolean;
11231
+ /**
11232
+ * Internal (voice client delegation): this is the delegated voice turn.
11233
+ * `userMessageId` names an existing transcript bubble to submit as the
11234
+ * turn's user message instead of appending a new one, so the spoken
11235
+ * request appears (and is sent) exactly once.
11236
+ */
11237
+ voiceTurn?: {
11238
+ userMessageId?: string;
11239
+ };
11410
11240
  }): Promise<void>;
11411
11241
  /**
11412
11242
  * Re-run the conversation from one user turn (docs/composer-gap-analysis.md
@@ -11430,8 +11260,6 @@ declare class AgentWidgetSession {
11430
11260
  * turn would carry no content; nothing is truncated in that case.
11431
11261
  */
11432
11262
  resubmitFrom(messageId: string, options: ComposerResubmitOptions): boolean;
11433
- /** Submission options → the wire payload shape; quote travels separately. */
11434
- private toComposerOptionsPayload;
11435
11263
  /**
11436
11264
  * Continue the conversation without adding a new user message.
11437
11265
  * Triggers the model to respond based on the current conversation state.
@@ -11466,13 +11294,6 @@ declare class AgentWidgetSession {
11466
11294
  */
11467
11295
  seedContent?: string;
11468
11296
  }): Promise<void>;
11469
- /**
11470
- * Install the native approval-bubble confirm handler on the WebMCP bridge
11471
- * when the integrator hasn't supplied a custom `webmcp.onConfirm`. Without
11472
- * this, the bridge falls back to a blunt `window.confirm`. Safe to call
11473
- * repeatedly (e.g. after the client is re-created in `updateConfig`).
11474
- */
11475
- private wireDefaultWebMcpConfirm;
11476
11297
  /**
11477
11298
  * Default WebMCP confirm gate: render Persona's native in-panel approval
11478
11299
  * bubble and resolve when the user clicks Approve/Deny. Returns immediately
@@ -11506,6 +11327,14 @@ declare class AgentWidgetSession {
11506
11327
  * 3. Append a user-visible bubble with the answer text so the
11507
11328
  * transcript reads naturally.
11508
11329
  */
11330
+ resolveAskUserQuestion(toolMessage: AgentWidgetMessage, answer: string | Record<string, string | string[]>): Promise<void>;
11331
+ /**
11332
+ * Resolve a paused auto-resolving LOCAL tool call (`webmcp:*` page tools
11333
+ * and the built-in `suggest_replies`) and post the result to `/resume`.
11334
+ * Triggered automatically from `handleEvent`; idempotent on the message's
11335
+ * `toolCall.id`. See `resolveWebMcpToolCall` in `session-actions.ts`.
11336
+ */
11337
+ resolveWebMcpToolCall(toolMessage: AgentWidgetMessage): Promise<void>;
11509
11338
  /**
11510
11339
  * Persist in-progress answers and the current page index for a multi-question
11511
11340
  * `ask_user_question` payload, so a refresh resumes on the same page with
@@ -11524,123 +11353,6 @@ declare class AgentWidgetSession {
11524
11353
  * answers via top-level merge.
11525
11354
  */
11526
11355
  markAskUserQuestionResolved(toolMessage: AgentWidgetMessage, answers?: Record<string, string | string[]>): void;
11527
- resolveAskUserQuestion(toolMessage: AgentWidgetMessage, answer: string | Record<string, string | string[]>): Promise<void>;
11528
- /**
11529
- * Collect an auto-resolving LOCAL-tool `await` (`webmcp:*` page tools
11530
- * and the built-in `suggest_replies`) into a per-executionId batch
11531
- * and schedule a single deferred flush. Parallel calls (core#3878) emit
11532
- * several `await`s for ONE paused execution within the same stream tick;
11533
- * buffering them and flushing once lets us post ONE `/resume` keyed by the
11534
- * per-call `webMcpToolCallId` rather than racing N name-keyed resumes on the
11535
- * same execution (which 404'd on the second and hung the turn).
11536
- *
11537
- * Deferred via `queueMicrotask` (epoch-guarded) for the same reason the old
11538
- * direct resolve was: handleEvent must return first so the dispatch's
11539
- * `connectStream` sees end-of-stream and releases the shared abortController
11540
- * before a resolve grabs it.
11541
- *
11542
- * Awaits without an `executionId` or `toolCall.id` can't be batched (no key)
11543
- *: route them straight to the single-call path, which surfaces the malformed
11544
- * wire shape via `onError` / an `isError` resume.
11545
- */
11546
- private enqueueWebMcpAwait;
11547
- /**
11548
- * Flush every buffered local-tool await batch, one `/resume` per executionId.
11549
- * Called once a stream ends (`status: idle` / `error`): by then all parallel
11550
- * `await`s the stream carried have been collected, even if split across
11551
- * SSE chunks. Deferred via `queueMicrotask` (epoch-guarded) so the idle
11552
- * handler returns first and the stream's end-of-stream teardown (streaming /
11553
- * abortController) settles before a resolve grabs them: the same ordering the
11554
- * single-call resolve always relied on.
11555
- */
11556
- private scheduleWebMcpBatchFlush;
11557
- /**
11558
- * Run a buffered batch of local-tool awaits for one executionId. Size 1
11559
- * (single call, or distinct-tool turns that happened to arrive alone) takes
11560
- * the original single-call path; size >1 (parallel calls) takes the batched
11561
- * path that posts ONE `/resume`. The batch is removed from the map up front
11562
- * so any later sibling re-emit (e.g. from a re-pause) forms a fresh batch
11563
- * rather than mutating one already in flight.
11564
- */
11565
- private flushWebMcpAwaitBatch;
11566
- private resolveWebMcpToolStartedAt;
11567
- /**
11568
- * Persisted-resolution guard for `suggest_replies`. The in-memory dedupe
11569
- * sets (`webMcpInflightKeys` / `webMcpResolvedKeys`) are cleared by
11570
- * hydrateMessages/clearMessages/cancel, but `suggestRepliesResolved`
11571
- * survives on the stored message, so a stale `await` re-emit after a
11572
- * hydration must not re-POST `/resume` for an already-resolved call (the
11573
- * historical double-resume failure mode the batching work exists to avoid).
11574
- * Checks the LIVE message first; the handleEvent snapshot is a fresh wire
11575
- * skeleton whose metadata never carries the flag.
11576
- */
11577
- private isSuggestRepliesAlreadyResolved;
11578
- private markWebMcpToolRunning;
11579
- private markWebMcpToolComplete;
11580
- /**
11581
- * Resolve one or more parallel local-tool awaits sharing one paused
11582
- * executionId with a SINGLE `/resume` (core#3878); `resolveWebMcpToolCall`
11583
- * delegates size-1 resolves here after its guards. By default each call is
11584
- * executed against the page registry concurrently: every gated call renders
11585
- * its own native approval bubble, and a sibling's confirm Promise never
11586
- * blocks another's execution. With `webmcp.execution: "sequential"` the
11587
- * calls run one at a time in emission order instead (see the config docs
11588
- * for when that matters). Outputs are keyed by per-call `webMcpToolCallId`
11589
- * (server prefers it over tool name; name-keying remains the fallback for
11590
- * legacy single/distinct-tool turns), so two calls to the SAME tool no longer
11591
- * collide. The server is tolerant: any call we omit (declined-after-abort,
11592
- * dedupe, exec failure) simply re-pauses and is retried on its re-emit.
11593
- *
11594
- * Owns the dedupe / abort / streaming machinery for both routes; resolved
11595
- * keys are marked on the shared resume POST's HTTP OK.
11596
- */
11597
- private resolveWebMcpToolCallBatch;
11598
- /**
11599
- * Resolve a paused auto-resolving LOCAL tool call and post the result to
11600
- * `/resume`: `webmcp:*` calls execute against the host page's tool
11601
- * registry; the built-in `suggest_replies` skips execution entirely and
11602
- * resumes with a canned "shown" result (the chips render from the message
11603
- * list, not from this resolve).
11604
- *
11605
- * Triggered automatically from `handleEvent` when an `await`-derived
11606
- * message arrives for such a tool: the user does not click a pill; the
11607
- * bridge's confirm-bubble gate (WebMCP only) is the only interactive
11608
- * surface.
11609
- *
11610
- * Idempotent on the message's `toolCall.id`: re-emits of the same await
11611
- * (e.g. from message coalescing) won't double-fire `tool.execute`. Failure
11612
- * modes, declined, timed out, throw, unknown tool, all resolve into a
11613
- * `{ isError: true, content: [...] }` payload that resumes the dispatch
11614
- * cleanly so the agent can recover.
11615
- *
11616
- * After the malformed-wire guards this is a thin gate over
11617
- * `resolveWebMcpToolCallBatch` with a size-1 batch: the dedupe check runs
11618
- * synchronously here so a stale re-emit never toggles streaming.
11619
- */
11620
- resolveWebMcpToolCall(toolMessage: AgentWidgetMessage): Promise<void>;
11621
- /**
11622
- * POST `/resume` with a SINGLE tool's output and pipe the resulting SSE
11623
- * stream back through `connectStream`. Shared by every single-call local-tool
11624
- * resolve path (ask_user_question and single WebMCP calls). Parallel WebMCP
11625
- * calls use `resolveWebMcpToolCallBatch`, which posts one resume for many.
11626
- *
11627
- * `resumeKey` is the `toolOutputs` map key: the per-call `webMcpToolCallId`
11628
- * for WebMCP (core#3878), or the tool name for ask_user_question / legacy
11629
- * servers. `onHttpOk` runs synchronously between the HTTP-status check and the
11630
- * stream pipe; it lets the WebMCP resolve path commit the dedupe flag at
11631
- * "server accepted the answer" rather than "stream finished cleanly".
11632
- */
11633
- private resumeWithToolOutput;
11634
- /**
11635
- * Tear down every in-flight WebMCP resolve and advance the epoch. Each
11636
- * resolve owns a dedicated AbortController (chained/parallel resolves don't
11637
- * share one), so we abort them individually; the aborts propagate into the
11638
- * bridge's execute race and into each `/resume` fetch signal. Bumping
11639
- * `webMcpEpoch` strands any resolve still deferred in a queued microtask: * it captured the prior epoch and bails before installing a fresh
11640
- * controller, so it can't escape this teardown. Called from every stop /
11641
- * new-turn boundary (cancel, clearMessages, hydrateMessages, sendMessage).
11642
- */
11643
- private abortWebMcpResolves;
11644
11356
  cancel(): void;
11645
11357
  clearMessages(): void;
11646
11358
  getArtifacts(): PersonaArtifactRecord[];
@@ -11649,68 +11361,9 @@ declare class AgentWidgetSession {
11649
11361
  selectArtifact(id: string | null): void;
11650
11362
  clearArtifacts(): void;
11651
11363
  upsertArtifact(manual: PersonaArtifactManualUpsert): PersonaArtifactRecord;
11652
- /**
11653
- * Injects (or refreshes) the in-thread artifact block for a programmatically
11654
- * upserted artifact, matching the streamed UX: the resolved display mode
11655
- * picks the component ("collapsed"/"panel" → reference card, "inline" → inline
11656
- * preview).
11657
- *
11658
- * Unlike the streamed path (which embeds content on `artifact_complete`),
11659
- * the programmatic record is complete up front, so the final markdown /
11660
- * file meta / component name + props are embedded immediately and the block
11661
- * hydrates after a refresh without the original registry state.
11662
- *
11663
- * A re-upsert of the same id rebuilds the existing block's persisted
11664
- * rawContent in place (the registry is not persisted, so hydration reads
11665
- * these props); without this the block would keep the first version's
11666
- * content after a refresh.
11667
- */
11668
- private injectArtifactRefBlock;
11669
- /**
11670
- * Re-materialize existing transcript artifact blocks after a live display
11671
- * preference update. One messages callback keeps conversion atomic.
11672
- */
11673
- private refreshArtifactReferenceBlocks;
11674
- private clearArtifactState;
11675
- private emitArtifactsState;
11676
- private applyArtifactStreamEvent;
11677
11364
  hydrateMessages(messages: AgentWidgetMessage[]): void;
11678
11365
  hydrateArtifacts(artifacts: PersonaArtifactRecord[], selectedId?: string | null): void;
11679
11366
  private handleEvent;
11680
- /**
11681
- * Record the latest SSE `id:` cursor and (lazily) form/advance the resume
11682
- * handle. Needs an executionId (lifted into `agentExecution` from the same
11683
- * frame's metadata, handled before this trailing cursor event) and the active
11684
- * assistant message id.
11685
- */
11686
- private trackCursor;
11687
- /**
11688
- * The drop gate: is this stream-end a dropped connection we should reconnect,
11689
- * rather than a graceful finish or an intentional `await` pause? ALL must
11690
- * hold (see plan §Design overview keystone 3).
11691
- */
11692
- private isDurableDrop;
11693
- /**
11694
- * True when the run is parked awaiting user input (an unanswered
11695
- * `ask_user_question` / local-tool await, or a pending approval). Those end
11696
- * the stream with `idle` and no terminal too, but are NOT drops.
11697
- */
11698
- private isAwaitPending;
11699
- /**
11700
- * Enter the reconnect flow. Keeps the assistant bubble open and `streaming`
11701
- * true, flips to `resuming`, and kicks the bounded backoff loop. Re-entrant
11702
- * calls (a second drop while reconnecting) are no-ops.
11703
- */
11704
- private beginReconnect;
11705
- /**
11706
- * Lazily import and instantiate the reconnect controller. Cached so repeated
11707
- * reconnects reuse one controller (and one module load). Deliberately kept off
11708
- * every hot path (`sendMessage` / `cancel` / `teardownReconnect`) so a widget
11709
- * that never reconnects never pulls the chunk.
11710
- */
11711
- private loadReconnectController;
11712
- /** The narrow surface the lazily-loaded reconnect loop drives. */
11713
- private buildReconnectHost;
11714
11367
  /** Public manual retry (e.g. a "Reconnect" button). */
11715
11368
  reconnectNow(): void;
11716
11369
  /**
@@ -11722,34 +11375,13 @@ declare class AgentWidgetSession {
11722
11375
  executionId: string;
11723
11376
  after: string;
11724
11377
  }): void;
11725
- /**
11726
- * Reopen the trailing assistant bubble (the persisted partial) so replayed
11727
- * deltas append to it. Returns its id, or null if the last turn doesn't end
11728
- * in a plain assistant message.
11729
- */
11730
- private reopenTrailingAssistant;
11731
- /**
11732
- * Tear down any in-flight/pending reconnect and clear the resume handle. Runs
11733
- * on every new turn / cancel / hydrate, so it must NOT pull the reconnect
11734
- * chunk: it only delegates to the controller if one was already created.
11735
- */
11736
- private teardownReconnect;
11737
- /** Drop the resume handle and notify the host (`onExecutionState(null)`). */
11738
- private clearResumable;
11739
- /**
11740
- * Surface the resume handle to the host for persistence. Immediate on
11741
- * create/clear; trailing-edge throttled on cursor advances so it isn't called
11742
- * per delta.
11743
- */
11744
- private notifyExecutionState;
11745
11378
  /** The current durable resume handle, if any (read-only). */
11746
11379
  getResumableHandle(): ResumableHandle | null;
11747
- private setStatus;
11748
- private setStreaming;
11749
11380
  /**
11750
11381
  * Speak the latest assistant message using the Web Speech API
11751
11382
  * if text-to-speech is enabled in the config.
11752
11383
  */
11384
+ /** `only`: speak it only if the latest assistant message is one of these. */
11753
11385
  private speakLatestAssistantMessage;
11754
11386
  /**
11755
11387
  * Pick the best available English voice from a list of SpeechSynthesisVoices.
@@ -11773,11 +11405,7 @@ declare class AgentWidgetSession {
11773
11405
  * Stop any in-progress text-to-speech / read-aloud playback.
11774
11406
  */
11775
11407
  stopSpeaking(): void;
11776
- private appendMessage;
11777
11408
  private upsertMessage;
11778
- private ensureSequence;
11779
- private nextSequence;
11780
- private sortMessages;
11781
11409
  }
11782
11410
 
11783
11411
  declare const formDefinitions: Record<string, {