@runtypelabs/persona 3.31.0 → 3.33.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.
- package/README.md +2 -0
- package/dist/animations/glyph-cycle.d.cts +1 -1
- package/dist/animations/glyph-cycle.d.ts +1 -1
- package/dist/animations/{types-quh7NmYD.d.cts → types-CthJFfNx.d.cts} +7 -0
- package/dist/animations/{types-quh7NmYD.d.ts → types-CthJFfNx.d.ts} +7 -0
- package/dist/animations/wipe.d.cts +1 -1
- package/dist/animations/wipe.d.ts +1 -1
- package/dist/codegen.cjs +4 -4
- package/dist/codegen.js +3 -3
- package/dist/index.cjs +43 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +288 -8
- package/dist/index.d.ts +288 -8
- package/dist/index.global.js +76 -79
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +43 -43
- package/dist/index.js.map +1 -1
- package/dist/launcher.global.js.map +1 -1
- package/dist/smart-dom-reader.d.cts +78 -2
- package/dist/smart-dom-reader.d.ts +78 -2
- package/dist/theme-editor.cjs +30 -30
- package/dist/theme-editor.d.cts +92 -5
- package/dist/theme-editor.d.ts +92 -5
- package/dist/theme-editor.js +30 -30
- package/dist/webmcp-polyfill.js +4 -0
- package/package.json +4 -3
- package/src/ask-user-question-tool.test.ts +148 -0
- package/src/ask-user-question-tool.ts +138 -0
- package/src/client.ts +43 -9
- package/src/components/messages.ts +10 -0
- package/src/components/suggestions.ts +49 -6
- package/src/index-core.ts +15 -0
- package/src/index-global.ts +39 -0
- package/src/runtime/host-layout.test.ts +158 -3
- package/src/runtime/host-layout.ts +95 -1
- package/src/session.ts +188 -32
- package/src/session.webmcp.test.ts +48 -0
- package/src/suggest-replies-tool.test.ts +445 -0
- package/src/suggest-replies-tool.ts +152 -0
- package/src/theme-editor/index.ts +2 -0
- package/src/theme-editor/webmcp/index.ts +2 -0
- package/src/theme-editor/webmcp/types.ts +16 -3
- package/src/types.ts +79 -2
- package/src/ui.suggest-replies.test.ts +237 -0
- package/src/ui.ts +57 -13
- package/src/utils/dock.test.ts +23 -1
- package/src/utils/dock.ts +2 -0
- package/src/version.ts +5 -2
- package/src/webmcp-bridge.test.ts +60 -0
- package/src/webmcp-bridge.ts +34 -1
- package/src/webmcp-polyfill.ts +16 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var He=Object.defineProperty;var Fe=(e,t,r)=>t in e?He(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var _=(e,t,r)=>Fe(e,typeof t!="symbol"?t+"":t,r);function A(e,t){let r=typeof e;if(r!==typeof t)return!1;if(Array.isArray(e)){if(!Array.isArray(t))return!1;let n=e.length;if(n!==t.length)return!1;for(let i=0;i<n;i++)if(!A(e[i],t[i]))return!1;return!0}if(r==="object"){if(!e||!t)return e===t;let n=Object.keys(e),i=Object.keys(t);if(n.length!==i.length)return!1;for(let o of n)if(!A(e[o],t[o]))return!1;return!0}return e===t}function S(e){return encodeURI(Be(e))}function Be(e){return e.replace(/~/g,"~0").replace(/\//g,"~1")}var Ke={prefixItems:!0,items:!0,allOf:!0,anyOf:!0,oneOf:!0},Ge={$defs:!0,definitions:!0,properties:!0,patternProperties:!0,dependentSchemas:!0},Ye={id:!0,$id:!0,$ref:!0,$schema:!0,$anchor:!0,$vocabulary:!0,$comment:!0,default:!0,enum:!0,const:!0,required:!0,type:!0,maximum:!0,minimum:!0,exclusiveMaximum:!0,exclusiveMinimum:!0,multipleOf:!0,maxLength:!0,minLength:!0,pattern:!0,format:!0,maxItems:!0,minItems:!0,uniqueItems:!0,maxProperties:!0,minProperties:!0},Ze=typeof self!="undefined"&&self.location&&self.location.origin!=="null"?new URL(self.location.origin+self.location.pathname+location.search):new URL("https://github.com/cfworker");function E(e,t=Object.create(null),r=Ze,n=""){if(e&&typeof e=="object"&&!Array.isArray(e)){let d=e.$id||e.id;if(d){let o=new URL(d,r.href);o.hash.length>1?t[o.href]=e:(o.hash="",n===""?r=o:E(e,t,r))}}else if(e!==!0&&e!==!1)return t;let i=r.href+(n?"#"+n:"");if(t[i]!==void 0)throw new Error(`Duplicate schema URI "${i}".`);if(t[i]=e,e===!0||e===!1)return t;if(e.__absolute_uri__===void 0&&Object.defineProperty(e,"__absolute_uri__",{enumerable:!1,value:i}),e.$ref&&e.__absolute_ref__===void 0){let d=new URL(e.$ref,r.href);d.hash=d.hash,Object.defineProperty(e,"__absolute_ref__",{enumerable:!1,value:d.href})}if(e.$recursiveRef&&e.__absolute_recursive_ref__===void 0){let d=new URL(e.$recursiveRef,r.href);d.hash=d.hash,Object.defineProperty(e,"__absolute_recursive_ref__",{enumerable:!1,value:d.href})}if(e.$anchor){let d=new URL("#"+e.$anchor,r.href);t[d.href]=e}for(let d in e){if(Ye[d])continue;let o=`${n}/${S(d)}`,s=e[d];if(Array.isArray(s)){if(Ke[d]){let h=s.length;for(let x=0;x<h;x++)E(s[x],t,r,`${o}/${x}`)}}else if(Ge[d])for(let h in s)E(s[h],t,r,`${o}/${S(h)}`);else E(s,t,r,o)}return t}var Xe=/^(\d\d\d\d)-(\d\d)-(\d\d)$/,Qe=[0,31,28,31,30,31,30,31,31,30,31,30,31],Le=/^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d(?::?\d\d)?)?$/i,et=/^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i,tt=/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,rt=/^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,nt=/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu,ot=/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,it=/^(?:\/(?:[^~/]|~0|~1)*)*$/,st=/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,dt=/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/,at=e=>{if(e[0]==='"')return!1;let[t,r,...n]=e.split("@");return!t||!r||n.length!==0||t.length>64||r.length>253||t[0]==="."||t.endsWith(".")||t.includes("..")||!/^[a-z0-9.-]+$/i.test(r)||!/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+$/i.test(t)?!1:r.split(".").every(i=>/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/i.test(i))},ft=/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,lt=/^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i,ut=e=>e.length>1&&e.length<80&&(/^P\d+([.,]\d+)?W$/.test(e)||/^P[\dYMDTHS]*(\d[.,]\d+)?[YMDHS]$/.test(e)&&/^P([.,\d]+Y)?([.,\d]+M)?([.,\d]+D)?(T([.,\d]+H)?([.,\d]+M)?([.,\d]+S)?)?$/.test(e));function k(e){return e.test.bind(e)}var me={date:Ee,time:Me.bind(void 0,!1),"date-time":pt,duration:ut,uri:$t,"uri-reference":k(tt),"uri-template":k(rt),url:k(nt),email:at,hostname:k(et),ipv4:k(ft),ipv6:k(lt),regex:wt,uuid:k(ot),"json-pointer":k(it),"json-pointer-uri-fragment":k(st),"relative-json-pointer":k(dt)};function mt(e){return e%4===0&&(e%100!==0||e%400===0)}function Ee(e){let t=e.match(Xe);if(!t)return!1;let r=+t[1],n=+t[2],i=+t[3];return n>=1&&n<=12&&i>=1&&i<=(n==2&&mt(r)?29:Qe[n])}function Me(e,t){let r=t.match(Le);if(!r)return!1;let n=+r[1],i=+r[2],d=+r[3],o=!!r[5];return(n<=23&&i<=59&&d<=59||n==23&&i==59&&d==60)&&(!e||o)}var ct=/t|\s/i;function pt(e){let t=e.split(ct);return t.length==2&&Ee(t[0])&&Me(!0,t[1])}var yt=/\/|:/,ht=/^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;function $t(e){return yt.test(e)&&ht.test(e)}var gt=/[^\\]\\Z/;function wt(e){if(gt.test(e))return!1;try{return new RegExp(e,"u"),!0}catch{return!1}}var Ce;(function(e){e[e.Flag=1]="Flag",e[e.Basic=2]="Basic",e[e.Detailed=4]="Detailed"})(Ce||(Ce={}));function Ne(e){let t=0,r=e.length,n=0,i;for(;n<r;)t++,i=e.charCodeAt(n++),i>=55296&&i<=56319&&n<r&&(i=e.charCodeAt(n),(i&64512)==56320&&n++);return t}function w(e,t,r="2019-09",n=E(t),i=!0,d=null,o="#",s="#",h=Object.create(null)){if(t===!0)return{valid:!0,errors:[]};if(t===!1)return{valid:!1,errors:[{instanceLocation:o,keyword:"false",keywordLocation:o,error:"False boolean schema."}]};let x=typeof e,$;switch(x){case"boolean":case"number":case"string":$=x;break;case"object":e===null?$="null":Array.isArray(e)?$="array":$="object";break;default:throw new Error(`Instances of "${x}" type are not supported.`)}let{$ref:T,$recursiveRef:q,$recursiveAnchor:M,type:I,const:z,enum:D,required:ye,not:he,anyOf:ee,allOf:te,oneOf:$e,if:ge,then:we,else:xe,format:U,properties:re,patternProperties:ne,additionalProperties:be,unevaluatedProperties:ve,minProperties:oe,maxProperties:ie,propertyNames:Se,dependentRequired:se,dependentSchemas:de,dependencies:ae,prefixItems:fe,items:R,additionalItems:Te,unevaluatedItems:_e,contains:Ie,minContains:O,maxContains:V,minItems:le,maxItems:ue,uniqueItems:Ue,minimum:C,maximum:N,exclusiveMinimum:J,exclusiveMaximum:W,multipleOf:H,minLength:F,maxLength:B,pattern:ke,__absolute_ref__:K,__absolute_recursive_ref__:Ve}=t,f=[];if(M===!0&&d===null&&(d=t),q==="#"){let m=d===null?n[Ve]:d,u=`${s}/$recursiveRef`,y=w(e,d===null?t:d,r,n,i,m,o,u,h);y.valid||f.push({instanceLocation:o,keyword:"$recursiveRef",keywordLocation:u,error:"A subschema had errors."},...y.errors)}if(T!==void 0){let u=n[K||T];if(u===void 0){let a=`Unresolved $ref "${T}".`;throw K&&K!==T&&(a+=` Absolute URI "${K}".`),a+=`
|
|
2
|
+
Known schemas:
|
|
3
|
+
- ${Object.keys(n).join(`
|
|
4
|
+
- `)}`,new Error(a)}let y=`${s}/$ref`,l=w(e,u,r,n,i,d,o,y,h);if(l.valid||f.push({instanceLocation:o,keyword:"$ref",keywordLocation:y,error:"A subschema had errors."},...l.errors),r==="4"||r==="7")return{valid:f.length===0,errors:f}}if(Array.isArray(I)){let m=I.length,u=!1;for(let y=0;y<m;y++)if($===I[y]||I[y]==="integer"&&$==="number"&&e%1===0&&e===e){u=!0;break}u||f.push({instanceLocation:o,keyword:"type",keywordLocation:`${s}/type`,error:`Instance type "${$}" is invalid. Expected "${I.join('", "')}".`})}else I==="integer"?($!=="number"||e%1||e!==e)&&f.push({instanceLocation:o,keyword:"type",keywordLocation:`${s}/type`,error:`Instance type "${$}" is invalid. Expected "${I}".`}):I!==void 0&&$!==I&&f.push({instanceLocation:o,keyword:"type",keywordLocation:`${s}/type`,error:`Instance type "${$}" is invalid. Expected "${I}".`});if(z!==void 0&&($==="object"||$==="array"?A(e,z)||f.push({instanceLocation:o,keyword:"const",keywordLocation:`${s}/const`,error:`Instance does not match ${JSON.stringify(z)}.`}):e!==z&&f.push({instanceLocation:o,keyword:"const",keywordLocation:`${s}/const`,error:`Instance does not match ${JSON.stringify(z)}.`})),D!==void 0&&($==="object"||$==="array"?D.some(m=>A(e,m))||f.push({instanceLocation:o,keyword:"enum",keywordLocation:`${s}/enum`,error:`Instance does not match any of ${JSON.stringify(D)}.`}):D.some(m=>e===m)||f.push({instanceLocation:o,keyword:"enum",keywordLocation:`${s}/enum`,error:`Instance does not match any of ${JSON.stringify(D)}.`})),he!==void 0){let m=`${s}/not`;w(e,he,r,n,i,d,o,m).valid&&f.push({instanceLocation:o,keyword:"not",keywordLocation:m,error:'Instance matched "not" schema.'})}let G=[];if(ee!==void 0){let m=`${s}/anyOf`,u=f.length,y=!1;for(let l=0;l<ee.length;l++){let a=ee[l],p=Object.create(h),c=w(e,a,r,n,i,M===!0?d:null,o,`${m}/${l}`,p);f.push(...c.errors),y=y||c.valid,c.valid&&G.push(p)}y?f.length=u:f.splice(u,0,{instanceLocation:o,keyword:"anyOf",keywordLocation:m,error:"Instance does not match any subschemas."})}if(te!==void 0){let m=`${s}/allOf`,u=f.length,y=!0;for(let l=0;l<te.length;l++){let a=te[l],p=Object.create(h),c=w(e,a,r,n,i,M===!0?d:null,o,`${m}/${l}`,p);f.push(...c.errors),y=y&&c.valid,c.valid&&G.push(p)}y?f.length=u:f.splice(u,0,{instanceLocation:o,keyword:"allOf",keywordLocation:m,error:"Instance does not match every subschema."})}if($e!==void 0){let m=`${s}/oneOf`,u=f.length,y=$e.filter((l,a)=>{let p=Object.create(h),c=w(e,l,r,n,i,M===!0?d:null,o,`${m}/${a}`,p);return f.push(...c.errors),c.valid&&G.push(p),c.valid}).length;y===1?f.length=u:f.splice(u,0,{instanceLocation:o,keyword:"oneOf",keywordLocation:m,error:`Instance does not match exactly one subschema (${y} matches).`})}if(($==="object"||$==="array")&&Object.assign(h,...G),ge!==void 0){let m=`${s}/if`;if(w(e,ge,r,n,i,d,o,m,h).valid){if(we!==void 0){let y=w(e,we,r,n,i,d,o,`${s}/then`,h);y.valid||f.push({instanceLocation:o,keyword:"if",keywordLocation:m,error:'Instance does not match "then" schema.'},...y.errors)}}else if(xe!==void 0){let y=w(e,xe,r,n,i,d,o,`${s}/else`,h);y.valid||f.push({instanceLocation:o,keyword:"if",keywordLocation:m,error:'Instance does not match "else" schema.'},...y.errors)}}if($==="object"){if(ye!==void 0)for(let l of ye)l in e||f.push({instanceLocation:o,keyword:"required",keywordLocation:`${s}/required`,error:`Instance does not have required property "${l}".`});let m=Object.keys(e);if(oe!==void 0&&m.length<oe&&f.push({instanceLocation:o,keyword:"minProperties",keywordLocation:`${s}/minProperties`,error:`Instance does not have at least ${oe} properties.`}),ie!==void 0&&m.length>ie&&f.push({instanceLocation:o,keyword:"maxProperties",keywordLocation:`${s}/maxProperties`,error:`Instance does not have at least ${ie} properties.`}),Se!==void 0){let l=`${s}/propertyNames`;for(let a in e){let p=`${o}/${S(a)}`,c=w(a,Se,r,n,i,d,p,l);c.valid||f.push({instanceLocation:o,keyword:"propertyNames",keywordLocation:l,error:`Property name "${a}" does not match schema.`},...c.errors)}}if(se!==void 0){let l=`${s}/dependantRequired`;for(let a in se)if(a in e){let p=se[a];for(let c of p)c in e||f.push({instanceLocation:o,keyword:"dependentRequired",keywordLocation:l,error:`Instance has "${a}" but does not have "${c}".`})}}if(de!==void 0)for(let l in de){let a=`${s}/dependentSchemas`;if(l in e){let p=w(e,de[l],r,n,i,d,o,`${a}/${S(l)}`,h);p.valid||f.push({instanceLocation:o,keyword:"dependentSchemas",keywordLocation:a,error:`Instance has "${l}" but does not match dependant schema.`},...p.errors)}}if(ae!==void 0){let l=`${s}/dependencies`;for(let a in ae)if(a in e){let p=ae[a];if(Array.isArray(p))for(let c of p)c in e||f.push({instanceLocation:o,keyword:"dependencies",keywordLocation:l,error:`Instance has "${a}" but does not have "${c}".`});else{let c=w(e,p,r,n,i,d,o,`${l}/${S(a)}`);c.valid||f.push({instanceLocation:o,keyword:"dependencies",keywordLocation:l,error:`Instance has "${a}" but does not match dependant schema.`},...c.errors)}}}let u=Object.create(null),y=!1;if(re!==void 0){let l=`${s}/properties`;for(let a in re){if(!(a in e))continue;let p=`${o}/${S(a)}`,c=w(e[a],re[a],r,n,i,d,p,`${l}/${S(a)}`);if(c.valid)h[a]=u[a]=!0;else if(y=i,f.push({instanceLocation:o,keyword:"properties",keywordLocation:l,error:`Property "${a}" does not match schema.`},...c.errors),y)break}}if(!y&&ne!==void 0){let l=`${s}/patternProperties`;for(let a in ne){let p=new RegExp(a,"u"),c=ne[a];for(let v in e){if(!p.test(v))continue;let Pe=`${o}/${S(v)}`,Oe=w(e[v],c,r,n,i,d,Pe,`${l}/${S(a)}`);Oe.valid?h[v]=u[v]=!0:(y=i,f.push({instanceLocation:o,keyword:"patternProperties",keywordLocation:l,error:`Property "${v}" matches pattern "${a}" but does not match associated schema.`},...Oe.errors))}}}if(!y&&be!==void 0){let l=`${s}/additionalProperties`;for(let a in e){if(u[a])continue;let p=`${o}/${S(a)}`,c=w(e[a],be,r,n,i,d,p,l);c.valid?h[a]=!0:(y=i,f.push({instanceLocation:o,keyword:"additionalProperties",keywordLocation:l,error:`Property "${a}" does not match additional properties schema.`},...c.errors))}}else if(!y&&ve!==void 0){let l=`${s}/unevaluatedProperties`;for(let a in e)if(!h[a]){let p=`${o}/${S(a)}`,c=w(e[a],ve,r,n,i,d,p,l);c.valid?h[a]=!0:f.push({instanceLocation:o,keyword:"unevaluatedProperties",keywordLocation:l,error:`Property "${a}" does not match unevaluated properties schema.`},...c.errors)}}}else if($==="array"){ue!==void 0&&e.length>ue&&f.push({instanceLocation:o,keyword:"maxItems",keywordLocation:`${s}/maxItems`,error:`Array has too many items (${e.length} > ${ue}).`}),le!==void 0&&e.length<le&&f.push({instanceLocation:o,keyword:"minItems",keywordLocation:`${s}/minItems`,error:`Array has too few items (${e.length} < ${le}).`});let m=e.length,u=0,y=!1;if(fe!==void 0){let l=`${s}/prefixItems`,a=Math.min(fe.length,m);for(;u<a;u++){let p=w(e[u],fe[u],r,n,i,d,`${o}/${u}`,`${l}/${u}`);if(h[u]=!0,!p.valid&&(y=i,f.push({instanceLocation:o,keyword:"prefixItems",keywordLocation:l,error:"Items did not match schema."},...p.errors),y))break}}if(R!==void 0){let l=`${s}/items`;if(Array.isArray(R)){let a=Math.min(R.length,m);for(;u<a;u++){let p=w(e[u],R[u],r,n,i,d,`${o}/${u}`,`${l}/${u}`);if(h[u]=!0,!p.valid&&(y=i,f.push({instanceLocation:o,keyword:"items",keywordLocation:l,error:"Items did not match schema."},...p.errors),y))break}}else for(;u<m;u++){let a=w(e[u],R,r,n,i,d,`${o}/${u}`,l);if(h[u]=!0,!a.valid&&(y=i,f.push({instanceLocation:o,keyword:"items",keywordLocation:l,error:"Items did not match schema."},...a.errors),y))break}if(!y&&Te!==void 0){let a=`${s}/additionalItems`;for(;u<m;u++){let p=w(e[u],Te,r,n,i,d,`${o}/${u}`,a);h[u]=!0,p.valid||(y=i,f.push({instanceLocation:o,keyword:"additionalItems",keywordLocation:a,error:"Items did not match additional items schema."},...p.errors))}}}if(Ie!==void 0)if(m===0&&O===void 0)f.push({instanceLocation:o,keyword:"contains",keywordLocation:`${s}/contains`,error:"Array is empty. It must contain at least one item matching the schema."});else if(O!==void 0&&m<O)f.push({instanceLocation:o,keyword:"minContains",keywordLocation:`${s}/minContains`,error:`Array has less items (${m}) than minContains (${O}).`});else{let l=`${s}/contains`,a=f.length,p=0;for(let c=0;c<m;c++){let v=w(e[c],Ie,r,n,i,d,`${o}/${c}`,l);v.valid?(h[c]=!0,p++):f.push(...v.errors)}p>=(O||0)&&(f.length=a),O===void 0&&V===void 0&&p===0?f.splice(a,0,{instanceLocation:o,keyword:"contains",keywordLocation:l,error:"Array does not contain item matching schema."}):O!==void 0&&p<O?f.push({instanceLocation:o,keyword:"minContains",keywordLocation:`${s}/minContains`,error:`Array must contain at least ${O} items matching schema. Only ${p} items were found.`}):V!==void 0&&p>V&&f.push({instanceLocation:o,keyword:"maxContains",keywordLocation:`${s}/maxContains`,error:`Array may contain at most ${V} items matching schema. ${p} items were found.`})}if(!y&&_e!==void 0){let l=`${s}/unevaluatedItems`;for(u;u<m;u++){if(h[u])continue;let a=w(e[u],_e,r,n,i,d,`${o}/${u}`,l);h[u]=!0,a.valid||f.push({instanceLocation:o,keyword:"unevaluatedItems",keywordLocation:l,error:"Items did not match unevaluated items schema."},...a.errors)}}if(Ue)for(let l=0;l<m;l++){let a=e[l],p=typeof a=="object"&&a!==null;for(let c=0;c<m;c++){if(l===c)continue;let v=e[c];(a===v||p&&(typeof v=="object"&&v!==null)&&A(a,v))&&(f.push({instanceLocation:o,keyword:"uniqueItems",keywordLocation:`${s}/uniqueItems`,error:`Duplicate items at indexes ${l} and ${c}.`}),l=Number.MAX_SAFE_INTEGER,c=Number.MAX_SAFE_INTEGER)}}}else if($==="number"){if(r==="4"?(C!==void 0&&(J===!0&&e<=C||e<C)&&f.push({instanceLocation:o,keyword:"minimum",keywordLocation:`${s}/minimum`,error:`${e} is less than ${J?"or equal to ":""} ${C}.`}),N!==void 0&&(W===!0&&e>=N||e>N)&&f.push({instanceLocation:o,keyword:"maximum",keywordLocation:`${s}/maximum`,error:`${e} is greater than ${W?"or equal to ":""} ${N}.`})):(C!==void 0&&e<C&&f.push({instanceLocation:o,keyword:"minimum",keywordLocation:`${s}/minimum`,error:`${e} is less than ${C}.`}),N!==void 0&&e>N&&f.push({instanceLocation:o,keyword:"maximum",keywordLocation:`${s}/maximum`,error:`${e} is greater than ${N}.`}),J!==void 0&&e<=J&&f.push({instanceLocation:o,keyword:"exclusiveMinimum",keywordLocation:`${s}/exclusiveMinimum`,error:`${e} is less than ${J}.`}),W!==void 0&&e>=W&&f.push({instanceLocation:o,keyword:"exclusiveMaximum",keywordLocation:`${s}/exclusiveMaximum`,error:`${e} is greater than or equal to ${W}.`})),H!==void 0){let m=e%H;Math.abs(0-m)>=11920929e-14&&Math.abs(H-m)>=11920929e-14&&f.push({instanceLocation:o,keyword:"multipleOf",keywordLocation:`${s}/multipleOf`,error:`${e} is not a multiple of ${H}.`})}}else if($==="string"){let m=F===void 0&&B===void 0?0:Ne(e);F!==void 0&&m<F&&f.push({instanceLocation:o,keyword:"minLength",keywordLocation:`${s}/minLength`,error:`String is too short (${m} < ${F}).`}),B!==void 0&&m>B&&f.push({instanceLocation:o,keyword:"maxLength",keywordLocation:`${s}/maxLength`,error:`String is too long (${m} > ${B}).`}),ke!==void 0&&!new RegExp(ke,"u").test(e)&&f.push({instanceLocation:o,keyword:"pattern",keywordLocation:`${s}/pattern`,error:"String does not match pattern."}),U!==void 0&&me[U]&&!me[U](e)&&f.push({instanceLocation:o,keyword:"format",keywordLocation:`${s}/format`,error:`String does not match format "${U}".`})}return{valid:f.length===0,errors:f}}var Y=class{constructor(t,r="2019-09",n=!0){_(this,"schema");_(this,"draft");_(this,"shortCircuit");_(this,"lookup");this.schema=t,this.draft=r,this.shortCircuit=n,this.lookup=E(t)}validate(t){return w(t,this.schema,this.draft,this.lookup,this.shortCircuit)}addSchema(t,r){r&&(t={...t,$id:r}),E(t,this.lookup)}};var Ae="Failed to parse input arguments",Q="Tool was executed but the invocation failed. For example, the script function threw an error",ce="Tool was cancelled",Z={type:"object",properties:{}},xt=["draft-2020-12","draft-07"],bt="__isWebMCPPolyfill",ze=Symbol("standardValidator"),g={installed:!1,previousNavigatorModelContextDescriptor:void 0,previousNavigatorModelContextTestingDescriptor:void 0,previousDocumentModelContextDescriptor:void 0,installedNavigatorModelContext:!1,installedNavigatorModelContextTesting:!1,installedDocumentModelContext:!1},vt=class extends EventTarget{constructor(){super(...arguments);_(this,"tools",new Map);_(this,"testingShim",null);_(this,"_ontoolchange",null);_(this,"unregisterToolDeprecationWarned",!1)}get ontoolchange(){return this._ontoolchange}set ontoolchange(t){this._ontoolchange=t}registerTool(t,r){var d;let n=r==null?void 0:r.signal;if(n!=null&&n.aborted){console.warn(`[WebMCPPolyfill] registerTool("${(d=t==null?void 0:t.name)!=null?d:"<unknown>"}") skipped: options.signal was already aborted.`);return}let i=Et(t,this.tools);this.tools.set(i.name,i),this.notifyToolsChanged(),n&&n.addEventListener("abort",()=>{this.tools.delete(i.name)&&this.notifyToolsChanged()},{once:!0})}unregisterTool(t){this.warnUnregisterToolDeprecationOnce();let r=_t(t);this.tools.delete(r)&&this.notifyToolsChanged()}getTools(){return Promise.resolve(this.getRegisteredToolInfos())}executeTool(t,r,n){return this.executeToolByName(t.name,r,n,!1)}getTestingShim(){return this.testingShim||(this.testingShim=new St(this)),this.testingShim}getToolInfos(){return[...this.tools.values()].map(t=>{var n;let r;try{r=JSON.stringify((n=t.inputSchema)!=null?n:{type:"object"})}catch{r='{"type":"object"}'}return{name:t.name,description:t.description,inputSchema:r}})}getRegisteredToolInfos(){return this.getToolInfos().map(t=>{var n,i,d;let r=this.tools.get(t.name);return{...t,title:(n=r==null?void 0:r.title)!=null?n:"",origin:(d=(i=globalThis.location)==null?void 0:i.origin)!=null?d:"",window:globalThis.window}})}async executeToolForTesting(t,r,n){return this.executeToolByName(t,r,n,!0)}async executeToolByName(t,r,n,i){var $;if(($=n==null?void 0:n.signal)!=null&&$.aborted)throw P(ce);let d=this.tools.get(t);if(!d)throw P(`Tool not found: ${t}`);let o=Tt(r),s=await At(o,d);if(s)throw P(s);let h=!0,x={requestUserInteraction:async T=>{if(!h)throw new Error(`ModelContextClient for tool "${t}" is no longer active after execute() resolved`);if(typeof T!="function")throw new TypeError("requestUserInteraction(callback) requires a function callback");return T()}};try{let T=d.execute(o,x),q=await Ut(Promise.resolve(T),n==null?void 0:n.signal);if(i)return qt(Jt(q));let M=JSON.stringify(q);return M===void 0?null:M}catch(T){throw P(T instanceof Error?`${Q}: ${T.message}`:Q)}finally{h=!1}}notifyToolsChanged(){queueMicrotask(()=>{var r,n;let t=new Event("toolchange");try{(r=this._ontoolchange)==null||r.call(this,t)}catch(i){console.warn("[WebMCPPolyfill] navigator.modelContext.ontoolchange handler threw:",i)}this.dispatchEvent(t),(n=this.testingShim)==null||n.dispatchToolChange()})}warnUnregisterToolDeprecationOnce(){this.unregisterToolDeprecationWarned||(this.unregisterToolDeprecationWarned=!0,console.warn("[WebMCPPolyfill] navigator.modelContext.unregisterTool() is deprecated. The April 23, 2026 WebMCP draft removed it in favor of registerTool(tool, { signal }) \u2014 pass an AbortSignal and abort it to unregister."))}},St=class extends EventTarget{constructor(t){super();_(this,"context");_(this,"_ontoolchange",null);this.context=t}listTools(){return this.context.getToolInfos()}executeTool(t,r,n){return this.context.executeToolForTesting(t,r,n)}getCrossDocumentScriptToolResult(){return Promise.resolve("[]")}get ontoolchange(){return this._ontoolchange}set ontoolchange(t){this._ontoolchange=t}registerToolsChangedCallback(t){if(typeof t!="function")throw new TypeError("Failed to execute 'registerToolsChangedCallback' on 'ModelContextTesting': parameter 1 is not of type 'Function'.");this.addEventListener("toolchange",t)}dispatchToolChange(){var r;let t=new Event("toolchange");try{(r=this._ontoolchange)==null||r.call(this,t)}catch(n){console.warn("[WebMCPPolyfill] ontoolchange handler threw:",n)}this.dispatchEvent(t),this.dispatchEvent(new Event("toolschanged"))}};function P(e){try{return new DOMException(e,"UnknownError")}catch{let t=new Error(e);return t.name="UnknownError",t}}function Tt(e){let t;try{t=JSON.parse(e)}catch{throw P(Ae)}if(!t||typeof t!="object"||Array.isArray(t))throw P(Ae);return t}function _t(e){if(typeof e=="string")return e;if(b(e)&&typeof e.name=="string")return e.name;throw new TypeError("Failed to execute 'unregisterTool' on 'ModelContext': parameter 1 must be a string or an object with a string name.")}function b(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function De(e){if(!b(e))return null;let t=e["~standard"];return b(t)?t:null}function It(e){let t=De(e);return!!(t&&t.version===1&&typeof t.validate=="function")}function kt(e){let t=De(e);return!t||t.version!==1||!b(t.jsonSchema)?!1:typeof t.jsonSchema.input=="function"}function X(e){return{"~standard":{version:1,vendor:"@mcp-b/webmcp-polyfill-json-schema",validate(t){if(!b(t))return{issues:[{message:"expected object arguments"}]};let r=Mt(t,e);return r?{issues:[r]}:{value:t}}}}}function Pt(e){for(let t of xt)try{let r=e["~standard"].jsonSchema.input({target:t});return Re(r),r}catch(r){console.warn(`[WebMCPPolyfill] Standard JSON Schema conversion failed for target "${t}":`,r)}throw new Error("Failed to convert Standard JSON Schema inputSchema to a JSON Schema object")}function Ot(e){if(e===void 0){let r=Z;return{inputSchema:r,standardValidator:X(r)}}if(kt(e)){let r=Pt(e);return{inputSchema:r,standardValidator:X(r)}}if(It(e))return{inputSchema:Z,standardValidator:e};if(Re(e),Object.keys(e).length===0)return{inputSchema:Z,standardValidator:X(Z)};let t=e.type===void 0?{type:"object",...e}:e;return{inputSchema:t,standardValidator:X(t)}}function Re(e){if(!b(e))throw new Error("inputSchema must be a JSON Schema object");j(e,"$")}function j(e,t){let r=e.type;if(r!==void 0&&typeof r!="string"&&!(Array.isArray(r)&&r.every(s=>typeof s=="string"&&s.length>0)))throw new Error(`Invalid JSON Schema at ${t}: "type" must be a string or string[]`);let n=e.required;if(n!==void 0&&!(Array.isArray(n)&&n.every(s=>typeof s=="string")))throw new Error(`Invalid JSON Schema at ${t}: "required" must be an array of strings`);let i=e.properties;if(i!==void 0){if(!b(i))throw new Error(`Invalid JSON Schema at ${t}: "properties" must be an object`);for(let[s,h]of Object.entries(i)){if(!b(h))throw new Error(`Invalid JSON Schema at ${t}.properties.${s}: expected object schema`);j(h,`${t}.properties.${s}`)}}let d=e.items;if(d!==void 0)if(Array.isArray(d))for(let[s,h]of d.entries()){if(!b(h))throw new Error(`Invalid JSON Schema at ${t}.items[${s}]: expected object schema`);j(h,`${t}.items[${s}]`)}else if(b(d))j(d,`${t}.items`);else throw new Error(`Invalid JSON Schema at ${t}: "items" must be an object or object[]`);for(let s of["allOf","anyOf","oneOf"]){let h=e[s];if(h!==void 0){if(!Array.isArray(h))throw new Error(`Invalid JSON Schema at ${t}: "${s}" must be an array`);for(let[x,$]of h.entries()){if(!b($))throw new Error(`Invalid JSON Schema at ${t}.${s}[${x}]: expected object schema`);j($,`${t}.${s}[${x}]`)}}}let o=e.not;if(o!==void 0){if(!b(o))throw new Error(`Invalid JSON Schema at ${t}: "not" must be an object schema`);j(o,`${t}.not`)}try{JSON.stringify(e)}catch{throw new Error(`Invalid JSON Schema at ${t}: schema must be JSON-serializable`)}}function Et(e,t){if(!e||typeof e!="object")throw new TypeError("registerTool(tool) requires a tool object");if(typeof e.name!="string"||e.name.length===0)throw new TypeError('Tool "name" must be a non-empty string');if(typeof e.description!="string"||e.description.length===0)throw new TypeError('Tool "description" must be a non-empty string');if(typeof e.execute!="function")throw new TypeError('Tool "execute" must be a function');if(t.has(e.name))throw new Error(`Tool already registered: ${e.name}`);let r=Ot(e.inputSchema),n=e.annotations,i=n?{...n,...n.readOnlyHint==="true"?{readOnlyHint:!0}:n.readOnlyHint==="false"?{readOnlyHint:!1}:{},...n.destructiveHint==="true"?{destructiveHint:!0}:n.destructiveHint==="false"?{destructiveHint:!1}:{},...n.idempotentHint==="true"?{idempotentHint:!0}:n.idempotentHint==="false"?{idempotentHint:!1}:{},...n.openWorldHint==="true"?{openWorldHint:!0}:n.openWorldHint==="false"?{openWorldHint:!1}:{}}:void 0;return{...e,...i?{annotations:i}:{},inputSchema:r.inputSchema,[ze]:r.standardValidator}}function Mt(e,t){let r=new Y(t,"2020-12",!0).validate(e);if(r.valid)return null;let n=r.errors[r.errors.length-1];return n?{message:n.error}:{message:"Input validation failed"}}function Ct(e){if(!e||e.length===0)return null;let t=e.map(r=>b(r)&&"key"in r?r.key:r).map(r=>String(r)).filter(r=>r.length>0);return t.length===0?null:t.join(".")}async function Nt(e,t){let r;try{r=await Promise.resolve(t["~standard"].validate(e))}catch(d){let o=d instanceof Error?`: ${d.message}`:"";return console.error("[WebMCPPolyfill] Standard Schema validation threw unexpectedly:",d),`Input validation error: schema validation failed${o}`}if(!r.issues||r.issues.length===0)return null;let n=r.issues[0];if(!n)return"Input validation error";let i=Ct(n==null?void 0:n.path);return i?`Input validation error: ${n.message} at ${i}`:`Input validation error: ${n.message}`}async function At(e,t){return Nt(e,t[ze])}function jt(e){return b(e)&&Array.isArray(e.content)}function zt(e){return e===null||typeof e=="string"||typeof e=="number"||typeof e=="boolean"}function pe(e){return zt(e)?Number.isFinite(e)||typeof e!="number":Array.isArray(e)?e.every(t=>pe(t)):b(e)?Object.values(e).every(t=>pe(t)):!1}function Dt(e){if(!(!b(e)||!pe(e)))return e}function Rt(e){var t;if(typeof e=="string")return e;try{return(t=JSON.stringify(e))!=null?t:String(e)}catch{return String(e)}}function Jt(e){if(jt(e))return e;let t=Dt(e);return{content:[{type:"text",text:Rt(e)}],...t?{structuredContent:t}:{},isError:!1}}function Wt(e){var t;for(let r of(t=e.content)!=null?t:[])if(r.type==="text"&&"text"in r&&typeof r.text=="string")return r.text;return null}function qt(e){var r;if(e.isError)throw P(((r=Wt(e))==null?void 0:r.replace(/^Error:\s*/i,"").trim())||Q);let t=e.metadata;if(t&&typeof t=="object"&&t.willNavigate)return null;try{return JSON.stringify(e)}catch{throw P(Q)}}function Ut(e,t){return t?t.aborted?Promise.reject(P(ce)):new Promise((r,n)=>{let i=()=>{d(),n(P(ce))},d=()=>{t.removeEventListener("abort",i)};t.addEventListener("abort",i,{once:!0}),e.then(o=>{d(),r(o)},o=>{d(),n(o)})}):e}function Je(){return typeof navigator!="undefined"?navigator:null}function We(){return typeof document!="undefined"?document:null}function Vt(e,t,r){Object.defineProperty(e,t,{configurable:!0,enumerable:!0,writable:!1,value:r})}function je(e,t){Object.defineProperty(e,"modelContext",{configurable:!0,enumerable:!0,writable:!1,value:t})}var L=!1;function Ht(e,t){Object.defineProperty(e,"modelContext",{configurable:!0,enumerable:!0,get(){return L||(L=!0,console.warn("[WebMCPPolyfill] navigator.modelContext is deprecated. The May 27, 2026 WebMCP draft moved the modelContext getter from Navigator to Document \u2014 use document.modelContext instead. See https://github.com/webmachinelearning/webmcp/pull/184.")),t}})}function qe(e){var h;let t=Je(),r=We();if(!t&&!r||(r==null?void 0:r.modelContext))return;let i=t==null?void 0:t.modelContext,d=!!i;if(g.installed&&Ft(),r&&i){g.previousDocumentModelContextDescriptor=Object.getOwnPropertyDescriptor(r,"modelContext"),je(r,i),g.installedDocumentModelContext=!0,g.installed=!0;return}if(d)return;let o=new vt,s=o;if(s[bt]=!0,r&&(g.previousDocumentModelContextDescriptor=Object.getOwnPropertyDescriptor(r,"modelContext"),je(r,s),g.installedDocumentModelContext=!0),t){g.previousNavigatorModelContextDescriptor=Object.getOwnPropertyDescriptor(t,"modelContext"),g.previousNavigatorModelContextTestingDescriptor=Object.getOwnPropertyDescriptor(t,"modelContextTesting"),L=!1,Ht(t,s),g.installedNavigatorModelContext=!0;let x=(h=e==null?void 0:e.installTestingShim)!=null?h:"if-missing",$=!!t.modelContextTesting;(x==="always"||(x===!0||x==="if-missing")&&!$)&&(Vt(t,"modelContextTesting",o.getTestingShim()),g.installedNavigatorModelContextTesting=!0)}g.installed=!0}function Ft(){if(!g.installed)return;let e=(n,i,d)=>{if(d){Object.defineProperty(n,i,d);return}delete n[i]},t=Je(),r=We();r&&g.installedDocumentModelContext&&e(r,"modelContext",g.previousDocumentModelContextDescriptor),t&&g.installedNavigatorModelContext&&e(t,"modelContext",g.previousNavigatorModelContextDescriptor),t&&g.installedNavigatorModelContextTesting&&e(t,"modelContextTesting",g.previousNavigatorModelContextTestingDescriptor),g.installed=!1,g.previousDocumentModelContextDescriptor=void 0,g.previousNavigatorModelContextDescriptor=void 0,g.previousNavigatorModelContextTestingDescriptor=void 0,g.installedDocumentModelContext=!1,g.installedNavigatorModelContext=!1,g.installedNavigatorModelContextTesting=!1,L=!1}if(typeof window!="undefined"&&typeof document!="undefined"){let e=window.__webMCPPolyfillOptions;if((e==null?void 0:e.autoInitialize)!==!1)try{qe(e)}catch(t){console.error("[WebMCPPolyfill] Auto-initialization failed:",t)}}export{qe as initializeWebMCPPolyfill};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/persona",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.33.0",
|
|
4
4
|
"description": "Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"access": "public"
|
|
110
110
|
},
|
|
111
111
|
"scripts": {
|
|
112
|
-
"build": "rimraf dist && pnpm run build:styles && pnpm run build:client && pnpm run build:installer && pnpm run build:launcher && pnpm run build:theme-ref && pnpm run build:codegen && pnpm run build:theme-editor && pnpm run build:testing && pnpm run build:smart-dom-reader && pnpm run build:plugin-kit && pnpm run build:animations",
|
|
112
|
+
"build": "rimraf dist && pnpm run build:styles && pnpm run build:client && pnpm run build:installer && pnpm run build:launcher && pnpm run build:webmcp-polyfill && pnpm run build:theme-ref && pnpm run build:codegen && pnpm run build:theme-editor && pnpm run build:testing && pnpm run build:smart-dom-reader && pnpm run build:plugin-kit && pnpm run build:animations",
|
|
113
113
|
"build:plugin-kit": "tsup src/plugin-kit.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
|
114
114
|
"build:theme-editor": "tsup src/theme-editor.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
|
115
115
|
"build:testing": "tsup src/testing.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting",
|
|
@@ -118,7 +118,8 @@
|
|
|
118
118
|
"build:theme-ref": "tsup src/theme-reference.ts --format esm,cjs --minify --dts",
|
|
119
119
|
"build:codegen": "tsup src/codegen.ts --format esm,cjs --minify --dts",
|
|
120
120
|
"build:styles": "node -e \"const fs=require('fs');fs.mkdirSync('dist',{recursive:true});fs.copyFileSync('src/styles/widget.css','dist/widget.css');\"",
|
|
121
|
-
"build:client": "tsup src/index.ts --format esm,cjs --minify --sourcemap --splitting false --dts --loader \".css=text\" && tsup
|
|
121
|
+
"build:client": "tsup src/index.ts --format esm,cjs --minify --sourcemap --splitting false --dts --loader \".css=text\" && tsup --config tsup.global.config.ts && node -e \"const fs=require('fs');for(const ext of ['.global.js','.global.js.map']){const from='dist/index-global'+ext;if(fs.existsSync(from))fs.renameSync(from,'dist/index'+ext);}\"",
|
|
122
|
+
"build:webmcp-polyfill": "tsup --config tsup.webmcp-polyfill.config.ts",
|
|
122
123
|
"build:installer": "tsup src/install.ts --format iife --global-name SiteAgentInstaller --out-dir dist --minify --sourcemap --no-splitting",
|
|
123
124
|
"build:launcher": "tsup src/launcher-global.ts --format iife --global-name AgentWidgetLauncher --minify --sourcemap --splitting false --out-dir dist && node -e \"const fs=require('fs');for(const ext of ['.global.js','.global.js.map']){const from='dist/launcher-global'+ext;if(fs.existsSync(from))fs.renameSync(from,'dist/launcher'+ext);}\"",
|
|
124
125
|
"lint": "eslint . --ext .ts",
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
ASK_USER_QUESTION_CLIENT_TOOL,
|
|
4
|
+
ASK_USER_QUESTION_PARAMETERS_SCHEMA,
|
|
5
|
+
builtInClientToolsForDispatch,
|
|
6
|
+
} from "./ask-user-question-tool";
|
|
7
|
+
import {
|
|
8
|
+
ASK_USER_QUESTION_MAX,
|
|
9
|
+
ASK_USER_QUESTION_TOOL_NAME,
|
|
10
|
+
} from "./components/ask-user-question-bubble";
|
|
11
|
+
import { AgentWidgetClient } from "./client";
|
|
12
|
+
import { computeClientToolsFingerprint } from "./webmcp-bridge";
|
|
13
|
+
import type { AgentWidgetConfig } from "./types";
|
|
14
|
+
|
|
15
|
+
describe("ASK_USER_QUESTION_CLIENT_TOOL definition", () => {
|
|
16
|
+
it("matches the renderer's tool name and origin/annotation contract", () => {
|
|
17
|
+
expect(ASK_USER_QUESTION_CLIENT_TOOL.name).toBe(ASK_USER_QUESTION_TOOL_NAME);
|
|
18
|
+
// `'sdk'` keeps the bare name on the wire (the server only prefixes
|
|
19
|
+
// `origin: 'webmcp'` tools) so the step_await routes to the answer sheet,
|
|
20
|
+
// not the WebMCP bridge. `'local'` is NOT a valid server-side origin.
|
|
21
|
+
expect(ASK_USER_QUESTION_CLIENT_TOOL.origin).toBe("sdk");
|
|
22
|
+
expect(ASK_USER_QUESTION_CLIENT_TOOL.annotations?.readOnlyHint).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("bounds questions to the renderer cap and options to 2-4", () => {
|
|
26
|
+
const questions = ASK_USER_QUESTION_PARAMETERS_SCHEMA.properties.questions;
|
|
27
|
+
expect(questions.minItems).toBe(1);
|
|
28
|
+
expect(questions.maxItems).toBe(ASK_USER_QUESTION_MAX);
|
|
29
|
+
const options = questions.items.properties.options;
|
|
30
|
+
expect(options.minItems).toBe(2);
|
|
31
|
+
expect(options.maxItems).toBe(4);
|
|
32
|
+
expect(questions.items.required).toEqual(["question", "options"]);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("builtInClientToolsForDispatch", () => {
|
|
37
|
+
it("returns nothing by default (expose is opt-in)", () => {
|
|
38
|
+
expect(builtInClientToolsForDispatch(undefined)).toEqual([]);
|
|
39
|
+
expect(builtInClientToolsForDispatch({} as AgentWidgetConfig)).toEqual([]);
|
|
40
|
+
expect(
|
|
41
|
+
builtInClientToolsForDispatch({
|
|
42
|
+
features: { askUserQuestion: {} },
|
|
43
|
+
} as AgentWidgetConfig)
|
|
44
|
+
).toEqual([]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns the tool when expose is true", () => {
|
|
48
|
+
expect(
|
|
49
|
+
builtInClientToolsForDispatch({
|
|
50
|
+
features: { askUserQuestion: { expose: true } },
|
|
51
|
+
} as AgentWidgetConfig)
|
|
52
|
+
).toEqual([ASK_USER_QUESTION_CLIENT_TOOL]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("ignores expose when the answer sheet is disabled", () => {
|
|
56
|
+
// Offering the agent a question tool the widget can't render an answer
|
|
57
|
+
// UI for would park the execution on a generic tool bubble forever.
|
|
58
|
+
expect(
|
|
59
|
+
builtInClientToolsForDispatch({
|
|
60
|
+
features: { askUserQuestion: { expose: true, enabled: false } },
|
|
61
|
+
} as AgentWidgetConfig)
|
|
62
|
+
).toEqual([]);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("AgentWidgetClient - built-in ask_user_question exposure", () => {
|
|
67
|
+
const captureDispatchBody = () => {
|
|
68
|
+
const captured: { body: string | null } = { body: null };
|
|
69
|
+
global.fetch = vi
|
|
70
|
+
.fn()
|
|
71
|
+
.mockImplementation(async (_url: string, options: { body: string }) => {
|
|
72
|
+
captured.body = options.body;
|
|
73
|
+
const encoder = new TextEncoder();
|
|
74
|
+
const stream = new ReadableStream({
|
|
75
|
+
start(controller) {
|
|
76
|
+
controller.enqueue(encoder.encode('data: {"type":"done"}\n\n'));
|
|
77
|
+
controller.close();
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
return new Response(stream, {
|
|
81
|
+
headers: { "Content-Type": "text/event-stream" },
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
return captured;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const userMessage = () => ({
|
|
88
|
+
id: "u1",
|
|
89
|
+
role: "user" as const,
|
|
90
|
+
content: "hi",
|
|
91
|
+
createdAt: new Date().toISOString(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("ships ask_user_question on clientTools when expose is on (no WebMCP)", async () => {
|
|
95
|
+
const captured = captureDispatchBody();
|
|
96
|
+
const client = new AgentWidgetClient({
|
|
97
|
+
apiUrl: "http://localhost:8000",
|
|
98
|
+
features: { askUserQuestion: { expose: true } },
|
|
99
|
+
});
|
|
100
|
+
await client.dispatch({ messages: [userMessage()] }, () => undefined);
|
|
101
|
+
const parsed = JSON.parse(captured.body!);
|
|
102
|
+
expect(parsed.clientTools).toEqual([ASK_USER_QUESTION_CLIENT_TOOL]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("omits clientTools entirely when expose is off and no WebMCP tools exist", async () => {
|
|
106
|
+
const captured = captureDispatchBody();
|
|
107
|
+
const client = new AgentWidgetClient({
|
|
108
|
+
apiUrl: "http://localhost:8000",
|
|
109
|
+
});
|
|
110
|
+
await client.dispatch({ messages: [userMessage()] }, () => undefined);
|
|
111
|
+
const parsed = JSON.parse(captured.body!);
|
|
112
|
+
expect(parsed.clientTools).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("leads the WebMCP snapshot when both are present", async () => {
|
|
116
|
+
const captured = captureDispatchBody();
|
|
117
|
+
const client = new AgentWidgetClient({
|
|
118
|
+
apiUrl: "http://localhost:8000",
|
|
119
|
+
features: { askUserQuestion: { expose: true } },
|
|
120
|
+
});
|
|
121
|
+
(
|
|
122
|
+
client as unknown as {
|
|
123
|
+
webMcpBridge: { snapshotForDispatch: () => unknown[] } | null;
|
|
124
|
+
}
|
|
125
|
+
).webMcpBridge = {
|
|
126
|
+
snapshotForDispatch: () => [
|
|
127
|
+
{ name: "search", description: "s", origin: "webmcp" },
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
await client.dispatch({ messages: [userMessage()] }, () => undefined);
|
|
131
|
+
const parsed = JSON.parse(captured.body!);
|
|
132
|
+
expect(parsed.clientTools).toEqual([
|
|
133
|
+
ASK_USER_QUESTION_CLIENT_TOOL,
|
|
134
|
+
{ name: "search", description: "s", origin: "webmcp" },
|
|
135
|
+
]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("changes the clientTools fingerprint when toggled (diff-only resend)", () => {
|
|
139
|
+
// In client-token mode the widget only resends the full clientTools[]
|
|
140
|
+
// when the fingerprint changes — toggling expose must change it so the
|
|
141
|
+
// server actually learns about (or forgets) the built-in tool.
|
|
142
|
+
const webMcpOnly = [{ name: "search", description: "s" }];
|
|
143
|
+
const withBuiltIn = [ASK_USER_QUESTION_CLIENT_TOOL, ...webMcpOnly];
|
|
144
|
+
expect(computeClientToolsFingerprint(withBuiltIn)).not.toBe(
|
|
145
|
+
computeClientToolsFingerprint(webMcpOnly)
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in `ask_user_question` client tool.
|
|
3
|
+
*
|
|
4
|
+
* The widget can advertise this tool to the agent on every dispatch via
|
|
5
|
+
* `clientTools[]` (set `features.askUserQuestion.expose: true`) — the same
|
|
6
|
+
* wire surface WebMCP page tools ride. The server registers it as a LOCAL
|
|
7
|
+
* tool under its bare name (`origin: 'sdk'` tools are not `webmcp:`-prefixed),
|
|
8
|
+
* so when the model calls it the execution pauses with a `step_await`
|
|
9
|
+
* (`awaitReason: "local_tool_required"`), the widget's answer-pill sheet
|
|
10
|
+
* renders, and `session.resolveAskUserQuestion()` resumes the execution with
|
|
11
|
+
* the structured answers.
|
|
12
|
+
*
|
|
13
|
+
* This replaces the previous integrator burden of hand-declaring the tool in
|
|
14
|
+
* a flow's `runtimeTools` and keeping that schema in sync with the renderer.
|
|
15
|
+
* Flows that already declare `ask_user_question` server-side should leave
|
|
16
|
+
* `expose` off — the model would otherwise see the tool twice.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
ASK_USER_QUESTION_MAX,
|
|
21
|
+
ASK_USER_QUESTION_TOOL_NAME,
|
|
22
|
+
} from "./components/ask-user-question-bubble";
|
|
23
|
+
import {
|
|
24
|
+
SUGGEST_REPLIES_CLIENT_TOOL,
|
|
25
|
+
shouldExposeSuggestReplies,
|
|
26
|
+
} from "./suggest-replies-tool";
|
|
27
|
+
import type { AgentWidgetConfig, ClientToolDefinition } from "./types";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* JSON Schema for the tool's parameters. Mirrors {@link AskUserQuestionPayload}
|
|
31
|
+
* — the shape `parseAskUserQuestionPayload` hydrates the answer sheet from —
|
|
32
|
+
* so the schema the model is held to and the schema the renderer expects can
|
|
33
|
+
* never drift.
|
|
34
|
+
*/
|
|
35
|
+
export const ASK_USER_QUESTION_PARAMETERS_SCHEMA = {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
questions: {
|
|
39
|
+
type: "array",
|
|
40
|
+
minItems: 1,
|
|
41
|
+
maxItems: ASK_USER_QUESTION_MAX,
|
|
42
|
+
description: "Questions to ask the user. Prefer a single question.",
|
|
43
|
+
items: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
question: {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "The complete question, ending with a question mark.",
|
|
49
|
+
},
|
|
50
|
+
header: {
|
|
51
|
+
type: "string",
|
|
52
|
+
maxLength: 12,
|
|
53
|
+
description: 'Short topic label, e.g. "Auth method".',
|
|
54
|
+
},
|
|
55
|
+
options: {
|
|
56
|
+
type: "array",
|
|
57
|
+
minItems: 2,
|
|
58
|
+
maxItems: 4,
|
|
59
|
+
description:
|
|
60
|
+
'2-4 distinct choices. Do NOT add an "Other" option — free text is automatic.',
|
|
61
|
+
items: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
label: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Concise choice text (1-5 words).",
|
|
67
|
+
},
|
|
68
|
+
description: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "What the option means or implies.",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ["label"],
|
|
74
|
+
additionalProperties: false,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
multiSelect: {
|
|
78
|
+
type: "boolean",
|
|
79
|
+
description: "Allow selecting multiple options. Default false.",
|
|
80
|
+
},
|
|
81
|
+
allowFreeText: {
|
|
82
|
+
type: "boolean",
|
|
83
|
+
description: "Show a free-text input. Default true.",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
required: ["question", "options"],
|
|
87
|
+
additionalProperties: false,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
required: ["questions"],
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
} as const;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The `ClientToolDefinition` shipped on `dispatch.clientTools[]` when
|
|
97
|
+
* `features.askUserQuestion.expose` is on. Exported so integrators who prefer
|
|
98
|
+
* declaring the tool server-side (a flow's `runtimeTools`) can reuse the same
|
|
99
|
+
* description and schema instead of hand-writing them.
|
|
100
|
+
*/
|
|
101
|
+
export const ASK_USER_QUESTION_CLIENT_TOOL: ClientToolDefinition = {
|
|
102
|
+
name: ASK_USER_QUESTION_TOOL_NAME,
|
|
103
|
+
description:
|
|
104
|
+
"Ask the user multiple-choice questions and wait for their answers. Use " +
|
|
105
|
+
"only when blocked on a decision that is the user's to make — a " +
|
|
106
|
+
"preference, a choice between valid approaches, or information you " +
|
|
107
|
+
"cannot infer. Each question offers 2-4 options plus an automatic " +
|
|
108
|
+
"free-text input. The result maps each question to its answer (an array " +
|
|
109
|
+
"when multiSelect); a question absent from the result was skipped.",
|
|
110
|
+
parametersSchema: ASK_USER_QUESTION_PARAMETERS_SCHEMA,
|
|
111
|
+
origin: "sdk",
|
|
112
|
+
annotations: { readOnlyHint: true },
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Built-in client tools to append to a dispatch's `clientTools[]` for the
|
|
117
|
+
* given widget config: `ask_user_question` and `suggest_replies`; future
|
|
118
|
+
* built-in local tools join here.
|
|
119
|
+
*
|
|
120
|
+
* Each tool is gated on BOTH of its feature flags: `expose` opts the tool
|
|
121
|
+
* into the agent's catalog, and `enabled !== false` guarantees the widget can
|
|
122
|
+
* actually render UI (and, for fire-and-forget tools, auto-resume) for it —
|
|
123
|
+
* exposing a tool with its feature disabled would park the execution on a
|
|
124
|
+
* generic tool bubble with no way to advance.
|
|
125
|
+
*/
|
|
126
|
+
export const builtInClientToolsForDispatch = (
|
|
127
|
+
config: AgentWidgetConfig | undefined,
|
|
128
|
+
): ClientToolDefinition[] => {
|
|
129
|
+
const tools: ClientToolDefinition[] = [];
|
|
130
|
+
const ask = config?.features?.askUserQuestion;
|
|
131
|
+
if (ask?.expose === true && ask.enabled !== false) {
|
|
132
|
+
tools.push(ASK_USER_QUESTION_CLIENT_TOOL);
|
|
133
|
+
}
|
|
134
|
+
if (shouldExposeSuggestReplies(config)) {
|
|
135
|
+
tools.push(SUGGEST_REPLIES_CLIENT_TOOL);
|
|
136
|
+
}
|
|
137
|
+
return tools;
|
|
138
|
+
};
|
package/src/client.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
WebMcpConfirmHandler
|
|
25
25
|
} from "./types";
|
|
26
26
|
import { WebMcpBridge, computeClientToolsFingerprint, isWebMcpToolName } from "./webmcp-bridge";
|
|
27
|
+
import { builtInClientToolsForDispatch } from "./ask-user-question-tool";
|
|
27
28
|
import {
|
|
28
29
|
extractTextFromJson,
|
|
29
30
|
createPlainTextParser,
|
|
@@ -201,6 +202,28 @@ export class AgentWidgetClient {
|
|
|
201
202
|
config.webmcp?.enabled === true ? new WebMcpBridge(config.webmcp) : null;
|
|
202
203
|
}
|
|
203
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Refresh config in place WITHOUT tearing down the live connection or the
|
|
207
|
+
* WebMCP bridge. `AgentWidgetSession.updateConfig` calls this when only
|
|
208
|
+
* connection-irrelevant fields changed (theme, copy, layout, suggestions, …),
|
|
209
|
+
* so a UI update that lands mid-turn — e.g. a `webmcp:*` tool restyling the
|
|
210
|
+
* widget while the agent's turn is still streaming — doesn't abandon the
|
|
211
|
+
* in-flight stream/resume. Connection or request-shaping changes (apiUrl,
|
|
212
|
+
* clientToken, webmcp, headers, parser, …) take the full client rebuild path
|
|
213
|
+
* in the session instead, which is the only place the bridge is recreated.
|
|
214
|
+
*
|
|
215
|
+
* Only the live-read `config` is refreshed (e.g. `iterationDisplay`); the
|
|
216
|
+
* constructor-derived request-shaping fields (apiUrl, headers, parser,
|
|
217
|
+
* contextProviders, middleware, …) are left untouched because the session
|
|
218
|
+
* routes any change to those down the full-rebuild path instead — so they are
|
|
219
|
+
* guaranteed unchanged here. The `webMcpBridge` instance and its
|
|
220
|
+
* installed-polyfill memo are deliberately preserved, which keeps any
|
|
221
|
+
* in-flight resolve alive.
|
|
222
|
+
*/
|
|
223
|
+
public updateConfig(next: AgentWidgetConfig): void {
|
|
224
|
+
this.config = next;
|
|
225
|
+
}
|
|
226
|
+
|
|
204
227
|
/**
|
|
205
228
|
* Set callback for capturing raw SSE events
|
|
206
229
|
*/
|
|
@@ -1035,11 +1058,18 @@ export class AgentWidgetClient {
|
|
|
1035
1058
|
}
|
|
1036
1059
|
};
|
|
1037
1060
|
|
|
1038
|
-
//
|
|
1039
|
-
//
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1061
|
+
// Client tools: built-in widget tools (ask_user_question, when exposed)
|
|
1062
|
+
// plus the per-turn WebMCP page-registry snapshot. Name collisions are
|
|
1063
|
+
// impossible — WebMCP entries are `webmcp:`-prefixed server-side while
|
|
1064
|
+
// `sdk`-origin built-ins keep bare names. Both kinds ride the same
|
|
1065
|
+
// diff-only fingerprint path in client-token mode. Kept to a single await
|
|
1066
|
+
// so dispatch microtask timing is unchanged.
|
|
1067
|
+
const clientTools = [
|
|
1068
|
+
...builtInClientToolsForDispatch(this.config),
|
|
1069
|
+
...((await this.webMcpBridge?.snapshotForDispatch()) ?? []),
|
|
1070
|
+
];
|
|
1071
|
+
if (clientTools.length > 0) {
|
|
1072
|
+
payload.clientTools = clientTools;
|
|
1043
1073
|
}
|
|
1044
1074
|
|
|
1045
1075
|
// Add context from providers
|
|
@@ -1096,10 +1126,14 @@ export class AgentWidgetClient {
|
|
|
1096
1126
|
...(this.config.flowId && { flowId: this.config.flowId })
|
|
1097
1127
|
};
|
|
1098
1128
|
|
|
1099
|
-
//
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1129
|
+
// Client tools: same built-in + WebMCP merge as buildAgentPayload
|
|
1130
|
+
// (flow-dispatch path).
|
|
1131
|
+
const clientTools = [
|
|
1132
|
+
...builtInClientToolsForDispatch(this.config),
|
|
1133
|
+
...((await this.webMcpBridge?.snapshotForDispatch()) ?? []),
|
|
1134
|
+
];
|
|
1135
|
+
if (clientTools.length > 0) {
|
|
1136
|
+
payload.clientTools = clientTools;
|
|
1103
1137
|
}
|
|
1104
1138
|
|
|
1105
1139
|
if (this.contextProviders.length) {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isAskUserQuestionMessage,
|
|
10
10
|
removeAskUserQuestionSheet,
|
|
11
11
|
} from "./ask-user-question-bubble";
|
|
12
|
+
import { isSuggestRepliesMessage } from "../suggest-replies-tool";
|
|
12
13
|
|
|
13
14
|
export const renderMessages = (
|
|
14
15
|
container: HTMLElement,
|
|
@@ -40,6 +41,15 @@ export const renderMessages = (
|
|
|
40
41
|
ensureAskUserQuestionSheet(message, config, composerOverlay ?? null);
|
|
41
42
|
}
|
|
42
43
|
return;
|
|
44
|
+
} else if (
|
|
45
|
+
isSuggestRepliesMessage(message) &&
|
|
46
|
+
config?.features?.suggestReplies?.enabled !== false
|
|
47
|
+
) {
|
|
48
|
+
// No transcript bubble — the chips above the composer are the only UI.
|
|
49
|
+
// When the feature is disabled the message falls through to the generic
|
|
50
|
+
// tool bubble below (and is never auto-resumed), making the parked
|
|
51
|
+
// execution visible instead of silently swallowed.
|
|
52
|
+
return;
|
|
43
53
|
} else if (message.variant === "tool" && message.toolCall) {
|
|
44
54
|
if (!showToolCalls) return;
|
|
45
55
|
bubble = createToolBubble(message, config);
|
|
@@ -9,29 +9,50 @@ export interface SuggestionButtons {
|
|
|
9
9
|
session: AgentWidgetSession,
|
|
10
10
|
textarea: HTMLTextAreaElement,
|
|
11
11
|
messages?: AgentWidgetMessage[],
|
|
12
|
-
config?: AgentWidgetSuggestionChipsConfig
|
|
12
|
+
config?: AgentWidgetSuggestionChipsConfig,
|
|
13
|
+
opts?: SuggestionRenderOptions
|
|
13
14
|
) => void;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
export interface SuggestionRenderOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Chips pushed by the agent's `suggest_replies` tool rather than the
|
|
20
|
+
* static `suggestionChips` config. Skips the before-first-user-message
|
|
21
|
+
* gate (the caller already applied the latest-turn visibility rule) and
|
|
22
|
+
* dispatches `persona:suggestReplies:*` DOM events.
|
|
23
|
+
*/
|
|
24
|
+
agentPushed?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
16
27
|
export const createSuggestions = (container: HTMLElement): SuggestionButtons => {
|
|
17
28
|
const suggestionButtons: HTMLButtonElement[] = [];
|
|
29
|
+
// render() runs on every message change; only announce agent-pushed chips
|
|
30
|
+
// when the visible set actually changes, not on each re-render pass.
|
|
31
|
+
let lastAgentShownKey: string | null = null;
|
|
18
32
|
|
|
19
33
|
const render = (
|
|
20
34
|
chips: string[] | undefined,
|
|
21
35
|
session: AgentWidgetSession,
|
|
22
36
|
textarea: HTMLTextAreaElement,
|
|
23
37
|
messages?: AgentWidgetMessage[],
|
|
24
|
-
chipsConfig?: AgentWidgetSuggestionChipsConfig
|
|
38
|
+
chipsConfig?: AgentWidgetSuggestionChipsConfig,
|
|
39
|
+
opts?: SuggestionRenderOptions
|
|
25
40
|
) => {
|
|
26
41
|
container.innerHTML = "";
|
|
27
42
|
suggestionButtons.length = 0;
|
|
43
|
+
const agentPushed = opts?.agentPushed === true;
|
|
44
|
+
if (!agentPushed) lastAgentShownKey = null;
|
|
28
45
|
if (!chips || !chips.length) return;
|
|
29
46
|
|
|
30
|
-
// Hide suggestions after the first user message is sent
|
|
47
|
+
// Hide config suggestions after the first user message is sent.
|
|
48
|
+
// Agent-pushed chips skip this gate — their visibility is the caller's
|
|
49
|
+
// latest-turn rule (last suggest_replies call with no user message after).
|
|
31
50
|
// Use provided messages or get from session
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
if (!agentPushed) {
|
|
52
|
+
const messagesToCheck = messages ?? (session ? session.getMessages() : []);
|
|
53
|
+
const hasUserMessage = messagesToCheck.some((msg) => msg.role === "user");
|
|
54
|
+
if (hasUserMessage) return;
|
|
55
|
+
}
|
|
35
56
|
|
|
36
57
|
const fragment = document.createDocumentFragment();
|
|
37
58
|
const streaming = session ? session.isStreaming() : false;
|
|
@@ -79,12 +100,34 @@ export const createSuggestions = (container: HTMLElement): SuggestionButtons =>
|
|
|
79
100
|
btn.addEventListener("click", () => {
|
|
80
101
|
if (!session || session.isStreaming()) return;
|
|
81
102
|
textarea.value = "";
|
|
103
|
+
if (agentPushed) {
|
|
104
|
+
container.dispatchEvent(
|
|
105
|
+
new CustomEvent("persona:suggestReplies:selected", {
|
|
106
|
+
detail: { suggestion: chip },
|
|
107
|
+
bubbles: true,
|
|
108
|
+
composed: true,
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
}
|
|
82
112
|
session.sendMessage(chip);
|
|
83
113
|
});
|
|
84
114
|
fragment.appendChild(btn);
|
|
85
115
|
suggestionButtons.push(btn);
|
|
86
116
|
});
|
|
87
117
|
container.appendChild(fragment);
|
|
118
|
+
if (agentPushed) {
|
|
119
|
+
const shownKey = JSON.stringify(chips);
|
|
120
|
+
if (shownKey !== lastAgentShownKey) {
|
|
121
|
+
lastAgentShownKey = shownKey;
|
|
122
|
+
container.dispatchEvent(
|
|
123
|
+
new CustomEvent("persona:suggestReplies:shown", {
|
|
124
|
+
detail: { suggestions: [...chips] },
|
|
125
|
+
bubbles: true,
|
|
126
|
+
composed: true,
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
88
131
|
};
|
|
89
132
|
|
|
90
133
|
return {
|
package/src/index-core.ts
CHANGED
|
@@ -134,6 +134,21 @@ export {
|
|
|
134
134
|
parseAskUserQuestionPayload
|
|
135
135
|
} from "./components/ask-user-question-bubble";
|
|
136
136
|
|
|
137
|
+
export {
|
|
138
|
+
ASK_USER_QUESTION_CLIENT_TOOL,
|
|
139
|
+
ASK_USER_QUESTION_PARAMETERS_SCHEMA,
|
|
140
|
+
builtInClientToolsForDispatch
|
|
141
|
+
} from "./ask-user-question-tool";
|
|
142
|
+
|
|
143
|
+
export {
|
|
144
|
+
SUGGEST_REPLIES_TOOL_NAME,
|
|
145
|
+
SUGGEST_REPLIES_CLIENT_TOOL,
|
|
146
|
+
SUGGEST_REPLIES_PARAMETERS_SCHEMA,
|
|
147
|
+
isSuggestRepliesMessage,
|
|
148
|
+
parseSuggestRepliesPayload,
|
|
149
|
+
latestAgentSuggestions
|
|
150
|
+
} from "./suggest-replies-tool";
|
|
151
|
+
|
|
137
152
|
export { initAgentWidgetFn as initAgentWidget };
|
|
138
153
|
export {
|
|
139
154
|
createWidgetHostLayout,
|