dewee-webchat 0.5.0-beta.3 → 0.5.0-beta.5
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 +24 -1
- package/dist/{chunk-Q5RDSRZG.js → chunk-HBAT4XZ6.js} +2 -2
- package/dist/{chunk-N7BJKIPT.js → chunk-UPJVNHF6.js} +1 -1
- package/dist/dewee-webchat.js +4 -4
- package/dist/{handler-BchS1kD4.d.ts → handler-_GEhpm1W.d.ts} +8 -0
- package/dist/index.js +2 -2
- package/dist/react.js +2 -2
- package/dist/server-identity.d.ts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +51 -10
- package/dist/vue.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -217,7 +217,30 @@ Only exact same-origin paths in both lists use the binding. This reads the Asset
|
|
|
217
217
|
**bypassing Worker middleware, Cloudflare Access, and cookie-based authorization**. Every file under
|
|
218
218
|
the configured prefixes must be public to any valid chat visitor; keep private files elsewhere,
|
|
219
219
|
inspect the build output, and exclude sensitive files using `.assetsignore`. The URL query is not
|
|
220
|
-
passed to the binding.
|
|
220
|
+
passed to the binding.
|
|
221
|
+
|
|
222
|
+
For a sibling hostname in the same zone, a host can instead configure a service binding to a Worker
|
|
223
|
+
that serves public images from the listed directories:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
createDeweeHandler({
|
|
227
|
+
// ... other handler options
|
|
228
|
+
imageSources: ["https://images.example.com/products/"],
|
|
229
|
+
cloudflarePublicImageServices: [
|
|
230
|
+
{ id: "catalog-v1", binding: env.PUBLIC_IMAGES, prefixes: ["https://images.example.com/products/"] },
|
|
231
|
+
],
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Both lists must allow the source. Each service needs a unique ID and non-overlapping prefixes; change
|
|
236
|
+
the ID whenever you repoint the binding so existing short-lived tickets cannot reach the new service.
|
|
237
|
+
The service ID is visible in the image ticket, but the source URL remains encrypted. The binding
|
|
238
|
+
calls the configured Worker directly, bypassing the
|
|
239
|
+
source hostname's Cloudflare security rules. Every configured prefix must be public to all chat
|
|
240
|
+
visitors; do not bind private services or paths. The Worker receives a GET with the approved path on
|
|
241
|
+
`https://public-images.invalid`, without the original query, cookies or authorization headers. It
|
|
242
|
+
must serve that path without relying on its hostname or a signed query string. A missing or invalid
|
|
243
|
+
response fails closed and never falls back to a network request.
|
|
221
244
|
|
|
222
245
|
Files are limited to 2 MiB and 4096
|
|
223
246
|
pixels per edge, and responses are not cached. Animated GIF/PNG/WebP are refused. Raw HTML,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
parseCredentialRequest,
|
|
12
12
|
parseJobAccessLaunch,
|
|
13
13
|
stripPageBlock
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-UPJVNHF6.js";
|
|
15
15
|
|
|
16
16
|
// src/actions/directives.ts
|
|
17
17
|
var DIRECTIVE_LANG = "dewee-action";
|
|
@@ -670,7 +670,7 @@ var ApiClient = class {
|
|
|
670
670
|
async imageUrl(source, signal) {
|
|
671
671
|
const res = await this.request(this.url("image-ticket"), { method: "POST", headers: await this.headers(true), body: JSON.stringify({ url: source }), redirect: "error", signal });
|
|
672
672
|
const data = await this.parse(res);
|
|
673
|
-
if (typeof data.ticket !== "string" || !/^[A-Za-z0-9_-]{1,8192}$/.test(data.ticket)) throw new Error("Invalid image ticket");
|
|
673
|
+
if (typeof data.ticket !== "string" || !/^(?:[A-Za-z0-9_-]{1,8192}|s\.[A-Za-z0-9_-]{1,64}\.[A-Za-z0-9_-]{1,8192})$/.test(data.ticket)) throw new Error("Invalid image ticket");
|
|
674
674
|
return this.url("image", { ticket: data.ticket });
|
|
675
675
|
}
|
|
676
676
|
async getConfig(force = false, probePageContext = false) {
|
|
@@ -21,7 +21,7 @@ function isLinkErrorCode(code) {
|
|
|
21
21
|
}
|
|
22
22
|
var HEADER_GATEWAY_URL = "x-dewee-gateway-url";
|
|
23
23
|
var HEADER_GATEWAY_TOKEN = "x-dewee-gateway-token";
|
|
24
|
-
var VERSION = "0.5.0-beta.
|
|
24
|
+
var VERSION = "0.5.0-beta.5";
|
|
25
25
|
|
|
26
26
|
// src/actions/page-block.ts
|
|
27
27
|
var ACTION_NAME_RE = /^[a-z][a-z0-9_.-]{0,63}$/;
|
package/dist/dewee-webchat.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";(()=>{var xe=Object.defineProperty;var it=(n,s,e)=>s in n?xe(n,s,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[s]=e;var st=(n,s)=>()=>(n&&(s=n(n=0)),s);var ve=(n,s)=>{for(var e in s)xe(n,e,{get:s[e],enumerable:!0})};var f=(n,s,e)=>it(n,typeof s!="symbol"?s+"":s,e);var We={};ve(We,{capturePageContext:()=>$t,providedPageContext:()=>qt,safePageUrl:()=>Fe});function Fe(n){try{let s=new URL(n);if(s.protocol!=="https:"&&s.protocol!=="http:")return;s.username="",s.password="",s.hash="";for(let e of[...s.searchParams.keys()]){let t=e.replace(/(\p{Lu}+)(\p{Lu}\p{Ll})/gu,"$1 $2").replace(/([\p{Ll}\p{N}])(\p{Lu})/gu,"$1 $2").replace(/(\p{L})(\p{N})/gu,"$1 $2").replace(/(\p{N})(\p{L})/gu,"$1 $2").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean),i=t.join(""),r=["api","access","auth","oauth","secret","private","client","session","signing","verification","reset","recovery","activation","confirmation","invite","license","encryption"];(t.some((c,a)=>["token","secret","password","passwd","pwd","auth","authorization","cookie","apikey","session","sid","sig","signature","otp","jwt","credential","credentials","nonce","bearer","ticket"].includes(c)||["code","key","state"].includes(c)&&(t.length===1||a>0&&r.includes(t[a-1])))||/token|secret|passw|apikey|credential|sessid|session|authorization|samlresponse/.test(i))&&s.searchParams.set(e,"[REDACTED]")}return new TextEncoder().encode(s.href).length<=2048?s.href:void 0}catch{return}}function R(n,s){return Array.from(n).slice(0,s).join("")}function Ye(n){if(n.content&&new TextEncoder().encode(JSON.stringify(n)).length>65536){let s=Array.from(n.content.text),e=0,t=s.length;for(;e<t;){let i=Math.ceil((e+t)/2);n.content.text=s.slice(0,i).join(""),n.content.truncated=!0,new TextEncoder().encode(JSON.stringify(n)).length<=65536?e=i:t=i-1}n.content.text=s.slice(0,e).join(""),n.content.truncated=!0}return n}function qt(n,s,e){if(!n||typeof n!="object"||Array.isArray(n))throw new TypeError("Invalid page context");let t=n,i={version:1,url:s.url,captured_at:s.captured_at};for(let[r,o]of[["title",300],["description",1e3]])typeof t[r]=="string"&&(i[r]=R(t[r].replace(/\s+/g," ").trim(),o));if(e==="content"&&(typeof t.selection=="string"&&(i.selection=R(t.selection,2e3)),t.content&&typeof t.content=="object"&&!Array.isArray(t.content))){let r=t.content;if(typeof r.text!="string"||r.format!=="text"&&r.format!=="markdown")throw new TypeError("Invalid page content");let o=R(r.text,12e3);i.content={format:r.format,text:o,truncated:r.truncated===!0||Array.from(r.text).length>12e3}}return Ye(i)}function F(n,s){for(let e=n;e;e=e.parentElement){if(e.matches(Ht)||s.some(i=>e.matches(i))||e instanceof HTMLDialogElement&&!e.open)return!1;if(e instanceof HTMLDetailsElement&&!e.open){let i=[...e.children].find(r=>r.tagName==="SUMMARY");if(!i||!n||i!==n&&!i.contains(n))return!1}if(e.hasAttribute("popover"))try{if(!e.matches(":popover-open"))return!1}catch{return!1}let t=getComputedStyle(e);if(t.display==="none"||t.visibility==="hidden"||t.visibility==="collapse"||t.opacity==="0"||t.contentVisibility==="hidden")return!1}return!(n&&typeof n.checkVisibility=="function"&&!n.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}))}function $t(n,s){let e=Fe(n.location.href);if(!e)return;let t=s.excludeSelectors??[];for(let m of t)n.documentElement.matches(m);let i=n.querySelector(s.selector||"main, article, [role='main']"),r={version:1,url:e,captured_at:new Date().toISOString()},o=R(n.title.replace(/\s+/g," ").trim(),300),c=R((n.querySelector('meta[name="description" i]')?.getAttribute("content")??"").replace(/\s+/g," ").trim(),1e3);if(o&&(r.title=o),c&&(r.description=c),s.mode!=="content"||!i||!F(i,t))return r;let a=n.createTreeWalker(i,NodeFilter.SHOW_TEXT),d=[],l=0,u=0,g=!1;for(;a.nextNode();){if(++u>5e3){g=!0;break}let m=a.currentNode;if(!F(m.parentElement,t))continue;let b=(m.textContent??"").replace(/\s+/g," ").trim();if(!b)continue;let x=12e3-l,w=R(b,x);if(d.push(w),l+=Array.from(w).length+1,Array.from(b).length>x||l>=12e3){g=!0;break}}let p=R(d.join(`
|
|
2
|
-
`),12e3);p&&(r.content={format:"text",text:p,truncated:g});let h=n.getSelection();if(h&&!h.isCollapsed&&h.rangeCount){let m=h.getRangeAt(0),b=m.commonAncestorContainer,x=b.nodeType===Node.ELEMENT_NODE?b:b.parentElement;if(x&&i.contains(x)&&F(x,t)){let w=i.querySelectorAll("*");if(w.length<=5e3&&![...w].some(y=>m.intersectsNode(y)&&!F(y,t))){let y=R(h.toString().trim(),2e3);y&&(r.selection=y)}}}return Ye(r)}var Ht,Qe=st(()=>{"use strict";Ht="script,style,noscript,template,svg,canvas,iframe,form,input,textarea,select,option,button,[contenteditable]:not([contenteditable='false']),[hidden],[aria-hidden='true'],[inert],dewee-chat"});var ye={};ve(ye,{ApiClient:()=>_,ApiError:()=>v,DOM_ACTION_NAMES:()=>De,DeweeChatElement:()=>q,RESERVED_HEADERS:()=>fe,defineDeweeChat:()=>we,mount:()=>Wt,renderMarkdown:()=>M});var rt=["IDENTITY_REQUIRED","IDENTITY_LINK_REQUIRED","IDENTITY_CONFLICT","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"];function W(n){return typeof n=="string"&&rt.includes(n)}var ot=["POLICY_DENIED","RATE_LIMITED","CHALLENGE_REQUIRED"];function Q(n){return typeof n=="string"&&ot.includes(n)}var at=["idempotency_key_reused","link_in_progress","linking_unavailable"];function Ce(n){return typeof n=="string"&&at.includes(n)}var
|
|
2
|
+
`),12e3);p&&(r.content={format:"text",text:p,truncated:g});let h=n.getSelection();if(h&&!h.isCollapsed&&h.rangeCount){let m=h.getRangeAt(0),b=m.commonAncestorContainer,x=b.nodeType===Node.ELEMENT_NODE?b:b.parentElement;if(x&&i.contains(x)&&F(x,t)){let w=i.querySelectorAll("*");if(w.length<=5e3&&![...w].some(y=>m.intersectsNode(y)&&!F(y,t))){let y=R(h.toString().trim(),2e3);y&&(r.selection=y)}}}return Ye(r)}var Ht,Qe=st(()=>{"use strict";Ht="script,style,noscript,template,svg,canvas,iframe,form,input,textarea,select,option,button,[contenteditable]:not([contenteditable='false']),[hidden],[aria-hidden='true'],[inert],dewee-chat"});var ye={};ve(ye,{ApiClient:()=>_,ApiError:()=>v,DOM_ACTION_NAMES:()=>De,DeweeChatElement:()=>q,RESERVED_HEADERS:()=>fe,defineDeweeChat:()=>we,mount:()=>Wt,renderMarkdown:()=>M});var rt=["IDENTITY_REQUIRED","IDENTITY_LINK_REQUIRED","IDENTITY_CONFLICT","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"];function W(n){return typeof n=="string"&&rt.includes(n)}var ot=["POLICY_DENIED","RATE_LIMITED","CHALLENGE_REQUIRED"];function Q(n){return typeof n=="string"&&ot.includes(n)}var at=["idempotency_key_reused","link_in_progress","linking_unavailable"];function Ce(n){return typeof n=="string"&&at.includes(n)}var Z="x-dewee-gateway-url",X="x-dewee-gateway-token";var Ee="dewee-action",ke="dewee-action-result",$={maxCalls:8,maxBlockBytes:8192},ct=/^[A-Za-z0-9_.:-]{1,64}$/,lt=/^\s{0,3}```\s*$/,Ae=n=>new RegExp("^\\s{0,3}```"+n.replace(/[.-]/g,"\\$&")+"\\s*$"),Se=Ae(Ee),dt=Ae(ke);function ee(n,s){let e=[];for(let t=0;t<n.length;t++){if(!s.test(n[t]))continue;let i=t,r=[],o=!1;for(t++;t<n.length;t++){if(lt.test(n[t])){o=!0;break}r.push(n[t])}e.push({start:i,end:Math.min(t,n.length-1),body:r,closed:o})}return e}var Te=(n,s)=>{let e=new Set;for(let t of s)for(let i=t.start;i<=t.end;i++)e.add(i);return n.filter((t,i)=>!e.has(i)).join(`
|
|
3
3
|
`).replace(/\n{3,}/g,`
|
|
4
4
|
|
|
5
5
|
`).trim()};function te(n){let s=n.replace(/\r\n?/g,`
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
`+JSON.stringify(n)+"\n```";function Le(n,s=4e3){let e=Math.max(64,Math.floor((s-16)/Math.max(1,n.length)));return n.map(i=>{let r={...i,result:ie(i.result)};if(r.result===void 0&&delete r.result,O(r).length<=e)return O(r);let o=JSON.stringify(r.result??null),c=e-O({...r,result:{truncated:!0,preview:""}}).length-8,a=O({...r,result:{truncated:!0,preview:o.slice(0,Math.max(0,c))}});return a.length<=e?a:O({id:i.id,name:i.name.slice(0,64),status:i.status,result:{truncated:!0}})}).join(`
|
|
15
15
|
|
|
16
16
|
`).slice(0,s)}function ie(n){if(n!==void 0)try{let s=JSON.stringify(n);return s===void 0?null:JSON.parse(s)}catch{return"[unserializable result]"}}var se=n=>n===null?"null":Array.isArray(n)?"array":typeof n=="number"?Number.isInteger(n)?"integer":"number":typeof n,pt=(n,s)=>{let e=se(n);return e===s||s==="number"&&e==="integer"};function B(n,s,e="args",t=0){if(!s||typeof s!="object")return null;if(t>32)return`${e}: schema is nested too deeply`;let i=s;if(i.type!==void 0){let r=Array.isArray(i.type)?i.type:[i.type];if(!r.some(o=>pt(n,o)))return`${e}: expected ${r.join(" or ")}, got ${se(n)}`}if(Array.isArray(i.enum)&&!i.enum.some(r=>Ie(r,n)))return`${e}: must be one of ${i.enum.map(r=>JSON.stringify(r)).join(", ")}`;if("const"in i&&!Ie(i.const,n))return`${e}: must equal ${JSON.stringify(i.const)}`;if(typeof n=="string"){if(typeof i.minLength=="number"&&n.length<i.minLength)return`${e}: shorter than ${i.minLength} characters`;if(typeof i.maxLength=="number"&&n.length>i.maxLength)return`${e}: longer than ${i.maxLength} characters`;if(typeof i.pattern=="string"){let r=null;try{r=new RegExp(i.pattern,"u")}catch{return`${e}: schema pattern is invalid`}if(!r.test(n))return`${e}: does not match ${i.pattern}`}}if(typeof n=="number"){if(typeof i.minimum=="number"&&n<i.minimum)return`${e}: must be >= ${i.minimum}`;if(typeof i.maximum=="number"&&n>i.maximum)return`${e}: must be <= ${i.maximum}`}if(Array.isArray(n)){if(typeof i.minItems=="number"&&n.length<i.minItems)return`${e}: needs at least ${i.minItems} items`;if(typeof i.maxItems=="number"&&n.length>i.maxItems)return`${e}: allows at most ${i.maxItems} items`;if(i.items&&typeof i.items=="object")for(let r=0;r<n.length;r++){let o=B(n[r],i.items,`${e}[${r}]`,t+1);if(o)return o}}if(se(n)==="object"){let r=n,o=i.properties&&typeof i.properties=="object"?i.properties:{};for(let c of Array.isArray(i.required)?i.required:[])if(!(c in r))return`${e}.${c}: is required`;for(let[c,a]of Object.entries(r))if(Object.prototype.hasOwnProperty.call(o,c)){let d=B(a,o[c],`${e}.${c}`,t+1);if(d)return d}else if(i.additionalProperties===!1)return`${e}.${c}: is not allowed`}return null}function Ie(n,s){return JSON.stringify(n)===JSON.stringify(s)}var Me=/^[a-z][a-z0-9_.-]{0,63}$/,re=["read","navigate","mutate","destructive"],T={maxActions:32,maxDescription:1e3,maxParametersBytes:4096,maxManifestBytes:16384,maxPageContextBytes:4096},ht="[[dewee-page v1]]",_e="[[/dewee-page]]";function oe(n){if(!n.startsWith(ht))return n;let s=n.indexOf(`
|
|
17
|
-
`+_e);return s<0?n:n.slice(s+_e.length+1).replace(/^\n+/,"")}var gt=n=>n==="mutate"||n==="destructive",D=class{constructor(){f(this,"map",new Map)}get size(){return this.map.size}get(s){return this.map.get(s)}names(){return[...this.map.keys()]}register(s){let e=Array.isArray(s)?s:[s];for(let t of e)ft(t);if(new Set([...this.map.keys(),...e.map(t=>t.name)]).size>T.maxActions)throw new TypeError(`dewee-webchat: at most ${T.maxActions} page actions`);for(let t of e)this.map.set(t.name,t)}unregister(s){return this.map.delete(s)}manifest(){return[...this.map.values()].map(s=>({name:s.name,description:s.description,sensitivity:s.sensitivity,parameters:s.parameters??{type:"object",properties:{}}}))}};function ft(n){let s=e=>{throw new TypeError(`dewee-webchat: invalid page action ${JSON.stringify(n?.name)}: ${e}`)};(!n||typeof n!="object")&&s("not an object"),(typeof n.name!="string"||!Me.test(n.name))&&s("name must match ^[a-z][a-z0-9_.-]{0,63}$"),(typeof n.description!="string"||n.description.length>T.maxDescription)&&s(`description must be a string up to ${T.maxDescription} chars`),re.includes(n.sensitivity)||s(`sensitivity must be one of ${re.join(", ")}`),typeof n.handler!="function"&&s("handler must be a function"),n.parameters!==void 0&&(typeof n.parameters!="object"||n.parameters===null||Array.isArray(n.parameters))&&s("parameters must be a JSON Schema object")}async function Oe(n,s){let e=a=>(s.onStatus?.("error"),{id:n.id,name:n.name,status:"error",error:a});if(!n.ok)return e(n.error);if(s.signal.aborted)return e("Cancelled");let t=s.registry.get(n.name);if(!t)return e(`Unknown action "${n.name}". Available: ${s.registry.names().join(", ")||"none"}`);let i=B(n.args,t.parameters??{type:"object"});if(i)return e(`Invalid arguments: ${i}`);if(gt(t.sensitivity)){s.onStatus?.("awaiting-confirmation");let{handler:a,...d}=t,l=!1;try{l=await Ne(Promise.resolve(s.confirm({...d,callId:n.id},n.args)),s.signal)===!0}catch{l=!1}if(!l||s.signal.aborted)return s.onStatus?.("denied"),{id:n.id,name:n.name,status:"denied",error:"The user declined this action."}}s.onStatus?.("running");let r=new AbortController,o=()=>r.abort();s.signal.addEventListener("abort",o,{once:!0});let c=setTimeout(o,s.timeoutMs??3e4);try{let a=await Ne(Promise.resolve().then(()=>t.handler(n.args,{callId:n.id,conversationId:s.conversationId,agent:s.agent,signal:r.signal})),r.signal);return s.onStatus?.("done"),{id:n.id,name:n.name,status:"ok",result:ie(a)??null}}catch(a){return e(r.signal.aborted?s.signal.aborted?"Cancelled":"Timed out":String(a?.message??a).slice(0,500)||"Action failed")}finally{clearTimeout(c),s.signal.removeEventListener("abort",o)}}function Ne(n,s){return s.aborted?Promise.reject(new Error("aborted")):new Promise((e,t)=>{let i=()=>t(new Error("aborted"));s.addEventListener("abort",i,{once:!0}),n.then(e,t).finally(()=>s.removeEventListener("abort",i))})}var De=["page.snapshot","page.scroll","page.click","page.fill"],mt=["a[href]","button","input","select","textarea","summary","[contenteditable]:not([contenteditable=false])",...["button","link","checkbox","radio","switch","tab","menuitem","option","combobox","textbox","searchbox"].map(n=>`[role=${n}]`)].join(","),bt=new Set(["checkbox","radio","file","submit","button","image","reset","hidden","password"]),wt=/\b(cc-[a-z-]+|one-time-code|current-password|new-password)\b/i,yt=/(passw|passcode|\bpin\b|otp|one.?time|cvv|cvc|card.?(num|no)|cc.?num|security.?code|\bssn\b)/i,pe=80,ge=60,xt=40,I=(n,s)=>n.length>s?n.slice(0,s-1)+"\u2026":n,E=n=>(n??"").replace(/\s+/g," ").trim(),ae={type:"string",pattern:"^e[0-9]{1,4}$"},ce={type:"integer",minimum:1};function He(n){let s=0,e=new Map,t=()=>{let l=n.root(),u=typeof l=="string"?document.querySelector(l):l;if(l!==void 0&&!u)throw new Error(`domActionsRoot ${JSON.stringify(l)} matches no element`);return u??document.body},i=(l,u)=>l.isConnected&&u.contains(l)&&!n.exclude?.(l)&&!l.closest("[data-dewee-ignore]")&&!Ct(l)&&Et(l),r=l=>{if(l.generation!==s)throw new Error(`Stale ref: generation ${l.generation} is not the latest snapshot (${s||"none"}). Call page.snapshot again.`);let u=e.get(l.ref);if(!u)throw new Error(`Unknown ref "${l.ref}". Call page.snapshot again.`);if(!i(u,t()))throw new Error(`Ref "${l.ref}" is no longer available. Call page.snapshot again.`);return u};return{actions:[{name:"page.snapshot",description:'List the interactive elements visible on the page (links, buttons, form fields) as lines `ref role "name" [state]`. Use the ref together with the returned generation in page.scroll, page.click and page.fill. Every snapshot invalidates older refs. Use query to filter by visible name, offset/limit to page through long lists.',sensitivity:"read",parameters:{type:"object",properties:{query:{type:"string",maxLength:100},offset:{type:"integer",minimum:0},limit:{type:"integer",minimum:1,maximum:100}},additionalProperties:!1},handler:({query:l,offset:u=0,limit:g=xt})=>{let p=t();s++,e=new Map;let h=E(l).toLowerCase(),m=[],b=0;for(let x of p.querySelectorAll(mt)){if(!i(x,p))continue;let w=de(x);if(h&&!w.toLowerCase().includes(h)||(b++,b<=u||m.length>=g))continue;let y=`e${b}`;e.set(y,x),m.push(`${y} ${w}`)}return{generation:s,url:location.pathname+location.search,title:I(document.title,pe),total:b,offset:u,elements:m,...ue(p)}}},{name:"page.scroll",description:"Scroll the page by about one screen (direction up/down) or to the top/bottom, or scroll an element from page.snapshot into view (ref + generation).",sensitivity:"read",parameters:{type:"object",properties:{direction:{type:"string",enum:["up","down","top","bottom"]},ref:ae,generation:ce},additionalProperties:!1},handler:({direction:l,ref:u,generation:g})=>{let p=t();if(u!==void 0){if(g===void 0)throw new Error("generation is required with ref");return r({ref:u,generation:g}).scrollIntoView?.({block:"center",inline:"nearest"}),{ref:u,...ue(p)}}if(!l)throw new Error("Give a direction or a ref");let h=$e(p),m=h===null?window.innerHeight:h.clientHeight,b=h===null?window.scrollY:h.scrollTop,x=h===null?document.documentElement.scrollHeight:h.scrollHeight,w=l==="top"?0:l==="bottom"?x:b+(l==="down"?.8:-.8)*m;return h===null?window.scrollTo({top:w}):h.scrollTop=w,{direction:l,...ue(p)}}},{name:"page.click",description:"Click an element from page.snapshot (ref + generation). The visitor approves every click.",sensitivity:"mutate",parameters:{type:"object",properties:{ref:ae,generation:ce},required:["ref","generation"],additionalProperties:!1},handler:l=>{let u=r(l);if(he(u))throw new Error(`Ref "${l.ref}" is disabled`);return u.scrollIntoView?.({block:"center",inline:"nearest"}),u instanceof HTMLElement?(u.focus({preventScroll:!0}),u.click()):u.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,composed:!0})),{ref:l.ref,clicked:de(u)}}},{name:"page.fill",description:"Replace the value of a text field, textarea, select (option value or label) or editable area from page.snapshot (ref + generation). The visitor approves every fill.",sensitivity:"mutate",parameters:{type:"object",properties:{ref:ae,generation:ce,text:{type:"string",maxLength:1e4}},required:["ref","generation","text"],additionalProperties:!1},handler:l=>{let u=r(l);if(he(u)||u.readOnly)throw new Error(`Ref "${l.ref}" is not editable`);return vt(u,l.text),{ref:l.ref,value:I(qe(u),ge)}}}],target:l=>{try{return de(r(l))}catch{return}}}}function vt(n,s){if(n instanceof HTMLInputElement){if(bt.has(n.type))throw new Error(`Inputs of type "${n.type}" cannot be filled; use page.click`);le(HTMLInputElement.prototype,n,s)}else if(n instanceof HTMLTextAreaElement)le(HTMLTextAreaElement.prototype,n,s);else if(n instanceof HTMLSelectElement){let e=[...n.options].find(t=>t.value===s)??[...n.options].find(t=>E(t.textContent).toLowerCase()===E(s).toLowerCase());if(!e)throw new Error(`No option "${I(s,ge)}"`);le(HTMLSelectElement.prototype,n,e.value)}else if(n instanceof HTMLElement&&n.isContentEditable){n.focus({preventScroll:!0}),n.textContent=s,n.dispatchEvent(new InputEvent("input",{bubbles:!0,composed:!0,inputType:"insertReplacementText",data:s}));return}else throw new Error("This element cannot be filled; use page.click");n.focus({preventScroll:!0}),n.dispatchEvent(new Event("input",{bubbles:!0,composed:!0})),n.dispatchEvent(new Event("change",{bubbles:!0}))}function le(n,s,e){let t=Object.getOwnPropertyDescriptor(n,"value")?.set;t?t.call(s,e):s.value=e}function qe(n){return n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement?n.value:E(n.textContent)}function he(n){return n.disabled===!0||n.closest("fieldset[disabled]")!==null||n.getAttribute("aria-disabled")==="true"}function Ct(n){if(n instanceof HTMLInputElement&&n.type==="password")return!0;if(!(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement))return!1;if(wt.test(n.getAttribute("autocomplete")??""))return!0;let s=[n.name,n.id,n.getAttribute("aria-label"),n.placeholder].filter(Boolean).join(" ");return yt.test(s)}function Et(n){if(n instanceof HTMLInputElement&&n.type==="hidden"||n.closest("[hidden],[inert],[aria-hidden=true]"))return!1;for(let s=n;s;s=s.parentElement){let e=getComputedStyle(s);if(e.display==="none"||s===n&&(e.visibility==="hidden"||e.visibility==="collapse"))return!1}return!0}function kt(n){let s=n.getAttribute("role");if(s)return s;let e=n.tagName.toLowerCase();if(e==="a")return"link";if(e==="select")return"combobox";if(e==="textarea")return"textbox";if(e==="summary")return"button";if(n instanceof HTMLInputElement){let t=n.type;return t==="checkbox"||t==="radio"?t:t==="submit"||t==="button"||t==="reset"||t==="image"?"button":t==="search"?"searchbox":t==="range"?"slider":t==="text"?"textbox":`textbox:${t}`}return n.isContentEditable?"textbox":e}function At(n){let s=n.getAttribute("aria-labelledby");if(s){let r=s.split(/\s+/).map(o=>E(document.getElementById(o)?.textContent)).filter(Boolean).join(" ");if(r)return r}let e=E(n.getAttribute("aria-label"));if(e)return e;let t=n.labels;if(t?.length){let r=E([...t].map(o=>o.textContent).join(" "));if(r)return r}return n instanceof HTMLInputElement&&["submit","button","reset"].includes(n.type)?E(n.value):(n instanceof HTMLSelectElement||n instanceof HTMLTextAreaElement||n instanceof HTMLInputElement?"":E(n.textContent))||E(n.getAttribute("placeholder"))||E(n.getAttribute("title"))||E(n.querySelector("img[alt]")?.getAttribute("alt"))||E(n.getAttribute("name"))}function de(n){let s=[kt(n),JSON.stringify(I(At(n),pe))];if(n instanceof HTMLInputElement&&(n.type==="checkbox"||n.type==="radio"))s.push(n.checked?"checked":"unchecked");else if(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement){let e=qe(n);e&&!(n instanceof HTMLInputElement&&["submit","button","reset"].includes(n.type))&&s.push(`value=${JSON.stringify(I(e,ge))}`)}return n instanceof HTMLAnchorElement&&s.push(`href=${JSON.stringify(I(n.getAttribute("href")??"",pe))}`),he(n)&&s.push("disabled"),s.join(" ")}function $e(n){return n===document.body||n===document.documentElement?null:n.scrollHeight>n.clientHeight?n:null}function ue(n){let s=$e(n);return s===null?{scrollY:Math.round(window.scrollY),maxScrollY:Math.max(0,Math.round(document.documentElement.scrollHeight-window.innerHeight))}:{scrollY:Math.round(s.scrollTop),maxScrollY:Math.max(0,Math.round(s.scrollHeight-s.clientHeight))}}function j(n){let s=()=>{throw new Error("Job access is unavailable")};if(!n||typeof n!="object"||Array.isArray(n))return s();let e=n;if(typeof e.bootstrap_url!="string"||typeof e.launch_token!="string"||!/^[A-Za-z0-9_.-]{16,16384}$/.test(e.launch_token)||typeof e.expires_at!="string")return s();let t;try{t=new URL(e.bootstrap_url)}catch{return s()}if(t.protocol!=="https:"||t.username||t.password||t.search||t.hash||t.pathname!=="/identity/job-access/launch"||t.href!==e.bootstrap_url)return s();let i=Date.parse(e.expires_at);return!Number.isFinite(i)||i<=Date.now()||i>Date.now()+6e4?s():{bootstrap_url:t.href,launch_token:e.launch_token,expires_at:e.expires_at}}var St=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Tt=new Set(["pending","applying","resolved","cancelled","expired","failed"]),V=n=>n&&typeof n=="object"&&!Array.isArray(n)?n:null,S=(n,s)=>typeof n=="string"&&n.length<=s,Be=n=>S(n,64)&&Number.isFinite(Date.parse(n));function z(n,s){let e=V(n),t=V(e?.target);if(!e||!t||!S(e.id,36)||!St.test(e.id)||e.schema_version!=="webchat-credential-request.v1"||typeof e.status!="string"||!Tt.has(e.status)||!S(e.title,300)||!S(e.purpose,4e3)||!S(t.type,100)||!S(t.mode,100)||!Array.isArray(e.fields)||e.fields.length>32||!Be(e.expires_at)||e.agent_key!==s||e.completion!=="hosted"||typeof e.resumable!="boolean")return null;let i=[];for(let r of e.fields){let o=V(r);if(!o||!S(o.key,100)||!/^[A-Za-z0-9_.-]+$/.test(o.key)||!S(o.label,300)||!S(o.kind,100)||typeof o.required!="boolean")return null;i.push({key:o.key,label:o.label,kind:o.kind,required:o.required})}return{id:e.id,schema_version:"webchat-credential-request.v1",status:e.status,title:e.title,purpose:e.purpose,target:{type:t.type,mode:t.mode},fields:i,expires_at:e.expires_at,agent_key:s,completion:"hosted",resumable:e.resumable}}function je(n,s,e){let t=V(n);if(!t||t.credential_request_id!==s||!S(t.url,4096)||!Be(t.expires_at)||Date.parse(t.expires_at)<=Date.now())return null;try{let i=new URL(t.url);return i.protocol!=="https:"||i.origin!==e||i.username||i.password||i.hash||i.pathname!=="/credential-requests/open"||!/^\?token=[A-Za-z0-9_-]{43}$/.test(i.search)?null:{credential_request_id:s,url:i.href,expires_at:t.expires_at}}catch{return null}}var Pt=new Set(["LINK_EXPIRED","LINK_REVOKED"]),Rt=6e4,fe=["content-type","authorization",X,Z],v=class extends Error{constructor(e,t,i,r,o){super(e);f(this,"status",t);f(this,"code",i);f(this,"remediation",r);f(this,"retryAfterMs",o)}};function Lt(n,s){if(typeof n=="number"&&Number.isFinite(n)&&n>0)return n;let e=Number(s.headers.get("retry-after"));return Number.isFinite(e)&&e>0?e*1e3:void 0}var Ve=n=>({get(s){try{return n().getItem(s)}catch{return null}},set(s,e){try{n().setItem(s,e)}catch{}},del(s){try{n().removeItem(s)}catch{}}}),U=Ve(()=>localStorage),L=Ve(()=>sessionStorage),_=class{constructor(s){f(this,"o",s);f(this,"session",null);f(this,"config",null);f(this,"sessionPromise",null);f(this,"supersededSessions",new Map);f(this,"sessionGeneration",0);f(this,"refreshAt",new WeakMap)}url(s,e){let t=this.o.endpoint.replace(/\/+$/,""),i=e?"?"+new URLSearchParams(e).toString():"";return`${t}/${s}${i}`}get storageKey(){return`dewee-webchat:session:${this.o.endpoint}`}get reuseKey(){return`dewee-webchat:reuse:${this.o.endpoint}`}reuseHint(){return L.get(this.reuseKey)}request(s,e){return(this.o.fetch??globalThis.fetch)(s,e)}async headers(s=!1){let e={},t=typeof this.o.headers=="function"?await this.o.headers():this.o.headers;for(let[i,r]of Object.entries(t??{}))typeof r=="string"&&!fe.includes(i.toLowerCase())&&(e[i.toLowerCase()]=r);if(e["content-type"]="application/json",this.o.gatewayUrl&&this.o.gatewayToken&&(e[X]=this.o.gatewayUrl,e[Z]=this.o.gatewayToken),s){let i=await this.ensureSession();e.authorization=`Bearer ${i.token}`}return e}async parse(s){let e=await s.json().catch(()=>({}));if(!s.ok){let t=e?.error?.remediation,i=t&&typeof t.action=="string"&&typeof t.audience=="string"?{action:t.action,audience:t.audience}:void 0;throw new v(e?.error?.message||`HTTP ${s.status}`,s.status,e?.error?.code,i,Lt(e?.error?.retryAfterMs,s))}return e}async imageUrl(s,e){let t=await this.request(this.url("image-ticket"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify({url:s}),redirect:"error",signal:e}),i=await this.parse(t);if(typeof i.ticket!="string"||!/^[A-Za-z0-9_-]{1,8192}$/.test(i.ticket))throw new Error("Invalid image ticket");return this.url("image",{ticket:i.ticket})}async getConfig(s=!1,e=!1){return this.config&&!s?this.config:(this.config=await this.parse(await this.request(this.url("config",e?{pageContext:"1"}:void 0),{headers:await this.headers()})),this.config)}fresh(s){if(s.verified)return Date.now()<(this.refreshAt.get(s)??0);let e=typeof s.expiresIn=="number"&&s.expiresIn>=0&&s.obtainedAt,t=e?s.obtainedAt+s.expiresIn:s.expiresAt,i=e?s.expiresIn:s.obtainedAt?s.expiresAt-s.obtainedAt:1/0,r=Math.min(864e5,i/2,Math.max(3e4,i*.1));return t-Date.now()>r}scheduleRefresh(s,e){let t=typeof s.expiresIn=="number"&&s.expiresIn>0?s.expiresIn:s.expiresAt-e;this.refreshAt.set(s,e+t-Math.min(Rt,t/2))}stored(){let s=U.get(this.storageKey);if(!s)return null;try{let e=JSON.parse(s);return e.verified?(U.del(this.storageKey),null):e}catch{return null}}visitorId(){return this.session?.visitorId??null}ensureSession(){if(this.sessionPromise)return this.sessionPromise;if(this.session&&this.fresh(this.session))return Promise.resolve(this.session);let s=this.sessionGeneration,e=this.loadSession(s).catch(t=>{if(s!==this.sessionGeneration)return this.supersededSessions.get(s);throw t}).finally(()=>{this.sessionPromise===e&&(this.sessionPromise=null),this.supersededSessions.delete(s)});return this.sessionPromise=e}async loadSession(s){if(!this.session){let e=this.stored();if(e){if(this.fresh(e)){let t=await this.getConfig();if(s!==this.sessionGeneration)return this.supersededSessions.get(s);if(!t.identity)return this.session=e}this.session=e}}return this.createSession(s)}async createSession(s){let e=await this.getConfig();if(s!==this.sessionGeneration)return this.supersededSessions.get(s);let t={},i=this.session&&!this.session.verified?this.session:this.stored();if(i?.token&&(t.token=i.token),e.identity){let l=this.session?.verified?this.session.reuseHint:L.get(this.reuseKey);l&&(t.reuseHint=l)}let r=async()=>this.parse(await this.request(this.url("session"),{method:"POST",headers:await this.headers(),body:JSON.stringify(t)})),o=async()=>{if(!this.o.turnstile)throw new v("Verification is required but no Turnstile handler is set",403,"turnstile_required");t.turnstileToken=await this.o.turnstile(e.turnstileSiteKey)},c;try{e.turnstileSiteKey&&!(e.refreshWithoutTurnstile&&!e.identity&&t.token&&!t.reuseHint)&&await o();try{c=await r()}catch(l){if(!(e.turnstileSiteKey&&!t.turnstileToken&&l instanceof v&&l.code==="turnstile_failed"))throw l;await o(),c=await r()}}catch(l){if(s!==this.sessionGeneration)return this.supersededSessions.get(s);throw this.session=null,l instanceof v&&l.status===401&&L.del(this.reuseKey),l}if(s!==this.sessionGeneration)return this.supersededSessions.get(s);let a=Date.now(),d={...c,obtainedAt:a};if(this.session=d,d.verified)this.scheduleRefresh(d,a),typeof d.reuseHint=="string"&&d.reuseHint!==""?L.set(this.reuseKey,d.reuseHint):L.del(this.reuseKey);else{L.del(this.reuseKey);let{reuseHint:l,...u}=d;U.set(this.storageKey,JSON.stringify(u))}return d}forgetSession(){if(this.session?.verified){L.del(this.reuseKey),this.session=null;return}this.session=null,U.del(this.storageKey)}async renewSession(){let s=this.sessionGeneration,e=this.sessionPromise;this.sessionGeneration++,this.session=null,this.sessionPromise=null;let t=this.ensureSession();return e&&this.supersededSessions.set(s,t),t}async jobAccess(s){try{if(!(await this.ensureSession()).verified)throw new v("Sign in to open job access",401,"IDENTITY_REQUIRED");if(s.aborted)throw new v("Job access was cancelled",0,"job_access_unavailable");let t=await this.request(this.url("job-access"),{method:"POST",headers:await this.headers(!0),body:"{}",signal:s,redirect:"error"});return j(await this.parse(t))}catch(e){let t=e instanceof v&&typeof e.code=="string"&&["IDENTITY_REQUIRED","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"].includes(e.code)?e.code:"job_access_unavailable";throw new v("Job access is unavailable. Sign in again or retry.",e instanceof v?e.status:0,t)}}async link(){let s=await this.signedInRequest(async()=>this.request(this.url("link"),{method:"POST",headers:await this.headers(!0),body:"{}"}));return this.parse(s)}async history(s,e){return(await this.historyDetails(s,e)).messages}async historyDetails(s,e){let t=await this.signedInRequest(async()=>this.request(this.url("history",{agent:s,conversationId:e}),{headers:await this.headers(!0)})),i=await this.parse(t),r=this.session?.verified&&this.config?.credentialFormOrigin&&Array.isArray(i.credentialRequests)?i.credentialRequests.slice(0,100).map(o=>z(o,s)).filter(o=>o!==null):[];return{messages:i.messages,credentialRequests:r}}async credentialRequest(s,e,t,i,r){let o=await this.ensureSession(),c=(await this.getConfig()).credentialFormOrigin;if(!o.verified||!c)throw new v("Credential request is unavailable",403,"credential_request_unavailable");try{let a=await this.headers(!0);if(this.session!==o)throw new Error("identity changed");let d=await this.request(this.url(s),{method:"POST",headers:a,body:JSON.stringify({agent:e,conversationId:t,credentialRequestId:i}),redirect:"error",signal:r}),l=await this.parse(d);if(this.session!==o)throw new Error("identity changed");let u=s==="credential-request-handoff"?je(l,i,c):z(l.credentialRequest,e);if(!u||"id"in u&&u.id!==i)throw new Error("invalid credential request");return u}catch(a){let d=a instanceof v&&["credential_requests_unavailable","credential_request_not_found","credential_request_not_ready","credential_request_resumed"].includes(a.code??"")?a.code:"credential_request_unavailable";throw new v("Credential request is unavailable",a instanceof v?a.status:0,d)}}async signedInRequest(s){await this.ensureSession();let e=await s();return e.status!==401||!this.session?.verified?e:(this.forgetSession(),await this.ensureSession(),s())}async chat(s,e,t){await this.ensureSession();let i=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:t});if(i.status===401&&!s.credentialRequestId){let a=this.visitorId();this.forgetSession(),await this.ensureSession(),s.actionsHash&&this.visitorId()!==a&&(s={...s,actionsHash:void 0}),i=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:t})}(!i.ok||!i.body)&&await this.parse(i);let r=i.body.pipeThrough(new TextDecoderStream).getReader(),o="",c=!1;for(;;){let{done:a,value:d}=await r.read();if(a)break;o+=d;let l;for(;(l=o.indexOf(`
|
|
17
|
+
`+_e);return s<0?n:n.slice(s+_e.length+1).replace(/^\n+/,"")}var gt=n=>n==="mutate"||n==="destructive",D=class{constructor(){f(this,"map",new Map)}get size(){return this.map.size}get(s){return this.map.get(s)}names(){return[...this.map.keys()]}register(s){let e=Array.isArray(s)?s:[s];for(let t of e)ft(t);if(new Set([...this.map.keys(),...e.map(t=>t.name)]).size>T.maxActions)throw new TypeError(`dewee-webchat: at most ${T.maxActions} page actions`);for(let t of e)this.map.set(t.name,t)}unregister(s){return this.map.delete(s)}manifest(){return[...this.map.values()].map(s=>({name:s.name,description:s.description,sensitivity:s.sensitivity,parameters:s.parameters??{type:"object",properties:{}}}))}};function ft(n){let s=e=>{throw new TypeError(`dewee-webchat: invalid page action ${JSON.stringify(n?.name)}: ${e}`)};(!n||typeof n!="object")&&s("not an object"),(typeof n.name!="string"||!Me.test(n.name))&&s("name must match ^[a-z][a-z0-9_.-]{0,63}$"),(typeof n.description!="string"||n.description.length>T.maxDescription)&&s(`description must be a string up to ${T.maxDescription} chars`),re.includes(n.sensitivity)||s(`sensitivity must be one of ${re.join(", ")}`),typeof n.handler!="function"&&s("handler must be a function"),n.parameters!==void 0&&(typeof n.parameters!="object"||n.parameters===null||Array.isArray(n.parameters))&&s("parameters must be a JSON Schema object")}async function Oe(n,s){let e=a=>(s.onStatus?.("error"),{id:n.id,name:n.name,status:"error",error:a});if(!n.ok)return e(n.error);if(s.signal.aborted)return e("Cancelled");let t=s.registry.get(n.name);if(!t)return e(`Unknown action "${n.name}". Available: ${s.registry.names().join(", ")||"none"}`);let i=B(n.args,t.parameters??{type:"object"});if(i)return e(`Invalid arguments: ${i}`);if(gt(t.sensitivity)){s.onStatus?.("awaiting-confirmation");let{handler:a,...d}=t,l=!1;try{l=await Ne(Promise.resolve(s.confirm({...d,callId:n.id},n.args)),s.signal)===!0}catch{l=!1}if(!l||s.signal.aborted)return s.onStatus?.("denied"),{id:n.id,name:n.name,status:"denied",error:"The user declined this action."}}s.onStatus?.("running");let r=new AbortController,o=()=>r.abort();s.signal.addEventListener("abort",o,{once:!0});let c=setTimeout(o,s.timeoutMs??3e4);try{let a=await Ne(Promise.resolve().then(()=>t.handler(n.args,{callId:n.id,conversationId:s.conversationId,agent:s.agent,signal:r.signal})),r.signal);return s.onStatus?.("done"),{id:n.id,name:n.name,status:"ok",result:ie(a)??null}}catch(a){return e(r.signal.aborted?s.signal.aborted?"Cancelled":"Timed out":String(a?.message??a).slice(0,500)||"Action failed")}finally{clearTimeout(c),s.signal.removeEventListener("abort",o)}}function Ne(n,s){return s.aborted?Promise.reject(new Error("aborted")):new Promise((e,t)=>{let i=()=>t(new Error("aborted"));s.addEventListener("abort",i,{once:!0}),n.then(e,t).finally(()=>s.removeEventListener("abort",i))})}var De=["page.snapshot","page.scroll","page.click","page.fill"],mt=["a[href]","button","input","select","textarea","summary","[contenteditable]:not([contenteditable=false])",...["button","link","checkbox","radio","switch","tab","menuitem","option","combobox","textbox","searchbox"].map(n=>`[role=${n}]`)].join(","),bt=new Set(["checkbox","radio","file","submit","button","image","reset","hidden","password"]),wt=/\b(cc-[a-z-]+|one-time-code|current-password|new-password)\b/i,yt=/(passw|passcode|\bpin\b|otp|one.?time|cvv|cvc|card.?(num|no)|cc.?num|security.?code|\bssn\b)/i,pe=80,ge=60,xt=40,I=(n,s)=>n.length>s?n.slice(0,s-1)+"\u2026":n,E=n=>(n??"").replace(/\s+/g," ").trim(),ae={type:"string",pattern:"^e[0-9]{1,4}$"},ce={type:"integer",minimum:1};function He(n){let s=0,e=new Map,t=()=>{let l=n.root(),u=typeof l=="string"?document.querySelector(l):l;if(l!==void 0&&!u)throw new Error(`domActionsRoot ${JSON.stringify(l)} matches no element`);return u??document.body},i=(l,u)=>l.isConnected&&u.contains(l)&&!n.exclude?.(l)&&!l.closest("[data-dewee-ignore]")&&!Ct(l)&&Et(l),r=l=>{if(l.generation!==s)throw new Error(`Stale ref: generation ${l.generation} is not the latest snapshot (${s||"none"}). Call page.snapshot again.`);let u=e.get(l.ref);if(!u)throw new Error(`Unknown ref "${l.ref}". Call page.snapshot again.`);if(!i(u,t()))throw new Error(`Ref "${l.ref}" is no longer available. Call page.snapshot again.`);return u};return{actions:[{name:"page.snapshot",description:'List the interactive elements visible on the page (links, buttons, form fields) as lines `ref role "name" [state]`. Use the ref together with the returned generation in page.scroll, page.click and page.fill. Every snapshot invalidates older refs. Use query to filter by visible name, offset/limit to page through long lists.',sensitivity:"read",parameters:{type:"object",properties:{query:{type:"string",maxLength:100},offset:{type:"integer",minimum:0},limit:{type:"integer",minimum:1,maximum:100}},additionalProperties:!1},handler:({query:l,offset:u=0,limit:g=xt})=>{let p=t();s++,e=new Map;let h=E(l).toLowerCase(),m=[],b=0;for(let x of p.querySelectorAll(mt)){if(!i(x,p))continue;let w=de(x);if(h&&!w.toLowerCase().includes(h)||(b++,b<=u||m.length>=g))continue;let y=`e${b}`;e.set(y,x),m.push(`${y} ${w}`)}return{generation:s,url:location.pathname+location.search,title:I(document.title,pe),total:b,offset:u,elements:m,...ue(p)}}},{name:"page.scroll",description:"Scroll the page by about one screen (direction up/down) or to the top/bottom, or scroll an element from page.snapshot into view (ref + generation).",sensitivity:"read",parameters:{type:"object",properties:{direction:{type:"string",enum:["up","down","top","bottom"]},ref:ae,generation:ce},additionalProperties:!1},handler:({direction:l,ref:u,generation:g})=>{let p=t();if(u!==void 0){if(g===void 0)throw new Error("generation is required with ref");return r({ref:u,generation:g}).scrollIntoView?.({block:"center",inline:"nearest"}),{ref:u,...ue(p)}}if(!l)throw new Error("Give a direction or a ref");let h=$e(p),m=h===null?window.innerHeight:h.clientHeight,b=h===null?window.scrollY:h.scrollTop,x=h===null?document.documentElement.scrollHeight:h.scrollHeight,w=l==="top"?0:l==="bottom"?x:b+(l==="down"?.8:-.8)*m;return h===null?window.scrollTo({top:w}):h.scrollTop=w,{direction:l,...ue(p)}}},{name:"page.click",description:"Click an element from page.snapshot (ref + generation). The visitor approves every click.",sensitivity:"mutate",parameters:{type:"object",properties:{ref:ae,generation:ce},required:["ref","generation"],additionalProperties:!1},handler:l=>{let u=r(l);if(he(u))throw new Error(`Ref "${l.ref}" is disabled`);return u.scrollIntoView?.({block:"center",inline:"nearest"}),u instanceof HTMLElement?(u.focus({preventScroll:!0}),u.click()):u.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,composed:!0})),{ref:l.ref,clicked:de(u)}}},{name:"page.fill",description:"Replace the value of a text field, textarea, select (option value or label) or editable area from page.snapshot (ref + generation). The visitor approves every fill.",sensitivity:"mutate",parameters:{type:"object",properties:{ref:ae,generation:ce,text:{type:"string",maxLength:1e4}},required:["ref","generation","text"],additionalProperties:!1},handler:l=>{let u=r(l);if(he(u)||u.readOnly)throw new Error(`Ref "${l.ref}" is not editable`);return vt(u,l.text),{ref:l.ref,value:I(qe(u),ge)}}}],target:l=>{try{return de(r(l))}catch{return}}}}function vt(n,s){if(n instanceof HTMLInputElement){if(bt.has(n.type))throw new Error(`Inputs of type "${n.type}" cannot be filled; use page.click`);le(HTMLInputElement.prototype,n,s)}else if(n instanceof HTMLTextAreaElement)le(HTMLTextAreaElement.prototype,n,s);else if(n instanceof HTMLSelectElement){let e=[...n.options].find(t=>t.value===s)??[...n.options].find(t=>E(t.textContent).toLowerCase()===E(s).toLowerCase());if(!e)throw new Error(`No option "${I(s,ge)}"`);le(HTMLSelectElement.prototype,n,e.value)}else if(n instanceof HTMLElement&&n.isContentEditable){n.focus({preventScroll:!0}),n.textContent=s,n.dispatchEvent(new InputEvent("input",{bubbles:!0,composed:!0,inputType:"insertReplacementText",data:s}));return}else throw new Error("This element cannot be filled; use page.click");n.focus({preventScroll:!0}),n.dispatchEvent(new Event("input",{bubbles:!0,composed:!0})),n.dispatchEvent(new Event("change",{bubbles:!0}))}function le(n,s,e){let t=Object.getOwnPropertyDescriptor(n,"value")?.set;t?t.call(s,e):s.value=e}function qe(n){return n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement?n.value:E(n.textContent)}function he(n){return n.disabled===!0||n.closest("fieldset[disabled]")!==null||n.getAttribute("aria-disabled")==="true"}function Ct(n){if(n instanceof HTMLInputElement&&n.type==="password")return!0;if(!(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement))return!1;if(wt.test(n.getAttribute("autocomplete")??""))return!0;let s=[n.name,n.id,n.getAttribute("aria-label"),n.placeholder].filter(Boolean).join(" ");return yt.test(s)}function Et(n){if(n instanceof HTMLInputElement&&n.type==="hidden"||n.closest("[hidden],[inert],[aria-hidden=true]"))return!1;for(let s=n;s;s=s.parentElement){let e=getComputedStyle(s);if(e.display==="none"||s===n&&(e.visibility==="hidden"||e.visibility==="collapse"))return!1}return!0}function kt(n){let s=n.getAttribute("role");if(s)return s;let e=n.tagName.toLowerCase();if(e==="a")return"link";if(e==="select")return"combobox";if(e==="textarea")return"textbox";if(e==="summary")return"button";if(n instanceof HTMLInputElement){let t=n.type;return t==="checkbox"||t==="radio"?t:t==="submit"||t==="button"||t==="reset"||t==="image"?"button":t==="search"?"searchbox":t==="range"?"slider":t==="text"?"textbox":`textbox:${t}`}return n.isContentEditable?"textbox":e}function At(n){let s=n.getAttribute("aria-labelledby");if(s){let r=s.split(/\s+/).map(o=>E(document.getElementById(o)?.textContent)).filter(Boolean).join(" ");if(r)return r}let e=E(n.getAttribute("aria-label"));if(e)return e;let t=n.labels;if(t?.length){let r=E([...t].map(o=>o.textContent).join(" "));if(r)return r}return n instanceof HTMLInputElement&&["submit","button","reset"].includes(n.type)?E(n.value):(n instanceof HTMLSelectElement||n instanceof HTMLTextAreaElement||n instanceof HTMLInputElement?"":E(n.textContent))||E(n.getAttribute("placeholder"))||E(n.getAttribute("title"))||E(n.querySelector("img[alt]")?.getAttribute("alt"))||E(n.getAttribute("name"))}function de(n){let s=[kt(n),JSON.stringify(I(At(n),pe))];if(n instanceof HTMLInputElement&&(n.type==="checkbox"||n.type==="radio"))s.push(n.checked?"checked":"unchecked");else if(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement||n instanceof HTMLSelectElement){let e=qe(n);e&&!(n instanceof HTMLInputElement&&["submit","button","reset"].includes(n.type))&&s.push(`value=${JSON.stringify(I(e,ge))}`)}return n instanceof HTMLAnchorElement&&s.push(`href=${JSON.stringify(I(n.getAttribute("href")??"",pe))}`),he(n)&&s.push("disabled"),s.join(" ")}function $e(n){return n===document.body||n===document.documentElement?null:n.scrollHeight>n.clientHeight?n:null}function ue(n){let s=$e(n);return s===null?{scrollY:Math.round(window.scrollY),maxScrollY:Math.max(0,Math.round(document.documentElement.scrollHeight-window.innerHeight))}:{scrollY:Math.round(s.scrollTop),maxScrollY:Math.max(0,Math.round(s.scrollHeight-s.clientHeight))}}function j(n){let s=()=>{throw new Error("Job access is unavailable")};if(!n||typeof n!="object"||Array.isArray(n))return s();let e=n;if(typeof e.bootstrap_url!="string"||typeof e.launch_token!="string"||!/^[A-Za-z0-9_.-]{16,16384}$/.test(e.launch_token)||typeof e.expires_at!="string")return s();let t;try{t=new URL(e.bootstrap_url)}catch{return s()}if(t.protocol!=="https:"||t.username||t.password||t.search||t.hash||t.pathname!=="/identity/job-access/launch"||t.href!==e.bootstrap_url)return s();let i=Date.parse(e.expires_at);return!Number.isFinite(i)||i<=Date.now()||i>Date.now()+6e4?s():{bootstrap_url:t.href,launch_token:e.launch_token,expires_at:e.expires_at}}var St=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,Tt=new Set(["pending","applying","resolved","cancelled","expired","failed"]),V=n=>n&&typeof n=="object"&&!Array.isArray(n)?n:null,S=(n,s)=>typeof n=="string"&&n.length<=s,Be=n=>S(n,64)&&Number.isFinite(Date.parse(n));function z(n,s){let e=V(n),t=V(e?.target);if(!e||!t||!S(e.id,36)||!St.test(e.id)||e.schema_version!=="webchat-credential-request.v1"||typeof e.status!="string"||!Tt.has(e.status)||!S(e.title,300)||!S(e.purpose,4e3)||!S(t.type,100)||!S(t.mode,100)||!Array.isArray(e.fields)||e.fields.length>32||!Be(e.expires_at)||e.agent_key!==s||e.completion!=="hosted"||typeof e.resumable!="boolean")return null;let i=[];for(let r of e.fields){let o=V(r);if(!o||!S(o.key,100)||!/^[A-Za-z0-9_.-]+$/.test(o.key)||!S(o.label,300)||!S(o.kind,100)||typeof o.required!="boolean")return null;i.push({key:o.key,label:o.label,kind:o.kind,required:o.required})}return{id:e.id,schema_version:"webchat-credential-request.v1",status:e.status,title:e.title,purpose:e.purpose,target:{type:t.type,mode:t.mode},fields:i,expires_at:e.expires_at,agent_key:s,completion:"hosted",resumable:e.resumable}}function je(n,s,e){let t=V(n);if(!t||t.credential_request_id!==s||!S(t.url,4096)||!Be(t.expires_at)||Date.parse(t.expires_at)<=Date.now())return null;try{let i=new URL(t.url);return i.protocol!=="https:"||i.origin!==e||i.username||i.password||i.hash||i.pathname!=="/credential-requests/open"||!/^\?token=[A-Za-z0-9_-]{43}$/.test(i.search)?null:{credential_request_id:s,url:i.href,expires_at:t.expires_at}}catch{return null}}var Pt=new Set(["LINK_EXPIRED","LINK_REVOKED"]),Rt=6e4,fe=["content-type","authorization",Z,X],v=class extends Error{constructor(e,t,i,r,o){super(e);f(this,"status",t);f(this,"code",i);f(this,"remediation",r);f(this,"retryAfterMs",o)}};function Lt(n,s){if(typeof n=="number"&&Number.isFinite(n)&&n>0)return n;let e=Number(s.headers.get("retry-after"));return Number.isFinite(e)&&e>0?e*1e3:void 0}var Ve=n=>({get(s){try{return n().getItem(s)}catch{return null}},set(s,e){try{n().setItem(s,e)}catch{}},del(s){try{n().removeItem(s)}catch{}}}),U=Ve(()=>localStorage),L=Ve(()=>sessionStorage),_=class{constructor(s){f(this,"o",s);f(this,"session",null);f(this,"config",null);f(this,"sessionPromise",null);f(this,"supersededSessions",new Map);f(this,"sessionGeneration",0);f(this,"refreshAt",new WeakMap)}url(s,e){let t=this.o.endpoint.replace(/\/+$/,""),i=e?"?"+new URLSearchParams(e).toString():"";return`${t}/${s}${i}`}get storageKey(){return`dewee-webchat:session:${this.o.endpoint}`}get reuseKey(){return`dewee-webchat:reuse:${this.o.endpoint}`}reuseHint(){return L.get(this.reuseKey)}request(s,e){return(this.o.fetch??globalThis.fetch)(s,e)}async headers(s=!1){let e={},t=typeof this.o.headers=="function"?await this.o.headers():this.o.headers;for(let[i,r]of Object.entries(t??{}))typeof r=="string"&&!fe.includes(i.toLowerCase())&&(e[i.toLowerCase()]=r);if(e["content-type"]="application/json",this.o.gatewayUrl&&this.o.gatewayToken&&(e[Z]=this.o.gatewayUrl,e[X]=this.o.gatewayToken),s){let i=await this.ensureSession();e.authorization=`Bearer ${i.token}`}return e}async parse(s){let e=await s.json().catch(()=>({}));if(!s.ok){let t=e?.error?.remediation,i=t&&typeof t.action=="string"&&typeof t.audience=="string"?{action:t.action,audience:t.audience}:void 0;throw new v(e?.error?.message||`HTTP ${s.status}`,s.status,e?.error?.code,i,Lt(e?.error?.retryAfterMs,s))}return e}async imageUrl(s,e){let t=await this.request(this.url("image-ticket"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify({url:s}),redirect:"error",signal:e}),i=await this.parse(t);if(typeof i.ticket!="string"||!/^(?:[A-Za-z0-9_-]{1,8192}|s\.[A-Za-z0-9_-]{1,64}\.[A-Za-z0-9_-]{1,8192})$/.test(i.ticket))throw new Error("Invalid image ticket");return this.url("image",{ticket:i.ticket})}async getConfig(s=!1,e=!1){return this.config&&!s?this.config:(this.config=await this.parse(await this.request(this.url("config",e?{pageContext:"1"}:void 0),{headers:await this.headers()})),this.config)}fresh(s){if(s.verified)return Date.now()<(this.refreshAt.get(s)??0);let e=typeof s.expiresIn=="number"&&s.expiresIn>=0&&s.obtainedAt,t=e?s.obtainedAt+s.expiresIn:s.expiresAt,i=e?s.expiresIn:s.obtainedAt?s.expiresAt-s.obtainedAt:1/0,r=Math.min(864e5,i/2,Math.max(3e4,i*.1));return t-Date.now()>r}scheduleRefresh(s,e){let t=typeof s.expiresIn=="number"&&s.expiresIn>0?s.expiresIn:s.expiresAt-e;this.refreshAt.set(s,e+t-Math.min(Rt,t/2))}stored(){let s=U.get(this.storageKey);if(!s)return null;try{let e=JSON.parse(s);return e.verified?(U.del(this.storageKey),null):e}catch{return null}}visitorId(){return this.session?.visitorId??null}ensureSession(){if(this.sessionPromise)return this.sessionPromise;if(this.session&&this.fresh(this.session))return Promise.resolve(this.session);let s=this.sessionGeneration,e=this.loadSession(s).catch(t=>{if(s!==this.sessionGeneration)return this.supersededSessions.get(s);throw t}).finally(()=>{this.sessionPromise===e&&(this.sessionPromise=null),this.supersededSessions.delete(s)});return this.sessionPromise=e}async loadSession(s){if(!this.session){let e=this.stored();if(e){if(this.fresh(e)){let t=await this.getConfig();if(s!==this.sessionGeneration)return this.supersededSessions.get(s);if(!t.identity)return this.session=e}this.session=e}}return this.createSession(s)}async createSession(s){let e=await this.getConfig();if(s!==this.sessionGeneration)return this.supersededSessions.get(s);let t={},i=this.session&&!this.session.verified?this.session:this.stored();if(i?.token&&(t.token=i.token),e.identity){let l=this.session?.verified?this.session.reuseHint:L.get(this.reuseKey);l&&(t.reuseHint=l)}let r=async()=>this.parse(await this.request(this.url("session"),{method:"POST",headers:await this.headers(),body:JSON.stringify(t)})),o=async()=>{if(!this.o.turnstile)throw new v("Verification is required but no Turnstile handler is set",403,"turnstile_required");t.turnstileToken=await this.o.turnstile(e.turnstileSiteKey)},c;try{e.turnstileSiteKey&&!(e.refreshWithoutTurnstile&&!e.identity&&t.token&&!t.reuseHint)&&await o();try{c=await r()}catch(l){if(!(e.turnstileSiteKey&&!t.turnstileToken&&l instanceof v&&l.code==="turnstile_failed"))throw l;await o(),c=await r()}}catch(l){if(s!==this.sessionGeneration)return this.supersededSessions.get(s);throw this.session=null,l instanceof v&&l.status===401&&L.del(this.reuseKey),l}if(s!==this.sessionGeneration)return this.supersededSessions.get(s);let a=Date.now(),d={...c,obtainedAt:a};if(this.session=d,d.verified)this.scheduleRefresh(d,a),typeof d.reuseHint=="string"&&d.reuseHint!==""?L.set(this.reuseKey,d.reuseHint):L.del(this.reuseKey);else{L.del(this.reuseKey);let{reuseHint:l,...u}=d;U.set(this.storageKey,JSON.stringify(u))}return d}forgetSession(){if(this.session?.verified){L.del(this.reuseKey),this.session=null;return}this.session=null,U.del(this.storageKey)}async renewSession(){let s=this.sessionGeneration,e=this.sessionPromise;this.sessionGeneration++,this.session=null,this.sessionPromise=null;let t=this.ensureSession();return e&&this.supersededSessions.set(s,t),t}async jobAccess(s){try{if(!(await this.ensureSession()).verified)throw new v("Sign in to open job access",401,"IDENTITY_REQUIRED");if(s.aborted)throw new v("Job access was cancelled",0,"job_access_unavailable");let t=await this.request(this.url("job-access"),{method:"POST",headers:await this.headers(!0),body:"{}",signal:s,redirect:"error"});return j(await this.parse(t))}catch(e){let t=e instanceof v&&typeof e.code=="string"&&["IDENTITY_REQUIRED","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"].includes(e.code)?e.code:"job_access_unavailable";throw new v("Job access is unavailable. Sign in again or retry.",e instanceof v?e.status:0,t)}}async link(){let s=await this.signedInRequest(async()=>this.request(this.url("link"),{method:"POST",headers:await this.headers(!0),body:"{}"}));return this.parse(s)}async history(s,e){return(await this.historyDetails(s,e)).messages}async historyDetails(s,e){let t=await this.signedInRequest(async()=>this.request(this.url("history",{agent:s,conversationId:e}),{headers:await this.headers(!0)})),i=await this.parse(t),r=this.session?.verified&&this.config?.credentialFormOrigin&&Array.isArray(i.credentialRequests)?i.credentialRequests.slice(0,100).map(o=>z(o,s)).filter(o=>o!==null):[];return{messages:i.messages,credentialRequests:r}}async credentialRequest(s,e,t,i,r){let o=await this.ensureSession(),c=(await this.getConfig()).credentialFormOrigin;if(!o.verified||!c)throw new v("Credential request is unavailable",403,"credential_request_unavailable");try{let a=await this.headers(!0);if(this.session!==o)throw new Error("identity changed");let d=await this.request(this.url(s),{method:"POST",headers:a,body:JSON.stringify({agent:e,conversationId:t,credentialRequestId:i}),redirect:"error",signal:r}),l=await this.parse(d);if(this.session!==o)throw new Error("identity changed");let u=s==="credential-request-handoff"?je(l,i,c):z(l.credentialRequest,e);if(!u||"id"in u&&u.id!==i)throw new Error("invalid credential request");return u}catch(a){let d=a instanceof v&&["credential_requests_unavailable","credential_request_not_found","credential_request_not_ready","credential_request_resumed"].includes(a.code??"")?a.code:"credential_request_unavailable";throw new v("Credential request is unavailable",a instanceof v?a.status:0,d)}}async signedInRequest(s){await this.ensureSession();let e=await s();return e.status!==401||!this.session?.verified?e:(this.forgetSession(),await this.ensureSession(),s())}async chat(s,e,t){await this.ensureSession();let i=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:t});if(i.status===401&&!s.credentialRequestId){let a=this.visitorId();this.forgetSession(),await this.ensureSession(),s.actionsHash&&this.visitorId()!==a&&(s={...s,actionsHash:void 0}),i=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:t})}(!i.ok||!i.body)&&await this.parse(i);let r=i.body.pipeThrough(new TextDecoderStream).getReader(),o="",c=!1;for(;;){let{done:a,value:d}=await r.read();if(a)break;o+=d;let l;for(;(l=o.indexOf(`
|
|
18
18
|
|
|
19
19
|
`))>=0;){let u=o.slice(0,l);o=o.slice(l+2);let g=u.split(`
|
|
20
20
|
`).filter(h=>h.startsWith("data:")).map(h=>h.slice(5).trimStart()).join(`
|
|
@@ -314,7 +314,7 @@ textarea::placeholder { color: var(--dw-muted); }
|
|
|
314
314
|
.agent-select { font-size: 16px; max-width: 200px; }
|
|
315
315
|
}
|
|
316
316
|
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
|
|
317
|
-
`;var Bt=5e3,Xe=new Set(["credential_requests_unavailable","credential_request_not_found"]),nt=["show-reasoning","show-tool-calls","allow-agent-switch","open","persist","hide-branding","launcher-avatar","dom-actions"],Ze=["endpoint","agent","title","subtitle","greeting","intro","placeholder","suggestions","avatar","position","mode","theme","accent-color","locale","gateway-url","gateway-token","width","height","z-index","dom-actions-root","image-origins","page-context","context-selector","exclude-selectors",...nt],et=n=>n.replace(/-([a-z])/g,(s,e)=>e.toUpperCase()),jt=n=>n!==null&&!/^(false|0|off|no)$/i.test(n),tt=()=>{let n=crypto.getRandomValues(new Uint8Array(9));return Array.from(n,s=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[s&63]).join("")},Vt=n=>n.replace(/^mcp_[^_]+(?:_[^_]+)*?__/,"").replace(/_/g," "),N={get(n){try{return localStorage.getItem(n)}catch{return null}},set(n,s){try{localStorage.setItem(n,s)}catch{}}},zt=null;function Ut(){return zt??(zt=new Promise((n,s)=>{if(window.turnstile)return n();let e=document.createElement("script");e.src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",e.async=!0,e.onload=()=>n(),e.onerror=()=>s(new Error("Could not load Turnstile")),document.head.appendChild(e)}))}var Jt=typeof HTMLElement>"u"?class{}:HTMLElement,q=class extends Jt{constructor(){super();f(this,"root");f(this,"overrides",{});f(this,"api",null);f(this,"apiKey","");f(this,"server",null);f(this,"sharedPage");f(this,"preparingPage",!1);f(this,"pagePending",!1);f(this,"pageGeneration",0);f(this,"agentId","");f(this,"conversationId","");f(this,"messages",[]);f(this,"running",null);f(this,"jobAccessLaunch",null);f(this,"credentialCards",new Map);f(this,"credentialGeneration",0);f(this,"credentialBusy",new Set);f(this,"credentialPopups",new Map);f(this,"credentialRequestsInFlight",new Set);f(this,"activated",!1);f(this,"probedPage",!1);f(this,"strings",me());f(this,"mql",null);f(this,"registry",new D);f(this,"propActionNames",new Set);f(this,"hooks",{});f(this,"dom",null);f(this,"acks",new Map);f(this,"cooldown",new G(e=>this.renderCooldown(e),()=>this.endCooldown()));f(this,"els");f(this,"onScheme",()=>this.applyTheme());f(this,"pendingFrame",0);f(this,"following",!0);this.root=this.attachShadow({mode:"open"})}static get observedAttributes(){return Ze}get config(){let e={endpoint:"/api/dewee"};for(let t of Ze){let i=this.getAttribute(t);i!==null&&(nt.includes(t)?e[et(t)]=jt(i):t==="suggestions"?e.suggestions=i.trim().startsWith("[")?be(i,[]):i.split("|").map(r=>r.trim()).filter(Boolean):t==="image-origins"?e.imageOrigins=i.trim().startsWith("[")?be(i,[]):i.split(",").map(r=>r.trim()).filter(Boolean):t==="exclude-selectors"?e.excludeSelectors=i.trim().startsWith("[")?be(i,[]):i.split(",").map(r=>r.trim()).filter(Boolean):t==="z-index"?e.zIndex=Number(i):e[et(t)]=i)}return{...e,...this.overrides,...this.hooks}}configure(e){let{actions:t,confirm:i,getPageContext:r,headers:o,fetch:c,domActionsRoot:a,contextProvider:d,...l}=e,u=g=>Object.prototype.hasOwnProperty.call(e,g);u("actions")&&this.setPropActions(t??[]),u("confirm")&&(this.hooks.confirm=i),u("getPageContext")&&(this.hooks.getPageContext=r),u("headers")&&(this.hooks.headers=o),u("fetch")&&(this.hooks.fetch=c),u("contextProvider")&&(this.hooks.contextProvider=d),u("domActionsRoot")&&(a===void 0?delete this.hooks.domActionsRoot:this.hooks.domActionsRoot=a),Object.keys(l).length&&(this.overrides={...this.overrides,...l},this.els&&this.applyConfig())}registerActions(e){this.registry.register(e)}unregisterAction(e){return this.propActionNames.delete(e),this.registry.unregister(e)}get headers(){return this.hooks.headers}set headers(e){this.hooks.headers=e}get fetch(){return this.hooks.fetch}set fetch(e){this.hooks.fetch=e}get actionNames(){return this.actionRegistry().names()}actionRegistry(){if(!this.config.domActions)return this.registry;this.dom??(this.dom=He({root:()=>this.hooks.domActionsRoot??this.getAttribute("dom-actions-root")??void 0,exclude:o=>o===this||this.contains(o)||o.closest("dewee-chat")!==null}));let e=this.registry.names().map(o=>this.registry.get(o)),t=T.maxActions-e.length,i=this.dom.actions.filter(o=>!this.registry.get(o.name)).slice(0,Math.max(0,t)),r=new D;return r.register([...e,...i]),r}setPropActions(e){let t=new Set(e.map(i=>i.name));for(let i of this.propActionNames)t.has(i)||this.registry.unregister(i);e.length&&this.registry.register(e),this.propActionNames=t}open(){this.config.mode!=="inline"&&(this.setAttribute("data-open",""),this.removeAttribute("data-unread"),this.els.launcher.setAttribute("aria-expanded","true"),this.activate(),setTimeout(()=>this.els.input.focus(),120),this.emit("dewee:open"))}close(){this.config.mode!=="inline"&&(this.removeAttribute("data-open"),this.els.launcher.setAttribute("aria-expanded","false"),this.emit("dewee:close"))}toggle(){this.hasAttribute("data-open")?this.close():this.open()}async send(e){let t=e.trim();if(!t||this.running||this.preparingPage||this.cooldown.active)return;this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate();let i;if(this.els.pageChoice.checked){if(this.pagePending){this.showNotice(this.strings.pageUnavailable);return}this.preparingPage=!0;let o=this.pageGeneration;try{let c=this.sharedPage,a=await this.captureCurrentPage();if(!this.els.pageChoice.checked||o!==this.pageGeneration)return;if(!c||!a||this.pageFingerprint(c)!==this.pageFingerprint(a)){this.sharedPage=a,this.renderPagePreview(),this.els.pageChoice.checked=!1,this.showNotice(this.strings.pageChanged);return}i=a}catch{this.sharedPage=void 0,this.els.pageChoice.checked=!1,this.renderPagePreview(),this.showNotice(this.strings.pageUnavailable);return}finally{this.preparingPage=!1}this.els.pageChoice.checked=!1,this.sharedPage=void 0,this.renderPagePreview()}this.els.input.value="",this.autosize();let r={message:t,hidden:!1};for(let o=0;r;o++){let c=await this.runTurn(r.message,r.hidden,o===0?i:void 0);if(r=null,c.status!=="done"||c.stopped||!this.actionRegistry().size)break;let{calls:a}=te(c.content);if(!a.length)break;c.actions=a.map(u=>({id:u.id,name:u.name,args:u.ok?u.args:void 0,status:"pending"}));let d=this.config.maxActionRounds??5;if(o>=d){for(let u of c.actions)Object.assign(u,{status:"error",error:"Automatic action limit reached"});this.updateMessage(c),this.showNotice(this.strings.actionLimit(d));break}this.updateMessage(c);let l=await this.runActions(c,a);l&&(r={message:Le(l,this.server?.maxMessageLength??4e3),hidden:!0})}}async runTurn(e,t,i){let r=this.credentialGeneration;this.pushMessage({role:"user",content:e,hidden:t});let o={role:"assistant",content:"",status:"streaming",startedAt:Date.now()};this.pushMessage(o),this.setRunning(new AbortController),this.emit("dewee:send",{message:e,agent:this.agentId,conversationId:this.conversationId,auto:t||void 0});try{await this.client().ensureSession(),await this.client().chat({...this.chatBody(e),...i?{page_context:i}:{}},c=>{r===this.credentialGeneration&&this.onStreamEvent(o,c)},this.running.signal),o.status==="streaming"&&(o.status="done")}catch(c){c.name==="AbortError"?(o.status="done",o.stopped=!0,o.content||(o.content=`_${this.strings.stopped}_`)):(o.status="error",this.showError(o,c))}finally{this.finishThinking(o),this.updateMessage(o),this.setRunning(null),o.status==="done"&&(o.actionsHash&&o.ackVisitor&&!o.stopped&&this.setAck(o.actionsHash,o.ackVisitor),this.emit("dewee:reply",{content:o.content,agent:this.agentId,conversationId:this.conversationId}),this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.setAttribute("data-unread",""))}return o}chatBody(e){let t={message:e,agent:this.agentId,conversationId:this.conversationId},i=this.actionRegistry();if(i.size){t.actions=i.manifest();let o=this.client().visitorId(),c=o?this.getAck(o):void 0;c&&(t.actionsHash=c)}let r=this.pageContext();return r!==void 0&&(t.pageContext=r),t}pageContext(){let e=this.hooks.getPageContext;if(e)try{let t=e();if(t==null)return;let i=new TextEncoder().encode(JSON.stringify(t)).length;return i<=T.maxPageContextBytes?t:(console.warn(`[dewee-webchat] getPageContext() returned ${i} bytes; the limit is ${T.maxPageContextBytes}. Context not sent.`),{truncated:!0})}catch(t){console.warn("[dewee-webchat] getPageContext() failed:",t);return}}ackKey(e){return`dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${e}:${this.conversationId}`}getAck(e){let t=this.ackKey(e);return this.acks.get(t)??(this.config.persist!==!1?N.get(t)??void 0:void 0)}setAck(e,t){let i=this.ackKey(t);this.acks.set(i,e),this.config.persist!==!1&&N.set(i,e)}async runActions(e,t){let i=new AbortController;this.setRunning(i);let r=[],o={agent:this.agentId,conversationId:this.conversationId},c=this.actionRegistry();try{for(let[a,d]of t.entries()){let l=e.actions[a],u=d.ok?c.get(d.name)?.sensitivity:void 0,g=d.ok?d.args:void 0;this.emit("dewee:action",{phase:"start",id:d.id,name:d.name,sensitivity:u,args:g,...o});let p=await Oe(d,{registry:c,confirm:(h,m)=>this.confirmAction(c,h,m,i.signal),signal:i.signal,timeoutMs:this.config.actionTimeoutMs,...o,onStatus:h=>{l.status=h,this.updateMessage(e)}});p.error&&(l.error=p.error),this.updateMessage(e),r.push(p),this.emit("dewee:action",{phase:"result",id:p.id,name:p.name,sensitivity:u,args:g,status:p.status,result:p.result,error:p.error,...o})}}finally{this.setRunning(null)}return i.signal.aborted?null:r}confirmAction(e,t,i,r){let c=this.dom?.actions.includes(e.get(t.name))?this.dom.target(i):void 0,a=c?{...i,target:c}:i;return this.hooks.confirm?this.hooks.confirm(t,a):(this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),Ue(this.els.panel,this.strings,t,a,r))}showNotice(e){let t=document.createElement("div");t.className="notice",t.setAttribute("role","status"),t.textContent=e,this.els.log.appendChild(t),this.scrollToBottom(!0)}stop(){this.running?.abort()}async renewSession(){this.stop(),this.clearCredentials(),this.renderLog();try{await this.client().renewSession()}catch(e){this.showError(null,e);return}this.activated&&await this.loadConversation()}reuseHint(){return this.client().reuseHint()}async startLink(){this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate();try{let e=await this.client().link();return this.renderLink(e),this.emit("dewee:link",{expiresAt:e.expiresAt,deepLinks:e.deepLinks}),e}catch(e){return this.showError(null,e),null}}async openJobAccess(){if(this.jobAccessLaunch)return;let e=new AbortController;this.jobAccessLaunch=e,this.els.jobAccess.disabled=!0;try{await ze(t=>this.client().jobAccess(t),e.signal),this.showNotice(this.strings.jobAccessOpened)}catch(t){if(e.signal.aborted)return;t instanceof k?this.showNotice(t.code==="popup_blocked"?this.strings.jobAccessPopupBlocked:t.code==="popup_closed"?this.strings.jobAccessPopupClosed:this.strings.jobAccessUnavailable):t instanceof v&&W(t.code)?this.showError(null,t):this.showNotice(this.strings.jobAccessUnavailable)}finally{this.jobAccessLaunch=null,this.els.jobAccess.disabled=!1}}renderLink(e){let t=this.strings,i=document.createElement("div");i.className="msg assistant link";let r=document.createElement("div");r.className="bubble";let o=document.createElement("div");o.className="md";let c=l=>{let u=document.createElement("p");return u.textContent=l,u},a=document.createElement("code");a.textContent=e.proofCode;let d=document.createElement("p");if(d.appendChild(a),o.append(c(t.linkIntro),d),e.deepLinks.length){let l=document.createElement("ul");for(let u of e.deepLinks){let g=document.createElement("li"),p=u.displayName||u.name;g.textContent=t.linkContinueOn(Ft(u.channelType))+(p?` \xB7 ${p}`:""),l.appendChild(g)}o.appendChild(l)}else o.appendChild(c(t.linkNoTargets));o.appendChild(c(t.linkExpires(Math.max(1,Math.round((e.expiresAt-Date.now())/6e4))))),r.appendChild(o),i.appendChild(r),this.messages.length||this.els.log.replaceChildren(),this.els.log.appendChild(i),this.scrollToBottom(!0)}newConversation(){this.stop(),this.clearCredentials(),this.conversationId=tt(),this.config.persist!==!1&&N.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),this.els.input.focus()}connectedCallback(){this.els||this.renderShell(),this.applyConfig(),(this.config.mode==="inline"||this.config.open)&&(this.config.mode!=="inline"?this.open():this.activate())}disconnectedCallback(){this.clearCredentials(),this.pageGeneration++,this.sharedPage=void 0,this.els.pageChoice.checked=!1,this.renderPagePreview(),K(this.root);for(let e of this.messages)e.view&&(e.view.mdKey=void 0);this.jobAccessLaunch?.abort(),this.stop(),this.mql?.removeEventListener("change",this.onScheme)}attributeChangedCallback(){this.els&&this.applyConfig()}emit(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}client(){let e=this.config,t=[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|");return(!this.api||t!==this.apiKey)&&(this.apiKey=t,this.api=new _({endpoint:e.endpoint,gatewayUrl:e.gatewayUrl,gatewayToken:e.gatewayToken,turnstile:i=>this.challenge(i),headers:()=>{let i=this.hooks.headers;return(typeof i=="function"?i():i)??{}},fetch:(i,r)=>(this.hooks.fetch??globalThis.fetch)(i,r)})),this.api}convKey(){return`dewee-webchat:conv:${this.config.endpoint}:${this.config.gatewayUrl||""}:${this.agentId}`}applyTheme(){let e=this.config.theme||"light",t=e==="dark"||e==="auto"&&!!this.mql?.matches;this.setAttribute("data-theme",t?"dark":"light")}applyConfig(){let e=this.config;this.strings=me(e.locale);let t=this.strings;this.setAttribute("data-position",e.position||"bottom-right"),this.setAttribute("data-mode",e.mode||"floating"),e.theme==="auto"&&!this.mql&&(this.mql=matchMedia("(prefers-color-scheme: dark)"),this.mql.addEventListener("change",this.onScheme)),this.applyTheme();let i=(o,c)=>c!==void 0&&c!==""?this.style.setProperty(o,String(c)):this.style.removeProperty(o);i("--dw-accent",e.accentColor),i("--dw-width",e.width),i("--dw-height",e.height),i("--dw-z",e.zIndex);let r=this.els;if(r.title.textContent=e.title||t.title,r.avatar.replaceChildren(),e.avatar){let o=document.createElement("img");o.src=e.avatar,o.alt="",r.avatar.appendChild(o)}else r.avatar.textContent=(e.title||t.title).trim().charAt(0).toUpperCase();if(r.launcher.innerHTML=e.avatar&&e.launcherAvatar?"":A.chat,e.avatar&&e.launcherAvatar){let o=document.createElement("img");o.src=e.avatar,o.alt="",r.launcher.appendChild(o)}if(r.launcher.insertAdjacentHTML("beforeend",'<span class="badge"></span>'),r.launcher.setAttribute("aria-label",t.open),r.close.setAttribute("aria-label",t.close),r.close.title=t.close,r.newChat.setAttribute("aria-label",t.newChat),r.newChat.title=t.newChat,r.jobAccess.setAttribute("aria-label",t.jobAccess),r.jobAccess.title=t.jobAccess,r.input.placeholder=e.placeholder||t.placeholder,r.pageLabel.textContent=t.pageShare,this.updatePageChoice(),r.foot.hidden=!!e.hideBranding,r.foot.querySelector(".by").textContent=t.poweredBy+" ",this.renderSubtitle(),this.updateSendButton(),this.activated&&this.api&&this.apiKey!==[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|"))this.activated=!1,this.server=null,this.probedPage=!1,this.activate();else if(this.activated&&!this.probedPage&&(e.pageContext==="metadata"||e.pageContext==="content"))this.activated=!1,this.activate();else if(this.activated&&e.agent&&e.agent!==this.agentId&&this.server)this.switchAgent(e.agent);else if(this.activated)if(!this.messages.length)this.renderLog();else for(let o of this.messages)this.updateMessage(o)}renderShell(){this.root.innerHTML=`
|
|
317
|
+
`;var Bt=5e3,Ze=new Set(["credential_requests_unavailable","credential_request_not_found"]),nt=["show-reasoning","show-tool-calls","allow-agent-switch","open","persist","hide-branding","launcher-avatar","dom-actions"],Xe=["endpoint","agent","title","subtitle","greeting","intro","placeholder","suggestions","avatar","position","mode","theme","accent-color","locale","gateway-url","gateway-token","width","height","z-index","dom-actions-root","image-origins","page-context","context-selector","exclude-selectors",...nt],et=n=>n.replace(/-([a-z])/g,(s,e)=>e.toUpperCase()),jt=n=>n!==null&&!/^(false|0|off|no)$/i.test(n),tt=()=>{let n=crypto.getRandomValues(new Uint8Array(9));return Array.from(n,s=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[s&63]).join("")},Vt=n=>n.replace(/^mcp_[^_]+(?:_[^_]+)*?__/,"").replace(/_/g," "),N={get(n){try{return localStorage.getItem(n)}catch{return null}},set(n,s){try{localStorage.setItem(n,s)}catch{}}},zt=null;function Ut(){return zt??(zt=new Promise((n,s)=>{if(window.turnstile)return n();let e=document.createElement("script");e.src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",e.async=!0,e.onload=()=>n(),e.onerror=()=>s(new Error("Could not load Turnstile")),document.head.appendChild(e)}))}var Jt=typeof HTMLElement>"u"?class{}:HTMLElement,q=class extends Jt{constructor(){super();f(this,"root");f(this,"overrides",{});f(this,"api",null);f(this,"apiKey","");f(this,"server",null);f(this,"sharedPage");f(this,"preparingPage",!1);f(this,"pagePending",!1);f(this,"pageGeneration",0);f(this,"agentId","");f(this,"conversationId","");f(this,"messages",[]);f(this,"running",null);f(this,"jobAccessLaunch",null);f(this,"credentialCards",new Map);f(this,"credentialGeneration",0);f(this,"credentialBusy",new Set);f(this,"credentialPopups",new Map);f(this,"credentialRequestsInFlight",new Set);f(this,"activated",!1);f(this,"probedPage",!1);f(this,"strings",me());f(this,"mql",null);f(this,"registry",new D);f(this,"propActionNames",new Set);f(this,"hooks",{});f(this,"dom",null);f(this,"acks",new Map);f(this,"cooldown",new G(e=>this.renderCooldown(e),()=>this.endCooldown()));f(this,"els");f(this,"onScheme",()=>this.applyTheme());f(this,"pendingFrame",0);f(this,"following",!0);this.root=this.attachShadow({mode:"open"})}static get observedAttributes(){return Xe}get config(){let e={endpoint:"/api/dewee"};for(let t of Xe){let i=this.getAttribute(t);i!==null&&(nt.includes(t)?e[et(t)]=jt(i):t==="suggestions"?e.suggestions=i.trim().startsWith("[")?be(i,[]):i.split("|").map(r=>r.trim()).filter(Boolean):t==="image-origins"?e.imageOrigins=i.trim().startsWith("[")?be(i,[]):i.split(",").map(r=>r.trim()).filter(Boolean):t==="exclude-selectors"?e.excludeSelectors=i.trim().startsWith("[")?be(i,[]):i.split(",").map(r=>r.trim()).filter(Boolean):t==="z-index"?e.zIndex=Number(i):e[et(t)]=i)}return{...e,...this.overrides,...this.hooks}}configure(e){let{actions:t,confirm:i,getPageContext:r,headers:o,fetch:c,domActionsRoot:a,contextProvider:d,...l}=e,u=g=>Object.prototype.hasOwnProperty.call(e,g);u("actions")&&this.setPropActions(t??[]),u("confirm")&&(this.hooks.confirm=i),u("getPageContext")&&(this.hooks.getPageContext=r),u("headers")&&(this.hooks.headers=o),u("fetch")&&(this.hooks.fetch=c),u("contextProvider")&&(this.hooks.contextProvider=d),u("domActionsRoot")&&(a===void 0?delete this.hooks.domActionsRoot:this.hooks.domActionsRoot=a),Object.keys(l).length&&(this.overrides={...this.overrides,...l},this.els&&this.applyConfig())}registerActions(e){this.registry.register(e)}unregisterAction(e){return this.propActionNames.delete(e),this.registry.unregister(e)}get headers(){return this.hooks.headers}set headers(e){this.hooks.headers=e}get fetch(){return this.hooks.fetch}set fetch(e){this.hooks.fetch=e}get actionNames(){return this.actionRegistry().names()}actionRegistry(){if(!this.config.domActions)return this.registry;this.dom??(this.dom=He({root:()=>this.hooks.domActionsRoot??this.getAttribute("dom-actions-root")??void 0,exclude:o=>o===this||this.contains(o)||o.closest("dewee-chat")!==null}));let e=this.registry.names().map(o=>this.registry.get(o)),t=T.maxActions-e.length,i=this.dom.actions.filter(o=>!this.registry.get(o.name)).slice(0,Math.max(0,t)),r=new D;return r.register([...e,...i]),r}setPropActions(e){let t=new Set(e.map(i=>i.name));for(let i of this.propActionNames)t.has(i)||this.registry.unregister(i);e.length&&this.registry.register(e),this.propActionNames=t}open(){this.config.mode!=="inline"&&(this.setAttribute("data-open",""),this.removeAttribute("data-unread"),this.els.launcher.setAttribute("aria-expanded","true"),this.activate(),setTimeout(()=>this.els.input.focus(),120),this.emit("dewee:open"))}close(){this.config.mode!=="inline"&&(this.removeAttribute("data-open"),this.els.launcher.setAttribute("aria-expanded","false"),this.emit("dewee:close"))}toggle(){this.hasAttribute("data-open")?this.close():this.open()}async send(e){let t=e.trim();if(!t||this.running||this.preparingPage||this.cooldown.active)return;this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate();let i;if(this.els.pageChoice.checked){if(this.pagePending){this.showNotice(this.strings.pageUnavailable);return}this.preparingPage=!0;let o=this.pageGeneration;try{let c=this.sharedPage,a=await this.captureCurrentPage();if(!this.els.pageChoice.checked||o!==this.pageGeneration)return;if(!c||!a||this.pageFingerprint(c)!==this.pageFingerprint(a)){this.sharedPage=a,this.renderPagePreview(),this.els.pageChoice.checked=!1,this.showNotice(this.strings.pageChanged);return}i=a}catch{this.sharedPage=void 0,this.els.pageChoice.checked=!1,this.renderPagePreview(),this.showNotice(this.strings.pageUnavailable);return}finally{this.preparingPage=!1}this.els.pageChoice.checked=!1,this.sharedPage=void 0,this.renderPagePreview()}this.els.input.value="",this.autosize();let r={message:t,hidden:!1};for(let o=0;r;o++){let c=await this.runTurn(r.message,r.hidden,o===0?i:void 0);if(r=null,c.status!=="done"||c.stopped||!this.actionRegistry().size)break;let{calls:a}=te(c.content);if(!a.length)break;c.actions=a.map(u=>({id:u.id,name:u.name,args:u.ok?u.args:void 0,status:"pending"}));let d=this.config.maxActionRounds??5;if(o>=d){for(let u of c.actions)Object.assign(u,{status:"error",error:"Automatic action limit reached"});this.updateMessage(c),this.showNotice(this.strings.actionLimit(d));break}this.updateMessage(c);let l=await this.runActions(c,a);l&&(r={message:Le(l,this.server?.maxMessageLength??4e3),hidden:!0})}}async runTurn(e,t,i){let r=this.credentialGeneration;this.pushMessage({role:"user",content:e,hidden:t});let o={role:"assistant",content:"",status:"streaming",startedAt:Date.now()};this.pushMessage(o),this.setRunning(new AbortController),this.emit("dewee:send",{message:e,agent:this.agentId,conversationId:this.conversationId,auto:t||void 0});try{await this.client().ensureSession(),await this.client().chat({...this.chatBody(e),...i?{page_context:i}:{}},c=>{r===this.credentialGeneration&&this.onStreamEvent(o,c)},this.running.signal),o.status==="streaming"&&(o.status="done")}catch(c){c.name==="AbortError"?(o.status="done",o.stopped=!0,o.content||(o.content=`_${this.strings.stopped}_`)):(o.status="error",this.showError(o,c))}finally{this.finishThinking(o),this.updateMessage(o),this.setRunning(null),o.status==="done"&&(o.actionsHash&&o.ackVisitor&&!o.stopped&&this.setAck(o.actionsHash,o.ackVisitor),this.emit("dewee:reply",{content:o.content,agent:this.agentId,conversationId:this.conversationId}),this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.setAttribute("data-unread",""))}return o}chatBody(e){let t={message:e,agent:this.agentId,conversationId:this.conversationId},i=this.actionRegistry();if(i.size){t.actions=i.manifest();let o=this.client().visitorId(),c=o?this.getAck(o):void 0;c&&(t.actionsHash=c)}let r=this.pageContext();return r!==void 0&&(t.pageContext=r),t}pageContext(){let e=this.hooks.getPageContext;if(e)try{let t=e();if(t==null)return;let i=new TextEncoder().encode(JSON.stringify(t)).length;return i<=T.maxPageContextBytes?t:(console.warn(`[dewee-webchat] getPageContext() returned ${i} bytes; the limit is ${T.maxPageContextBytes}. Context not sent.`),{truncated:!0})}catch(t){console.warn("[dewee-webchat] getPageContext() failed:",t);return}}ackKey(e){return`dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${e}:${this.conversationId}`}getAck(e){let t=this.ackKey(e);return this.acks.get(t)??(this.config.persist!==!1?N.get(t)??void 0:void 0)}setAck(e,t){let i=this.ackKey(t);this.acks.set(i,e),this.config.persist!==!1&&N.set(i,e)}async runActions(e,t){let i=new AbortController;this.setRunning(i);let r=[],o={agent:this.agentId,conversationId:this.conversationId},c=this.actionRegistry();try{for(let[a,d]of t.entries()){let l=e.actions[a],u=d.ok?c.get(d.name)?.sensitivity:void 0,g=d.ok?d.args:void 0;this.emit("dewee:action",{phase:"start",id:d.id,name:d.name,sensitivity:u,args:g,...o});let p=await Oe(d,{registry:c,confirm:(h,m)=>this.confirmAction(c,h,m,i.signal),signal:i.signal,timeoutMs:this.config.actionTimeoutMs,...o,onStatus:h=>{l.status=h,this.updateMessage(e)}});p.error&&(l.error=p.error),this.updateMessage(e),r.push(p),this.emit("dewee:action",{phase:"result",id:p.id,name:p.name,sensitivity:u,args:g,status:p.status,result:p.result,error:p.error,...o})}}finally{this.setRunning(null)}return i.signal.aborted?null:r}confirmAction(e,t,i,r){let c=this.dom?.actions.includes(e.get(t.name))?this.dom.target(i):void 0,a=c?{...i,target:c}:i;return this.hooks.confirm?this.hooks.confirm(t,a):(this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),Ue(this.els.panel,this.strings,t,a,r))}showNotice(e){let t=document.createElement("div");t.className="notice",t.setAttribute("role","status"),t.textContent=e,this.els.log.appendChild(t),this.scrollToBottom(!0)}stop(){this.running?.abort()}async renewSession(){this.stop(),this.clearCredentials(),this.renderLog();try{await this.client().renewSession()}catch(e){this.showError(null,e);return}this.activated&&await this.loadConversation()}reuseHint(){return this.client().reuseHint()}async startLink(){this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate();try{let e=await this.client().link();return this.renderLink(e),this.emit("dewee:link",{expiresAt:e.expiresAt,deepLinks:e.deepLinks}),e}catch(e){return this.showError(null,e),null}}async openJobAccess(){if(this.jobAccessLaunch)return;let e=new AbortController;this.jobAccessLaunch=e,this.els.jobAccess.disabled=!0;try{await ze(t=>this.client().jobAccess(t),e.signal),this.showNotice(this.strings.jobAccessOpened)}catch(t){if(e.signal.aborted)return;t instanceof k?this.showNotice(t.code==="popup_blocked"?this.strings.jobAccessPopupBlocked:t.code==="popup_closed"?this.strings.jobAccessPopupClosed:this.strings.jobAccessUnavailable):t instanceof v&&W(t.code)?this.showError(null,t):this.showNotice(this.strings.jobAccessUnavailable)}finally{this.jobAccessLaunch=null,this.els.jobAccess.disabled=!1}}renderLink(e){let t=this.strings,i=document.createElement("div");i.className="msg assistant link";let r=document.createElement("div");r.className="bubble";let o=document.createElement("div");o.className="md";let c=l=>{let u=document.createElement("p");return u.textContent=l,u},a=document.createElement("code");a.textContent=e.proofCode;let d=document.createElement("p");if(d.appendChild(a),o.append(c(t.linkIntro),d),e.deepLinks.length){let l=document.createElement("ul");for(let u of e.deepLinks){let g=document.createElement("li"),p=u.displayName||u.name;g.textContent=t.linkContinueOn(Ft(u.channelType))+(p?` \xB7 ${p}`:""),l.appendChild(g)}o.appendChild(l)}else o.appendChild(c(t.linkNoTargets));o.appendChild(c(t.linkExpires(Math.max(1,Math.round((e.expiresAt-Date.now())/6e4))))),r.appendChild(o),i.appendChild(r),this.messages.length||this.els.log.replaceChildren(),this.els.log.appendChild(i),this.scrollToBottom(!0)}newConversation(){this.stop(),this.clearCredentials(),this.conversationId=tt(),this.config.persist!==!1&&N.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),this.els.input.focus()}connectedCallback(){this.els||this.renderShell(),this.applyConfig(),(this.config.mode==="inline"||this.config.open)&&(this.config.mode!=="inline"?this.open():this.activate())}disconnectedCallback(){this.clearCredentials(),this.pageGeneration++,this.sharedPage=void 0,this.els.pageChoice.checked=!1,this.renderPagePreview(),K(this.root);for(let e of this.messages)e.view&&(e.view.mdKey=void 0);this.jobAccessLaunch?.abort(),this.stop(),this.mql?.removeEventListener("change",this.onScheme)}attributeChangedCallback(){this.els&&this.applyConfig()}emit(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}client(){let e=this.config,t=[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|");return(!this.api||t!==this.apiKey)&&(this.apiKey=t,this.api=new _({endpoint:e.endpoint,gatewayUrl:e.gatewayUrl,gatewayToken:e.gatewayToken,turnstile:i=>this.challenge(i),headers:()=>{let i=this.hooks.headers;return(typeof i=="function"?i():i)??{}},fetch:(i,r)=>(this.hooks.fetch??globalThis.fetch)(i,r)})),this.api}convKey(){return`dewee-webchat:conv:${this.config.endpoint}:${this.config.gatewayUrl||""}:${this.agentId}`}applyTheme(){let e=this.config.theme||"light",t=e==="dark"||e==="auto"&&!!this.mql?.matches;this.setAttribute("data-theme",t?"dark":"light")}applyConfig(){let e=this.config;this.strings=me(e.locale);let t=this.strings;this.setAttribute("data-position",e.position||"bottom-right"),this.setAttribute("data-mode",e.mode||"floating"),e.theme==="auto"&&!this.mql&&(this.mql=matchMedia("(prefers-color-scheme: dark)"),this.mql.addEventListener("change",this.onScheme)),this.applyTheme();let i=(o,c)=>c!==void 0&&c!==""?this.style.setProperty(o,String(c)):this.style.removeProperty(o);i("--dw-accent",e.accentColor),i("--dw-width",e.width),i("--dw-height",e.height),i("--dw-z",e.zIndex);let r=this.els;if(r.title.textContent=e.title||t.title,r.avatar.replaceChildren(),e.avatar){let o=document.createElement("img");o.src=e.avatar,o.alt="",r.avatar.appendChild(o)}else r.avatar.textContent=(e.title||t.title).trim().charAt(0).toUpperCase();if(r.launcher.innerHTML=e.avatar&&e.launcherAvatar?"":A.chat,e.avatar&&e.launcherAvatar){let o=document.createElement("img");o.src=e.avatar,o.alt="",r.launcher.appendChild(o)}if(r.launcher.insertAdjacentHTML("beforeend",'<span class="badge"></span>'),r.launcher.setAttribute("aria-label",t.open),r.close.setAttribute("aria-label",t.close),r.close.title=t.close,r.newChat.setAttribute("aria-label",t.newChat),r.newChat.title=t.newChat,r.jobAccess.setAttribute("aria-label",t.jobAccess),r.jobAccess.title=t.jobAccess,r.input.placeholder=e.placeholder||t.placeholder,r.pageLabel.textContent=t.pageShare,this.updatePageChoice(),r.foot.hidden=!!e.hideBranding,r.foot.querySelector(".by").textContent=t.poweredBy+" ",this.renderSubtitle(),this.updateSendButton(),this.activated&&this.api&&this.apiKey!==[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|"))this.activated=!1,this.server=null,this.probedPage=!1,this.activate();else if(this.activated&&!this.probedPage&&(e.pageContext==="metadata"||e.pageContext==="content"))this.activated=!1,this.activate();else if(this.activated&&e.agent&&e.agent!==this.agentId&&this.server)this.switchAgent(e.agent);else if(this.activated)if(!this.messages.length)this.renderLog();else for(let o of this.messages)this.updateMessage(o)}renderShell(){this.root.innerHTML=`
|
|
318
318
|
<style>${Ge}</style>
|
|
319
319
|
<button class="launcher" part="launcher" aria-expanded="false" aria-haspopup="dialog"></button>
|
|
320
320
|
<section class="panel" part="panel" role="dialog" aria-label="Chat">
|
|
@@ -342,7 +342,7 @@ textarea::placeholder { color: var(--dw-muted); }
|
|
|
342
342
|
</form>
|
|
343
343
|
</section>`;let e=i=>this.root.querySelector(i);this.els={launcher:e(".launcher"),panel:e(".panel"),log:e(".log"),title:e(".title"),subtitle:e(".subtitle"),status:e(".status"),avatar:e(".avatar"),select:e(".agent-select"),input:e("textarea"),send:e(".send"),newChat:e(".new"),jobAccess:e(".job-access"),close:e(".close"),foot:e(".foot"),cooldown:e(".cooldown"),announcer:e(".announcer"),pageChoice:e(".page-choice"),pageWrap:e(".page-context"),pageLabel:e(".page-label"),pagePreview:e(".page-preview")};let t=this.els;t.launcher.addEventListener("click",()=>this.toggle()),t.close.addEventListener("click",()=>this.close()),t.log.addEventListener("scroll",()=>{this.following=this.nearBottom()},{passive:!0}),t.newChat.addEventListener("click",()=>this.newConversation()),t.jobAccess.addEventListener("click",()=>{this.openJobAccess()}),t.pageChoice.addEventListener("change",()=>{this.choosePage()}),t.select.addEventListener("change",()=>this.switchAgent(t.select.value)),e("form").addEventListener("submit",i=>{i.preventDefault(),this.running?this.stop():this.send(t.input.value)}),t.input.addEventListener("keydown",i=>{i.key==="Enter"&&!i.shiftKey&&!i.isComposing&&i.keyCode!==229&&(i.preventDefault(),this.running||this.send(t.input.value))}),t.input.addEventListener("input",()=>{this.autosize(),this.updateSendButton()}),t.panel.addEventListener("keydown",i=>{i.key==="Escape"&&this.close()})}autosize(){let e=this.els.input;e.style.height="auto",e.style.height=Math.min(e.scrollHeight,140)+"px"}updatePageChoice(){let e=this.config.pageContext,t=(e==="metadata"||e==="content")&&this.server?.pageContextVersions?.includes(1)===!0;this.els.pageWrap.hidden=!t,t||(this.pageGeneration++,this.pagePending=!1,this.els.pageChoice.checked=!1,this.sharedPage=void 0,this.renderPagePreview())}async captureCurrentPage(){let e=this.config;if(e.pageContext!=="metadata"&&e.pageContext!=="content"||!this.server?.pageContextVersions?.includes(1))return;let t=document.location.href,{capturePageContext:i,providedPageContext:r}=await Promise.resolve().then(()=>(Qe(),We)),o=this.hooks.contextProvider,c=JSON.stringify([e.pageContext,e.contextSelector,e.excludeSelectors]),a=i(document,{mode:o?"metadata":e.pageContext,selector:e.contextSelector,excludeSelectors:e.excludeSelectors}),d=a;if(a&&o){let u;try{let g=await Promise.race([o(),new Promise((p,h)=>{u=setTimeout(()=>h(new Error("Page context timed out")),5e3)})]);d=r(g,a,e.pageContext)}finally{u&&clearTimeout(u)}}let l=this.config;if(!(!this.isConnected||t!==document.location.href||o!==this.hooks.contextProvider||c!==JSON.stringify([l.pageContext,l.contextSelector,l.excludeSelectors])))return d}pageFingerprint(e){let{captured_at:t,...i}=e;return JSON.stringify(i)}renderPagePreview(){let e=this.els.pagePreview;e.hidden=!this.sharedPage,e.textContent=this.sharedPage?[this.sharedPage.title,this.sharedPage.url,this.sharedPage.description,this.sharedPage.selection,this.sharedPage.content?.text].filter(Boolean).join(`
|
|
344
344
|
|
|
345
|
-
`):""}async choosePage(){let e=++this.pageGeneration;if(!this.els.pageChoice.checked){this.pagePending=!1,this.sharedPage=void 0,this.renderPagePreview();return}this.sharedPage=void 0,this.renderPagePreview(),this.pagePending=!0;try{let t=await this.captureCurrentPage();if(e!==this.pageGeneration||!this.els.pageChoice.checked)return;if(!t)throw new Error("Page unavailable");this.sharedPage=t,this.renderPagePreview(),this.emit("dewee:context-ready",{available:!0})}catch{if(e!==this.pageGeneration)return;this.sharedPage=void 0,this.els.pageChoice.checked=!1,this.renderPagePreview(),this.showNotice(this.strings.pageUnavailable),this.emit("dewee:context-error",{code:"page_unavailable"})}finally{e===this.pageGeneration&&(this.pagePending=!1)}}updateSendButton(){let e=this.els.send;e.innerHTML=this.running?A.stop:A.send,e.setAttribute("aria-label",this.running?this.strings.stop:this.strings.send),e.title=e.getAttribute("aria-label"),e.disabled=!this.running&&(this.cooldown.active||!this.els.input.value.trim())}startCooldown(e){this.cooldown.start(typeof e=="number"&&Number.isFinite(e)&&e>0?e:Bt),this.cooldown.active&&(this.renderCooldown(this.cooldown.secondsLeft),this.els.announcer.textContent=this.strings.cooldownStart(this.cooldown.secondsLeft),this.updateSendButton())}renderCooldown(e){this.els.cooldown.hidden=!1,this.els.cooldown.textContent=this.strings.cooldown(e)}endCooldown(){this.els.cooldown.hidden=!0,this.els.cooldown.textContent="",this.els.announcer.textContent=this.strings.cooldownOver,this.updateSendButton()}setRunning(e){this.running=e,this.updateSendButton()}renderSubtitle(){this.els.jobAccess.hidden=this.server?.identity!==!0;let e=this.els,t=this.config,i=this.server?.agents??[],r=t.allowAgentSwitch!==!1&&!!this.server?.allowAgentSwitch&&i.length>1;e.select.hidden=!r,e.status.hidden=r,r&&(e.select.replaceChildren(...i.map(c=>new Option(c.name||c.id,c.id,!1,c.id===this.agentId))),e.select.setAttribute("aria-label","Agent"));let o=i.find(c=>c.id===this.agentId);e.status.textContent=t.subtitle||(o?.name&&o.name!==t.title?`${this.strings.online} \xB7 ${o.name}`:this.strings.online)}async activate(){if(!this.activated){this.activated=!0,this.renderLog();try{this.probedPage=this.config.pageContext==="metadata"||this.config.pageContext==="content",this.server=await this.client().getConfig(!0,this.probedPage),this.els.input.maxLength=this.server.maxMessageLength;let e=this.config.agent,t=i=>!!i&&(this.server.agents.length===0||this.server.agents.some(r=>r.id===i));this.agentId=t(e)?e:this.server.defaultAgent||this.server.agents[0]?.id||e||"",await this.loadConversation()}catch(e){this.activated=!1,this.renderLog(),this.showError(null,e)}this.renderSubtitle(),this.updatePageChoice(),this.emit("dewee:ready",{agent:this.agentId,config:this.server})}}async switchAgent(e){e!==this.agentId&&(this.stop(),this.agentId=e,this.renderSubtitle(),await this.loadConversation())}async loadConversation(){this.clearCredentials();let e=this.credentialGeneration,t=this.config.persist!==!1,i=t?N.get(this.convKey()):null;if(this.conversationId=i||tt(),t&&!i&&N.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),!!i)try{let r=await this.client().historyDetails(this.agentId,this.conversationId);if(e!==this.credentialGeneration)return;this.messages=Kt(r.messages.map(o=>({...o,status:"done"})));for(let o of r.credentialRequests)this.credentialCards.set(o.id,o);this.renderLog()}catch{}}renderLog(){let e=this.els.log;K(e),e.replaceChildren(),this.messages.length||e.appendChild(this.renderWelcome());for(let t of this.messages)e.appendChild(this.renderMessage(t));for(let t of this.credentialCards.values())e.appendChild(this.renderCredential(t));e.scrollTop=e.scrollHeight}clearCredentials(){this.credentialGeneration++;for(let e of this.credentialPopups.keys())this.closeCredentialPopup(e);for(let e of this.credentialRequestsInFlight)e.abort();this.credentialRequestsInFlight.clear(),this.credentialCards.clear(),this.credentialBusy.clear()}closeCredentialPopup(e){let t=this.credentialPopups.get(e);if(t){this.credentialPopups.delete(e),t.timer&&clearTimeout(t.timer),t.controller.abort();try{t.popup.close()}catch{}}}discardCredential(e){this.closeCredentialPopup(e),this.credentialCards.delete(e)}credentialButton(e,t,i,r=!1){let o=document.createElement("button");return o.type="button",o.className="btn",o.textContent=t,o.disabled=r||this.credentialBusy.has(e.id),o.addEventListener("click",i),o}renderCredential(e){let t=document.createElement("section");t.className="msg assistant credential-request";let i=document.createElement("div");i.className="bubble";let r=document.createElement("strong");r.textContent=e.title;let o=document.createElement("p");o.textContent=e.purpose;let c=document.createElement("p");c.textContent=this.strings.credential.intro;let a=document.createElement("p");a.textContent=[e.target.type,e.target.mode].filter(Boolean).join(" \xB7 ");let d=document.createElement("p");d.setAttribute("role","status");let l=e.status==="pending"&&Date.parse(e.expires_at)<=Date.now()?"expired":e.status;if(this.credentialBusy.has(e.id)){t.setAttribute("aria-busy","true");let h=document.createElement("span");h.className="spin",h.setAttribute("aria-hidden","true"),d.append(h,` ${this.strings.credential.status.applying}`)}else d.textContent=this.strings.credential.status[l];let u=document.createElement("ul");for(let h of e.fields){let m=document.createElement("li");m.textContent=`${h.label} (${h.required?this.strings.credential.required:this.strings.credential.optional})`,u.appendChild(m)}let g=document.createElement("p");g.textContent=`${this.strings.credential.expires}: ${new Date(e.expires_at).toLocaleString()}`;let p=document.createElement("div");return p.className="credential-actions",l==="pending"&&p.append(this.credentialButton(e,this.strings.credential.open,()=>{this.openCredential(e)},this.credentialPopups.has(e.id)),this.credentialButton(e,this.strings.credential.cancel,()=>{this.updateCredential(e,"credential-request-cancel")})),e.resumable&&p.append(this.credentialButton(e,this.strings.credential.continue,()=>{this.resumeCredential(e)})),i.append(r,o,a,u,c,g,d,p),t.appendChild(i),t}async updateCredential(e,t){if(!this.credentialCards.has(e.id)||this.credentialBusy.has(e.id))return;let i=this.credentialGeneration,r=this.agentId,o=this.conversationId,c=new AbortController;this.credentialRequestsInFlight.add(c),this.credentialBusy.add(e.id),this.renderLog();try{let a=await this.client().credentialRequest(t,r,o,e.id,c.signal);if(i!==this.credentialGeneration||!("id"in a))return;this.credentialCards.set(a.id,a),t==="credential-request-cancel"&&this.closeCredentialPopup(e.id)}catch(a){i===this.credentialGeneration&&a instanceof v&&Xe.has(a.code??"")?this.discardCredential(e.id):i===this.credentialGeneration&&t!=="credential-request"&&this.showNotice(this.strings.credential.unavailable)}finally{this.credentialRequestsInFlight.delete(c),i===this.credentialGeneration&&(this.credentialBusy.delete(e.id),this.renderLog())}}async openCredential(e){if(this.credentialCards.get(e.id)!==e||this.credentialBusy.has(e.id)||this.credentialPopups.has(e.id))return;let t;try{t=window.open("about:blank",`dewee-credential-${crypto.randomUUID()}`,"popup,width=720,height=800")}catch{t=null}if(!t){this.showNotice(this.strings.credential.unavailable);return}let i=this.credentialGeneration,r=this.agentId,o=this.conversationId,c={popup:t,controller:new AbortController,timer:void 0};this.credentialPopups.set(e.id,c),this.credentialBusy.add(e.id),this.renderLog();try{t.opener=null;let a=await this.client().credentialRequest("credential-request-handoff",r,o,e.id,c.controller.signal);if(i!==this.credentialGeneration||t.closed||!("url"in a))throw new Error("cancelled");let d=t.document.createElement("meta");d.name="referrer",d.content="no-referrer",t.document.head.append(d),t.location.replace(a.url);let l=async()=>{if(i!==this.credentialGeneration)return;let u=this.credentialCards.get(e.id);u&&(u.status==="pending"||u.status==="applying")&&await this.updateCredential(u,"credential-request");let g=this.credentialCards.get(e.id);i===this.credentialGeneration&&!t.closed&&g&&Date.parse(g.expires_at)>Date.now()&&this.credentialPopups.get(e.id)===c?g.status==="pending"||g.status==="applying"?c.timer=setTimeout(l,4e3):(this.closeCredentialPopup(e.id),this.renderLog()):(this.credentialPopups.get(e.id)===c&&this.closeCredentialPopup(e.id),i===this.credentialGeneration&&this.renderLog())};c.timer=setTimeout(l,4e3)}catch{this.credentialPopups.get(e.id)===c&&this.closeCredentialPopup(e.id),i===this.credentialGeneration&&this.showNotice(this.strings.credential.unavailable)}finally{i===this.credentialGeneration&&(this.credentialBusy.delete(e.id),this.renderLog())}}async resumeCredential(e){if(this.credentialCards.get(e.id)!==e||this.running||this.credentialBusy.has(e.id))return;let t=this.credentialGeneration,i=this.agentId,r=this.conversationId,o={role:"assistant",content:"",status:"streaming",startedAt:Date.now()},c=new AbortController;this.credentialBusy.add(e.id),this.pushMessage(o),this.setRunning(c);try{await this.client().chat({message:"",agent:i,conversationId:r,credentialRequestId:e.id},a=>{t===this.credentialGeneration&&this.onStreamEvent(o,a)},this.running.signal),o.status==="streaming"&&(o.status="done"),t===this.credentialGeneration&&(this.credentialBusy.delete(e.id),await this.updateCredential(e,"credential-request"))}catch(a){t===this.credentialGeneration&&(this.messages=this.messages.filter(d=>d!==o),a instanceof v&&Xe.has(a.code??"")?this.discardCredential(e.id):a instanceof v&&a.code==="credential_request_resumed"?(this.credentialBusy.delete(e.id),await this.updateCredential(e,"credential-request")):this.showNotice(this.strings.credential.unavailable))}finally{this.running===c&&this.setRunning(null),t===this.credentialGeneration&&(this.credentialBusy.delete(e.id),this.updateMessage(o),this.renderLog())}}renderWelcome(){let e=this.config,t=this.strings,i=document.createElement("div");i.className="welcome";let r=document.createElement("h2");r.textContent=e.greeting||t.greeting;let o=document.createElement("p");if(o.textContent=e.intro||t.greetingBody,i.append(r,o),e.suggestions?.length){let c=document.createElement("div");c.className="suggestions";for(let a of e.suggestions){let d=document.createElement("button");d.className="chip",d.type="button",d.textContent=a,d.addEventListener("click",()=>this.send(a)),c.appendChild(d)}i.appendChild(c)}return i}pushMessage(e){this.messages.length||this.els.log.replaceChildren(),this.messages.push(e),this.els.log.appendChild(this.renderMessage(e)),this.scrollToBottom(!0)}renderMessage(e){let t=document.createElement("div");t.className=`msg ${e.role}${e.status==="error"?" error":""}`,t.hidden=!!e.hidden;let i=document.createElement("div");i.className="bubble",t.appendChild(i);let r=document.createElement("div");r.className="trace";let o=document.createElement("div");o.className=e.role==="assistant"?"md":"";let c=document.createElement("div");return c.className="actions",c.hidden=!0,e.role==="assistant"&&i.appendChild(r),i.appendChild(o),e.role==="assistant"&&i.appendChild(c),e.view={root:t,trace:r,md:o,tools:new Map,actions:c},this.updateMessage(e),t}updateMessage(e){let t=e.view;if(!t)return;let i=this.config;if(e.role==="user"){t.md.textContent=e.content,t.root.classList.toggle("refused",!!e.refusal);let a=t.root.querySelector(".refusal");e.refusal&&!a&&(a=document.createElement("div"),a.className="refusal",t.root.appendChild(a)),a&&e.refusal?a.textContent=`\u26A0\uFE0F ${e.refusal}`:a?.remove();return}if(t.root.classList.toggle("error",e.status==="error"),i.showReasoning!==!1&&e.thinking){t.thinking||(t.thinking=document.createElement("details"),t.thinking.className="thinking",t.thinking.innerHTML='<summary></summary><div class="thinking-body"></div>',t.trace.prepend(t.thinking));let a=e.status==="streaming"&&!e.thinkingMs,d=a?this.strings.thinking:e.thinkingMs?this.strings.thoughtFor(Math.max(1,Math.round(e.thinkingMs/1e3))):this.strings.reasoning,l=t.thinking.querySelector("summary");l.innerHTML=`${a?'<span class="spin"></span>':A.brain}<span></span>${A.down.replace("<svg",'<svg class="chev"')}`,l.querySelector("span:not(.spin)").textContent=d,t.thinking.querySelector(".thinking-body").textContent=e.thinking}else t.thinking&&(t.thinking.remove(),t.thinking=void 0);if(i.showToolCalls!==!1)for(let a of e.toolCalls??[])this.renderTool(t,a);else t.tools.forEach(a=>a.remove()),t.tools.clear();t.trace.hidden=!t.trace.childElementCount;let r=this.actionRegistry().size||e.actions?Pe(e.content):e.content,o=e.status==="streaming"&&!r;t.md.hidden=!o&&!r&&!!e.actions?.length;let c=JSON.stringify([r,o,e.status==="streaming",i.endpoint,i.imageOrigins,this.strings.thinking]);t.mdKey!==c&&(t.mdKey=c,K(t.md),o?t.md.innerHTML=`<span class="typing" aria-label="${this.strings.thinking}"><i></i><i></i><i></i></span>`:t.md.replaceChildren(M(document,r,{imageOrigins:Array.isArray(i.imageOrigins)?i.imageOrigins:[],imageProxy:(a,d)=>this.client().imageUrl(a,d),deferImages:e.status==="streaming"})),t.md.classList.toggle("cursor",e.status==="streaming")),this.renderActions(t,e.actions??[])}renderActions(e,t){e.actions.hidden=!t.length,e.actions.replaceChildren(...t.map(i=>{let r=document.createElement("div");r.className="action",r.dataset.status=i.status;let o=i.status==="running"||i.status==="awaiting-confirmation";r.innerHTML=`${o?'<span class="spin"></span>':A.action}<span class="action-name"></span><span class="action-state"></span>`,r.querySelector(".action-name").textContent=i.name||"?",r.querySelector(".action-state").textContent=this.strings.actionStatus[i.status];let c=[i.args&&Object.keys(i.args).length?JSON.stringify(i.args):"",i.error??""].filter(Boolean).join(`
|
|
345
|
+
`):""}async choosePage(){let e=++this.pageGeneration;if(!this.els.pageChoice.checked){this.pagePending=!1,this.sharedPage=void 0,this.renderPagePreview();return}this.sharedPage=void 0,this.renderPagePreview(),this.pagePending=!0;try{let t=await this.captureCurrentPage();if(e!==this.pageGeneration||!this.els.pageChoice.checked)return;if(!t)throw new Error("Page unavailable");this.sharedPage=t,this.renderPagePreview(),this.emit("dewee:context-ready",{available:!0})}catch{if(e!==this.pageGeneration)return;this.sharedPage=void 0,this.els.pageChoice.checked=!1,this.renderPagePreview(),this.showNotice(this.strings.pageUnavailable),this.emit("dewee:context-error",{code:"page_unavailable"})}finally{e===this.pageGeneration&&(this.pagePending=!1)}}updateSendButton(){let e=this.els.send;e.innerHTML=this.running?A.stop:A.send,e.setAttribute("aria-label",this.running?this.strings.stop:this.strings.send),e.title=e.getAttribute("aria-label"),e.disabled=!this.running&&(this.cooldown.active||!this.els.input.value.trim())}startCooldown(e){this.cooldown.start(typeof e=="number"&&Number.isFinite(e)&&e>0?e:Bt),this.cooldown.active&&(this.renderCooldown(this.cooldown.secondsLeft),this.els.announcer.textContent=this.strings.cooldownStart(this.cooldown.secondsLeft),this.updateSendButton())}renderCooldown(e){this.els.cooldown.hidden=!1,this.els.cooldown.textContent=this.strings.cooldown(e)}endCooldown(){this.els.cooldown.hidden=!0,this.els.cooldown.textContent="",this.els.announcer.textContent=this.strings.cooldownOver,this.updateSendButton()}setRunning(e){this.running=e,this.updateSendButton()}renderSubtitle(){this.els.jobAccess.hidden=this.server?.identity!==!0;let e=this.els,t=this.config,i=this.server?.agents??[],r=t.allowAgentSwitch!==!1&&!!this.server?.allowAgentSwitch&&i.length>1;e.select.hidden=!r,e.status.hidden=r,r&&(e.select.replaceChildren(...i.map(c=>new Option(c.name||c.id,c.id,!1,c.id===this.agentId))),e.select.setAttribute("aria-label","Agent"));let o=i.find(c=>c.id===this.agentId);e.status.textContent=t.subtitle||(o?.name&&o.name!==t.title?`${this.strings.online} \xB7 ${o.name}`:this.strings.online)}async activate(){if(!this.activated){this.activated=!0,this.renderLog();try{this.probedPage=this.config.pageContext==="metadata"||this.config.pageContext==="content",this.server=await this.client().getConfig(!0,this.probedPage),this.els.input.maxLength=this.server.maxMessageLength;let e=this.config.agent,t=i=>!!i&&(this.server.agents.length===0||this.server.agents.some(r=>r.id===i));this.agentId=t(e)?e:this.server.defaultAgent||this.server.agents[0]?.id||e||"",await this.loadConversation()}catch(e){this.activated=!1,this.renderLog(),this.showError(null,e)}this.renderSubtitle(),this.updatePageChoice(),this.emit("dewee:ready",{agent:this.agentId,config:this.server})}}async switchAgent(e){e!==this.agentId&&(this.stop(),this.agentId=e,this.renderSubtitle(),await this.loadConversation())}async loadConversation(){this.clearCredentials();let e=this.credentialGeneration,t=this.config.persist!==!1,i=t?N.get(this.convKey()):null;if(this.conversationId=i||tt(),t&&!i&&N.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),!!i)try{let r=await this.client().historyDetails(this.agentId,this.conversationId);if(e!==this.credentialGeneration)return;this.messages=Kt(r.messages.map(o=>({...o,status:"done"})));for(let o of r.credentialRequests)this.credentialCards.set(o.id,o);this.renderLog()}catch{}}renderLog(){let e=this.els.log;K(e),e.replaceChildren(),this.messages.length||e.appendChild(this.renderWelcome());for(let t of this.messages)e.appendChild(this.renderMessage(t));for(let t of this.credentialCards.values())e.appendChild(this.renderCredential(t));e.scrollTop=e.scrollHeight}clearCredentials(){this.credentialGeneration++;for(let e of this.credentialPopups.keys())this.closeCredentialPopup(e);for(let e of this.credentialRequestsInFlight)e.abort();this.credentialRequestsInFlight.clear(),this.credentialCards.clear(),this.credentialBusy.clear()}closeCredentialPopup(e){let t=this.credentialPopups.get(e);if(t){this.credentialPopups.delete(e),t.timer&&clearTimeout(t.timer),t.controller.abort();try{t.popup.close()}catch{}}}discardCredential(e){this.closeCredentialPopup(e),this.credentialCards.delete(e)}credentialButton(e,t,i,r=!1){let o=document.createElement("button");return o.type="button",o.className="btn",o.textContent=t,o.disabled=r||this.credentialBusy.has(e.id),o.addEventListener("click",i),o}renderCredential(e){let t=document.createElement("section");t.className="msg assistant credential-request";let i=document.createElement("div");i.className="bubble";let r=document.createElement("strong");r.textContent=e.title;let o=document.createElement("p");o.textContent=e.purpose;let c=document.createElement("p");c.textContent=this.strings.credential.intro;let a=document.createElement("p");a.textContent=[e.target.type,e.target.mode].filter(Boolean).join(" \xB7 ");let d=document.createElement("p");d.setAttribute("role","status");let l=e.status==="pending"&&Date.parse(e.expires_at)<=Date.now()?"expired":e.status;if(this.credentialBusy.has(e.id)){t.setAttribute("aria-busy","true");let h=document.createElement("span");h.className="spin",h.setAttribute("aria-hidden","true"),d.append(h,` ${this.strings.credential.status.applying}`)}else d.textContent=this.strings.credential.status[l];let u=document.createElement("ul");for(let h of e.fields){let m=document.createElement("li");m.textContent=`${h.label} (${h.required?this.strings.credential.required:this.strings.credential.optional})`,u.appendChild(m)}let g=document.createElement("p");g.textContent=`${this.strings.credential.expires}: ${new Date(e.expires_at).toLocaleString()}`;let p=document.createElement("div");return p.className="credential-actions",l==="pending"&&p.append(this.credentialButton(e,this.strings.credential.open,()=>{this.openCredential(e)},this.credentialPopups.has(e.id)),this.credentialButton(e,this.strings.credential.cancel,()=>{this.updateCredential(e,"credential-request-cancel")})),e.resumable&&p.append(this.credentialButton(e,this.strings.credential.continue,()=>{this.resumeCredential(e)})),i.append(r,o,a,u,c,g,d,p),t.appendChild(i),t}async updateCredential(e,t){if(!this.credentialCards.has(e.id)||this.credentialBusy.has(e.id))return;let i=this.credentialGeneration,r=this.agentId,o=this.conversationId,c=new AbortController;this.credentialRequestsInFlight.add(c),this.credentialBusy.add(e.id),this.renderLog();try{let a=await this.client().credentialRequest(t,r,o,e.id,c.signal);if(i!==this.credentialGeneration||!("id"in a))return;this.credentialCards.set(a.id,a),t==="credential-request-cancel"&&this.closeCredentialPopup(e.id)}catch(a){i===this.credentialGeneration&&a instanceof v&&Ze.has(a.code??"")?this.discardCredential(e.id):i===this.credentialGeneration&&t!=="credential-request"&&this.showNotice(this.strings.credential.unavailable)}finally{this.credentialRequestsInFlight.delete(c),i===this.credentialGeneration&&(this.credentialBusy.delete(e.id),this.renderLog())}}async openCredential(e){if(this.credentialCards.get(e.id)!==e||this.credentialBusy.has(e.id)||this.credentialPopups.has(e.id))return;let t;try{t=window.open("about:blank",`dewee-credential-${crypto.randomUUID()}`,"popup,width=720,height=800")}catch{t=null}if(!t){this.showNotice(this.strings.credential.unavailable);return}let i=this.credentialGeneration,r=this.agentId,o=this.conversationId,c={popup:t,controller:new AbortController,timer:void 0};this.credentialPopups.set(e.id,c),this.credentialBusy.add(e.id),this.renderLog();try{t.opener=null;let a=await this.client().credentialRequest("credential-request-handoff",r,o,e.id,c.controller.signal);if(i!==this.credentialGeneration||t.closed||!("url"in a))throw new Error("cancelled");let d=t.document.createElement("meta");d.name="referrer",d.content="no-referrer",t.document.head.append(d),t.location.replace(a.url);let l=async()=>{if(i!==this.credentialGeneration)return;let u=this.credentialCards.get(e.id);u&&(u.status==="pending"||u.status==="applying")&&await this.updateCredential(u,"credential-request");let g=this.credentialCards.get(e.id);i===this.credentialGeneration&&!t.closed&&g&&Date.parse(g.expires_at)>Date.now()&&this.credentialPopups.get(e.id)===c?g.status==="pending"||g.status==="applying"?c.timer=setTimeout(l,4e3):(this.closeCredentialPopup(e.id),this.renderLog()):(this.credentialPopups.get(e.id)===c&&this.closeCredentialPopup(e.id),i===this.credentialGeneration&&this.renderLog())};c.timer=setTimeout(l,4e3)}catch{this.credentialPopups.get(e.id)===c&&this.closeCredentialPopup(e.id),i===this.credentialGeneration&&this.showNotice(this.strings.credential.unavailable)}finally{i===this.credentialGeneration&&(this.credentialBusy.delete(e.id),this.renderLog())}}async resumeCredential(e){if(this.credentialCards.get(e.id)!==e||this.running||this.credentialBusy.has(e.id))return;let t=this.credentialGeneration,i=this.agentId,r=this.conversationId,o={role:"assistant",content:"",status:"streaming",startedAt:Date.now()},c=new AbortController;this.credentialBusy.add(e.id),this.pushMessage(o),this.setRunning(c);try{await this.client().chat({message:"",agent:i,conversationId:r,credentialRequestId:e.id},a=>{t===this.credentialGeneration&&this.onStreamEvent(o,a)},this.running.signal),o.status==="streaming"&&(o.status="done"),t===this.credentialGeneration&&(this.credentialBusy.delete(e.id),await this.updateCredential(e,"credential-request"))}catch(a){t===this.credentialGeneration&&(this.messages=this.messages.filter(d=>d!==o),a instanceof v&&Ze.has(a.code??"")?this.discardCredential(e.id):a instanceof v&&a.code==="credential_request_resumed"?(this.credentialBusy.delete(e.id),await this.updateCredential(e,"credential-request")):this.showNotice(this.strings.credential.unavailable))}finally{this.running===c&&this.setRunning(null),t===this.credentialGeneration&&(this.credentialBusy.delete(e.id),this.updateMessage(o),this.renderLog())}}renderWelcome(){let e=this.config,t=this.strings,i=document.createElement("div");i.className="welcome";let r=document.createElement("h2");r.textContent=e.greeting||t.greeting;let o=document.createElement("p");if(o.textContent=e.intro||t.greetingBody,i.append(r,o),e.suggestions?.length){let c=document.createElement("div");c.className="suggestions";for(let a of e.suggestions){let d=document.createElement("button");d.className="chip",d.type="button",d.textContent=a,d.addEventListener("click",()=>this.send(a)),c.appendChild(d)}i.appendChild(c)}return i}pushMessage(e){this.messages.length||this.els.log.replaceChildren(),this.messages.push(e),this.els.log.appendChild(this.renderMessage(e)),this.scrollToBottom(!0)}renderMessage(e){let t=document.createElement("div");t.className=`msg ${e.role}${e.status==="error"?" error":""}`,t.hidden=!!e.hidden;let i=document.createElement("div");i.className="bubble",t.appendChild(i);let r=document.createElement("div");r.className="trace";let o=document.createElement("div");o.className=e.role==="assistant"?"md":"";let c=document.createElement("div");return c.className="actions",c.hidden=!0,e.role==="assistant"&&i.appendChild(r),i.appendChild(o),e.role==="assistant"&&i.appendChild(c),e.view={root:t,trace:r,md:o,tools:new Map,actions:c},this.updateMessage(e),t}updateMessage(e){let t=e.view;if(!t)return;let i=this.config;if(e.role==="user"){t.md.textContent=e.content,t.root.classList.toggle("refused",!!e.refusal);let a=t.root.querySelector(".refusal");e.refusal&&!a&&(a=document.createElement("div"),a.className="refusal",t.root.appendChild(a)),a&&e.refusal?a.textContent=`\u26A0\uFE0F ${e.refusal}`:a?.remove();return}if(t.root.classList.toggle("error",e.status==="error"),i.showReasoning!==!1&&e.thinking){t.thinking||(t.thinking=document.createElement("details"),t.thinking.className="thinking",t.thinking.innerHTML='<summary></summary><div class="thinking-body"></div>',t.trace.prepend(t.thinking));let a=e.status==="streaming"&&!e.thinkingMs,d=a?this.strings.thinking:e.thinkingMs?this.strings.thoughtFor(Math.max(1,Math.round(e.thinkingMs/1e3))):this.strings.reasoning,l=t.thinking.querySelector("summary");l.innerHTML=`${a?'<span class="spin"></span>':A.brain}<span></span>${A.down.replace("<svg",'<svg class="chev"')}`,l.querySelector("span:not(.spin)").textContent=d,t.thinking.querySelector(".thinking-body").textContent=e.thinking}else t.thinking&&(t.thinking.remove(),t.thinking=void 0);if(i.showToolCalls!==!1)for(let a of e.toolCalls??[])this.renderTool(t,a);else t.tools.forEach(a=>a.remove()),t.tools.clear();t.trace.hidden=!t.trace.childElementCount;let r=this.actionRegistry().size||e.actions?Pe(e.content):e.content,o=e.status==="streaming"&&!r;t.md.hidden=!o&&!r&&!!e.actions?.length;let c=JSON.stringify([r,o,e.status==="streaming",i.endpoint,i.imageOrigins,this.strings.thinking]);t.mdKey!==c&&(t.mdKey=c,K(t.md),o?t.md.innerHTML=`<span class="typing" aria-label="${this.strings.thinking}"><i></i><i></i><i></i></span>`:t.md.replaceChildren(M(document,r,{imageOrigins:Array.isArray(i.imageOrigins)?i.imageOrigins:[],imageProxy:(a,d)=>this.client().imageUrl(a,d),deferImages:e.status==="streaming"})),t.md.classList.toggle("cursor",e.status==="streaming")),this.renderActions(t,e.actions??[])}renderActions(e,t){e.actions.hidden=!t.length,e.actions.replaceChildren(...t.map(i=>{let r=document.createElement("div");r.className="action",r.dataset.status=i.status;let o=i.status==="running"||i.status==="awaiting-confirmation";r.innerHTML=`${o?'<span class="spin"></span>':A.action}<span class="action-name"></span><span class="action-state"></span>`,r.querySelector(".action-name").textContent=i.name||"?",r.querySelector(".action-state").textContent=this.strings.actionStatus[i.status];let c=[i.args&&Object.keys(i.args).length?JSON.stringify(i.args):"",i.error??""].filter(Boolean).join(`
|
|
346
346
|
`);return c&&(r.title=c),r}))}renderTool(e,t){let i=e.tools.get(t.id);i||(i=document.createElement("details"),i.className="tool",i.innerHTML=`<summary><span class="lead"></span><span class="tool-name"></span><span class="tool-state"></span>${A.down.replace("<svg",'<svg class="chev"')}</summary><div class="tool-body"></div>`,e.tools.set(t.id,i),e.trace.appendChild(i)),i.dataset.status=t.status,i.querySelector(".lead").innerHTML=t.status==="running"?'<span class="spin"></span>':A.tool;let r=i.querySelector(".tool-name");r.textContent=Vt(t.name),r.setAttribute("title",t.name),i.querySelector(".tool-state").textContent=t.status==="running"?this.strings.toolRunning:t.status==="error"?this.strings.toolError:this.strings.toolDone;let o=i.querySelector(".tool-body");o.replaceChildren();let c=(a,d)=>{if(d==null||d==="")return;let l=document.createElement("div");l.className="label",l.textContent=a;let u=document.createElement("pre");u.textContent=typeof d=="string"?Yt(d):JSON.stringify(d,null,2),o.append(l,u)};c(this.strings.args,t.arguments),c(this.strings.result,t.result)}onStreamEvent(e,t){switch(t.type){case"start":t.actionsHash&&(e.actionsHash=t.actionsHash,e.ackVisitor=this.client().visitorId()??void 0),t.conversationId&&t.conversationId!==this.conversationId&&(this.conversationId=t.conversationId,this.config.persist!==!1&&N.set(this.convKey(),t.conversationId));break;case"thinking":e.thinking=(e.thinking||"")+t.text;break;case"delta":this.finishThinking(e),e.content+=t.text;break;case"tool_call":this.finishThinking(e),(e.toolCalls??(e.toolCalls=[])).push({id:t.id,name:t.name,arguments:t.arguments,status:"running"});break;case"tool_result":{let i=e.toolCalls?.find(r=>r.id===t.id)??(e.toolCalls??(e.toolCalls=[]))[e.toolCalls.push({id:t.id,name:t.name,status:"running"})-1];i.status=t.isError?"error":"done",i.result=t.result,i.isError=t.isError;break}case"credential_request":this.credentialCards.set(t.credentialRequest.id,t.credentialRequest),this.renderLog();break;case"done":this.finishThinking(e),t.content&&t.content.length>=e.content.length&&(e.content=t.content),e.status="done";break;case"error":e.status="error",this.showError(e,new v(t.message,500,t.code,t.remediation,t.retryAfterMs));break}this.scheduleUpdate(e)}scheduleUpdate(e){this.pendingFrame||(this.pendingFrame=requestAnimationFrame(()=>{this.pendingFrame=0,this.updateMessage(e),this.following&&this.scrollToBottom(!0)}))}finishThinking(e){e.thinking&&!e.thinkingMs&&e.startedAt&&(e.thinkingMs=Date.now()-e.startedAt)}nearBottom(){let e=this.els.log;return e.scrollHeight-e.scrollTop-e.clientHeight<96}scrollToBottom(e=!1){let t=this.els.log;(e||this.nearBottom())&&(t.scrollTop=t.scrollHeight,this.following=!0)}showError(e,t){let{code:i,remediation:r,retryAfterMs:o}=t,a=(W(i)?this.strings.identityErrors[i]:Q(i)?this.strings.admissionErrors[i]:Ce(i)?this.strings.linkErrors[i]:"")||t.message||this.strings.errorGeneric;if(this.emit("dewee:error",{message:a,code:i,...r&&{remediation:r},...o&&{retryAfterMs:o}}),i==="RATE_LIMITED"&&this.startCooldown(o),e&&Q(i)&&this.refuseInline(e,i,a))return;if(e){e.content=e.content?`${e.content}
|
|
347
347
|
|
|
348
348
|
> \u26A0\uFE0F ${a}`:`\u26A0\uFE0F ${a}`,e.status="error",this.updateMessage(e);return}let d=document.createElement("div");d.className="msg error",d.innerHTML='<div class="bubble"></div>',d.firstElementChild.textContent=`\u26A0\uFE0F ${a}`,this.els.log.appendChild(d)}refuseInline(e,t,i){let r=this.messages.indexOf(e),o=r>0?this.messages[r-1]:void 0;return o?.role!=="user"||o.hidden||(this.els.input.value.trim()||(this.els.input.value=o.content,this.autosize()),t!=="POLICY_DENIED"||e.content)?!1:(e.status="error",e.hidden=!0,e.view&&(e.view.root.hidden=!0),o.refusal=i,this.updateMessage(o),!0)}async challenge(e){await Ut();let t=document.createElement("div");t.slot="challenge",this.appendChild(t);try{return await new Promise((i,r)=>{window.turnstile.render(t,{sitekey:e,appearance:"interaction-only",theme:this.getAttribute("data-theme")==="dark"?"dark":"light",callback:i,"error-callback":()=>r(new v("Verification failed",403,"turnstile_failed"))})})}finally{t.remove()}}};function Kt(n){return n.forEach((s,e)=>{if(s.role==="user"){s.content=oe(s.content),ne(s.content)&&(s.hidden=!0);return}if(!s.content.includes("```dewee-action"))return;let{calls:t}=te(s.content);if(!t.length)return;let i=n[e+1],r=i?.role==="user"?oe(i.content):"",o=ne(r)?Re(r):[];s.actions=t.map(c=>{let a=o.find(l=>l.id===c.id),d=a?a.status==="ok"?"done":a.status:"error";return{id:c.id,name:c.name,args:c.ok?c.args:void 0,status:d,error:a?a.error:"Not run"}})}),n}var Gt={telegram:"Telegram",discord:"Discord",zalo:"Zalo",slack:"Slack",whatsapp:"WhatsApp",messenger:"Messenger",feishu:"Feishu",line:"LINE"};function Ft(n){let s=n.toLowerCase().split(/[_-]/)[0];return Gt[s]??(n?n.charAt(0).toUpperCase()+n.slice(1):"chat")}function be(n,s){try{return JSON.parse(n)}catch{return s}}function Yt(n){let s=n.trim();if(!/^[[{]/.test(s))return n;try{return JSON.stringify(JSON.parse(s),null,2)}catch{return n}}function we(n="dewee-chat"){typeof window>"u"||!window.customElements||customElements.get(n)||customElements.define(n,class extends q{})}function Wt(n={},s){we();let e=document.createElement("dewee-chat");return e.configure(n),((typeof s=="string"?document.querySelector(s):s)??document.body).appendChild(e),e}we();var Qt=document.currentScript,Y=Qt?.dataset;if(Y&&Y.endpoint&&Y.autoMount!=="false"){let n=()=>{let s=document.createElement("dewee-chat");for(let[e,t]of Object.entries(Y))e==="autoMount"||t===void 0||s.setAttribute(e.replace(/[A-Z]/g,i=>"-"+i.toLowerCase()),t);document.body.appendChild(s)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",n):n()}window.DeweeWebchat=ye;})();
|
|
@@ -301,6 +301,14 @@ interface DeweeHandlerOptions {
|
|
|
301
301
|
};
|
|
302
302
|
prefixes: string[];
|
|
303
303
|
};
|
|
304
|
+
/** Public-only service bindings for approved HTTPS image directory prefixes. Bypasses host security rules. */
|
|
305
|
+
cloudflarePublicImageServices?: Array<{
|
|
306
|
+
id: string;
|
|
307
|
+
binding: {
|
|
308
|
+
fetch(request: Request): Promise<Response>;
|
|
309
|
+
};
|
|
310
|
+
prefixes: string[];
|
|
311
|
+
}>;
|
|
304
312
|
/**
|
|
305
313
|
* Legacy gateway transport: let visitors bring their own gateway URL + token (sent per request, never stored).
|
|
306
314
|
* Meant for playgrounds. `allowedHosts` restricts which gateways may be targeted.
|
package/dist/index.js
CHANGED
package/dist/react.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineDeweeChat
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-HBAT4XZ6.js";
|
|
4
|
+
import "./chunk-UPJVNHF6.js";
|
|
5
5
|
|
|
6
6
|
// src/react.ts
|
|
7
7
|
import { createElement, forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AuthenticatedUser, C as CcpError, a as CcpOptions, b as DeweeHandler, c as DeweeHandlerOptions, d as DeweeIdentityOptions, D as DeweeIdentitySdk, e as DeweeTransport, I as IdentityPrivateJwk, f as IdentitySessionStore, g as IdentitySigner, M as MemoryRateLimits, R as RateLimitBucket, h as RateLimiter, S as StoredIdentitySession, V as VisitorClaims, i as createDeweeHandler, m as memoryIdentitySessionStore, j as memoryRateLimiter, o as optionsFromEnv, s as signVisitorToken, v as verifyVisitorToken } from './handler-
|
|
1
|
+
export { A as AuthenticatedUser, C as CcpError, a as CcpOptions, b as DeweeHandler, c as DeweeHandlerOptions, d as DeweeIdentityOptions, D as DeweeIdentitySdk, e as DeweeTransport, I as IdentityPrivateJwk, f as IdentitySessionStore, g as IdentitySigner, M as MemoryRateLimits, R as RateLimitBucket, h as RateLimiter, S as StoredIdentitySession, V as VisitorClaims, i as createDeweeHandler, m as memoryIdentitySessionStore, j as memoryRateLimiter, o as optionsFromEnv, s as signVisitorToken, v as verifyVisitorToken } from './handler-_GEhpm1W.js';
|
|
2
2
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
3
|
import { R as Remediation, H as HistoryMessage, A as AgentInfo, C as ChatStreamEvent, a as ActionManifestEntry, b as AdmissionErrorCode } from './protocol-DjEdC6QU.js';
|
|
4
4
|
export { c as ADMISSION_ERROR_CODES, d as ActionSensitivity, e as ChatRequestBody, E as ErrorBody, I as IdentityErrorCode, L as LinkResponse, f as LinkTarget, P as PublicConfig, S as SessionResponse, i as isAdmissionErrorCode } from './protocol-DjEdC6QU.js';
|
package/dist/server.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
parseJobAccessLaunch,
|
|
16
16
|
serializePageContext,
|
|
17
17
|
stripPageBlock
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-UPJVNHF6.js";
|
|
19
19
|
|
|
20
20
|
// src/server/admission-errors.ts
|
|
21
21
|
var MESSAGES = {
|
|
@@ -1370,7 +1370,7 @@ async function verifyImageTicket(secret, namespace, ticket) {
|
|
|
1370
1370
|
return null;
|
|
1371
1371
|
}
|
|
1372
1372
|
}
|
|
1373
|
-
function allowedImageSource(raw, prefixes, endpoint, cloudflare, publicAssets = []) {
|
|
1373
|
+
function allowedImageSource(raw, prefixes, endpoint, cloudflare, publicAssets = [], publicServices = []) {
|
|
1374
1374
|
try {
|
|
1375
1375
|
if (!raw || raw.length > 4096 || /[\u0000-\u0020\u007f\\]/.test(raw)) return null;
|
|
1376
1376
|
const match = /^https:\/\/[^/?#]+([^?#]*)/i.exec(raw);
|
|
@@ -1384,7 +1384,8 @@ function allowedImageSource(raw, prefixes, endpoint, cloudflare, publicAssets =
|
|
|
1384
1384
|
const suffix = endpoint.hostname.split(".").slice(-2).join(".");
|
|
1385
1385
|
const sameZone = source.hostname === suffix || source.hostname.endsWith(`.${suffix}`);
|
|
1386
1386
|
const isPublicAsset = source.origin === endpoint.origin && publicAssets.some((prefix) => source.pathname.startsWith(prefix));
|
|
1387
|
-
|
|
1387
|
+
const isPublicService = publicServices.some((prefix) => prefix.origin === source.origin && source.pathname.startsWith(prefix.pathname));
|
|
1388
|
+
if (sameZone && !isPublicAsset && !isPublicService) return null;
|
|
1388
1389
|
}
|
|
1389
1390
|
return prefixes.some((prefix) => prefix.origin === source.origin && source.pathname.startsWith(prefix.pathname)) ? source : null;
|
|
1390
1391
|
} catch {
|
|
@@ -1739,6 +1740,35 @@ function createDeweeHandler(options) {
|
|
|
1739
1740
|
throw new Error("[dewee-webchat] Public image asset prefixes must be explicit absolute directory paths");
|
|
1740
1741
|
}
|
|
1741
1742
|
const assetPrefixes = publicAssets?.prefixes ?? [];
|
|
1743
|
+
if (o.cloudflarePublicImageServices !== void 0 && (!Array.isArray(o.cloudflarePublicImageServices) || o.cloudflarePublicImageServices.length > 16)) {
|
|
1744
|
+
throw new Error("[dewee-webchat] Public image services must be an array");
|
|
1745
|
+
}
|
|
1746
|
+
const publicServices = (o.cloudflarePublicImageServices ?? []).map((service) => {
|
|
1747
|
+
if (!service || typeof service.id !== "string" || !/^[A-Za-z0-9_-]{1,64}$/.test(service.id) || typeof service.binding?.fetch !== "function" || !Array.isArray(service.prefixes) || !service.prefixes.length) {
|
|
1748
|
+
throw new Error("[dewee-webchat] Public image services require a binding and directory prefixes");
|
|
1749
|
+
}
|
|
1750
|
+
const prefixes = service.prefixes.map((raw) => {
|
|
1751
|
+
let url;
|
|
1752
|
+
if (typeof raw !== "string" || !/^https:\/\/[^/?#]+\/(?:[A-Za-z0-9_-]+\/)+$/.test(raw)) {
|
|
1753
|
+
throw new Error("[dewee-webchat] Invalid public image service prefix");
|
|
1754
|
+
}
|
|
1755
|
+
try {
|
|
1756
|
+
url = new URL(raw);
|
|
1757
|
+
} catch {
|
|
1758
|
+
throw new Error("[dewee-webchat] Invalid public image service prefix");
|
|
1759
|
+
}
|
|
1760
|
+
if (url.protocol !== "https:" || url.username || url.password || url.port || url.search || url.hash || !/^\/(?:[A-Za-z0-9_-]+\/)+$/.test(url.pathname)) {
|
|
1761
|
+
throw new Error("[dewee-webchat] Public image service prefixes must be HTTPS origins with explicit directory paths");
|
|
1762
|
+
}
|
|
1763
|
+
return url;
|
|
1764
|
+
});
|
|
1765
|
+
return { id: service.id, binding: service.binding, prefixes };
|
|
1766
|
+
});
|
|
1767
|
+
const servicePrefixes = publicServices.flatMap((service) => service.prefixes);
|
|
1768
|
+
if (new Set(publicServices.map((service) => service.id)).size !== publicServices.length || servicePrefixes.some((prefix, index) => servicePrefixes.slice(index + 1).some((other) => prefix.origin === other.origin && (prefix.pathname.startsWith(other.pathname) || other.pathname.startsWith(prefix.pathname))))) {
|
|
1769
|
+
throw new Error("[dewee-webchat] Public image service ids and directory prefixes must not overlap");
|
|
1770
|
+
}
|
|
1771
|
+
const serviceFor = (source) => publicServices.find((service) => service.prefixes.some((prefix) => prefix.origin === source.origin && source.pathname.startsWith(prefix.pathname)));
|
|
1742
1772
|
let activeImages = 0;
|
|
1743
1773
|
const imageNamespace = `${o.ccp?.chatUrl ?? o.gatewayUrl ?? ""}
|
|
1744
1774
|
${o.ccp?.integrationId ?? o.tenantId ?? ""}`;
|
|
@@ -2661,32 +2691,43 @@ ${o.ccp?.integrationId ?? o.tenantId ?? ""}`;
|
|
|
2661
2691
|
}
|
|
2662
2692
|
const raw = parsed?.url;
|
|
2663
2693
|
const worker = globalThis.navigator?.userAgent === "Cloudflare-Workers";
|
|
2664
|
-
const source = typeof raw === "string" ? allowedImageSource(raw, imageSources, new URL(req.url), worker, worker ? assetPrefixes : []) : null;
|
|
2694
|
+
const source = typeof raw === "string" ? allowedImageSource(raw, imageSources, new URL(req.url), worker, worker ? assetPrefixes : [], worker ? servicePrefixes : []) : null;
|
|
2665
2695
|
if (!source) return fail(req, 403, "Image not allowed", "forbidden_image");
|
|
2666
2696
|
const endpoint = new URL(req.url);
|
|
2667
2697
|
const asset = publicAssets && source.origin === endpoint.origin && assetPrefixes.some((prefix) => source.pathname.startsWith(prefix));
|
|
2698
|
+
const service = asset ? void 0 : serviceFor(source);
|
|
2668
2699
|
const namespace = asset ? `${imageNamespace}
|
|
2669
|
-
assets:${endpoint.origin}` : imageNamespace
|
|
2670
|
-
|
|
2700
|
+
assets:${endpoint.origin}` : service ? `${imageNamespace}
|
|
2701
|
+
service:${endpoint.origin}:${service.id}` : imageNamespace;
|
|
2702
|
+
const ticket = await issueImageTicket(secret, namespace, claims.v, source.href);
|
|
2703
|
+
return json(req, { ticket: service ? `s.${service.id}.${ticket}` : ticket });
|
|
2671
2704
|
}
|
|
2672
2705
|
if (req.method === "GET" && action === "image" && imageSources.length) {
|
|
2673
2706
|
const ticket = new URL(req.url).searchParams.get("ticket") ?? "";
|
|
2674
2707
|
const endpoint = new URL(req.url);
|
|
2675
|
-
const
|
|
2708
|
+
const tagged = /^s\.([A-Za-z0-9_-]{1,64})\.([A-Za-z0-9_-]{1,8192})$/.exec(ticket);
|
|
2709
|
+
const serviceId = tagged?.[1];
|
|
2710
|
+
if (serviceId && !publicServices.some((service2) => service2.id === serviceId)) return fail(req, 401, "Image ticket expired", "unauthorized");
|
|
2711
|
+
const serviceClaims = tagged ? await verifyImageTicket(secret, `${imageNamespace}
|
|
2712
|
+
service:${endpoint.origin}:${serviceId}`, tagged[2]) : null;
|
|
2713
|
+
const assetClaims = tagged ? null : await verifyImageTicket(secret, `${imageNamespace}
|
|
2676
2714
|
assets:${endpoint.origin}`, ticket);
|
|
2677
|
-
const claims = assetClaims ?? await verifyImageTicket(secret, imageNamespace, ticket);
|
|
2715
|
+
const claims = tagged ? serviceClaims : assetClaims ?? await verifyImageTicket(secret, imageNamespace, ticket);
|
|
2678
2716
|
if (!claims) return fail(req, 401, "Image ticket expired", "unauthorized");
|
|
2679
2717
|
const worker = globalThis.navigator?.userAgent === "Cloudflare-Workers";
|
|
2680
|
-
const source = allowedImageSource(claims.source, imageSources, endpoint, worker, worker ? assetPrefixes : []);
|
|
2718
|
+
const source = allowedImageSource(claims.source, imageSources, endpoint, worker, worker ? assetPrefixes : [], worker ? servicePrefixes : []);
|
|
2681
2719
|
if (!source) return fail(req, 403, "Image not allowed", "forbidden_image");
|
|
2682
2720
|
const asset = publicAssets && source.origin === endpoint.origin && assetPrefixes.some((prefix) => source.pathname.startsWith(prefix));
|
|
2683
2721
|
if (Boolean(asset) !== Boolean(assetClaims)) return fail(req, 403, "Image not allowed", "forbidden_image");
|
|
2722
|
+
const service = asset ? void 0 : serviceFor(source);
|
|
2723
|
+
if (service?.id !== serviceId) return fail(req, 403, "Image not allowed", "forbidden_image");
|
|
2684
2724
|
if (limiter && !await limiter(`image:${claims.visitor}`, "preauth")) return fail(req, 429, "Too many requests, try again later", "rate_limited");
|
|
2685
2725
|
if (activeImages >= 8) return fail(req, 429, "Too many requests, try again later", "rate_limited");
|
|
2686
2726
|
activeImages++;
|
|
2687
2727
|
try {
|
|
2688
2728
|
const assetFetch = (input, init) => publicAssets.binding.fetch(new Request(new URL(new URL(String(input)).pathname, "https://assets.invalid"), init));
|
|
2689
|
-
const
|
|
2729
|
+
const serviceFetch = (input, init) => service.binding.fetch(new Request(new URL(new URL(String(input)).pathname, "https://public-images.invalid"), init));
|
|
2730
|
+
const result = await fetchRasterImage(source.href, asset ? assetFetch : service ? serviceFetch : worker ? globalThis.fetch : publicImageFetchNode, worker || service ? "manual" : "error");
|
|
2690
2731
|
return result ?? fail(req, 502, "Image unavailable", "image_unavailable");
|
|
2691
2732
|
} finally {
|
|
2692
2733
|
activeImages--;
|
package/dist/vue.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dewee-webchat",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.5",
|
|
4
4
|
"description": "Embeddable chat widget for Dewee AI agents — web component, React/Vue/Astro wrappers and a secure backend handler (Workers, Next.js, Node, Bun, Deno).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|