@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
@@ -0,0 +1,427 @@
1
+ // ============================================================================
2
+ // Output Throughput Tracker
3
+ // ============================================================================
4
+ //
5
+ // Derives an output tokens-per-second metric from the widget's existing SSE
6
+ // event stream, for display in the Events diagnostics screen. This is a passive
7
+ // consumer: it never mutates dispatch payloads, never forces debug mode, and
8
+ // never changes the wire contract — it only inspects the `(type, payload)`
9
+ // events that already flow through the SSE tap.
10
+ //
11
+ // Throughput is estimated live from visible text deltas while a run streams,
12
+ // then prefers exact provider usage (output tokens) when terminal events carry
13
+ // it. A run starts when the stream starts (or lazily on the first visible
14
+ // delta), stays "running" across intermediate step/turn completions, and only
15
+ // finalizes on terminal `flow_complete` / `agent_complete`. Stream errors mark
16
+ // the metric unavailable rather than leaving it stuck "running".
17
+
18
+ export type ThroughputMetricStatus = "idle" | "running" | "complete" | "error";
19
+
20
+ export type ThroughputMetricSource = "usage" | "estimate";
21
+
22
+ export interface ThroughputMetric {
23
+ status: ThroughputMetricStatus;
24
+ /** Output tokens per second, when computable. */
25
+ tokensPerSecond?: number;
26
+ /** Output tokens counted/estimated for the run. */
27
+ outputTokens?: number;
28
+ /** Duration window the rate was computed over (ms). */
29
+ durationMs?: number;
30
+ /** Whether `outputTokens` came from provider usage or a text estimate. */
31
+ source?: ThroughputMetricSource;
32
+ }
33
+
34
+ interface ThroughputRunStats {
35
+ startedAt: number;
36
+ firstDeltaAt?: number;
37
+ /**
38
+ * Running character count of accumulated visible text, estimated via the
39
+ * ~4 chars/token heuristic. Tracked as a counter (not the concatenated
40
+ * string) so the estimate stays O(1) per delta over a long stream.
41
+ */
42
+ visibleCharCount: number;
43
+ exactOutputTokens: number;
44
+ }
45
+
46
+ // Below this streamed window we don't trust the rate; fall back to provider
47
+ // execution time or whole-request duration instead.
48
+ const THROUGHPUT_MIN_DURATION_MS = 250;
49
+
50
+ // Request-level lifecycle events: each marks the beginning of a NEW request.
51
+ // The SSE tap fires for every payload type regardless of whether the client has
52
+ // a handler for it, so any of these that the server emits starts the run with
53
+ // an accurate `startedAt` (capturing time-to-first-token). These RESET any run
54
+ // already in progress, so a prior stream that ended without a terminal/error
55
+ // frame (e.g. `session.cancel()`) doesn't bleed its tokens into the next one.
56
+ const REQUEST_START_EVENTS = new Set([
57
+ "flow_start",
58
+ "flow_run_start",
59
+ "agent_start",
60
+ "dispatch_start",
61
+ "run_start",
62
+ ]);
63
+
64
+ // Per-step markers that fire repeatedly WITHIN a single request (a flow emits
65
+ // one per step). These only lazily begin a run — they must never reset, or a
66
+ // multi-step response would restart the metric between steps. If no request- or
67
+ // step-start event is emitted, the first visible delta lazily starts the run.
68
+ const STEP_START_EVENTS = new Set(["step_start", "execution_start"]);
69
+
70
+ const VISIBLE_DELTA_EVENTS = new Set([
71
+ "step_delta",
72
+ "step_chunk",
73
+ "chunk",
74
+ "agent_turn_delta",
75
+ ]);
76
+
77
+ const INTERMEDIATE_COMPLETE_EVENTS = new Set([
78
+ "step_complete",
79
+ "agent_turn_complete",
80
+ ]);
81
+
82
+ const TERMINAL_COMPLETE_EVENTS = new Set(["flow_complete", "agent_complete"]);
83
+
84
+ const ERROR_EVENTS = new Set([
85
+ "step_error",
86
+ "flow_error",
87
+ "agent_error",
88
+ "dispatch_error",
89
+ "error",
90
+ ]);
91
+
92
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
93
+ typeof value === "object" && value !== null && !Array.isArray(value);
94
+
95
+ const toFiniteNumber = (value: unknown): number | undefined =>
96
+ typeof value === "number" && Number.isFinite(value) ? value : undefined;
97
+
98
+ const getRecord = (
99
+ value: Record<string, unknown>,
100
+ key: string
101
+ ): Record<string, unknown> | undefined => {
102
+ const nested = value[key];
103
+ return isRecord(nested) ? nested : undefined;
104
+ };
105
+
106
+ /** Token estimate from a character count: ~4 chars/token, floor of 1. */
107
+ function estimateTokensFromCharCount(charCount: number): number {
108
+ return charCount > 0 ? Math.max(1, Math.ceil(charCount / 4)) : 0;
109
+ }
110
+
111
+ /** Simple token estimate matching the dashboard heuristic: ~4 chars/token. */
112
+ export function estimateOutputTokens(text: string): number {
113
+ return estimateTokensFromCharCount(text.trim().length);
114
+ }
115
+
116
+ function calculateTokensPerSecond(
117
+ outputTokens: number,
118
+ durationMs: number | undefined
119
+ ): number | undefined {
120
+ if (
121
+ outputTokens <= 0 ||
122
+ durationMs === undefined ||
123
+ durationMs < THROUGHPUT_MIN_DURATION_MS
124
+ ) {
125
+ return undefined;
126
+ }
127
+ return outputTokens / (durationMs / 1000);
128
+ }
129
+
130
+ function resolveEventType(
131
+ eventType: string,
132
+ payload: Record<string, unknown>
133
+ ): string {
134
+ return typeof payload.type === "string" ? payload.type : eventType;
135
+ }
136
+
137
+ function getTextDelta(payload: Record<string, unknown>): string {
138
+ if (typeof payload.text === "string") return payload.text;
139
+ if (typeof payload.delta === "string") return payload.delta;
140
+ if (typeof payload.content === "string") return payload.content;
141
+ if (typeof payload.chunk === "string") return payload.chunk;
142
+ return "";
143
+ }
144
+
145
+ /**
146
+ * Only count visible model output.
147
+ *
148
+ * For `agent_turn_delta`, count only a contentType of exactly `text` as
149
+ * visible, matching the client renderer (which appends streaming assistant
150
+ * text only when `contentType === "text"`); `thinking`, `tool_input`, any
151
+ * other value, and a missing contentType are ignored so throughput never
152
+ * includes deltas the chat UI doesn't render.
153
+ *
154
+ * For `step_delta` / `step_chunk`, skip tool and context steps — those carry
155
+ * tool I/O, not model-visible text — mirroring the widget's own renderer.
156
+ */
157
+ function isVisibleTextDelta(
158
+ type: string,
159
+ payload: Record<string, unknown>
160
+ ): boolean {
161
+ if (type === "step_delta" || type === "step_chunk") {
162
+ return payload.stepType !== "tool" && payload.executionType !== "context";
163
+ }
164
+
165
+ if (type !== "agent_turn_delta") return true;
166
+
167
+ const contentType =
168
+ typeof payload.contentType === "string"
169
+ ? payload.contentType
170
+ : typeof payload.content_type === "string"
171
+ ? payload.content_type
172
+ : undefined;
173
+
174
+ return contentType === "text";
175
+ }
176
+
177
+ /** Extract exact output tokens from a variety of usage payload shapes. */
178
+ function getOutputTokens(payload: Record<string, unknown>): number | undefined {
179
+ const result = getRecord(payload, "result");
180
+ const candidates = [
181
+ getRecord(payload, "tokens"),
182
+ getRecord(payload, "totalTokens"),
183
+ result ? getRecord(result, "tokens") : undefined,
184
+ getRecord(payload, "usage"),
185
+ result ? getRecord(result, "usage") : undefined,
186
+ ];
187
+
188
+ for (const candidate of candidates) {
189
+ if (!candidate) continue;
190
+ const outputTokens =
191
+ toFiniteNumber(candidate.output) ??
192
+ toFiniteNumber(candidate.outputTokens) ??
193
+ toFiniteNumber(candidate.completionTokens);
194
+ if (outputTokens !== undefined) return outputTokens;
195
+ }
196
+
197
+ return (
198
+ toFiniteNumber(payload.outputTokens) ??
199
+ toFiniteNumber(payload.completionTokens) ??
200
+ (result
201
+ ? (toFiniteNumber(result.outputTokens) ??
202
+ toFiniteNumber(result.completionTokens))
203
+ : undefined)
204
+ );
205
+ }
206
+
207
+ /** Extract provider execution time (ms) from a variety of payload shapes. */
208
+ function getExecutionTimeMs(
209
+ payload: Record<string, unknown>
210
+ ): number | undefined {
211
+ const result = getRecord(payload, "result");
212
+ return (
213
+ toFiniteNumber(payload.executionTime) ??
214
+ toFiniteNumber(payload.executionTimeMs) ??
215
+ toFiniteNumber(payload.execution_time) ??
216
+ toFiniteNumber(payload.duration) ??
217
+ (result
218
+ ? (toFiniteNumber(result.executionTime) ??
219
+ toFiniteNumber(result.executionTimeMs))
220
+ : undefined)
221
+ );
222
+ }
223
+
224
+ function defaultClock(): number {
225
+ if (
226
+ typeof performance !== "undefined" &&
227
+ typeof performance.now === "function"
228
+ ) {
229
+ return performance.now();
230
+ }
231
+ return Date.now();
232
+ }
233
+
234
+ /**
235
+ * Tracks output throughput across one streamed run at a time. Feed it every SSE
236
+ * event via {@link processEvent}; read the current state via {@link getMetric}.
237
+ */
238
+ export class ThroughputTracker {
239
+ private metric: ThroughputMetric = { status: "idle" };
240
+ private run: ThroughputRunStats | null = null;
241
+ private readonly now: () => number;
242
+
243
+ constructor(now: () => number = defaultClock) {
244
+ this.now = now;
245
+ }
246
+
247
+ getMetric(): ThroughputMetric {
248
+ // While a run is streaming, recompute the elapsed window (and rate) from the
249
+ // clock on each read. The view polls this every ~200ms, so without this a
250
+ // pause between deltas would keep showing the stale rate from the last
251
+ // event; recomputing lets the displayed tok/s decay as time passes.
252
+ const run = this.run;
253
+ if (
254
+ run &&
255
+ this.metric.status === "running" &&
256
+ run.firstDeltaAt !== undefined &&
257
+ this.metric.outputTokens !== undefined
258
+ ) {
259
+ const durationMs = this.now() - run.firstDeltaAt;
260
+ return {
261
+ ...this.metric,
262
+ durationMs,
263
+ tokensPerSecond: calculateTokensPerSecond(
264
+ this.metric.outputTokens,
265
+ durationMs
266
+ ),
267
+ };
268
+ }
269
+ return this.metric;
270
+ }
271
+
272
+ /** Reset back to idle (e.g. when the chat is cleared). */
273
+ reset(): void {
274
+ this.run = null;
275
+ this.metric = { status: "idle" };
276
+ }
277
+
278
+ private startRun(now: number): void {
279
+ this.run = {
280
+ startedAt: now,
281
+ visibleCharCount: 0,
282
+ exactOutputTokens: 0,
283
+ };
284
+ this.metric = { status: "running" };
285
+ }
286
+
287
+ processEvent(eventType: string, payload: unknown): void {
288
+ if (!isRecord(payload)) {
289
+ // Non-object payloads can still signal lifecycle (e.g. bare "error").
290
+ if (ERROR_EVENTS.has(eventType) && this.run) {
291
+ this.run = null;
292
+ this.metric = { status: "error" };
293
+ }
294
+ return;
295
+ }
296
+
297
+ const type = resolveEventType(eventType, payload);
298
+ const now = this.now();
299
+
300
+ if (REQUEST_START_EVENTS.has(type)) {
301
+ // New request — start fresh, discarding any incomplete prior run.
302
+ this.startRun(now);
303
+ return;
304
+ }
305
+
306
+ if (STEP_START_EVENTS.has(type)) {
307
+ // Mid-request step marker — only begin a run if none is active.
308
+ if (!this.run) this.startRun(now);
309
+ return;
310
+ }
311
+
312
+ if (VISIBLE_DELTA_EVENTS.has(type)) {
313
+ if (!isVisibleTextDelta(type, payload)) return;
314
+ const text = getTextDelta(payload);
315
+ if (!text) return;
316
+
317
+ // Lazily start a run if the stream began without a recognized start event.
318
+ if (!this.run) this.startRun(now);
319
+ const stats = this.run!;
320
+
321
+ stats.firstDeltaAt ??= now;
322
+ stats.visibleCharCount += text.length;
323
+
324
+ // Add the live char estimate of the CURRENT (not-yet-completed) step on
325
+ // top of any exact usage already booked from completed steps, so the
326
+ // count only grows — it never drops back to a bare estimate mid-run.
327
+ const outputTokens =
328
+ stats.exactOutputTokens +
329
+ estimateTokensFromCharCount(stats.visibleCharCount);
330
+ const durationMs = now - stats.firstDeltaAt;
331
+ this.metric = {
332
+ status: "running",
333
+ tokensPerSecond: calculateTokensPerSecond(outputTokens, durationMs),
334
+ outputTokens,
335
+ durationMs,
336
+ source: stats.exactOutputTokens > 0 ? "usage" : "estimate",
337
+ };
338
+ return;
339
+ }
340
+
341
+ if (INTERMEDIATE_COMPLETE_EVENTS.has(type)) {
342
+ // Accumulate exact usage but keep the run going — these fire per
343
+ // step/turn, not at the end of the whole run.
344
+ if (!this.run) return;
345
+ const stats = this.run;
346
+ const exact = getOutputTokens(payload);
347
+ if (exact !== undefined) {
348
+ stats.exactOutputTokens += exact;
349
+ // This step's visible text is now represented exactly by provider
350
+ // usage — drop it from the running char estimate so the two don't
351
+ // double-count once the next step starts streaming.
352
+ stats.visibleCharCount = 0;
353
+ }
354
+
355
+ const usingExact = stats.exactOutputTokens > 0;
356
+ const outputTokens =
357
+ stats.exactOutputTokens +
358
+ estimateTokensFromCharCount(stats.visibleCharCount);
359
+ const durationMs = this.resolveDuration(stats, payload, now);
360
+ this.metric = {
361
+ status: "running",
362
+ tokensPerSecond: calculateTokensPerSecond(outputTokens, durationMs),
363
+ outputTokens,
364
+ durationMs,
365
+ source: usingExact ? "usage" : "estimate",
366
+ };
367
+ return;
368
+ }
369
+
370
+ if (TERMINAL_COMPLETE_EVENTS.has(type)) {
371
+ if (!this.run) return;
372
+ const stats = this.run;
373
+ // Prefer exact output tokens from this terminal event, else accumulated
374
+ // usage from intermediate completes, else the text estimate.
375
+ const terminalExact = getOutputTokens(payload);
376
+ // Prefer a total from the terminal event; otherwise sum exact usage booked
377
+ // from intermediate completes plus the estimate of any still-streamed text
378
+ // not yet covered by a usage report.
379
+ const outputTokens =
380
+ terminalExact ??
381
+ stats.exactOutputTokens +
382
+ estimateTokensFromCharCount(stats.visibleCharCount);
383
+ const source: ThroughputMetricSource =
384
+ terminalExact !== undefined || stats.exactOutputTokens > 0
385
+ ? "usage"
386
+ : "estimate";
387
+ const durationMs = this.resolveDuration(stats, payload, now);
388
+ this.metric = {
389
+ status: "complete",
390
+ tokensPerSecond: calculateTokensPerSecond(outputTokens, durationMs),
391
+ outputTokens,
392
+ durationMs,
393
+ source,
394
+ };
395
+ this.run = null;
396
+ return;
397
+ }
398
+
399
+ if (ERROR_EVENTS.has(type)) {
400
+ if (!this.run) return;
401
+ this.run = null;
402
+ this.metric = { status: "error" };
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Prefer the streamed visible-output window when it clears the minimum
408
+ * threshold; otherwise fall back to provider execution time, then to the
409
+ * whole-request duration.
410
+ */
411
+ private resolveDuration(
412
+ stats: ThroughputRunStats,
413
+ payload: Record<string, unknown>,
414
+ now: number
415
+ ): number {
416
+ const streamedDurationMs =
417
+ stats.firstDeltaAt !== undefined ? now - stats.firstDeltaAt : undefined;
418
+ if (
419
+ streamedDurationMs !== undefined &&
420
+ streamedDurationMs >= THROUGHPUT_MIN_DURATION_MS
421
+ ) {
422
+ return streamedDurationMs;
423
+ }
424
+ const executionTimeMs = getExecutionTimeMs(payload);
425
+ return executionTimeMs ?? now - stats.startedAt;
426
+ }
427
+ }
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Runtype
3
+ Copyright (c) 2025 mcp-b contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
@@ -0,0 +1,61 @@
1
+ # Vendored: `@mcp-b/smart-dom-reader`
2
+
3
+ This directory contains a **vendored copy** of [`@mcp-b/smart-dom-reader`](https://github.com/WebMCP-org/npm-packages/tree/main/packages/smart-dom-reader)
4
+ (v2.3.1, MIT, © 2025 mcp-b contributors), consumed only by the optional
5
+ `@runtypelabs/persona/smart-dom-reader` entry point (`src/smart-dom-reader.ts`)
6
+ and the pure mapper (`src/utils/smart-dom-adapter.ts`, type-only).
7
+
8
+ ## Why vendored instead of a dependency
9
+
10
+ Every published version of the package (2.3.1, 2.3.2, 3.0.0) is **mis-published**:
11
+ its `package.json` declares
12
+
13
+ ```json
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } }
17
+ ```
18
+
19
+ but the build tool (`vp pack`, vite-plus/rolldown) only emits `dist/index.mjs` +
20
+ `dist/index.d.mts`. The files referenced by `package.json` are **absent from the
21
+ tarball**, so the package cannot be imported by name in Node or any bundler
22
+ (`ERR_MODULE_NOT_FOUND`), and TypeScript cannot resolve its types. This affects
23
+ the package itself and every downstream consumer, so making it an optional peer
24
+ dependency would ship a feature that no integrator could actually load.
25
+
26
+ Vendoring the built artifact into this opt-in entry sidesteps the broken module
27
+ resolution entirely: the code is bundled into `dist/smart-dom-reader.{js,cjs}`
28
+ and never touches a package resolver. Consumers who never import the
29
+ `/smart-dom-reader` entry pay nothing.
30
+
31
+ > **Follow-up:** raise the packaging bug with the upstream maintainer. Once a
32
+ > corrected release exists, this vendor dir can be replaced with a normal
33
+ > optional peer dependency (see the original plan).
34
+
35
+ ## Files
36
+
37
+ - `index.js` — upstream `dist/index.mjs`, with two local edits (see header comment).
38
+ - `index.d.ts` — upstream `dist/index.d.mts` verbatim (sourceMappingURL stripped).
39
+ - `LICENSE` — upstream MIT license.
40
+
41
+ ## Local modifications to `index.js`
42
+
43
+ The **only** changes from upstream `dist/index.mjs` are:
44
+
45
+ 1. Removed the top-level `import { createRequire } from "node:module";` — a
46
+ Node-only builtin that breaks browser bundling.
47
+ 2. Replaced `var __require = createRequire(import.meta.url);` with
48
+ `var __require = void 0;`. `__require` is referenced only inside a guarded
49
+ `typeof __require === "function"` Node fallback in `resolveSmartDomReader()`;
50
+ the browser path (`typeof window !== "undefined"`) returns before reaching it,
51
+ so this is a runtime no-op in the browser.
52
+
53
+ ## How to update
54
+
55
+ 1. `npm pack @mcp-b/smart-dom-reader@<version>` and extract the tarball.
56
+ 2. Copy `dist/index.mjs` → `index.js` and `dist/index.d.mts` → `index.d.ts`.
57
+ 3. Re-apply the two edits above (strip the `node:module` import; neutralize
58
+ `__require`) and the provenance headers. Strip trailing `sourceMappingURL`
59
+ comments.
60
+ 4. Copy the upstream `LICENSE`.
61
+ 5. Re-run `pnpm --filter @runtypelabs/persona build typecheck test:run`.