@runtypelabs/persona 3.21.3 → 3.23.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 (66) hide show
  1. package/README.md +67 -0
  2. package/dist/animations/glyph-cycle.cjs +2 -262
  3. package/dist/animations/glyph-cycle.d.cts +1 -1
  4. package/dist/animations/glyph-cycle.d.ts +1 -1
  5. package/dist/animations/glyph-cycle.js +2 -235
  6. package/dist/animations/{types-CWPIj66R.d.cts → types-BZVr1YOV.d.cts} +10 -0
  7. package/dist/animations/{types-CWPIj66R.d.ts → types-BZVr1YOV.d.ts} +10 -0
  8. package/dist/animations/wipe.cjs +2 -72
  9. package/dist/animations/wipe.d.cts +1 -1
  10. package/dist/animations/wipe.d.ts +1 -1
  11. package/dist/animations/wipe.js +2 -45
  12. package/dist/index.cjs +52 -45
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +474 -6
  15. package/dist/index.d.ts +474 -6
  16. package/dist/index.global.js +107 -97
  17. package/dist/index.global.js.map +1 -1
  18. package/dist/index.js +52 -45
  19. package/dist/index.js.map +1 -1
  20. package/dist/smart-dom-reader.cjs +23 -0
  21. package/dist/smart-dom-reader.d.cts +4521 -0
  22. package/dist/smart-dom-reader.d.ts +4521 -0
  23. package/dist/smart-dom-reader.js +23 -0
  24. package/dist/testing.cjs +3 -84
  25. package/dist/testing.js +3 -55
  26. package/dist/theme-editor.cjs +57 -22501
  27. package/dist/theme-editor.d.cts +348 -1
  28. package/dist/theme-editor.d.ts +348 -1
  29. package/dist/theme-editor.js +57 -22503
  30. package/package.json +16 -6
  31. package/src/client.test.ts +165 -0
  32. package/src/client.ts +144 -23
  33. package/src/components/event-stream-view.ts +122 -1
  34. package/src/index.ts +26 -0
  35. package/src/session.test.ts +258 -0
  36. package/src/session.ts +886 -30
  37. package/src/session.webmcp.test.ts +815 -0
  38. package/src/smart-dom-reader.test.ts +135 -0
  39. package/src/smart-dom-reader.ts +135 -0
  40. package/src/theme-editor/color-utils.test.ts +59 -0
  41. package/src/theme-editor/color-utils.ts +38 -2
  42. package/src/theme-editor/index.ts +35 -0
  43. package/src/theme-editor/webmcp/coerce.test.ts +86 -0
  44. package/src/theme-editor/webmcp/coerce.ts +286 -0
  45. package/src/theme-editor/webmcp/index.ts +45 -0
  46. package/src/theme-editor/webmcp/summary.ts +324 -0
  47. package/src/theme-editor/webmcp/tools.test.ts +205 -0
  48. package/src/theme-editor/webmcp/tools.ts +795 -0
  49. package/src/theme-editor/webmcp/types.ts +87 -0
  50. package/src/types.ts +186 -0
  51. package/src/ui.composer-keyboard.test.ts +229 -0
  52. package/src/ui.ts +151 -8
  53. package/src/utils/composer-history.test.ts +128 -0
  54. package/src/utils/composer-history.ts +113 -0
  55. package/src/utils/message-fingerprint.test.ts +20 -0
  56. package/src/utils/message-fingerprint.ts +2 -0
  57. package/src/utils/smart-dom-adapter.test.ts +257 -0
  58. package/src/utils/smart-dom-adapter.ts +217 -0
  59. package/src/utils/throughput-tracker.test.ts +366 -0
  60. package/src/utils/throughput-tracker.ts +427 -0
  61. package/{LICENSE → src/vendor/smart-dom-reader/LICENSE} +2 -2
  62. package/src/vendor/smart-dom-reader/README.md +61 -0
  63. package/src/vendor/smart-dom-reader/index.d.ts +476 -0
  64. package/src/vendor/smart-dom-reader/index.js +1618 -0
  65. package/src/webmcp-bridge.test.ts +429 -0
  66. package/src/webmcp-bridge.ts +547 -0
package/README.md CHANGED
@@ -459,6 +459,72 @@ const myRules: ParseRule[] = [
459
459
  ];
460
460
  ```
461
461
 
462
+ #### Optional: smart-dom-reader provider
463
+
464
+ The default reader above is a zero-dependency `TreeWalker` and **does not pierce shadow
465
+ DOM**. For pages built from web components, an optional provider backed by a
466
+ vendored copy of [`@mcp-b/smart-dom-reader`](https://github.com/WebMCP-org/npm-packages/tree/main/packages/smart-dom-reader)
467
+ ships as a **separate entry point**, `@runtypelabs/persona/smart-dom-reader`. It is **not**
468
+ imported by the main bundle, so consumers who never import this subpath pay nothing — no
469
+ extra install, no bundle weight, no IIFE/CDN impact.
470
+
471
+ It adds, over the default reader: **Shadow-DOM piercing**, form grouping, and page
472
+ landmarks/state — while still emitting Persona's `EnrichedPageElement[]` shape so it
473
+ formats and flows through the same pipeline.
474
+
475
+ ```ts
476
+ import initAgentWidget from '@runtypelabs/persona';
477
+ import { createSmartDomReaderContextProvider } from '@runtypelabs/persona/smart-dom-reader';
478
+
479
+ initAgentWidget({
480
+ // ...config
481
+ contextProviders: [
482
+ createSmartDomReaderContextProvider({
483
+ // 'interactive' (default) | 'full' — full adds semantic content AND is required
484
+ // for shadow-DOM piercing (shadow descendants surface only in full mode).
485
+ mode: 'full',
486
+ contextKey: 'pageContext', // key under payload.context (default)
487
+ // root: document.querySelector('main') // optional: scope to a subtree, skip chrome
488
+ })
489
+ ]
490
+ });
491
+ ```
492
+
493
+ `contextProviders` are honored on the **agent** send path (`buildAgentPayload` merges each
494
+ provider's result into `payload.context` on every request). If you drive the legacy
495
+ flow/`buildPayload` path, call `collectSmartDomContext()` yourself and inject the result.
496
+
497
+ You can also use the pieces directly:
498
+
499
+ ```ts
500
+ import {
501
+ collectSmartDomContext, // → EnrichedPageElement[] (parity with collectEnrichedPageContext)
502
+ smartDomResultToEnriched // pure mapper: SmartDOMResult → EnrichedPageElement[]
503
+ } from '@runtypelabs/persona/smart-dom-reader';
504
+ import { formatEnrichedContext } from '@runtypelabs/persona';
505
+
506
+ const pageContext = formatEnrichedContext(collectSmartDomContext({ mode: 'full' }));
507
+ ```
508
+
509
+ Both `collectSmartDomContext()` and `createSmartDomReaderContextProvider()` accept a
510
+ `root` element to scope extraction to a subtree (parity with `collectEnrichedPageContext`'s
511
+ `root`) — useful to read only your main content region and skip nav/sidebars. Shadow DOM
512
+ inside the subtree is still pierced.
513
+
514
+ > **Actionability caveat.** Persona's click loop (`utils/actions.ts`) drives
515
+ > `document.querySelector`, which cannot pierce shadow roots or evaluate XPath. The adapter
516
+ > therefore prefers plain-CSS selectors; elements reachable only via shadow-piercing or
517
+ > XPath selectors are surfaced to the model as **context only** and are **not clickable**
518
+ > through the current `message_and_click` handler.
519
+
520
+ > **Why vendored, not a dependency.** Every published version of `@mcp-b/smart-dom-reader`
521
+ > (2.3.1–3.0.0) is mis-published — its `package.json` points to `dist/index.js` /
522
+ > `dist/index.d.ts` while the build only ships `.mjs` / `.d.mts`, so it cannot be imported
523
+ > by name in Node or any bundler. The library (MIT, zero-dep) is therefore vendored under
524
+ > `packages/widget/src/vendor/smart-dom-reader/`; see that directory's `README.md` for
525
+ > provenance and update steps. Once upstream republishes correctly this can revert to a
526
+ > normal optional peer dependency.
527
+
462
528
  ### DOM Events
463
529
 
464
530
  The widget dispatches custom DOM events that you can listen to for integration with your application:
@@ -2278,6 +2344,7 @@ config: {
2278
2344
  | `showReasoning` | `boolean?` | Show thinking/reasoning bubbles. Default: `true`. |
2279
2345
  | `showToolCalls` | `boolean?` | Show tool usage bubbles. Default: `true`. |
2280
2346
  | `showEventStreamToggle` | `boolean?` | Show the event stream inspector toggle in the header. Default: `false`. |
2347
+ | `composerHistory` | `boolean?` | `Up`/`Down` arrows in the composer navigate previously sent user messages for re-entry/editing (shell / Slack style). Entered only when the caret is at the start of the input, so multi-line cursor movement is preserved. Default: `true`. |
2281
2348
  | `eventStream` | `EventStreamConfig?` | Event stream inspector configuration: `badgeColors`, `timestampFormat`, `showSequenceNumbers`, `maxEvents`, `descriptionFields`, `classNames`. |
2282
2349
  | `artifacts` | `AgentWidgetArtifactsFeature?` | Artifact sidebar: `enabled`, `allowedTypes`, optional `layout` (see below). |
2283
2350
 
@@ -1,62 +1,4 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/animations/glyph-cycle.ts
21
- var glyph_cycle_exports = {};
22
- __export(glyph_cycle_exports, {
23
- default: () => glyph_cycle_default,
24
- glyphCycle: () => glyphCycle
25
- });
26
- module.exports = __toCommonJS(glyph_cycle_exports);
27
-
28
- // src/utils/stream-animation.ts
29
- var BUILTIN_PLUGINS = [
30
- {
31
- name: "typewriter",
32
- containerClass: "persona-stream-typewriter",
33
- wrap: "char",
34
- useCaret: true
35
- },
36
- {
37
- name: "pop-bubble",
38
- bubbleClass: "persona-stream-pop",
39
- wrap: "none"
40
- },
41
- {
42
- name: "letter-rise",
43
- containerClass: "persona-stream-letter-rise",
44
- wrap: "char"
45
- },
46
- {
47
- name: "word-fade",
48
- containerClass: "persona-stream-word-fade",
49
- wrap: "word"
50
- }
51
- ];
52
- var globalRegistry = /* @__PURE__ */ new Map();
53
- for (const plugin of BUILTIN_PLUGINS) globalRegistry.set(plugin.name, plugin);
54
- var registerStreamAnimationPlugin = (plugin) => {
55
- globalRegistry.set(plugin.name, plugin);
56
- };
57
-
58
- // src/animations/glyph-cycle.ts
59
- var STYLES = `
1
+ "use strict";var d=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var W=(e,t)=>{for(var n in t)d(e,n,{get:t[n],enumerable:!0})},R=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of P(t))!H.call(e,a)&&a!==n&&d(e,a,{get:()=>t[a],enumerable:!(r=L(t,a))||r.enumerable});return e};var N=e=>R(d({},"__esModule",{value:!0}),e);var J={};W(J,{default:()=>X,glyphCycle:()=>g});module.exports=N(J);var k=[{name:"typewriter",containerClass:"persona-stream-typewriter",wrap:"char",useCaret:!0},{name:"pop-bubble",bubbleClass:"persona-stream-pop",wrap:"none"},{name:"letter-rise",containerClass:"persona-stream-letter-rise",wrap:"char"},{name:"word-fade",containerClass:"persona-stream-word-fade",wrap:"word"}],f=new Map;for(let e of k)f.set(e.name,e);var A=e=>{f.set(e.name,e)};var D=`
60
2
  [data-persona-root] .persona-stream-glyph-cycle .persona-stream-char {
61
3
  animation: persona-stream-glyph-cycle-fade
62
4
  calc(var(--persona-stream-step, 120ms) * 1.5) ease-out both;
@@ -74,206 +16,4 @@ var STYLES = `
74
16
  opacity: 1 !important;
75
17
  }
76
18
  }
77
- `.trim();
78
- var GLYPHS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$%&@";
79
- var TICK_COUNT = 10;
80
- var BASE_TICK_MS = 120;
81
- var DEFAULT_STEP_MS = 120;
82
- var CROSS_FLICKER_PROBABILITY = 0.4;
83
- var BUFFER_THRESHOLD = 50;
84
- var getStepMs = (container) => {
85
- var _a;
86
- if (!container) return DEFAULT_STEP_MS;
87
- const raw = (_a = container.style.getPropertyValue("--persona-stream-step")) == null ? void 0 : _a.trim();
88
- const match = raw.match(/([\d.]+)\s*ms/);
89
- return match ? parseFloat(match[1]) : DEFAULT_STEP_MS;
90
- };
91
- var getTickMs = (span) => {
92
- const container = span.closest(".persona-stream-glyph-cycle");
93
- const step = getStepMs(container);
94
- return BASE_TICK_MS * step / DEFAULT_STEP_MS;
95
- };
96
- var randomGlyph = (avoid) => {
97
- let ch = GLYPHS[Math.floor(Math.random() * GLYPHS.length)];
98
- if (avoid && ch === avoid) {
99
- ch = GLYPHS[(GLYPHS.indexOf(ch) + 1) % GLYPHS.length];
100
- }
101
- return ch;
102
- };
103
- var flickerLaterSiblings = (span) => {
104
- const container = span.closest(".persona-stream-glyph-cycle");
105
- if (!container) return;
106
- const unsettled = container.querySelectorAll(
107
- ".persona-stream-char[data-glyph-cycle-final]"
108
- );
109
- let seenSelf = false;
110
- for (const other of Array.from(unsettled)) {
111
- if (other === span) {
112
- seenSelf = true;
113
- continue;
114
- }
115
- if (!seenSelf) continue;
116
- if (Math.random() < CROSS_FLICKER_PROBABILITY) {
117
- other.textContent = randomGlyph();
118
- }
119
- }
120
- };
121
- var containerNextSettleAt = /* @__PURE__ */ new WeakMap();
122
- var DEFAULT_ARRIVAL_MS = 25;
123
- var STAGGER_DRAIN_FACTOR = 0.5;
124
- var MIN_STAGGER_MS = 6;
125
- var EMA_ALPHA = 0.25;
126
- var containerLastScheduleAt = /* @__PURE__ */ new WeakMap();
127
- var containerArrivalMs = /* @__PURE__ */ new WeakMap();
128
- var observeStagger = (container, now) => {
129
- var _a;
130
- if (!container) return DEFAULT_ARRIVAL_MS * STAGGER_DRAIN_FACTOR;
131
- const last = containerLastScheduleAt.get(container);
132
- containerLastScheduleAt.set(container, now);
133
- let observed = (_a = containerArrivalMs.get(container)) != null ? _a : DEFAULT_ARRIVAL_MS;
134
- if (last !== void 0) {
135
- const interval = now - last;
136
- if (interval > 1) {
137
- observed = observed * (1 - EMA_ALPHA) + interval * EMA_ALPHA;
138
- containerArrivalMs.set(container, observed);
139
- }
140
- }
141
- return Math.max(MIN_STAGGER_MS, observed * STAGGER_DRAIN_FACTOR);
142
- };
143
- var activeCyclesByMessage = /* @__PURE__ */ new Map();
144
- var getMessageId = (span) => {
145
- var _a;
146
- const bubble = span.closest("[data-message-id]");
147
- return (_a = bubble == null ? void 0 : bubble.dataset.messageId) != null ? _a : null;
148
- };
149
- var incrementActive = (messageId) => {
150
- var _a;
151
- if (!messageId) return;
152
- activeCyclesByMessage.set(messageId, ((_a = activeCyclesByMessage.get(messageId)) != null ? _a : 0) + 1);
153
- };
154
- var decrementActive = (messageId) => {
155
- var _a;
156
- if (!messageId) return;
157
- const count = (_a = activeCyclesByMessage.get(messageId)) != null ? _a : 0;
158
- if (count <= 1) activeCyclesByMessage.delete(messageId);
159
- else activeCyclesByMessage.set(messageId, count - 1);
160
- };
161
- var scheduleCycle = (span) => {
162
- var _a;
163
- if (span.dataset.glyphCycleScheduled === "true") return;
164
- const finalChar = (_a = span.textContent) != null ? _a : "";
165
- if (!finalChar || /\s/.test(finalChar)) return;
166
- span.dataset.glyphCycleScheduled = "true";
167
- span.dataset.glyphCycleFinal = finalChar;
168
- span.setAttribute("data-preserve-runtime", "stream-glyph-cycle");
169
- span.textContent = randomGlyph();
170
- const messageId = getMessageId(span);
171
- if (messageId) span.dataset.glyphCycleMessageId = messageId;
172
- incrementActive(messageId);
173
- const container = span.closest(".persona-stream-glyph-cycle");
174
- const now = Date.now();
175
- const staggerMs = observeStagger(container, now);
176
- const tickMs = getTickMs(span);
177
- const baseDurationMs = TICK_COUNT * tickMs;
178
- let settleAt = now + baseDurationMs;
179
- if (container) {
180
- const prev = containerNextSettleAt.get(container);
181
- if (prev !== void 0) settleAt = Math.max(settleAt, prev);
182
- containerNextSettleAt.set(container, settleAt + staggerMs);
183
- }
184
- startCycle(span, finalChar, settleAt);
185
- };
186
- var startCycle = (span, finalChar, settleAt) => {
187
- var _a;
188
- if (span.dataset.glyphCycleStarted === "true") return;
189
- span.dataset.glyphCycleStarted = "true";
190
- const tickMs = getTickMs(span);
191
- let lastGlyph = (_a = span.textContent) != null ? _a : void 0;
192
- const step = () => {
193
- var _a2;
194
- if (!span.isConnected) return;
195
- if (Date.now() >= settleAt) {
196
- span.textContent = finalChar;
197
- span.removeAttribute("data-preserve-runtime");
198
- delete span.dataset.glyphCycleStarted;
199
- delete span.dataset.glyphCycleFinal;
200
- decrementActive((_a2 = span.dataset.glyphCycleMessageId) != null ? _a2 : null);
201
- delete span.dataset.glyphCycleMessageId;
202
- return;
203
- }
204
- const glyph = randomGlyph(lastGlyph);
205
- span.textContent = glyph;
206
- lastGlyph = glyph;
207
- flickerLaterSiblings(span);
208
- setTimeout(step, tickMs);
209
- };
210
- setTimeout(step, tickMs);
211
- };
212
- var processCharSpans = (root) => {
213
- var _a;
214
- const spans = (_a = root.querySelectorAll) == null ? void 0 : _a.call(
215
- root,
216
- ".persona-stream-glyph-cycle .persona-stream-char:not([data-glyph-cycle-scheduled])"
217
- );
218
- if (!spans) return;
219
- for (const span of Array.from(spans)) {
220
- scheduleCycle(span);
221
- }
222
- };
223
- var isElement = (node) => node.nodeType === 1;
224
- var glyphCycle = {
225
- name: "glyph-cycle",
226
- containerClass: "persona-stream-glyph-cycle",
227
- wrap: "char",
228
- // Narrow the default skip list so inline `<code>` and fenced `<pre>`
229
- // code blocks both render as cycling glyphs along with everything else.
230
- // Links stay clickable; <script>/<style> stay untouched.
231
- skipTags: ["a", "script", "style"],
232
- styles: STYLES,
233
- bufferContent(content) {
234
- if (content.length < BUFFER_THRESHOLD) return "";
235
- let boldPairs = 0;
236
- let lastSafe = -1;
237
- let i = 0;
238
- while (i < content.length) {
239
- if (content[i] === "*" && content[i + 1] === "*") {
240
- boldPairs += 1;
241
- i += 2;
242
- continue;
243
- }
244
- if (/\s/.test(content[i]) && boldPairs % 2 === 0) {
245
- lastSafe = i;
246
- }
247
- i += 1;
248
- }
249
- if (lastSafe < 0) return "";
250
- return content.slice(0, lastSafe);
251
- },
252
- isAnimating(message) {
253
- var _a;
254
- return ((_a = activeCyclesByMessage.get(message.id)) != null ? _a : 0) > 0;
255
- },
256
- onAttach(root) {
257
- processCharSpans(root);
258
- const observer = new MutationObserver((mutations) => {
259
- for (const mutation of mutations) {
260
- for (const node of Array.from(mutation.addedNodes)) {
261
- if (!isElement(node)) continue;
262
- if (node.classList.contains("persona-stream-char") && node.closest(".persona-stream-glyph-cycle")) {
263
- scheduleCycle(node);
264
- } else {
265
- processCharSpans(node);
266
- }
267
- }
268
- }
269
- });
270
- observer.observe(root, { childList: true, subtree: true });
271
- return () => observer.disconnect();
272
- }
273
- };
274
- registerStreamAnimationPlugin(glyphCycle);
275
- var glyph_cycle_default = glyphCycle;
276
- // Annotate the CommonJS export names for ESM import in node:
277
- 0 && (module.exports = {
278
- glyphCycle
279
- });
19
+ `.trim(),l="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$%&@",_=10,I=120,u=120,F=.4,B=50,O=e=>{var r;if(!e)return u;let n=((r=e.style.getPropertyValue("--persona-stream-step"))==null?void 0:r.trim()).match(/([\d.]+)\s*ms/);return n?parseFloat(n[1]):u},x=e=>{let t=e.closest(".persona-stream-glyph-cycle"),n=O(t);return I*n/u},p=e=>{let t=l[Math.floor(Math.random()*l.length)];return e&&t===e&&(t=l[(l.indexOf(t)+1)%l.length]),t},G=e=>{let t=e.closest(".persona-stream-glyph-cycle");if(!t)return;let n=t.querySelectorAll(".persona-stream-char[data-glyph-cycle-final]"),r=!1;for(let a of Array.from(n)){if(a===e){r=!0;continue}r&&Math.random()<F&&(a.textContent=p())}},S=new WeakMap,T=25,C=.5,U=6,M=.25,w=new WeakMap,E=new WeakMap,$=(e,t)=>{var a;if(!e)return T*C;let n=w.get(e);w.set(e,t);let r=(a=E.get(e))!=null?a:T;if(n!==void 0){let s=t-n;s>1&&(r=r*(1-M)+s*M,E.set(e,r))}return Math.max(U,r*C)},i=new Map,j=e=>{var n;let t=e.closest("[data-message-id]");return(n=t==null?void 0:t.dataset.messageId)!=null?n:null},K=e=>{var t;e&&i.set(e,((t=i.get(e))!=null?t:0)+1)},V=e=>{var n;if(!e)return;let t=(n=i.get(e))!=null?n:0;t<=1?i.delete(e):i.set(e,t-1)},v=e=>{var h;if(e.dataset.glyphCycleScheduled==="true")return;let t=(h=e.textContent)!=null?h:"";if(!t||/\s/.test(t))return;e.dataset.glyphCycleScheduled="true",e.dataset.glyphCycleFinal=t,e.setAttribute("data-preserve-runtime","stream-glyph-cycle"),e.textContent=p();let n=j(e);n&&(e.dataset.glyphCycleMessageId=n),K(n);let r=e.closest(".persona-stream-glyph-cycle"),a=Date.now(),s=$(r,a),c=x(e),m=_*c,o=a+m;if(r){let y=S.get(r);y!==void 0&&(o=Math.max(o,y)),S.set(r,o+s)}Y(e,t,o)},Y=(e,t,n)=>{var c;if(e.dataset.glyphCycleStarted==="true")return;e.dataset.glyphCycleStarted="true";let r=x(e),a=(c=e.textContent)!=null?c:void 0,s=()=>{var o;if(!e.isConnected)return;if(Date.now()>=n){e.textContent=t,e.removeAttribute("data-preserve-runtime"),delete e.dataset.glyphCycleStarted,delete e.dataset.glyphCycleFinal,V((o=e.dataset.glyphCycleMessageId)!=null?o:null),delete e.dataset.glyphCycleMessageId;return}let m=p(a);e.textContent=m,a=m,G(e),setTimeout(s,r)};setTimeout(s,r)},b=e=>{var n;let t=(n=e.querySelectorAll)==null?void 0:n.call(e,".persona-stream-glyph-cycle .persona-stream-char:not([data-glyph-cycle-scheduled])");if(t)for(let r of Array.from(t))v(r)},q=e=>e.nodeType===1,g={name:"glyph-cycle",containerClass:"persona-stream-glyph-cycle",wrap:"char",skipTags:["a","script","style"],styles:D,bufferContent(e){if(e.length<B)return"";let t=0,n=-1,r=0;for(;r<e.length;){if(e[r]==="*"&&e[r+1]==="*"){t+=1,r+=2;continue}/\s/.test(e[r])&&t%2===0&&(n=r),r+=1}return n<0?"":e.slice(0,n)},isAnimating(e){var t;return((t=i.get(e.id))!=null?t:0)>0},onAttach(e){b(e);let t=new MutationObserver(n=>{for(let r of n)for(let a of Array.from(r.addedNodes))q(a)&&(a.classList.contains("persona-stream-char")&&a.closest(".persona-stream-glyph-cycle")?v(a):b(a))});return t.observe(e,{childList:!0,subtree:!0}),()=>t.disconnect()}};A(g);var X=g;0&&(module.exports={glyphCycle});
@@ -1,4 +1,4 @@
1
- import { S as StreamAnimationPlugin } from './types-CWPIj66R.cjs';
1
+ import { S as StreamAnimationPlugin } from './types-BZVr1YOV.cjs';
2
2
 
3
3
  declare const glyphCycle: StreamAnimationPlugin;
4
4
 
@@ -1,4 +1,4 @@
1
- import { S as StreamAnimationPlugin } from './types-CWPIj66R.js';
1
+ import { S as StreamAnimationPlugin } from './types-BZVr1YOV.js';
2
2
 
3
3
  declare const glyphCycle: StreamAnimationPlugin;
4
4
 
@@ -1,35 +1,4 @@
1
- // src/utils/stream-animation.ts
2
- var BUILTIN_PLUGINS = [
3
- {
4
- name: "typewriter",
5
- containerClass: "persona-stream-typewriter",
6
- wrap: "char",
7
- useCaret: true
8
- },
9
- {
10
- name: "pop-bubble",
11
- bubbleClass: "persona-stream-pop",
12
- wrap: "none"
13
- },
14
- {
15
- name: "letter-rise",
16
- containerClass: "persona-stream-letter-rise",
17
- wrap: "char"
18
- },
19
- {
20
- name: "word-fade",
21
- containerClass: "persona-stream-word-fade",
22
- wrap: "word"
23
- }
24
- ];
25
- var globalRegistry = /* @__PURE__ */ new Map();
26
- for (const plugin of BUILTIN_PLUGINS) globalRegistry.set(plugin.name, plugin);
27
- var registerStreamAnimationPlugin = (plugin) => {
28
- globalRegistry.set(plugin.name, plugin);
29
- };
30
-
31
- // src/animations/glyph-cycle.ts
32
- var STYLES = `
1
+ var v=[{name:"typewriter",containerClass:"persona-stream-typewriter",wrap:"char",useCaret:!0},{name:"pop-bubble",bubbleClass:"persona-stream-pop",wrap:"none"},{name:"letter-rise",containerClass:"persona-stream-letter-rise",wrap:"char"},{name:"word-fade",containerClass:"persona-stream-word-fade",wrap:"word"}],h=new Map;for(let e of v)h.set(e.name,e);var y=e=>{h.set(e.name,e)};var L=`
33
2
  [data-persona-root] .persona-stream-glyph-cycle .persona-stream-char {
34
3
  animation: persona-stream-glyph-cycle-fade
35
4
  calc(var(--persona-stream-step, 120ms) * 1.5) ease-out both;
@@ -47,206 +16,4 @@ var STYLES = `
47
16
  opacity: 1 !important;
48
17
  }
49
18
  }
50
- `.trim();
51
- var GLYPHS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$%&@";
52
- var TICK_COUNT = 10;
53
- var BASE_TICK_MS = 120;
54
- var DEFAULT_STEP_MS = 120;
55
- var CROSS_FLICKER_PROBABILITY = 0.4;
56
- var BUFFER_THRESHOLD = 50;
57
- var getStepMs = (container) => {
58
- var _a;
59
- if (!container) return DEFAULT_STEP_MS;
60
- const raw = (_a = container.style.getPropertyValue("--persona-stream-step")) == null ? void 0 : _a.trim();
61
- const match = raw.match(/([\d.]+)\s*ms/);
62
- return match ? parseFloat(match[1]) : DEFAULT_STEP_MS;
63
- };
64
- var getTickMs = (span) => {
65
- const container = span.closest(".persona-stream-glyph-cycle");
66
- const step = getStepMs(container);
67
- return BASE_TICK_MS * step / DEFAULT_STEP_MS;
68
- };
69
- var randomGlyph = (avoid) => {
70
- let ch = GLYPHS[Math.floor(Math.random() * GLYPHS.length)];
71
- if (avoid && ch === avoid) {
72
- ch = GLYPHS[(GLYPHS.indexOf(ch) + 1) % GLYPHS.length];
73
- }
74
- return ch;
75
- };
76
- var flickerLaterSiblings = (span) => {
77
- const container = span.closest(".persona-stream-glyph-cycle");
78
- if (!container) return;
79
- const unsettled = container.querySelectorAll(
80
- ".persona-stream-char[data-glyph-cycle-final]"
81
- );
82
- let seenSelf = false;
83
- for (const other of Array.from(unsettled)) {
84
- if (other === span) {
85
- seenSelf = true;
86
- continue;
87
- }
88
- if (!seenSelf) continue;
89
- if (Math.random() < CROSS_FLICKER_PROBABILITY) {
90
- other.textContent = randomGlyph();
91
- }
92
- }
93
- };
94
- var containerNextSettleAt = /* @__PURE__ */ new WeakMap();
95
- var DEFAULT_ARRIVAL_MS = 25;
96
- var STAGGER_DRAIN_FACTOR = 0.5;
97
- var MIN_STAGGER_MS = 6;
98
- var EMA_ALPHA = 0.25;
99
- var containerLastScheduleAt = /* @__PURE__ */ new WeakMap();
100
- var containerArrivalMs = /* @__PURE__ */ new WeakMap();
101
- var observeStagger = (container, now) => {
102
- var _a;
103
- if (!container) return DEFAULT_ARRIVAL_MS * STAGGER_DRAIN_FACTOR;
104
- const last = containerLastScheduleAt.get(container);
105
- containerLastScheduleAt.set(container, now);
106
- let observed = (_a = containerArrivalMs.get(container)) != null ? _a : DEFAULT_ARRIVAL_MS;
107
- if (last !== void 0) {
108
- const interval = now - last;
109
- if (interval > 1) {
110
- observed = observed * (1 - EMA_ALPHA) + interval * EMA_ALPHA;
111
- containerArrivalMs.set(container, observed);
112
- }
113
- }
114
- return Math.max(MIN_STAGGER_MS, observed * STAGGER_DRAIN_FACTOR);
115
- };
116
- var activeCyclesByMessage = /* @__PURE__ */ new Map();
117
- var getMessageId = (span) => {
118
- var _a;
119
- const bubble = span.closest("[data-message-id]");
120
- return (_a = bubble == null ? void 0 : bubble.dataset.messageId) != null ? _a : null;
121
- };
122
- var incrementActive = (messageId) => {
123
- var _a;
124
- if (!messageId) return;
125
- activeCyclesByMessage.set(messageId, ((_a = activeCyclesByMessage.get(messageId)) != null ? _a : 0) + 1);
126
- };
127
- var decrementActive = (messageId) => {
128
- var _a;
129
- if (!messageId) return;
130
- const count = (_a = activeCyclesByMessage.get(messageId)) != null ? _a : 0;
131
- if (count <= 1) activeCyclesByMessage.delete(messageId);
132
- else activeCyclesByMessage.set(messageId, count - 1);
133
- };
134
- var scheduleCycle = (span) => {
135
- var _a;
136
- if (span.dataset.glyphCycleScheduled === "true") return;
137
- const finalChar = (_a = span.textContent) != null ? _a : "";
138
- if (!finalChar || /\s/.test(finalChar)) return;
139
- span.dataset.glyphCycleScheduled = "true";
140
- span.dataset.glyphCycleFinal = finalChar;
141
- span.setAttribute("data-preserve-runtime", "stream-glyph-cycle");
142
- span.textContent = randomGlyph();
143
- const messageId = getMessageId(span);
144
- if (messageId) span.dataset.glyphCycleMessageId = messageId;
145
- incrementActive(messageId);
146
- const container = span.closest(".persona-stream-glyph-cycle");
147
- const now = Date.now();
148
- const staggerMs = observeStagger(container, now);
149
- const tickMs = getTickMs(span);
150
- const baseDurationMs = TICK_COUNT * tickMs;
151
- let settleAt = now + baseDurationMs;
152
- if (container) {
153
- const prev = containerNextSettleAt.get(container);
154
- if (prev !== void 0) settleAt = Math.max(settleAt, prev);
155
- containerNextSettleAt.set(container, settleAt + staggerMs);
156
- }
157
- startCycle(span, finalChar, settleAt);
158
- };
159
- var startCycle = (span, finalChar, settleAt) => {
160
- var _a;
161
- if (span.dataset.glyphCycleStarted === "true") return;
162
- span.dataset.glyphCycleStarted = "true";
163
- const tickMs = getTickMs(span);
164
- let lastGlyph = (_a = span.textContent) != null ? _a : void 0;
165
- const step = () => {
166
- var _a2;
167
- if (!span.isConnected) return;
168
- if (Date.now() >= settleAt) {
169
- span.textContent = finalChar;
170
- span.removeAttribute("data-preserve-runtime");
171
- delete span.dataset.glyphCycleStarted;
172
- delete span.dataset.glyphCycleFinal;
173
- decrementActive((_a2 = span.dataset.glyphCycleMessageId) != null ? _a2 : null);
174
- delete span.dataset.glyphCycleMessageId;
175
- return;
176
- }
177
- const glyph = randomGlyph(lastGlyph);
178
- span.textContent = glyph;
179
- lastGlyph = glyph;
180
- flickerLaterSiblings(span);
181
- setTimeout(step, tickMs);
182
- };
183
- setTimeout(step, tickMs);
184
- };
185
- var processCharSpans = (root) => {
186
- var _a;
187
- const spans = (_a = root.querySelectorAll) == null ? void 0 : _a.call(
188
- root,
189
- ".persona-stream-glyph-cycle .persona-stream-char:not([data-glyph-cycle-scheduled])"
190
- );
191
- if (!spans) return;
192
- for (const span of Array.from(spans)) {
193
- scheduleCycle(span);
194
- }
195
- };
196
- var isElement = (node) => node.nodeType === 1;
197
- var glyphCycle = {
198
- name: "glyph-cycle",
199
- containerClass: "persona-stream-glyph-cycle",
200
- wrap: "char",
201
- // Narrow the default skip list so inline `<code>` and fenced `<pre>`
202
- // code blocks both render as cycling glyphs along with everything else.
203
- // Links stay clickable; <script>/<style> stay untouched.
204
- skipTags: ["a", "script", "style"],
205
- styles: STYLES,
206
- bufferContent(content) {
207
- if (content.length < BUFFER_THRESHOLD) return "";
208
- let boldPairs = 0;
209
- let lastSafe = -1;
210
- let i = 0;
211
- while (i < content.length) {
212
- if (content[i] === "*" && content[i + 1] === "*") {
213
- boldPairs += 1;
214
- i += 2;
215
- continue;
216
- }
217
- if (/\s/.test(content[i]) && boldPairs % 2 === 0) {
218
- lastSafe = i;
219
- }
220
- i += 1;
221
- }
222
- if (lastSafe < 0) return "";
223
- return content.slice(0, lastSafe);
224
- },
225
- isAnimating(message) {
226
- var _a;
227
- return ((_a = activeCyclesByMessage.get(message.id)) != null ? _a : 0) > 0;
228
- },
229
- onAttach(root) {
230
- processCharSpans(root);
231
- const observer = new MutationObserver((mutations) => {
232
- for (const mutation of mutations) {
233
- for (const node of Array.from(mutation.addedNodes)) {
234
- if (!isElement(node)) continue;
235
- if (node.classList.contains("persona-stream-char") && node.closest(".persona-stream-glyph-cycle")) {
236
- scheduleCycle(node);
237
- } else {
238
- processCharSpans(node);
239
- }
240
- }
241
- }
242
- });
243
- observer.observe(root, { childList: true, subtree: true });
244
- return () => observer.disconnect();
245
- }
246
- };
247
- registerStreamAnimationPlugin(glyphCycle);
248
- var glyph_cycle_default = glyphCycle;
249
- export {
250
- glyph_cycle_default as default,
251
- glyphCycle
252
- };
19
+ `.trim(),l="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$%&@",P=10,H=120,d=120,W=.4,R=50,N=e=>{var r;if(!e)return d;let n=((r=e.style.getPropertyValue("--persona-stream-step"))==null?void 0:r.trim()).match(/([\d.]+)\s*ms/);return n?parseFloat(n[1]):d},E=e=>{let t=e.closest(".persona-stream-glyph-cycle"),n=N(t);return H*n/d},u=e=>{let t=l[Math.floor(Math.random()*l.length)];return e&&t===e&&(t=l[(l.indexOf(t)+1)%l.length]),t},k=e=>{let t=e.closest(".persona-stream-glyph-cycle");if(!t)return;let n=t.querySelectorAll(".persona-stream-char[data-glyph-cycle-final]"),r=!1;for(let a of Array.from(n)){if(a===e){r=!0;continue}r&&Math.random()<W&&(a.textContent=u())}},f=new WeakMap,A=25,S=.5,D=6,T=.25,C=new WeakMap,M=new WeakMap,_=(e,t)=>{var a;if(!e)return A*S;let n=C.get(e);C.set(e,t);let r=(a=M.get(e))!=null?a:A;if(n!==void 0){let s=t-n;s>1&&(r=r*(1-T)+s*T,M.set(e,r))}return Math.max(D,r*S)},i=new Map,I=e=>{var n;let t=e.closest("[data-message-id]");return(n=t==null?void 0:t.dataset.messageId)!=null?n:null},F=e=>{var t;e&&i.set(e,((t=i.get(e))!=null?t:0)+1)},B=e=>{var n;if(!e)return;let t=(n=i.get(e))!=null?n:0;t<=1?i.delete(e):i.set(e,t-1)},b=e=>{var p;if(e.dataset.glyphCycleScheduled==="true")return;let t=(p=e.textContent)!=null?p:"";if(!t||/\s/.test(t))return;e.dataset.glyphCycleScheduled="true",e.dataset.glyphCycleFinal=t,e.setAttribute("data-preserve-runtime","stream-glyph-cycle"),e.textContent=u();let n=I(e);n&&(e.dataset.glyphCycleMessageId=n),F(n);let r=e.closest(".persona-stream-glyph-cycle"),a=Date.now(),s=_(r,a),c=E(e),m=P*c,o=a+m;if(r){let g=f.get(r);g!==void 0&&(o=Math.max(o,g)),f.set(r,o+s)}O(e,t,o)},O=(e,t,n)=>{var c;if(e.dataset.glyphCycleStarted==="true")return;e.dataset.glyphCycleStarted="true";let r=E(e),a=(c=e.textContent)!=null?c:void 0,s=()=>{var o;if(!e.isConnected)return;if(Date.now()>=n){e.textContent=t,e.removeAttribute("data-preserve-runtime"),delete e.dataset.glyphCycleStarted,delete e.dataset.glyphCycleFinal,B((o=e.dataset.glyphCycleMessageId)!=null?o:null),delete e.dataset.glyphCycleMessageId;return}let m=u(a);e.textContent=m,a=m,k(e),setTimeout(s,r)};setTimeout(s,r)},w=e=>{var n;let t=(n=e.querySelectorAll)==null?void 0:n.call(e,".persona-stream-glyph-cycle .persona-stream-char:not([data-glyph-cycle-scheduled])");if(t)for(let r of Array.from(t))b(r)},G=e=>e.nodeType===1,x={name:"glyph-cycle",containerClass:"persona-stream-glyph-cycle",wrap:"char",skipTags:["a","script","style"],styles:L,bufferContent(e){if(e.length<R)return"";let t=0,n=-1,r=0;for(;r<e.length;){if(e[r]==="*"&&e[r+1]==="*"){t+=1,r+=2;continue}/\s/.test(e[r])&&t%2===0&&(n=r),r+=1}return n<0?"":e.slice(0,n)},isAnimating(e){var t;return((t=i.get(e.id))!=null?t:0)>0},onAttach(e){w(e);let t=new MutationObserver(n=>{for(let r of n)for(let a of Array.from(r.addedNodes))G(a)&&(a.classList.contains("persona-stream-char")&&a.closest(".persona-stream-glyph-cycle")?b(a):w(a))});return t.observe(e,{childList:!0,subtree:!0}),()=>t.disconnect()}};y(x);var j=x;export{j as default,x as glyphCycle};
@@ -73,6 +73,16 @@ type AgentMessageMetadata = {
73
73
  * `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
74
74
  */
75
75
  awaitingLocalTool?: boolean;
76
+ /**
77
+ * The provider per-call id (`toolu_…`) carried on the `step_await` /
78
+ * `flow_await` events for a LOCAL tool (core#3878). Present only when the
79
+ * server emits it. Two PARALLEL calls to the same tool in one turn share a
80
+ * `toolName` (and a collapsed `toolId`) but get DISTINCT `webMcpToolCallId`s,
81
+ * so this is the key the widget batches a single `/resume` on — preferred
82
+ * over tool name, which collides for same-tool parallel calls. Absent →
83
+ * fall back to the legacy name-keyed resume contract.
84
+ */
85
+ webMcpToolCallId?: string;
76
86
  /**
77
87
  * Set to `true` once the user has picked / typed / dismissed an answer for
78
88
  * an `ask_user_question` tool call, so renderers stop re-mounting the
@@ -73,6 +73,16 @@ type AgentMessageMetadata = {
73
73
  * `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
74
74
  */
75
75
  awaitingLocalTool?: boolean;
76
+ /**
77
+ * The provider per-call id (`toolu_…`) carried on the `step_await` /
78
+ * `flow_await` events for a LOCAL tool (core#3878). Present only when the
79
+ * server emits it. Two PARALLEL calls to the same tool in one turn share a
80
+ * `toolName` (and a collapsed `toolId`) but get DISTINCT `webMcpToolCallId`s,
81
+ * so this is the key the widget batches a single `/resume` on — preferred
82
+ * over tool name, which collides for same-tool parallel calls. Absent →
83
+ * fall back to the legacy name-keyed resume contract.
84
+ */
85
+ webMcpToolCallId?: string;
76
86
  /**
77
87
  * Set to `true` once the user has picked / typed / dismissed an answer for
78
88
  * an `ask_user_question` tool call, so renderers stop re-mounting the