@runtypelabs/persona 3.34.0 → 3.35.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 (186) hide show
  1. package/README.md +11 -11
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/{types-CthJFfNx.d.cts → types-DgYsuwXL.d.cts} +15 -14
  5. package/dist/animations/{types-CthJFfNx.d.ts → types-DgYsuwXL.d.ts} +15 -14
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/codegen.cjs +11 -11
  9. package/dist/codegen.js +10 -10
  10. package/dist/index.cjs +46 -44
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +514 -158
  13. package/dist/index.d.ts +514 -158
  14. package/dist/index.global.js +41 -89
  15. package/dist/index.global.js.map +1 -1
  16. package/dist/index.js +46 -44
  17. package/dist/index.js.map +1 -1
  18. package/dist/install.global.js +1 -1
  19. package/dist/install.global.js.map +1 -1
  20. package/dist/launcher.global.js +2 -2
  21. package/dist/launcher.global.js.map +1 -1
  22. package/dist/markdown-parsers.js +53 -0
  23. package/dist/plugin-kit.d.cts +4 -4
  24. package/dist/plugin-kit.d.ts +4 -4
  25. package/dist/runtype-tts.js +1 -0
  26. package/dist/smart-dom-reader.cjs +1 -1
  27. package/dist/smart-dom-reader.d.cts +352 -103
  28. package/dist/smart-dom-reader.d.ts +352 -103
  29. package/dist/smart-dom-reader.js +1 -1
  30. package/dist/theme-editor-preview.cjs +203 -0
  31. package/dist/theme-editor-preview.d.cts +5748 -0
  32. package/dist/theme-editor-preview.d.ts +5748 -0
  33. package/dist/theme-editor-preview.js +203 -0
  34. package/dist/theme-editor.cjs +14 -131
  35. package/dist/theme-editor.d.cts +362 -574
  36. package/dist/theme-editor.d.ts +362 -574
  37. package/dist/theme-editor.js +14 -131
  38. package/dist/theme-reference.cjs +1 -1
  39. package/dist/theme-reference.d.cts +1 -1
  40. package/dist/theme-reference.d.ts +1 -1
  41. package/dist/theme-reference.js +1 -1
  42. package/dist/voice-worklet-player.cjs +80 -0
  43. package/dist/voice-worklet-player.d.cts +215 -0
  44. package/dist/voice-worklet-player.d.ts +215 -0
  45. package/dist/voice-worklet-player.js +80 -0
  46. package/dist/widget.css +54 -36
  47. package/package.json +16 -2
  48. package/src/animations/glyph-cycle.ts +11 -14
  49. package/src/animations/wipe.ts +1 -1
  50. package/src/ask-user-question-tool.test.ts +1 -1
  51. package/src/ask-user-question-tool.ts +6 -8
  52. package/src/client.test.ts +67 -12
  53. package/src/client.ts +40 -32
  54. package/src/codegen.test.ts +1 -1
  55. package/src/codegen.ts +1 -1
  56. package/src/components/approval-bubble.test.ts +1 -1
  57. package/src/components/approval-bubble.ts +3 -3
  58. package/src/components/artifact-pane.ts +3 -3
  59. package/src/components/ask-user-question-bubble.test.ts +14 -14
  60. package/src/components/ask-user-question-bubble.ts +10 -10
  61. package/src/components/composer-builder.ts +17 -16
  62. package/src/components/composer-parts.test.ts +54 -0
  63. package/src/components/composer-parts.ts +95 -126
  64. package/src/components/demo-carousel.ts +1 -1
  65. package/src/components/event-stream-view.test.ts +7 -7
  66. package/src/components/event-stream-view.ts +5 -5
  67. package/src/components/header-builder.ts +33 -27
  68. package/src/components/header-layouts.ts +1 -1
  69. package/src/components/header-parts.test.ts +90 -0
  70. package/src/components/header-parts.ts +62 -93
  71. package/src/components/launcher.test.ts +11 -0
  72. package/src/components/launcher.ts +1 -1
  73. package/src/components/message-bubble.test.ts +74 -2
  74. package/src/components/message-bubble.ts +25 -11
  75. package/src/components/messages.ts +2 -2
  76. package/src/components/panel.test.ts +1 -1
  77. package/src/components/panel.ts +92 -97
  78. package/src/components/pill-composer-builder.ts +28 -24
  79. package/src/components/reasoning-bubble.ts +2 -2
  80. package/src/components/suggestions.ts +1 -1
  81. package/src/components/tool-bubble.ts +1 -1
  82. package/src/components/widget-view.test.ts +181 -0
  83. package/src/components/widget-view.ts +234 -0
  84. package/src/defaults.ts +54 -51
  85. package/src/generated/runtype-openapi-contract.ts +5 -0
  86. package/src/index-core.ts +30 -4
  87. package/src/index-global.ts +73 -6
  88. package/src/index.ts +7 -1
  89. package/src/install.test.ts +11 -11
  90. package/src/install.ts +28 -18
  91. package/src/launcher-global.ts +25 -14
  92. package/src/markdown-parsers-eager.ts +20 -0
  93. package/src/markdown-parsers-entry.ts +4 -0
  94. package/src/markdown-parsers-loader.ts +49 -0
  95. package/src/plugin-kit.test.ts +1 -1
  96. package/src/plugin-kit.ts +7 -7
  97. package/src/plugins/types.ts +5 -5
  98. package/src/postprocessors.ts +27 -14
  99. package/src/runtime/host-layout.test.ts +1 -1
  100. package/src/runtime/host-layout.ts +4 -5
  101. package/src/runtime/init.ts +2 -2
  102. package/src/session.test.ts +5 -6
  103. package/src/session.ts +333 -227
  104. package/src/session.voice.test.ts +146 -0
  105. package/src/session.webmcp.test.ts +24 -25
  106. package/src/smart-dom-reader.ts +3 -4
  107. package/src/styles/widget.css +54 -36
  108. package/src/suggest-replies-tool.test.ts +3 -3
  109. package/src/suggest-replies-tool.ts +5 -5
  110. package/src/testing/index.ts +1 -1
  111. package/src/theme-editor/color-utils.ts +2 -2
  112. package/src/theme-editor/index.ts +4 -13
  113. package/src/theme-editor/presets.ts +1 -1
  114. package/src/theme-editor/preview-utils.ts +22 -20
  115. package/src/theme-editor/preview.ts +4 -1
  116. package/src/theme-editor/role-mappings.ts +3 -3
  117. package/src/theme-editor/sections.ts +21 -20
  118. package/src/theme-editor/types.ts +1 -1
  119. package/src/theme-editor/webmcp/index.ts +1 -1
  120. package/src/theme-editor/webmcp/summary.ts +1 -2
  121. package/src/theme-editor/webmcp/tools.ts +30 -30
  122. package/src/theme-editor/webmcp/types.ts +2 -2
  123. package/src/theme-editor-preview.ts +10 -0
  124. package/src/theme-reference.ts +26 -26
  125. package/src/types.ts +368 -99
  126. package/src/ui.approval-plugin.test.ts +3 -3
  127. package/src/ui.ask-user-question-plugin.test.ts +9 -9
  128. package/src/ui.component-directive.test.ts +4 -4
  129. package/src/ui.composer-bar.test.ts +10 -11
  130. package/src/ui.composer-keyboard.test.ts +5 -5
  131. package/src/ui.header-icon-color.test.ts +59 -0
  132. package/src/ui.scroll.test.ts +5 -6
  133. package/src/ui.stop-button.test.ts +3 -4
  134. package/src/ui.suggest-replies.test.ts +2 -2
  135. package/src/ui.ts +292 -131
  136. package/src/utils/buttons.ts +1 -1
  137. package/src/utils/code-generators.ts +1 -1
  138. package/src/utils/composer-history.ts +3 -3
  139. package/src/utils/dom-context.test.ts +2 -2
  140. package/src/utils/dom-context.ts +7 -7
  141. package/src/utils/dom.test.ts +67 -0
  142. package/src/utils/dom.ts +75 -0
  143. package/src/utils/dropdown.ts +2 -2
  144. package/src/utils/event-stream-buffer.test.ts +2 -2
  145. package/src/utils/message-fingerprint.test.ts +9 -0
  146. package/src/utils/message-fingerprint.ts +5 -0
  147. package/src/utils/morph.ts +1 -1
  148. package/src/utils/sanitize.ts +32 -16
  149. package/src/utils/sequence-buffer.test.ts +8 -8
  150. package/src/utils/sequence-buffer.ts +4 -4
  151. package/src/utils/smart-dom-adapter.test.ts +1 -1
  152. package/src/utils/smart-dom-adapter.ts +4 -5
  153. package/src/utils/speech-text.test.ts +122 -0
  154. package/src/utils/speech-text.ts +101 -0
  155. package/src/utils/stream-animation.test.ts +3 -3
  156. package/src/utils/stream-animation.ts +6 -6
  157. package/src/utils/theme.test.ts +1 -1
  158. package/src/utils/throughput-tracker.test.ts +11 -11
  159. package/src/utils/throughput-tracker.ts +9 -9
  160. package/src/utils/tokens.ts +13 -13
  161. package/src/utils/virtual-scroller.ts +1 -1
  162. package/src/vendor/smart-dom-reader/README.md +4 -4
  163. package/src/vendor/smart-dom-reader/index.d.ts +1 -1
  164. package/src/vendor/smart-dom-reader/index.js +2 -2
  165. package/src/version.ts +1 -1
  166. package/src/voice/audio-playback-manager.test.ts +159 -0
  167. package/src/voice/audio-playback-manager.ts +145 -30
  168. package/src/voice/browser-speech-engine.ts +130 -0
  169. package/src/voice/fallback-speech-engine.ts +89 -0
  170. package/src/voice/index.ts +21 -0
  171. package/src/voice/read-aloud-controller.test.ts +143 -0
  172. package/src/voice/read-aloud-controller.ts +136 -0
  173. package/src/voice/runtype-speech-engine.test.ts +280 -0
  174. package/src/voice/runtype-speech-engine.ts +211 -0
  175. package/src/voice/runtype-tts-entry.ts +10 -0
  176. package/src/voice/runtype-tts-loader.test.ts +47 -0
  177. package/src/voice/runtype-tts-loader.ts +42 -0
  178. package/src/voice/runtype-voice-provider.ts +332 -537
  179. package/src/voice/voice-factory.ts +26 -4
  180. package/src/voice/voice.test.ts +321 -7
  181. package/src/voice/worklet-playback-engine.ts +272 -0
  182. package/src/voice-worklet-player.ts +39 -0
  183. package/src/webmcp-bridge.test.ts +6 -6
  184. package/src/webmcp-bridge.ts +24 -25
  185. package/src/webmcp-polyfill.ts +1 -1
  186. package/src/voice/voice-activity-detector.ts +0 -90
@@ -0,0 +1,146 @@
1
+ // Session-level wiring for the realtime (runtype) voice path (Option B).
2
+ //
3
+ // The provider's protocol behavior is covered in voice/voice.test.ts; here we
4
+ // mock the voice factory to a fake provider and drive its onTranscript/onMetrics
5
+ // callbacks to verify how session.setupVoice() feeds the chat thread.
6
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
7
+ import type { AgentWidgetMessage, VoiceMetrics } from './types';
8
+
9
+ // vi.mock is hoisted above module init, so the shared fake must be hoisted too.
10
+ const h = vi.hoisted(() => {
11
+ const state: {
12
+ transcriptCb:
13
+ | ((role: 'user' | 'assistant', text: string, isFinal: boolean) => void)
14
+ | null;
15
+ metricsCb: ((m: VoiceMetrics) => void) | null;
16
+ } = { transcriptCb: null, metricsCb: null };
17
+
18
+ const fakeProvider = {
19
+ type: 'runtype' as const,
20
+ connect: async () => {},
21
+ disconnect: async () => {},
22
+ startListening: async () => {},
23
+ stopListening: async () => {},
24
+ onResult: () => {},
25
+ onError: () => {},
26
+ onStatusChange: () => {},
27
+ onTranscript: (cb: typeof state.transcriptCb) => {
28
+ state.transcriptCb = cb;
29
+ },
30
+ onMetrics: (cb: typeof state.metricsCb) => {
31
+ state.metricsCb = cb;
32
+ },
33
+ };
34
+
35
+ return { state, fakeProvider };
36
+ });
37
+
38
+ vi.mock('./voice', async (importOriginal) => {
39
+ // Keep the real exports (ReadAloudController, BrowserSpeechEngine, …) and
40
+ // override only the provider factories so the realtime voice flow uses the
41
+ // fake provider.
42
+ const actual = await importOriginal<typeof import('./voice')>();
43
+ return {
44
+ ...actual,
45
+ createVoiceProvider: () => h.fakeProvider,
46
+ createBestAvailableVoiceProvider: () => h.fakeProvider,
47
+ isVoiceSupported: () => true,
48
+ };
49
+ });
50
+
51
+ import { AgentWidgetSession } from './session';
52
+
53
+ describe('AgentWidgetSession - realtime voice onTranscript (Option B)', () => {
54
+ let session: AgentWidgetSession;
55
+ let messages: AgentWidgetMessage[] = [];
56
+ let streaming = false;
57
+ let metricsSeen: VoiceMetrics[] = [];
58
+
59
+ const drive = (
60
+ role: 'user' | 'assistant',
61
+ text: string,
62
+ isFinal: boolean,
63
+ ) => h.state.transcriptCb!(role, text, isFinal);
64
+
65
+ beforeEach(() => {
66
+ h.state.transcriptCb = null;
67
+ h.state.metricsCb = null;
68
+ messages = [];
69
+ streaming = false;
70
+ metricsSeen = [];
71
+
72
+ session = new AgentWidgetSession(
73
+ {
74
+ apiUrl: 'http://localhost:8000',
75
+ voiceRecognition: {
76
+ enabled: true,
77
+ provider: { type: 'runtype', runtype: { agentId: 'a1' } },
78
+ onMetrics: (m) => {
79
+ metricsSeen.push(m);
80
+ },
81
+ },
82
+ },
83
+ {
84
+ onMessagesChanged: (m) => {
85
+ messages = m;
86
+ },
87
+ onStatusChanged: () => {},
88
+ onStreamingChanged: (s) => {
89
+ streaming = s;
90
+ },
91
+ onError: () => {},
92
+ },
93
+ );
94
+ session.setupVoice();
95
+ });
96
+
97
+ it('registers an onTranscript handler', () => {
98
+ expect(h.state.transcriptCb).toBeTypeOf('function');
99
+ });
100
+
101
+ it('grows the user bubble live, then finalizes and shows a typing indicator', () => {
102
+ drive('user', 'what are', false);
103
+ expect(messages).toHaveLength(1);
104
+ expect(messages[0].role).toBe('user');
105
+ expect(messages[0].content).toBe('what are');
106
+ expect(messages[0].voiceProcessing).toBe(true);
107
+
108
+ drive('user', 'what are your hours', false);
109
+ expect(messages).toHaveLength(1); // upsert in place, not a new bubble
110
+ expect(messages[0].content).toBe('what are your hours');
111
+
112
+ drive('user', 'what are your hours?', true);
113
+ expect(messages).toHaveLength(2);
114
+ expect(messages[0].content).toBe('what are your hours?');
115
+ expect(messages[0].voiceProcessing).toBe(false); // cleared on final
116
+ expect(messages[1].role).toBe('assistant');
117
+ expect(messages[1].content).toBe('');
118
+ expect(messages[1].streaming).toBe(true); // typing indicator
119
+ expect(streaming).toBe(true);
120
+ });
121
+
122
+ it('fills the assistant reply on its final frame and clears streaming', () => {
123
+ drive('user', 'hi', true);
124
+ drive('assistant', 'Hello! How can I help?', true);
125
+
126
+ const assistant = messages.find((m) => m.role === 'assistant');
127
+ expect(assistant?.content).toBe('Hello! How can I help?');
128
+ expect(assistant?.streaming).toBe(false);
129
+ expect(assistant?.voiceProcessing).toBe(false);
130
+ expect(streaming).toBe(false);
131
+ });
132
+
133
+ it('starts a fresh user bubble on the next turn', () => {
134
+ drive('user', 'first', true);
135
+ drive('assistant', 'reply one', true);
136
+ drive('user', 'second', false);
137
+
138
+ const userMessages = messages.filter((m) => m.role === 'user');
139
+ expect(userMessages.map((m) => m.content)).toEqual(['first', 'second']);
140
+ });
141
+
142
+ it('forwards metrics to the config hook', () => {
143
+ h.state.metricsCb!({ llmMs: 100, totalMs: 250 });
144
+ expect(metricsSeen).toEqual([{ llmMs: 100, totalMs: 250 }]);
145
+ });
146
+ });
@@ -33,7 +33,7 @@ const makeSession = (overrides?: {
33
33
  content: [{ type: "text", text: "ok" }],
34
34
  }),
35
35
  );
36
- // Mimic AgentWidgetClient.executeWebMcpToolCall returns null when bridge
36
+ // Mimic AgentWidgetClient.executeWebMcpToolCall: returns null when bridge
37
37
  // not configured. We toggle via isOperational below.
38
38
  client.executeWebMcpToolCall = vi.fn(
39
39
  () =>
@@ -75,7 +75,7 @@ const awaitingMessage = (
75
75
  },
76
76
  });
77
77
 
78
- describe("AgentWidgetSession WebMCP resolve", () => {
78
+ describe("AgentWidgetSession: WebMCP resolve", () => {
79
79
  beforeEach(() => {
80
80
  vi.clearAllMocks();
81
81
  });
@@ -130,7 +130,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
130
130
 
131
131
  it("still resumes (with isError) when the bridge is not operational", async () => {
132
132
  // BugBot finding #1: previously, handleEvent skipped resolveWebMcpToolCall
133
- // entirely when `isWebMcpOperational()` was false leaving the dispatch
133
+ // entirely when `isWebMcpOperational()` was false: leaving the dispatch
134
134
  // hung. The session must surface an actionable error to /resume instead.
135
135
  const { session, resumeSpy } = makeSession({
136
136
  isOperational: false,
@@ -190,8 +190,8 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
190
190
  expect(afterFailedResume?.toolCall?.result).toBeUndefined();
191
191
  expect(afterFailedResume?.toolCall?.durationMs).toBeUndefined();
192
192
 
193
- await session.resolveWebMcpToolCall(msg); // retry must be allowed
194
- await session.resolveWebMcpToolCall(msg); // post-success must be blocked
193
+ await session.resolveWebMcpToolCall(msg); // retry: must be allowed
194
+ await session.resolveWebMcpToolCall(msg); // post-success: must be blocked
195
195
 
196
196
  expect(executeSpy).toHaveBeenCalledTimes(2);
197
197
  expect(resumeSpy).toHaveBeenCalledTimes(2);
@@ -233,7 +233,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
233
233
  });
234
234
 
235
235
  it("aborts an in-flight resolve when cancel() is called", async () => {
236
- // BugBot finding #6 (cont.) the bridge execute race should reject on
236
+ // BugBot finding #6 (cont.): the bridge execute race should reject on
237
237
  // cancel so the dispatch doesn't fire a stale /resume after the user
238
238
  // stops.
239
239
  let release: () => void = () => undefined;
@@ -263,8 +263,8 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
263
263
  it("does NOT abort the shared session abortController", async () => {
264
264
  // The chained-turn fix: a webmcp resolve must leave `this.abortController`
265
265
  // untouched. In a chain (tool A → /resume → tool B) that shared controller
266
- // is still piping A's resume SSE the very stream that just delivered B's
267
- // step_await so aborting it strands B (it never executes; its /resume is
266
+ // is still piping A's resume SSE: the very stream that just delivered B's
267
+ // step_await, so aborting it strands B (it never executes; its /resume is
268
268
  // never POSTed; the dispatch hangs forever). Resolves use a dedicated
269
269
  // per-call controller tracked in `webMcpResolveControllers` instead.
270
270
  const { session } = makeSession();
@@ -547,7 +547,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
547
547
  });
548
548
 
549
549
  it("an error event does not clear streaming while a webmcp resolve is in flight", () => {
550
- // BugBot: the error handler mirrors the idle handler it must not tear
550
+ // BugBot: the error handler mirrors the idle handler: it must not tear
551
551
  // down streaming while a sibling/successor resolve is still executing.
552
552
  const stuck = new Promise<WebMcpToolResult>(() => undefined);
553
553
  const session = makeSession({ executeImpl: () => stuck }).session;
@@ -559,7 +559,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
559
559
  });
560
560
 
561
561
  it("connectStream error does not clear streaming while a webmcp resolve is in flight", async () => {
562
- // BugBot: connectStream's catch mirrors the error/idle handlers a failed
562
+ // BugBot: connectStream's catch mirrors the error/idle handlers: a failed
563
563
  // resume stream must not tear down streaming while another resolve runs.
564
564
  const session = new AgentWidgetSession(
565
565
  { apiUrl: "http://test", webmcp: { enabled: true } },
@@ -603,7 +603,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
603
603
  // BugBot finding #14: previously the bridge was constructed whenever a
604
604
  // `webmcp` block existed, regardless of `enabled`. That left
605
605
  // `executeWebMcpToolCall` returning a non-null promise even when WebMCP
606
- // was explicitly disabled making the session's "WebMCP not enabled"
606
+ // was explicitly disabled: making the session's "WebMCP not enabled"
607
607
  // resume branch dead code. Constructor now gates on `enabled === true`.
608
608
  const session = new AgentWidgetSession(
609
609
  { apiUrl: "http://test", webmcp: { enabled: false } },
@@ -652,7 +652,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
652
652
 
653
653
  it("marks resolved on HTTP /resume success, not on stream completion", async () => {
654
654
  // BugBot finding #8: if the resume HTTP response is OK but the downstream
655
- // SSE stream errors, we still want dedupe to block re-emits the server
655
+ // SSE stream errors, we still want dedupe to block re-emits: the server
656
656
  // has already accepted the answer.
657
657
  const { session, resumeSpy, executeSpy } = makeSession();
658
658
  // Make connectStream throw to simulate a broken downstream SSE.
@@ -663,7 +663,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
663
663
 
664
664
  const msg = awaitingMessage("tool-1", "webmcp:search");
665
665
  await session.resolveWebMcpToolCall(msg);
666
- await session.resolveWebMcpToolCall(msg); // re-emit must be blocked
666
+ await session.resolveWebMcpToolCall(msg); // re-emit: must be blocked
667
667
 
668
668
  expect(executeSpy).toHaveBeenCalledTimes(1);
669
669
  expect(resumeSpy).toHaveBeenCalledTimes(1);
@@ -678,7 +678,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
678
678
  await session.resolveWebMcpToolCall(
679
679
  awaitingMessage("tool-1", "webmcp:search", "exec-1"),
680
680
  );
681
- // Different execution, same toolCall.id must be allowed.
681
+ // Different execution, same toolCall.id: must be allowed.
682
682
  await session.resolveWebMcpToolCall(
683
683
  awaitingMessage("tool-1", "webmcp:search", "exec-2"),
684
684
  );
@@ -690,12 +690,12 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
690
690
  // BugBot finding #11: clearing the resolved set on sendMessage would
691
691
  // let a stale step_await from the prior /resume's still-active SSE
692
692
  // re-trigger execute(). With executionId-scoped keys, the prior
693
- // execution's resolved entries persist so stale re-emits stay blocked.
693
+ // execution's resolved entries persist, so stale re-emits stay blocked.
694
694
  const { session, executeSpy, resumeSpy } = makeSession();
695
695
  await session.resolveWebMcpToolCall(
696
696
  awaitingMessage("tool-1", "webmcp:search", "exec-1"),
697
697
  );
698
- // Stale re-emit from exec-1 after a new turn started still blocked.
698
+ // Stale re-emit from exec-1 after a new turn started: still blocked.
699
699
  await session.resolveWebMcpToolCall(
700
700
  awaitingMessage("tool-1", "webmcp:search", "exec-1"),
701
701
  );
@@ -781,7 +781,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
781
781
 
782
782
  it("keys a single call's /resume by webMcpToolCallId when present", async () => {
783
783
  // core#3878: when the server emits a per-call id, the single-call path keys
784
- // /resume by it (server prefers id over name) not by the wire tool name.
784
+ // /resume by it (server prefers id over name): not by the wire tool name.
785
785
  const { session, resumeSpy } = makeSession({
786
786
  executeReturn: { content: [{ type: "text", text: "added" }] },
787
787
  });
@@ -812,7 +812,7 @@ describe("AgentWidgetSession — WebMCP resolve", () => {
812
812
  });
813
813
  });
814
814
 
815
- describe("AgentWidgetSession WebMCP parallel batched resume (core#3878)", () => {
815
+ describe("AgentWidgetSession: WebMCP parallel batched resume (core#3878)", () => {
816
816
  beforeEach(() => {
817
817
  vi.clearAllMocks();
818
818
  });
@@ -853,8 +853,7 @@ describe("AgentWidgetSession — WebMCP parallel batched resume (core#3878)", ()
853
853
  message: msg,
854
854
  });
855
855
 
856
- // The batch flushes only when the stream that delivered the awaits ENDS
857
- // the client emits `status: idle` at stream end. Simulate that.
856
+ // The batch flushes only when the stream that delivered the awaits ENDS: // the client emits `status: idle` at stream end. Simulate that.
858
857
  const endStream = (session: AgentWidgetSession) =>
859
858
  (session as unknown as { handleEvent: (e: unknown) => void }).handleEvent({
860
859
  type: "status",
@@ -893,7 +892,7 @@ describe("AgentWidgetSession — WebMCP parallel batched resume (core#3878)", ()
893
892
  // Both page tools ran.
894
893
  expect(executeSpy).toHaveBeenCalledTimes(2);
895
894
 
896
- // Exactly ONE /resume for the shared execution not one per tool.
895
+ // Exactly ONE /resume for the shared execution: not one per tool.
897
896
  expect(resumeSpy).toHaveBeenCalledTimes(1);
898
897
  const [execId, toolOutputs] = resumeSpy.mock.calls[0]! as unknown as [
899
898
  string,
@@ -906,7 +905,7 @@ describe("AgentWidgetSession — WebMCP parallel batched resume (core#3878)", ()
906
905
  expect(toolOutputs["toolu_B"].content[0].text).toBe("added SHOE-007");
907
906
  });
908
907
 
909
- it("executes siblings concurrently one call's gate Promise does not block the other", async () => {
908
+ it("executes siblings concurrently: one call's gate Promise does not block the other", async () => {
910
909
  // The native approval bubble parks each call's execute on a Promise. A
911
910
  // gated sibling must not head-of-line-block the others: both executes
912
911
  // should be in flight before either completes.
@@ -1078,7 +1077,7 @@ describe("AgentWidgetSession — WebMCP parallel batched resume (core#3878)", ()
1078
1077
  it("a UI-only updateConfig preserves the client, bridge, and in-flight WebMCP state", async () => {
1079
1078
  // Self-styling widget: a `webmcp:*` theme tool mutates config and re-renders
1080
1079
  // mid-turn. updateConfig must NOT swap the client when only display fields
1081
- // (theme/copy/…) change doing so would abort the very turn that's
1080
+ // (theme/copy/…) change: doing so would abort the very turn that's
1082
1081
  // restyling the widget and strand the paused execution. (Inverse of the
1083
1082
  // teardown test above.)
1084
1083
  const { session } = makeSession();
@@ -1128,7 +1127,7 @@ describe("AgentWidgetSession — WebMCP parallel batched resume (core#3878)", ()
1128
1127
  feed(session, parallelAwait("toolu_A", "SHOE-001"));
1129
1128
  feed(session, parallelAwait("toolu_B", "SHOE-007"));
1130
1129
  // Teardown clears the buffered batch (and bumps the epoch) BEFORE the
1131
- // stream-end flush even if a late idle arrives, nothing should resolve.
1130
+ // stream-end flush: even if a late idle arrives, nothing should resolve.
1132
1131
  session.clearMessages();
1133
1132
  endStream(session);
1134
1133
  await flushMicrotasks();
@@ -1160,7 +1159,7 @@ describe("AgentWidgetSession — WebMCP parallel batched resume (core#3878)", ()
1160
1159
  // The parked confirm Promise resolves false (declined) and the map clears.
1161
1160
  await expect(pending).resolves.toBe(false);
1162
1161
  expect(s.webMcpApprovalResolvers.size).toBe(0);
1163
- // The bubble must not be left visually "pending" it flips to denied so no
1162
+ // The bubble must not be left visually "pending": it flips to denied so no
1164
1163
  // stale Approve/Deny remains clickable.
1165
1164
  const bubble = s.messages.find((m) => m.variant === "approval");
1166
1165
  expect(bubble?.approval?.status).toBe("denied");
@@ -4,11 +4,10 @@
4
4
  * Adapts `@mcp-b/smart-dom-reader` into Persona's enriched page-context pipeline and
5
5
  * exposes a ready-made {@link AgentWidgetContextProvider} you can drop into
6
6
  * `config.contextProviders`. This is the ONLY module that imports the smart-dom-reader
7
- * runtime value, so the library never reaches the main bundle or the IIFE/CDN build
8
- * importing this subpath is opt-in.
7
+ * runtime value, so the library never reaches the main bundle or the IIFE/CDN build: * importing this subpath is opt-in.
9
8
  *
10
9
  * The library is **vendored** under `src/vendor/smart-dom-reader/` (it is mis-published
11
- * on npm and cannot be imported by name see that directory's README). Vendoring it
10
+ * on npm and cannot be imported by name: see that directory's README). Vendoring it
12
11
  * here means consumers need no extra install: the code is bundled into this entry, and
13
12
  * consumers who never import this subpath pay nothing.
14
13
  *
@@ -65,7 +64,7 @@ export interface SmartDomContextOptions extends SmartDomAdapterOptions {
65
64
  /** Document to extract from. Default: the global `document`. Ignored when `root` is set. */
66
65
  document?: Document;
67
66
  /**
68
- * Scope extraction to this element's subtree instead of the whole document parity
67
+ * Scope extraction to this element's subtree instead of the whole document: parity
69
68
  * with `collectEnrichedPageContext`'s `root`. Useful to read only a main-content region
70
69
  * and skip site chrome (nav, sidebars). Shadow DOM inside the subtree is still pierced.
71
70
  * When set, `document` is ignored.