@runtypelabs/persona 4.28.0 → 4.28.1
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.
- package/README.md +25 -0
- package/dist/{approval-ui-QAKWNFNM.js → approval-ui-OBBI62ES.js} +1 -1
- package/dist/approval-ui.cjs +2 -2
- package/dist/approval-ui.d.cts +7 -5
- package/dist/approval-ui.d.ts +7 -5
- package/dist/approval-ui.js +2 -2
- package/dist/artifacts-ui.d.cts +7 -5
- package/dist/artifacts-ui.d.ts +7 -5
- package/dist/{chunk-NQLDCJAP.js → chunk-D4TDKCT5.js} +2 -2
- package/dist/chunk-FNXK3VTC.js +1 -0
- package/dist/{client-history-LV3KHEFA.js → client-history-QGAFMCBE.js} +1 -1
- package/dist/client-history.js +1 -1
- package/dist/{client-stream-N22JHTLE.js → client-stream-WXD2FL23.js} +1 -1
- package/dist/client-stream.js +3 -3
- package/dist/codegen.cjs +4 -4
- package/dist/codegen.js +5 -5
- package/dist/context-mentions.d.cts +7 -5
- package/dist/context-mentions.d.ts +7 -5
- package/dist/event-stream-view.d.cts +7 -5
- package/dist/event-stream-view.d.ts +7 -5
- package/dist/forms-ui.d.cts +7 -5
- package/dist/forms-ui.d.ts +7 -5
- package/dist/index.cjs +40 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -30
- package/dist/index.d.ts +32 -30
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +41 -41
- package/dist/index.js.map +1 -1
- package/dist/session-reconnect.d.cts +7 -5
- package/dist/session-reconnect.d.ts +7 -5
- package/dist/smart-dom-reader.d.cts +7 -5
- package/dist/smart-dom-reader.d.ts +7 -5
- package/dist/theme-editor-preview.cjs +51 -51
- package/dist/theme-editor-preview.d.cts +7 -5
- package/dist/theme-editor-preview.d.ts +7 -5
- package/dist/theme-editor-preview.js +4 -4
- package/dist/theme-editor.d.cts +8 -6
- package/dist/theme-editor.d.ts +8 -6
- package/dist/{voice-runtime-LVUMPUDS.js → voice-runtime-TWFJ4UUS.js} +1 -1
- package/dist/voice-runtime.cjs +1 -1
- package/dist/voice-runtime.d.cts +7 -5
- package/dist/voice-runtime.d.ts +7 -5
- package/dist/voice-runtime.js +1 -1
- package/dist/webmcp-runtime-5Q3IHAHP.js +6 -0
- package/dist/webmcp-runtime.cjs +2 -2
- package/dist/webmcp-runtime.d.cts +22 -26
- package/dist/webmcp-runtime.d.ts +22 -26
- package/dist/webmcp-runtime.js +2 -2
- package/package.json +5 -2
- package/src/generated/runtype-openapi-contract.ts +3 -0
- package/src/theme-editor/webmcp/types.ts +1 -1
- package/src/types.ts +7 -5
- package/src/webmcp-bridge.test.ts +87 -21
- package/src/webmcp-bridge.ts +21 -26
- package/src/webmcp-polyfill.integration.test.ts +339 -0
- package/src/webmcp-polyfill.ts +7 -4
- package/src/webmcp-runtime-bundle.test.ts +1 -1
- package/src/webmcp-runtime-entry.ts +34 -19
- package/dist/chunk-AALCT54S.js +0 -1
- package/dist/webmcp-runtime-V6ZWW3PI.js +0 -6
|
@@ -121,7 +121,7 @@ export class WebMcpBridge {
|
|
|
121
121
|
|
|
122
122
|
let infos: ModelContextToolInfo[];
|
|
123
123
|
try {
|
|
124
|
-
infos = await mc.getTools();
|
|
124
|
+
infos = (await mc.getTools()).filter(isLocalTool);
|
|
125
125
|
} catch (err) {
|
|
126
126
|
log.warn("getTools() threw: shipping an empty WebMCP snapshot.", err);
|
|
127
127
|
return [];
|
|
@@ -162,7 +162,7 @@ export class WebMcpBridge {
|
|
|
162
162
|
* - tool not in registry (e.g. unmounted between snapshot and call)
|
|
163
163
|
* - tool excluded by the client allowlist
|
|
164
164
|
* - user declined the confirm gate
|
|
165
|
-
* - `execute()` threw or
|
|
165
|
+
* - `execute()` threw or returned a non-serializable result
|
|
166
166
|
* - `execute()` exceeded the 30s timeout
|
|
167
167
|
* - `signal` fired (session-level `cancel()`)
|
|
168
168
|
*
|
|
@@ -204,7 +204,7 @@ export class WebMcpBridge {
|
|
|
204
204
|
|
|
205
205
|
let infos: ModelContextToolInfo[];
|
|
206
206
|
try {
|
|
207
|
-
infos = await mc.getTools();
|
|
207
|
+
infos = (await mc.getTools()).filter(isLocalTool);
|
|
208
208
|
} catch (err) {
|
|
209
209
|
const message = err instanceof Error ? err.message : String(err);
|
|
210
210
|
return errorResult(`Failed to read WebMCP registry: ${message}`);
|
|
@@ -275,7 +275,7 @@ export class WebMcpBridge {
|
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
try {
|
|
278
|
-
const raw = await mc.executeTool(info,
|
|
278
|
+
const raw = await mc.executeTool(info, createCompatibleToolInput(args), {
|
|
279
279
|
signal: controller.signal,
|
|
280
280
|
});
|
|
281
281
|
return normalizeSerializedResult(raw);
|
|
@@ -329,7 +329,8 @@ export class WebMcpBridge {
|
|
|
329
329
|
const mod = await this.#deps.loadPolyfill();
|
|
330
330
|
// Idempotent: no-ops if `document.modelContext` already exists (native or
|
|
331
331
|
// a prior install by the host page).
|
|
332
|
-
mod.initializeWebMCPPolyfill();
|
|
332
|
+
if ("initializeWebMCPPolyfill" in mod) mod.initializeWebMCPPolyfill();
|
|
333
|
+
else mod.installWebMCP();
|
|
333
334
|
this.#installed = true;
|
|
334
335
|
} catch (err) {
|
|
335
336
|
log.warn(
|
|
@@ -516,6 +517,28 @@ const normalizeSerializedResult = (raw: string | null): WebMcpToolResult => {
|
|
|
516
517
|
return { content: [{ type: "text", text }] };
|
|
517
518
|
};
|
|
518
519
|
|
|
520
|
+
/**
|
|
521
|
+
* One invocation works with both contracts: v5/native previews coerce input to
|
|
522
|
+
* JSON text, whereas v6 serializes the object. Keep the coercion hook off the
|
|
523
|
+
* JSON payload and clone first so we never mutate caller-owned arguments.
|
|
524
|
+
* Do not probe by executing/retrying: page tools may have irreversible effects.
|
|
525
|
+
*/
|
|
526
|
+
const createCompatibleToolInput = (args: unknown): object => {
|
|
527
|
+
let json: string;
|
|
528
|
+
try {
|
|
529
|
+
json = JSON.stringify(args) ?? "{}";
|
|
530
|
+
} catch {
|
|
531
|
+
// Preserve the historical fallback for non-serializable arguments.
|
|
532
|
+
json = "{}";
|
|
533
|
+
}
|
|
534
|
+
const input: unknown = JSON.parse(json);
|
|
535
|
+
if (input === null || typeof input !== "object") {
|
|
536
|
+
throw new TypeError("WebMCP tool arguments must be an object or array.");
|
|
537
|
+
}
|
|
538
|
+
Object.defineProperty(input, Symbol.toPrimitive, { value: () => json });
|
|
539
|
+
return input;
|
|
540
|
+
};
|
|
541
|
+
|
|
519
542
|
const errorResult = (message: string): WebMcpToolResult => ({
|
|
520
543
|
isError: true,
|
|
521
544
|
content: [{ type: "text", text: message }],
|
|
@@ -549,20 +572,12 @@ const previewArgs = (args: unknown): string => {
|
|
|
549
572
|
}
|
|
550
573
|
};
|
|
551
574
|
|
|
552
|
-
/**
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (args === undefined) return "{}";
|
|
559
|
-
try {
|
|
560
|
-
const json = JSON.stringify(args);
|
|
561
|
-
return json === undefined ? "{}" : json;
|
|
562
|
-
} catch {
|
|
563
|
-
return "{}";
|
|
564
|
-
}
|
|
565
|
-
};
|
|
575
|
+
/** Keep name-only dispatch scoped to this document, not related frames. */
|
|
576
|
+
const isLocalTool = (info: ModelContextToolInfo): boolean =>
|
|
577
|
+
(info.window === undefined ||
|
|
578
|
+
(typeof window !== "undefined" && info.window === window)) &&
|
|
579
|
+
(info.origin === undefined ||
|
|
580
|
+
(typeof location !== "undefined" && info.origin === location.origin));
|
|
566
581
|
|
|
567
582
|
/**
|
|
568
583
|
* `JSON.stringify` that tolerates circular references and non-serializable
|
package/dist/chunk-AALCT54S.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var t="4.28.0";var n=t;export{n as a};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
var h="webmcp:",T=t=>t.startsWith(h)?t.slice(h.length):t,u={warn(t,...e){typeof console<"u"&&typeof console.warn=="function"&&console.warn(`[Persona/WebMCP] ${t}`,...e)}},b=class{constructor(e,n){this.config=e;this.#e=n,this.#o=e.onConfirm??null,this.#r=3e4}#o;#r;#t=!1;#i=null;#l=!1;#e;setConfirmHandler(e){this.#o=e}isOperational(){return this.config.enabled!==!0||!this.#t?!1:this.#n()!==null}async snapshotForDispatch(){if(await this.#s(),this.config.enabled!==!0)return[];let e=this.#n();if(!e)return[];let n;try{n=await e.getTools()}catch(i){return u.warn("getTools() threw: shipping an empty WebMCP snapshot.",i),[]}this.#e.recordToolDisplayTitles(n);let o=typeof location<"u"?location.origin:"";return n.filter(i=>this.#c(i.name)).map(i=>{let l=W(i.annotations);return{name:i.name,description:i.description,parametersSchema:P(i.inputSchema),origin:"webmcp",...o?{pageOrigin:o}:{},...l?{annotations:l}:{},...l?.untrustedContentHint===!0?{untrustedContentHint:!0}:{}}})}async executeToolCall(e,n,o){if(await this.#s(),this.config.enabled!==!0)return s("WebMCP is not enabled on this widget.");let i=this.#n();if(!i){let r=typeof document<"u"&&!!document.modelContext;return s(r?"WebMCP is not operational: document.modelContext is present but does not expose the strict getTools()/executeTool() surface (likely a different or older WebMCP polyfill).":"WebMCP bridge is not operational on this page (document.modelContext not available).")}let l=T(e),c;try{c=await i.getTools()}catch(r){let p=r instanceof Error?r.message:String(r);return s(`Failed to read WebMCP registry: ${p}`)}this.#e.recordToolDisplayTitles(c);let f=c.find(r=>r.name===l);if(!f)return s(`WebMCP tool not registered on this page: ${l}`);if(!this.#c(l))return s(`WebMCP tool not allowed by client allowlist: ${l}`);if(o?.aborted)return s("Aborted by cancel()");let m=this.#e.getToolDisplayTitle(l),C={toolName:l,args:n,description:f.description,...m?{title:m}:{},reason:"gate"};if(!await this.#u(C))return s("User declined the tool call.");if(o?.aborted)return s("Aborted by cancel()");let a=new AbortController,y=!1,M=setTimeout(()=>{y=!0,a.abort()},this.#r),g=()=>a.abort();o&&(o.aborted?a.abort():o.addEventListener("abort",g,{once:!0}));try{let r=await i.executeTool(f,k(n),{signal:a.signal});return w(r)}catch(r){if(y)return s(`WebMCP tool '${l}' timed out after ${this.#r}ms`);if(o?.aborted)return s("Aborted by cancel()");let p=r instanceof Error?r.message:String(r);return s(p)}finally{clearTimeout(M),o&&o.removeEventListener("abort",g)}}#s(){return this.config.enabled!==!0?Promise.resolve():(this.#i||(this.#i=this.#a()),this.#i)}async#a(){try{if(this.#n()){this.#t=!0;return}(await this.#e.loadPolyfill()).initializeWebMCPPolyfill(),this.#t=!0}catch(e){u.warn("Failed to load @mcp-b/webmcp-polyfill: WebMCP consumption disabled.",e),this.#t=!1}}#n(){if(typeof document>"u")return null;let e=document.modelContext;if(!e||typeof e!="object")return null;let n=e;return typeof n.getTools!="function"||typeof n.executeTool!="function"?(this.#l||(this.#l=!0,u.warn("document.modelContext is present but does not expose getTools()/executeTool(): WebMCP consumption is disabled. Another (incompatible or older) WebMCP polyfill likely installed document.modelContext before Persona. Remove it, or use a polyfill implementing the strict standard surface (e.g. @mcp-b/webmcp-polyfill).")),null):e}async#u(e){let n=this.#o??A;try{return await n(e)}catch(o){return u.warn(`Confirm handler threw for WebMCP tool '${e.toolName}'; declining.`,o),!1}}#c(e){let n=this.config.allowlist;return!n||n.length===0?!0:n.some(o=>x(e,o))}},x=(t,e)=>{if(e==="*")return!0;let n=e.replace(/[.+?^${}()|[\]\\]/g,"\\$&");return new RegExp("^"+n.replace(/\*/g,".*")+"$").test(t)},d={type:"object",properties:{}},W=t=>{if(!t||typeof t!="object")return;let e={...typeof t.readOnlyHint=="boolean"?{readOnlyHint:t.readOnlyHint}:{},...typeof t.untrustedContentHint=="boolean"?{untrustedContentHint:t.untrustedContentHint}:{}};return Object.keys(e).length>0?e:void 0},P=t=>{if(t===void 0||t==="")return{...d};if(typeof t=="object")return t!==null&&!Array.isArray(t)?t:{...d};try{let e=JSON.parse(t);return e!==null&&typeof e=="object"&&!Array.isArray(e)?e:{...d}}catch{return{...d}}},w=t=>{if(t==null)return{content:[{type:"text",text:""}]};let e;try{e=JSON.parse(t)}catch{return{content:[{type:"text",text:t}]}}return e!==null&&typeof e=="object"&&Array.isArray(e.content)?e:{content:[{type:"text",text:typeof e=="string"?e:D(e)}]}},s=t=>({isError:!0,content:[{type:"text",text:t}]}),A=async t=>{if(typeof window>"u"||typeof window.confirm!="function")return!1;let e=O(t.args),n=`Allow the AI to call ${t.toolName}`+(e?`
|
|
2
|
-
|
|
3
|
-
Arguments:
|
|
4
|
-
${e}`:"")+(t.description?`
|
|
5
|
-
|
|
6
|
-
${t.description}`:"");return window.confirm(n)},O=t=>{if(t==null)return"";try{let e=JSON.stringify(t,null,2);return e.length>500?e.slice(0,500)+"\u2026":e}catch{return String(t)}},k=t=>{if(t===void 0)return"{}";try{let e=JSON.stringify(t);return e===void 0?"{}":e}catch{return"{}"}},D=t=>{if(t===void 0)return"";try{return JSON.stringify(t)}catch{return String(t)}};export{b as WebMcpBridge};
|