dewee-webchat 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -127,12 +127,35 @@ Attributes use kebab-case (`show-reasoning="false"`), `data-*` on the script tag
127
127
  | `actions`, `confirm`, `getPageContext` | | [Page actions](#agent-page-actions) (JS/props only) |
128
128
  | `maxActionRounds` | `5` | Automatic action turns per visitor message |
129
129
  | `actionTimeoutMs` | `30000` | Per-handler timeout |
130
+ | `headers` | | Extra request headers, object or `() => object \| Promise<object>` called before every request (JS/props only) |
131
+ | `fetch` | `globalThis.fetch` | Custom fetch for every widget request (JS/props only) |
130
132
 
131
133
  Methods: `open()`, `close()`, `toggle()`, `send(text)`, `stop()`, `newConversation()`, `configure(partial)`,
132
134
  `registerActions(actions)`, `unregisterAction(name)`.
133
135
  Events: `dewee:ready`, `dewee:open`, `dewee:close`, `dewee:send`, `dewee:reply`, `dewee:error`, `dewee:action`.
134
136
  CSS variables: `--dw-accent`, `--dw-font`, `--dw-radius`, `--dw-width`, `--dw-height`, `--dw-z`.
135
137
 
138
+ ## Custom headers and fetch
139
+
140
+ Behind a same-origin BFF that wants a CSRF header, cookies or tracing:
141
+
142
+ ```tsx
143
+ <DeweeChat
144
+ endpoint="/dashboard/api/v1/dewee"
145
+ headers={() => ({ "x-csrf-token": readCsrfToken() })} // evaluated before every request
146
+ fetch={(input, init) => fetch(input, { ...init, credentials: "include" })}
147
+ />
148
+ ```
149
+
150
+ Vanilla: `chat.configure({ headers, fetch })` or the `chat.headers` / `chat.fetch` properties. `headers` applies to
151
+ every request to `endpoint` (config, session, history, chat). It cannot override `content-type`, `authorization`,
152
+ `x-dewee-gateway-url` or `x-dewee-gateway-token` (case-insensitive; such keys are dropped). Header names are sent
153
+ lower-cased. For a cross-origin `endpoint`, the backend's CORS preflight must allow the extra headers; the built-in
154
+ handlers only allow their own.
155
+
156
+ Visitor sessions are reused until less than 10% of the token lifetime remains (at most 24 h, at least 30 s), so
157
+ backends with short session TTLs do not mint a new session on every request.
158
+
136
159
  ## Agent page actions
137
160
 
138
161
  Let the agent operate the page the visitor is on: navigate, filter a table, cancel a job. The page
@@ -93,7 +93,7 @@ function stripPageBlock(content) {
93
93
  // src/protocol.ts
94
94
  var HEADER_GATEWAY_URL = "x-dewee-gateway-url";
95
95
  var HEADER_GATEWAY_TOKEN = "x-dewee-gateway-token";
96
- var VERSION = "0.2.0";
96
+ var VERSION = "0.3.0";
97
97
 
98
98
  export {
99
99
  ACTION_NAME_RE,
@@ -5,7 +5,7 @@ import {
5
5
  HEADER_GATEWAY_URL,
6
6
  SENSITIVITIES,
7
7
  stripPageBlock
8
- } from "./chunk-JX6AH37X.js";
8
+ } from "./chunk-W6A4HVFB.js";
9
9
 
10
10
  // src/actions/directives.ts
11
11
  var DIRECTIVE_LANG = "dewee-action";
@@ -276,6 +276,7 @@ function raceAbort(p, signal) {
276
276
  }
277
277
 
278
278
  // src/widget/api-client.ts
279
+ var RESERVED_HEADERS = ["content-type", "authorization", HEADER_GATEWAY_URL, HEADER_GATEWAY_TOKEN];
279
280
  var ApiError = class extends Error {
280
281
  constructor(message, status, code) {
281
282
  super(message);
@@ -322,8 +323,22 @@ var ApiClient = class {
322
323
  get storageKey() {
323
324
  return `dewee-webchat:session:${this.o.endpoint}`;
324
325
  }
325
- headers(auth = false) {
326
- const h = { "content-type": "application/json" };
326
+ /** Session reuse: refresh once less than 10% of the token lifetime (at most 24 h, at least 30 s) remains. */
327
+ fresh(s) {
328
+ const lifetime = s.obtainedAt ? s.expiresAt - s.obtainedAt : Infinity;
329
+ const margin = Math.min(864e5, Math.max(3e4, lifetime * 0.1));
330
+ return s.expiresAt - Date.now() > margin;
331
+ }
332
+ request(url, init) {
333
+ return (this.o.fetch ?? globalThis.fetch)(url, init);
334
+ }
335
+ async headers(auth = false) {
336
+ const h = {};
337
+ const extra = typeof this.o.headers === "function" ? await this.o.headers() : this.o.headers;
338
+ for (const [k, v] of Object.entries(extra ?? {})) {
339
+ if (typeof v === "string" && !RESERVED_HEADERS.includes(k.toLowerCase())) h[k.toLowerCase()] = v;
340
+ }
341
+ h["content-type"] = "application/json";
327
342
  if (this.o.gatewayUrl && this.o.gatewayToken) {
328
343
  h[HEADER_GATEWAY_URL] = this.o.gatewayUrl;
329
344
  h[HEADER_GATEWAY_TOKEN] = this.o.gatewayToken;
@@ -338,18 +353,18 @@ var ApiClient = class {
338
353
  }
339
354
  async getConfig(force = false) {
340
355
  if (this.config && !force) return this.config;
341
- this.config = await this.parse(await fetch(this.url("config"), { headers: this.headers() }));
356
+ this.config = await this.parse(await this.request(this.url("config"), { headers: await this.headers() }));
342
357
  return this.config;
343
358
  }
344
359
  /** Returns a valid visitor session, creating or refreshing it when needed. */
345
360
  ensureSession() {
346
- if (this.session && this.session.expiresAt - Date.now() > 864e5) return Promise.resolve(this.session);
361
+ if (this.session && this.fresh(this.session)) return Promise.resolve(this.session);
347
362
  if (!this.session) {
348
363
  const saved = store.get(this.storageKey);
349
364
  if (saved) {
350
365
  try {
351
366
  const s = JSON.parse(saved);
352
- if (s.expiresAt - Date.now() > 864e5) return Promise.resolve(this.session = s);
367
+ if (this.fresh(s)) return Promise.resolve(this.session = s);
353
368
  this.session = s;
354
369
  } catch {
355
370
  }
@@ -366,11 +381,11 @@ var ApiClient = class {
366
381
  body.turnstileToken = await this.o.turnstile(cfg.turnstileSiteKey);
367
382
  }
368
383
  const s = await this.parse(
369
- await fetch(this.url("session"), { method: "POST", headers: this.headers(), body: JSON.stringify(body) })
384
+ await this.request(this.url("session"), { method: "POST", headers: await this.headers(), body: JSON.stringify(body) })
370
385
  );
371
- this.session = s;
372
- store.set(this.storageKey, JSON.stringify(s));
373
- return s;
386
+ this.session = { ...s, obtainedAt: Date.now() };
387
+ store.set(this.storageKey, JSON.stringify(this.session));
388
+ return this.session;
374
389
  }
375
390
  forgetSession() {
376
391
  this.session = null;
@@ -378,17 +393,17 @@ var ApiClient = class {
378
393
  }
379
394
  async history(agent, conversationId) {
380
395
  await this.ensureSession();
381
- const res = await fetch(this.url("history", { agent, conversationId }), { headers: this.headers(true) });
396
+ const res = await this.request(this.url("history", { agent, conversationId }), { headers: await this.headers(true) });
382
397
  return (await this.parse(res)).messages;
383
398
  }
384
399
  /** Sends a message and calls `onEvent` for every streamed event. Resolves when the run ends. */
385
400
  async chat(input, onEvent, signal) {
386
401
  await this.ensureSession();
387
- let res = await fetch(this.url("chat"), { method: "POST", headers: this.headers(true), body: JSON.stringify(input), signal });
402
+ let res = await this.request(this.url("chat"), { method: "POST", headers: await this.headers(true), body: JSON.stringify(input), signal });
388
403
  if (res.status === 401) {
389
404
  this.forgetSession();
390
405
  await this.ensureSession();
391
- res = await fetch(this.url("chat"), { method: "POST", headers: this.headers(true), body: JSON.stringify(input), signal });
406
+ res = await this.request(this.url("chat"), { method: "POST", headers: await this.headers(true), body: JSON.stringify(input), signal });
392
407
  }
393
408
  if (!res.ok || !res.body) await this.parse(res);
394
409
  const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
@@ -1101,11 +1116,13 @@ var DeweeChatElement = class extends ElementBase {
1101
1116
  }
1102
1117
  /** Updates configuration at runtime (wins over attributes). */
1103
1118
  configure(partial) {
1104
- const { actions, confirm, getPageContext, ...rest } = partial;
1119
+ const { actions, confirm, getPageContext, headers, fetch, ...rest } = partial;
1105
1120
  const has = (k) => Object.prototype.hasOwnProperty.call(partial, k);
1106
1121
  if (has("actions")) this.setPropActions(actions ?? []);
1107
1122
  if (has("confirm")) this.hooks.confirm = confirm;
1108
1123
  if (has("getPageContext")) this.hooks.getPageContext = getPageContext;
1124
+ if (has("headers")) this.hooks.headers = headers;
1125
+ if (has("fetch")) this.hooks.fetch = fetch;
1109
1126
  if (!Object.keys(rest).length) return;
1110
1127
  this.overrides = { ...this.overrides, ...rest };
1111
1128
  if (this.els) this.applyConfig();
@@ -1119,6 +1136,20 @@ var DeweeChatElement = class extends ElementBase {
1119
1136
  this.propActionNames.delete(name);
1120
1137
  return this.registry.unregister(name);
1121
1138
  }
1139
+ /** Extra request headers (property only; same as `configure({ headers })`). */
1140
+ get headers() {
1141
+ return this.hooks.headers;
1142
+ }
1143
+ set headers(v) {
1144
+ this.hooks.headers = v;
1145
+ }
1146
+ /** Custom fetch for widget requests (property only; same as `configure({ fetch })`). */
1147
+ get fetch() {
1148
+ return this.hooks.fetch;
1149
+ }
1150
+ set fetch(v) {
1151
+ this.hooks.fetch = v;
1152
+ }
1122
1153
  /** Names of the currently registered page actions. */
1123
1154
  get actionNames() {
1124
1155
  return this.registry.names();
@@ -1331,7 +1362,13 @@ var DeweeChatElement = class extends ElementBase {
1331
1362
  endpoint: c.endpoint,
1332
1363
  gatewayUrl: c.gatewayUrl,
1333
1364
  gatewayToken: c.gatewayToken,
1334
- turnstile: (siteKey) => this.challenge(siteKey)
1365
+ turnstile: (siteKey) => this.challenge(siteKey),
1366
+ // Read the current hooks on every request so configure({ headers, fetch }) applies at once.
1367
+ headers: () => {
1368
+ const h = this.hooks.headers;
1369
+ return (typeof h === "function" ? h() : h) ?? {};
1370
+ },
1371
+ fetch: (input, init) => (this.hooks.fetch ?? globalThis.fetch)(input, init)
1335
1372
  });
1336
1373
  }
1337
1374
  return this.api;
@@ -1841,6 +1878,7 @@ function mount(config = {}, target) {
1841
1878
  defineDeweeChat();
1842
1879
 
1843
1880
  export {
1881
+ RESERVED_HEADERS,
1844
1882
  ApiError,
1845
1883
  ApiClient,
1846
1884
  renderMarkdown,
@@ -1,24 +1,24 @@
1
- "use strict";(()=>{var U=Object.defineProperty;var wt=(i,s,t)=>s in i?U(i,s,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[s]=t;var xt=(i,s)=>{for(var t in s)U(i,t,{get:s[t],enumerable:!0})};var h=(i,s,t)=>wt(i,typeof s!="symbol"?s+"":s,t);var K={};xt(K,{ApiClient:()=>S,ApiError:()=>v,DeweeChatElement:()=>L,defineDeweeChat:()=>V,mount:()=>Ot,renderMarkdown:()=>E});var F="dewee-action",G="dewee-action-result",I={maxCalls:8,maxBlockBytes:8192},bt=/^[A-Za-z0-9_.:-]{1,64}$/,yt=/^\s{0,3}```\s*$/,W=i=>new RegExp("^\\s{0,3}```"+i.replace(/[.-]/g,"\\$&")+"\\s*$"),Y=W(F),Z=W(G);function _(i,s){let t=[];for(let e=0;e<i.length;e++){if(!s.test(i[e]))continue;let n=e,r=[],o=!1;for(e++;e<i.length;e++){if(yt.test(i[e])){o=!0;break}r.push(i[e])}t.push({start:n,end:Math.min(e,i.length-1),body:r,closed:o})}return t}var Q=(i,s)=>{let t=new Set;for(let e of s)for(let n=e.start;n<=e.end;n++)t.add(n);return i.filter((e,n)=>!t.has(n)).join(`
1
+ "use strict";(()=>{var W=Object.defineProperty;var bt=(i,s,t)=>s in i?W(i,s,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[s]=t;var xt=(i,s)=>{for(var t in s)W(i,t,{get:s[t],enumerable:!0})};var h=(i,s,t)=>bt(i,typeof s!="symbol"?s+"":s,t);var G={};xt(G,{ApiClient:()=>E,ApiError:()=>v,DeweeChatElement:()=>P,RESERVED_HEADERS:()=>K,defineDeweeChat:()=>F,mount:()=>Dt,renderMarkdown:()=>S});var Y="dewee-action",Z="dewee-action-result",L={maxCalls:8,maxBlockBytes:8192},yt=/^[A-Za-z0-9_.:-]{1,64}$/,vt=/^\s{0,3}```\s*$/,Q=i=>new RegExp("^\\s{0,3}```"+i.replace(/[.-]/g,"\\$&")+"\\s*$"),X=Q(Y),tt=Q(Z);function N(i,s){let t=[];for(let n=0;n<i.length;n++){if(!s.test(i[n]))continue;let e=n,r=[],o=!1;for(n++;n<i.length;n++){if(vt.test(i[n])){o=!0;break}r.push(i[n])}t.push({start:e,end:Math.min(n,i.length-1),body:r,closed:o})}return t}var et=(i,s)=>{let t=new Set;for(let n of s)for(let e=n.start;e<=n.end;e++)t.add(e);return i.filter((n,e)=>!t.has(e)).join(`
2
2
  `).replace(/\n{3,}/g,`
3
3
 
4
- `).trim()};function H(i){let s=i.replace(/\r\n?/g,`
4
+ `).trim()};function _(i){let s=i.replace(/\r\n?/g,`
5
5
  `).split(`
6
- `),t=_(s,Y),e=[],n=new Set;return t.forEach((r,o)=>{let a=`call-${o+1}`,l=r.body.join(`
7
- `),c=(m,w=a,x="")=>e.push({ok:!1,id:w,name:x,error:m});if(o>=I.maxCalls)return c(`Too many action calls in one reply (max ${I.maxCalls})`);if(!r.closed)return c("Unclosed dewee-action block");if(new TextEncoder().encode(l).length>I.maxBlockBytes)return c(`Action block exceeds ${I.maxBlockBytes} bytes`);let g;try{g=JSON.parse(l)}catch{return c("Action block is not valid JSON")}if(typeof g!="object"||g===null||Array.isArray(g))return c("Action block must be a JSON object");let p=typeof g.id=="string"&&bt.test(g.id)?g.id:a,d=typeof g.name=="string"?g.name.slice(0,64):"";if(n.has(p))return c(`Duplicate action call id "${p}"`,`${p}-${o+1}`,d);if(n.add(p),!d)return c("Action name is required",p);let u=g.args??{};if(typeof u!="object"||u===null||Array.isArray(u))return c("args must be a JSON object",p,d);e.push({ok:!0,id:p,name:d,args:u})}),{text:t.length?Q(s,t):i,calls:e}}function X(i){if(!i.includes("```"+F))return i;let s=i.replace(/\r\n?/g,`
6
+ `),t=N(s,X),n=[],e=new Set;return t.forEach((r,o)=>{let a=`call-${o+1}`,c=r.body.join(`
7
+ `),l=(m,w=a,b="")=>n.push({ok:!1,id:w,name:b,error:m});if(o>=L.maxCalls)return l(`Too many action calls in one reply (max ${L.maxCalls})`);if(!r.closed)return l("Unclosed dewee-action block");if(new TextEncoder().encode(c).length>L.maxBlockBytes)return l(`Action block exceeds ${L.maxBlockBytes} bytes`);let d;try{d=JSON.parse(c)}catch{return l("Action block is not valid JSON")}if(typeof d!="object"||d===null||Array.isArray(d))return l("Action block must be a JSON object");let g=typeof d.id=="string"&&yt.test(d.id)?d.id:a,p=typeof d.name=="string"?d.name.slice(0,64):"";if(e.has(g))return l(`Duplicate action call id "${g}"`,`${g}-${o+1}`,p);if(e.add(g),!p)return l("Action name is required",g);let u=d.args??{};if(typeof u!="object"||u===null||Array.isArray(u))return l("args must be a JSON object",g,p);n.push({ok:!0,id:g,name:p,args:u})}),{text:t.length?et(s,t):i,calls:n}}function nt(i){if(!i.includes("```"+Y))return i;let s=i.replace(/\r\n?/g,`
8
8
  `).split(`
9
- `);return Q(s,_(s,Y))}function B(i){return Z.test(i.trimStart().split(`
10
- `,1)[0]??"")}function tt(i){let s=i.replace(/\r\n?/g,`
9
+ `);return et(s,N(s,X))}function O(i){return tt.test(i.trimStart().split(`
10
+ `,1)[0]??"")}function it(i){let s=i.replace(/\r\n?/g,`
11
11
  `).split(`
12
- `),t=[];for(let e of _(s,Z))try{let n=JSON.parse(e.body.join(`
13
- `));n&&typeof n.id=="string"&&["ok","denied","error"].includes(n.status)&&t.push(n)}catch{}return t}var P=i=>"```"+G+`
14
- `+JSON.stringify(i)+"\n```";function et(i,s=4e3){let t=Math.max(64,Math.floor((s-16)/Math.max(1,i.length)));return i.map(n=>{let r={...n,result:O(n.result)};if(r.result===void 0&&delete r.result,P(r).length<=t)return P(r);let o=JSON.stringify(r.result??null),a=t-P({...r,result:{truncated:!0,preview:""}}).length-8,l=P({...r,result:{truncated:!0,preview:o.slice(0,Math.max(0,a))}});return l.length<=t?l:P({id:n.id,name:n.name.slice(0,64),status:n.status,result:{truncated:!0}})}).join(`
12
+ `),t=[];for(let n of N(s,tt))try{let e=JSON.parse(n.body.join(`
13
+ `));e&&typeof e.id=="string"&&["ok","denied","error"].includes(e.status)&&t.push(e)}catch{}return t}var R=i=>"```"+Z+`
14
+ `+JSON.stringify(i)+"\n```";function st(i,s=4e3){let t=Math.max(64,Math.floor((s-16)/Math.max(1,i.length)));return i.map(e=>{let r={...e,result:B(e.result)};if(r.result===void 0&&delete r.result,R(r).length<=t)return R(r);let o=JSON.stringify(r.result??null),a=t-R({...r,result:{truncated:!0,preview:""}}).length-8,c=R({...r,result:{truncated:!0,preview:o.slice(0,Math.max(0,a))}});return c.length<=t?c:R({id:e.id,name:e.name.slice(0,64),status:e.status,result:{truncated:!0}})}).join(`
15
15
 
16
- `).slice(0,s)}function O(i){if(i!==void 0)try{let s=JSON.stringify(i);return s===void 0?null:JSON.parse(s)}catch{return"[unserializable result]"}}var z=i=>i===null?"null":Array.isArray(i)?"array":typeof i=="number"?Number.isInteger(i)?"integer":"number":typeof i,vt=(i,s)=>{let t=z(i);return t===s||s==="number"&&t==="integer"};function R(i,s,t="args",e=0){if(!s||typeof s!="object")return null;if(e>32)return`${t}: schema is nested too deeply`;let n=s;if(n.type!==void 0){let r=Array.isArray(n.type)?n.type:[n.type];if(!r.some(o=>vt(i,o)))return`${t}: expected ${r.join(" or ")}, got ${z(i)}`}if(Array.isArray(n.enum)&&!n.enum.some(r=>nt(r,i)))return`${t}: must be one of ${n.enum.map(r=>JSON.stringify(r)).join(", ")}`;if("const"in n&&!nt(n.const,i))return`${t}: must equal ${JSON.stringify(n.const)}`;if(typeof i=="string"){if(typeof n.minLength=="number"&&i.length<n.minLength)return`${t}: shorter than ${n.minLength} characters`;if(typeof n.maxLength=="number"&&i.length>n.maxLength)return`${t}: longer than ${n.maxLength} characters`;if(typeof n.pattern=="string"){let r=null;try{r=new RegExp(n.pattern,"u")}catch{return`${t}: schema pattern is invalid`}if(!r.test(i))return`${t}: does not match ${n.pattern}`}}if(typeof i=="number"){if(typeof n.minimum=="number"&&i<n.minimum)return`${t}: must be >= ${n.minimum}`;if(typeof n.maximum=="number"&&i>n.maximum)return`${t}: must be <= ${n.maximum}`}if(Array.isArray(i)){if(typeof n.minItems=="number"&&i.length<n.minItems)return`${t}: needs at least ${n.minItems} items`;if(typeof n.maxItems=="number"&&i.length>n.maxItems)return`${t}: allows at most ${n.maxItems} items`;if(n.items&&typeof n.items=="object")for(let r=0;r<i.length;r++){let o=R(i[r],n.items,`${t}[${r}]`,e+1);if(o)return o}}if(z(i)==="object"){let r=i,o=n.properties&&typeof n.properties=="object"?n.properties:{};for(let a of Array.isArray(n.required)?n.required:[])if(!(a in r))return`${t}.${a}: is required`;for(let[a,l]of Object.entries(r))if(Object.prototype.hasOwnProperty.call(o,a)){let c=R(l,o[a],`${t}.${a}`,e+1);if(c)return c}else if(n.additionalProperties===!1)return`${t}.${a}: is not allowed`}return null}function nt(i,s){return JSON.stringify(i)===JSON.stringify(s)}var st=/^[a-z][a-z0-9_.-]{0,63}$/,D=["read","navigate","mutate","destructive"],C={maxActions:32,maxDescription:1e3,maxParametersBytes:4096,maxManifestBytes:16384,maxPageContextBytes:4096},kt="[[dewee-page v1]]",it="[[/dewee-page]]";function j(i){if(!i.startsWith(kt))return i;let s=i.indexOf(`
17
- `+it);return s<0?i:i.slice(s+it.length+1).replace(/^\n+/,"")}var Ct=i=>i==="mutate"||i==="destructive",$=class{constructor(){h(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 t=Array.isArray(s)?s:[s];for(let e of t)At(e);if(new Set([...this.map.keys(),...t.map(e=>e.name)]).size>C.maxActions)throw new TypeError(`dewee-webchat: at most ${C.maxActions} page actions`);for(let e of t)this.map.set(e.name,e)}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 At(i){let s=t=>{throw new TypeError(`dewee-webchat: invalid page action ${JSON.stringify(i?.name)}: ${t}`)};(!i||typeof i!="object")&&s("not an object"),(typeof i.name!="string"||!st.test(i.name))&&s("name must match ^[a-z][a-z0-9_.-]{0,63}$"),(typeof i.description!="string"||i.description.length>C.maxDescription)&&s(`description must be a string up to ${C.maxDescription} chars`),D.includes(i.sensitivity)||s(`sensitivity must be one of ${D.join(", ")}`),typeof i.handler!="function"&&s("handler must be a function"),i.parameters!==void 0&&(typeof i.parameters!="object"||i.parameters===null||Array.isArray(i.parameters))&&s("parameters must be a JSON Schema object")}async function ot(i,s){let t=l=>(s.onStatus?.("error"),{id:i.id,name:i.name,status:"error",error:l});if(!i.ok)return t(i.error);if(s.signal.aborted)return t("Cancelled");let e=s.registry.get(i.name);if(!e)return t(`Unknown action "${i.name}". Available: ${s.registry.names().join(", ")||"none"}`);let n=R(i.args,e.parameters??{type:"object"});if(n)return t(`Invalid arguments: ${n}`);if(Ct(e.sensitivity)){s.onStatus?.("awaiting-confirmation");let{handler:l,...c}=e,g=!1;try{g=await rt(Promise.resolve(s.confirm({...c,callId:i.id},i.args)),s.signal)===!0}catch{g=!1}if(!g||s.signal.aborted)return s.onStatus?.("denied"),{id:i.id,name:i.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 a=setTimeout(o,s.timeoutMs??3e4);try{let l=await rt(Promise.resolve().then(()=>e.handler(i.args,{callId:i.id,conversationId:s.conversationId,agent:s.agent,signal:r.signal})),r.signal);return s.onStatus?.("done"),{id:i.id,name:i.name,status:"ok",result:O(l)??null}}catch(l){return t(r.signal.aborted?s.signal.aborted?"Cancelled":"Timed out":String(l?.message??l).slice(0,500)||"Action failed")}finally{clearTimeout(a),s.signal.removeEventListener("abort",o)}}function rt(i,s){return s.aborted?Promise.reject(new Error("aborted")):new Promise((t,e)=>{let n=()=>e(new Error("aborted"));s.addEventListener("abort",n,{once:!0}),i.then(t,e).finally(()=>s.removeEventListener("abort",n))})}var at="x-dewee-gateway-url",lt="x-dewee-gateway-token";var v=class extends Error{constructor(t,e,n){super(t);h(this,"status",e);h(this,"code",n)}},J={get(i){try{return localStorage.getItem(i)}catch{return null}},set(i,s){try{localStorage.setItem(i,s)}catch{}},del(i){try{localStorage.removeItem(i)}catch{}}},S=class{constructor(s){h(this,"o",s);h(this,"session",null);h(this,"config",null);h(this,"sessionPromise",null)}url(s,t){let e=this.o.endpoint.replace(/\/+$/,""),n=t?"?"+new URLSearchParams(t).toString():"";return`${e}/${s}${n}`}get storageKey(){return`dewee-webchat:session:${this.o.endpoint}`}headers(s=!1){let t={"content-type":"application/json"};return this.o.gatewayUrl&&this.o.gatewayToken&&(t[at]=this.o.gatewayUrl,t[lt]=this.o.gatewayToken),s&&this.session&&(t.authorization=`Bearer ${this.session.token}`),t}async parse(s){let t=await s.json().catch(()=>({}));if(!s.ok)throw new v(t?.error?.message||`HTTP ${s.status}`,s.status,t?.error?.code);return t}async getConfig(s=!1){return this.config&&!s?this.config:(this.config=await this.parse(await fetch(this.url("config"),{headers:this.headers()})),this.config)}ensureSession(){if(this.session&&this.session.expiresAt-Date.now()>864e5)return Promise.resolve(this.session);if(!this.session){let s=J.get(this.storageKey);if(s)try{let t=JSON.parse(s);if(t.expiresAt-Date.now()>864e5)return Promise.resolve(this.session=t);this.session=t}catch{}}return this.sessionPromise??(this.sessionPromise=this.createSession().finally(()=>this.sessionPromise=null))}async createSession(){let s=await this.getConfig(),t={};if(this.session?.token&&(t.token=this.session.token),s.turnstileSiteKey){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(s.turnstileSiteKey)}let e=await this.parse(await fetch(this.url("session"),{method:"POST",headers:this.headers(),body:JSON.stringify(t)}));return this.session=e,J.set(this.storageKey,JSON.stringify(e)),e}forgetSession(){this.session=null,J.del(this.storageKey)}async history(s,t){await this.ensureSession();let e=await fetch(this.url("history",{agent:s,conversationId:t}),{headers:this.headers(!0)});return(await this.parse(e)).messages}async chat(s,t,e){await this.ensureSession();let n=await fetch(this.url("chat"),{method:"POST",headers:this.headers(!0),body:JSON.stringify(s),signal:e});n.status===401&&(this.forgetSession(),await this.ensureSession(),n=await fetch(this.url("chat"),{method:"POST",headers:this.headers(!0),body:JSON.stringify(s),signal:e})),(!n.ok||!n.body)&&await this.parse(n);let r=n.body.pipeThrough(new TextDecoderStream).getReader(),o="";for(;;){let{done:a,value:l}=await r.read();if(a)break;o+=l;let c;for(;(c=o.indexOf(`
16
+ `).slice(0,s)}function B(i){if(i!==void 0)try{let s=JSON.stringify(i);return s===void 0?null:JSON.parse(s)}catch{return"[unserializable result]"}}var D=i=>i===null?"null":Array.isArray(i)?"array":typeof i=="number"?Number.isInteger(i)?"integer":"number":typeof i,kt=(i,s)=>{let t=D(i);return t===s||s==="number"&&t==="integer"};function I(i,s,t="args",n=0){if(!s||typeof s!="object")return null;if(n>32)return`${t}: schema is nested too deeply`;let e=s;if(e.type!==void 0){let r=Array.isArray(e.type)?e.type:[e.type];if(!r.some(o=>kt(i,o)))return`${t}: expected ${r.join(" or ")}, got ${D(i)}`}if(Array.isArray(e.enum)&&!e.enum.some(r=>rt(r,i)))return`${t}: must be one of ${e.enum.map(r=>JSON.stringify(r)).join(", ")}`;if("const"in e&&!rt(e.const,i))return`${t}: must equal ${JSON.stringify(e.const)}`;if(typeof i=="string"){if(typeof e.minLength=="number"&&i.length<e.minLength)return`${t}: shorter than ${e.minLength} characters`;if(typeof e.maxLength=="number"&&i.length>e.maxLength)return`${t}: longer than ${e.maxLength} characters`;if(typeof e.pattern=="string"){let r=null;try{r=new RegExp(e.pattern,"u")}catch{return`${t}: schema pattern is invalid`}if(!r.test(i))return`${t}: does not match ${e.pattern}`}}if(typeof i=="number"){if(typeof e.minimum=="number"&&i<e.minimum)return`${t}: must be >= ${e.minimum}`;if(typeof e.maximum=="number"&&i>e.maximum)return`${t}: must be <= ${e.maximum}`}if(Array.isArray(i)){if(typeof e.minItems=="number"&&i.length<e.minItems)return`${t}: needs at least ${e.minItems} items`;if(typeof e.maxItems=="number"&&i.length>e.maxItems)return`${t}: allows at most ${e.maxItems} items`;if(e.items&&typeof e.items=="object")for(let r=0;r<i.length;r++){let o=I(i[r],e.items,`${t}[${r}]`,n+1);if(o)return o}}if(D(i)==="object"){let r=i,o=e.properties&&typeof e.properties=="object"?e.properties:{};for(let a of Array.isArray(e.required)?e.required:[])if(!(a in r))return`${t}.${a}: is required`;for(let[a,c]of Object.entries(r))if(Object.prototype.hasOwnProperty.call(o,a)){let l=I(c,o[a],`${t}.${a}`,n+1);if(l)return l}else if(e.additionalProperties===!1)return`${t}.${a}: is not allowed`}return null}function rt(i,s){return JSON.stringify(i)===JSON.stringify(s)}var at=/^[a-z][a-z0-9_.-]{0,63}$/,z=["read","navigate","mutate","destructive"],C={maxActions:32,maxDescription:1e3,maxParametersBytes:4096,maxManifestBytes:16384,maxPageContextBytes:4096},Ct="[[dewee-page v1]]",ot="[[/dewee-page]]";function j(i){if(!i.startsWith(Ct))return i;let s=i.indexOf(`
17
+ `+ot);return s<0?i:i.slice(s+ot.length+1).replace(/^\n+/,"")}var At=i=>i==="mutate"||i==="destructive",$=class{constructor(){h(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 t=Array.isArray(s)?s:[s];for(let n of t)Et(n);if(new Set([...this.map.keys(),...t.map(n=>n.name)]).size>C.maxActions)throw new TypeError(`dewee-webchat: at most ${C.maxActions} page actions`);for(let n of t)this.map.set(n.name,n)}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 Et(i){let s=t=>{throw new TypeError(`dewee-webchat: invalid page action ${JSON.stringify(i?.name)}: ${t}`)};(!i||typeof i!="object")&&s("not an object"),(typeof i.name!="string"||!at.test(i.name))&&s("name must match ^[a-z][a-z0-9_.-]{0,63}$"),(typeof i.description!="string"||i.description.length>C.maxDescription)&&s(`description must be a string up to ${C.maxDescription} chars`),z.includes(i.sensitivity)||s(`sensitivity must be one of ${z.join(", ")}`),typeof i.handler!="function"&&s("handler must be a function"),i.parameters!==void 0&&(typeof i.parameters!="object"||i.parameters===null||Array.isArray(i.parameters))&&s("parameters must be a JSON Schema object")}async function ct(i,s){let t=c=>(s.onStatus?.("error"),{id:i.id,name:i.name,status:"error",error:c});if(!i.ok)return t(i.error);if(s.signal.aborted)return t("Cancelled");let n=s.registry.get(i.name);if(!n)return t(`Unknown action "${i.name}". Available: ${s.registry.names().join(", ")||"none"}`);let e=I(i.args,n.parameters??{type:"object"});if(e)return t(`Invalid arguments: ${e}`);if(At(n.sensitivity)){s.onStatus?.("awaiting-confirmation");let{handler:c,...l}=n,d=!1;try{d=await lt(Promise.resolve(s.confirm({...l,callId:i.id},i.args)),s.signal)===!0}catch{d=!1}if(!d||s.signal.aborted)return s.onStatus?.("denied"),{id:i.id,name:i.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 a=setTimeout(o,s.timeoutMs??3e4);try{let c=await lt(Promise.resolve().then(()=>n.handler(i.args,{callId:i.id,conversationId:s.conversationId,agent:s.agent,signal:r.signal})),r.signal);return s.onStatus?.("done"),{id:i.id,name:i.name,status:"ok",result:B(c)??null}}catch(c){return t(r.signal.aborted?s.signal.aborted?"Cancelled":"Timed out":String(c?.message??c).slice(0,500)||"Action failed")}finally{clearTimeout(a),s.signal.removeEventListener("abort",o)}}function lt(i,s){return s.aborted?Promise.reject(new Error("aborted")):new Promise((t,n)=>{let e=()=>n(new Error("aborted"));s.addEventListener("abort",e,{once:!0}),i.then(t,n).finally(()=>s.removeEventListener("abort",e))})}var q="x-dewee-gateway-url",J="x-dewee-gateway-token";var K=["content-type","authorization",q,J],v=class extends Error{constructor(t,n,e){super(t);h(this,"status",n);h(this,"code",e)}},V={get(i){try{return localStorage.getItem(i)}catch{return null}},set(i,s){try{localStorage.setItem(i,s)}catch{}},del(i){try{localStorage.removeItem(i)}catch{}}},E=class{constructor(s){h(this,"o",s);h(this,"session",null);h(this,"config",null);h(this,"sessionPromise",null)}url(s,t){let n=this.o.endpoint.replace(/\/+$/,""),e=t?"?"+new URLSearchParams(t).toString():"";return`${n}/${s}${e}`}get storageKey(){return`dewee-webchat:session:${this.o.endpoint}`}fresh(s){let t=s.obtainedAt?s.expiresAt-s.obtainedAt:1/0,n=Math.min(864e5,Math.max(3e4,t*.1));return s.expiresAt-Date.now()>n}request(s,t){return(this.o.fetch??globalThis.fetch)(s,t)}async headers(s=!1){let t={},n=typeof this.o.headers=="function"?await this.o.headers():this.o.headers;for(let[e,r]of Object.entries(n??{}))typeof r=="string"&&!K.includes(e.toLowerCase())&&(t[e.toLowerCase()]=r);return t["content-type"]="application/json",this.o.gatewayUrl&&this.o.gatewayToken&&(t[q]=this.o.gatewayUrl,t[J]=this.o.gatewayToken),s&&this.session&&(t.authorization=`Bearer ${this.session.token}`),t}async parse(s){let t=await s.json().catch(()=>({}));if(!s.ok)throw new v(t?.error?.message||`HTTP ${s.status}`,s.status,t?.error?.code);return t}async getConfig(s=!1){return this.config&&!s?this.config:(this.config=await this.parse(await this.request(this.url("config"),{headers:await this.headers()})),this.config)}ensureSession(){if(this.session&&this.fresh(this.session))return Promise.resolve(this.session);if(!this.session){let s=V.get(this.storageKey);if(s)try{let t=JSON.parse(s);if(this.fresh(t))return Promise.resolve(this.session=t);this.session=t}catch{}}return this.sessionPromise??(this.sessionPromise=this.createSession().finally(()=>this.sessionPromise=null))}async createSession(){let s=await this.getConfig(),t={};if(this.session?.token&&(t.token=this.session.token),s.turnstileSiteKey){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(s.turnstileSiteKey)}let n=await this.parse(await this.request(this.url("session"),{method:"POST",headers:await this.headers(),body:JSON.stringify(t)}));return this.session={...n,obtainedAt:Date.now()},V.set(this.storageKey,JSON.stringify(this.session)),this.session}forgetSession(){this.session=null,V.del(this.storageKey)}async history(s,t){await this.ensureSession();let n=await this.request(this.url("history",{agent:s,conversationId:t}),{headers:await this.headers(!0)});return(await this.parse(n)).messages}async chat(s,t,n){await this.ensureSession();let e=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:n});e.status===401&&(this.forgetSession(),await this.ensureSession(),e=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:n})),(!e.ok||!e.body)&&await this.parse(e);let r=e.body.pipeThrough(new TextDecoderStream).getReader(),o="";for(;;){let{done:a,value:c}=await r.read();if(a)break;o+=c;let l;for(;(l=o.indexOf(`
18
18
 
19
- `))>=0;){let g=o.slice(0,c);o=o.slice(c+2);let p=g.split(`
20
- `).filter(d=>d.startsWith("data:")).map(d=>d.slice(5).trimStart()).join(`
21
- `);if(p)try{t(JSON.parse(p))}catch{}}}}};var St=0;function ct(i,s,t,e,n){let r=`dw-confirm-${++St}`,o=i.getRootNode(),a=o.activeElement,l=document.createElement("div");l.className="confirm-backdrop",l.innerHTML=`
19
+ `))>=0;){let d=o.slice(0,l);o=o.slice(l+2);let g=d.split(`
20
+ `).filter(p=>p.startsWith("data:")).map(p=>p.slice(5).trimStart()).join(`
21
+ `);if(g)try{t(JSON.parse(g))}catch{}}}}};var St=0;function dt(i,s,t,n,e){let r=`dw-confirm-${++St}`,o=i.getRootNode(),a=o.activeElement,c=document.createElement("div");c.className="confirm-backdrop",c.innerHTML=`
22
22
  <div class="confirm" role="alertdialog" aria-modal="true" aria-labelledby="${r}-t" aria-describedby="${r}-d">
23
23
  <h3 id="${r}-t"></h3>
24
24
  <div id="${r}-d" class="confirm-desc">
@@ -31,12 +31,12 @@
31
31
  <button type="button" class="btn deny"></button>
32
32
  <button type="button" class="btn allow"></button>
33
33
  </div>
34
- </div>`;let c=w=>l.querySelector(w);c("h3").textContent=s.confirmTitle,c("code").textContent=t.name;let g=c(".sens");if(g.textContent=t.sensitivity,g.dataset.level=t.sensitivity,c(".confirm-text").textContent=t.description,t.sensitivity==="destructive"){let w=c(".confirm-warn");w.hidden=!1,w.textContent=s.confirmDestructive}let p=JSON.stringify(e,null,2),d=c(".confirm-args");p&&p!=="{}"?d.textContent=p:d.remove();let u=c(".deny"),m=c(".allow");return u.textContent=s.deny,m.textContent=s.allow,t.sensitivity==="destructive"&&m.classList.add("danger"),new Promise(w=>{let x=!1,y=A=>{x||(x=!0,n?.removeEventListener("abort",M),l.remove(),a?.focus?.(),w(A))},M=()=>y(!1);if(n?.aborted)return y(!1);n?.addEventListener("abort",M,{once:!0}),u.addEventListener("click",()=>y(!1)),m.addEventListener("click",()=>y(!0)),l.addEventListener("keydown",A=>{A.key==="Escape"?(A.preventDefault(),A.stopPropagation(),y(!1)):A.key==="Tab"&&(A.preventDefault(),(o.activeElement===u?m:u).focus())}),i.appendChild(l),u.focus()})}var Et={title:"Assistant",greeting:"Hi there \u{1F44B}",greetingBody:"Ask me anything. I can look things up and use tools to help you.",placeholder:"Type a message\u2026",send:"Send",stop:"Stop",newChat:"New conversation",close:"Close chat",open:"Open chat",thinking:"Thinking\u2026",thoughtFor:i=>`Thought for ${i}s`,reasoning:"Reasoning",toolRunning:"running",toolDone:"done",toolError:"error",args:"Arguments",result:"Result",online:"Online",poweredBy:"Powered by",errorGeneric:"Something went wrong. Please try again.",stopped:"Stopped.",actionStatus:{pending:"queued","awaiting-confirmation":"needs approval",running:"running",done:"done",denied:"declined",error:"failed"},confirmTitle:"Allow this action?",confirmDestructive:"This can't be undone.",allow:"Allow",deny:"Deny",actionLimit:i=>`Stopped after ${i} automatic action rounds. Send a message to continue.`},Tt={title:"Tr\u1EE3 l\xFD",greeting:"Xin ch\xE0o \u{1F44B}",greetingBody:"B\u1EA1n c\u1EA7n h\u1ECFi g\xEC c\u1EE9 nh\u1EAFn nh\xE9. M\xECnh c\xF3 th\u1EC3 tra c\u1EE9u v\xE0 d\xF9ng c\xF4ng c\u1EE5 \u0111\u1EC3 h\u1ED7 tr\u1EE3 b\u1EA1n.",placeholder:"Nh\u1EADp tin nh\u1EAFn\u2026",send:"G\u1EEDi",stop:"D\u1EEBng",newChat:"Cu\u1ED9c tr\xF2 chuy\u1EC7n m\u1EDBi",close:"\u0110\xF3ng khung chat",open:"M\u1EDF khung chat",thinking:"\u0110ang suy ngh\u0129\u2026",thoughtFor:i=>`\u0110\xE3 suy ngh\u0129 ${i} gi\xE2y`,reasoning:"Suy lu\u1EADn",toolRunning:"\u0111ang ch\u1EA1y",toolDone:"xong",toolError:"l\u1ED7i",args:"Tham s\u1ED1",result:"K\u1EBFt qu\u1EA3",online:"\u0110ang tr\u1EF1c tuy\u1EBFn",poweredBy:"V\u1EADn h\xE0nh b\u1EDFi",errorGeneric:"C\xF3 l\u1ED7i x\u1EA3y ra. B\u1EA1n th\u1EED l\u1EA1i gi\xFAp m\xECnh nh\xE9.",stopped:"\u0110\xE3 d\u1EEBng.",actionStatus:{pending:"ch\u1EDD","awaiting-confirmation":"c\u1EA7n x\xE1c nh\u1EADn",running:"\u0111ang ch\u1EA1y",done:"xong",denied:"\u0111\xE3 t\u1EEB ch\u1ED1i",error:"l\u1ED7i"},confirmTitle:"Cho ph\xE9p thao t\xE1c n\xE0y?",confirmDestructive:"Thao t\xE1c n\xE0y kh\xF4ng th\u1EC3 ho\xE0n t\xE1c.",allow:"Cho ph\xE9p",deny:"T\u1EEB ch\u1ED1i",actionLimit:i=>`\u0110\xE3 d\u1EEBng sau ${i} l\u01B0\u1EE3t thao t\xE1c t\u1EF1 \u0111\u1ED9ng. G\u1EEDi tin nh\u1EAFn \u0111\u1EC3 ti\u1EBFp t\u1EE5c.`};function q(i){return(i||(typeof document<"u"?document.documentElement.lang:"")||(typeof navigator<"u"?navigator.language:"")||"en").toLowerCase().startsWith("vi")?Tt:Et}var b={action:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 4 14h7l-1 8 9-12h-7l1-8Z"/></svg>',chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a8.5 8.5 0 0 1-12.4 7.6L3 21l1.4-5.1A8.5 8.5 0 1 1 21 12Z"/><path d="M8.5 11h.01M12 11h.01M15.5 11h.01"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>',down:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>',newChat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"/><path d="M16.4 3.6a2.1 2.1 0 1 1 3 3L7 19l-4 1 1-4Z"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg>',stop:'<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2.5"/></svg>',brain:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24A2.5 2.5 0 0 0 14.5 2Z"/></svg>',tool:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76Z"/></svg>'};function f(i,s,t){let e=i.createElement(s);return t&&(e.className=t),e}function Mt(i){let s=i.trim();return/^(https?:|mailto:|tel:)/i.test(s)||/^[/#?]/.test(s)&&!s.startsWith("//")?s:null}var Pt=/(`+)([\s\S]*?[^`])\1(?!`)|\*\*([^*]+?)\*\*|__([^_]+?)__|~~([^~]+?)~~|\*([^*\s][^*]*?)\*|(?<![\w])_([^_\s][^_]*?)_(?![\w])|\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/;function k(i,s,t){let e=new RegExp(Pt.source,"g"),n=0;for(let r;r=e.exec(s);)if(r.index>n&&t.appendChild(i.createTextNode(s.slice(n,r.index))),n=r.index+r[0].length,r[2]!==void 0){let o=f(i,"code");o.textContent=r[2],t.appendChild(o)}else if(r[3]!==void 0||r[4]!==void 0){let o=f(i,"strong");k(i,r[3]??r[4],o),t.appendChild(o)}else if(r[5]!==void 0){let o=f(i,"del");k(i,r[5],o),t.appendChild(o)}else if(r[6]!==void 0||r[7]!==void 0){let o=f(i,"em");k(i,r[6]??r[7],o),t.appendChild(o)}else if(r[8]!==void 0){let o=Mt(r[9]);if(o){let a=f(i,"a");a.href=o,a.target="_blank",a.rel="noopener noreferrer nofollow",k(i,r[8],a),t.appendChild(a)}else t.appendChild(i.createTextNode(r[8]))}else if(r[10]!==void 0){let o=f(i,"a");o.href=r[10],o.target="_blank",o.rel="noopener noreferrer nofollow",o.textContent=r[10],t.appendChild(o)}n<s.length&&t.appendChild(i.createTextNode(s.slice(n)))}var dt=i=>/^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(i),gt=i=>i.trim().replace(/^\||\|$/g,"").split("|").map(s=>s.trim());function E(i,s){let t=i.createDocumentFragment(),e=s.replace(/\r\n?/g,`
34
+ </div>`;let l=w=>c.querySelector(w);l("h3").textContent=s.confirmTitle,l("code").textContent=t.name;let d=l(".sens");if(d.textContent=t.sensitivity,d.dataset.level=t.sensitivity,l(".confirm-text").textContent=t.description,t.sensitivity==="destructive"){let w=l(".confirm-warn");w.hidden=!1,w.textContent=s.confirmDestructive}let g=JSON.stringify(n,null,2),p=l(".confirm-args");g&&g!=="{}"?p.textContent=g:p.remove();let u=l(".deny"),m=l(".allow");return u.textContent=s.deny,m.textContent=s.allow,t.sensitivity==="destructive"&&m.classList.add("danger"),new Promise(w=>{let b=!1,y=A=>{b||(b=!0,e?.removeEventListener("abort",M),c.remove(),a?.focus?.(),w(A))},M=()=>y(!1);if(e?.aborted)return y(!1);e?.addEventListener("abort",M,{once:!0}),u.addEventListener("click",()=>y(!1)),m.addEventListener("click",()=>y(!0)),c.addEventListener("keydown",A=>{A.key==="Escape"?(A.preventDefault(),A.stopPropagation(),y(!1)):A.key==="Tab"&&(A.preventDefault(),(o.activeElement===u?m:u).focus())}),i.appendChild(c),u.focus()})}var Tt={title:"Assistant",greeting:"Hi there \u{1F44B}",greetingBody:"Ask me anything. I can look things up and use tools to help you.",placeholder:"Type a message\u2026",send:"Send",stop:"Stop",newChat:"New conversation",close:"Close chat",open:"Open chat",thinking:"Thinking\u2026",thoughtFor:i=>`Thought for ${i}s`,reasoning:"Reasoning",toolRunning:"running",toolDone:"done",toolError:"error",args:"Arguments",result:"Result",online:"Online",poweredBy:"Powered by",errorGeneric:"Something went wrong. Please try again.",stopped:"Stopped.",actionStatus:{pending:"queued","awaiting-confirmation":"needs approval",running:"running",done:"done",denied:"declined",error:"failed"},confirmTitle:"Allow this action?",confirmDestructive:"This can't be undone.",allow:"Allow",deny:"Deny",actionLimit:i=>`Stopped after ${i} automatic action rounds. Send a message to continue.`},Mt={title:"Tr\u1EE3 l\xFD",greeting:"Xin ch\xE0o \u{1F44B}",greetingBody:"B\u1EA1n c\u1EA7n h\u1ECFi g\xEC c\u1EE9 nh\u1EAFn nh\xE9. M\xECnh c\xF3 th\u1EC3 tra c\u1EE9u v\xE0 d\xF9ng c\xF4ng c\u1EE5 \u0111\u1EC3 h\u1ED7 tr\u1EE3 b\u1EA1n.",placeholder:"Nh\u1EADp tin nh\u1EAFn\u2026",send:"G\u1EEDi",stop:"D\u1EEBng",newChat:"Cu\u1ED9c tr\xF2 chuy\u1EC7n m\u1EDBi",close:"\u0110\xF3ng khung chat",open:"M\u1EDF khung chat",thinking:"\u0110ang suy ngh\u0129\u2026",thoughtFor:i=>`\u0110\xE3 suy ngh\u0129 ${i} gi\xE2y`,reasoning:"Suy lu\u1EADn",toolRunning:"\u0111ang ch\u1EA1y",toolDone:"xong",toolError:"l\u1ED7i",args:"Tham s\u1ED1",result:"K\u1EBFt qu\u1EA3",online:"\u0110ang tr\u1EF1c tuy\u1EBFn",poweredBy:"V\u1EADn h\xE0nh b\u1EDFi",errorGeneric:"C\xF3 l\u1ED7i x\u1EA3y ra. B\u1EA1n th\u1EED l\u1EA1i gi\xFAp m\xECnh nh\xE9.",stopped:"\u0110\xE3 d\u1EEBng.",actionStatus:{pending:"ch\u1EDD","awaiting-confirmation":"c\u1EA7n x\xE1c nh\u1EADn",running:"\u0111ang ch\u1EA1y",done:"xong",denied:"\u0111\xE3 t\u1EEB ch\u1ED1i",error:"l\u1ED7i"},confirmTitle:"Cho ph\xE9p thao t\xE1c n\xE0y?",confirmDestructive:"Thao t\xE1c n\xE0y kh\xF4ng th\u1EC3 ho\xE0n t\xE1c.",allow:"Cho ph\xE9p",deny:"T\u1EEB ch\u1ED1i",actionLimit:i=>`\u0110\xE3 d\u1EEBng sau ${i} l\u01B0\u1EE3t thao t\xE1c t\u1EF1 \u0111\u1ED9ng. G\u1EEDi tin nh\u1EAFn \u0111\u1EC3 ti\u1EBFp t\u1EE5c.`};function U(i){return(i||(typeof document<"u"?document.documentElement.lang:"")||(typeof navigator<"u"?navigator.language:"")||"en").toLowerCase().startsWith("vi")?Mt:Tt}var x={action:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 4 14h7l-1 8 9-12h-7l1-8Z"/></svg>',chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a8.5 8.5 0 0 1-12.4 7.6L3 21l1.4-5.1A8.5 8.5 0 1 1 21 12Z"/><path d="M8.5 11h.01M12 11h.01M15.5 11h.01"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>',down:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>',newChat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"/><path d="M16.4 3.6a2.1 2.1 0 1 1 3 3L7 19l-4 1 1-4Z"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg>',stop:'<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2.5"/></svg>',brain:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24A2.5 2.5 0 0 0 14.5 2Z"/></svg>',tool:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76Z"/></svg>'};function f(i,s,t){let n=i.createElement(s);return t&&(n.className=t),n}function Rt(i){let s=i.trim();return/^(https?:|mailto:|tel:)/i.test(s)||/^[/#?]/.test(s)&&!s.startsWith("//")?s:null}var Pt=/(`+)([\s\S]*?[^`])\1(?!`)|\*\*([^*]+?)\*\*|__([^_]+?)__|~~([^~]+?)~~|\*([^*\s][^*]*?)\*|(?<![\w])_([^_\s][^_]*?)_(?![\w])|\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/;function k(i,s,t){let n=new RegExp(Pt.source,"g"),e=0;for(let r;r=n.exec(s);)if(r.index>e&&t.appendChild(i.createTextNode(s.slice(e,r.index))),e=r.index+r[0].length,r[2]!==void 0){let o=f(i,"code");o.textContent=r[2],t.appendChild(o)}else if(r[3]!==void 0||r[4]!==void 0){let o=f(i,"strong");k(i,r[3]??r[4],o),t.appendChild(o)}else if(r[5]!==void 0){let o=f(i,"del");k(i,r[5],o),t.appendChild(o)}else if(r[6]!==void 0||r[7]!==void 0){let o=f(i,"em");k(i,r[6]??r[7],o),t.appendChild(o)}else if(r[8]!==void 0){let o=Rt(r[9]);if(o){let a=f(i,"a");a.href=o,a.target="_blank",a.rel="noopener noreferrer nofollow",k(i,r[8],a),t.appendChild(a)}else t.appendChild(i.createTextNode(r[8]))}else if(r[10]!==void 0){let o=f(i,"a");o.href=r[10],o.target="_blank",o.rel="noopener noreferrer nofollow",o.textContent=r[10],t.appendChild(o)}e<s.length&&t.appendChild(i.createTextNode(s.slice(e)))}var pt=i=>/^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(i),gt=i=>i.trim().replace(/^\||\|$/g,"").split("|").map(s=>s.trim());function S(i,s){let t=i.createDocumentFragment(),n=s.replace(/\r\n?/g,`
35
35
  `).split(`
36
- `),n=0;for(;n<e.length;){let r=e[n];if(!r.trim()){n++;continue}let o=r.match(/^\s*(```+|~~~+)\s*([\w+-]*)/);if(o){let p=[];for(n++;n<e.length&&!e[n].trim().startsWith(o[1]);)p.push(e[n++]);n++;let d=f(i,"pre"),u=f(i,"code");o[2]&&(u.dataset.lang=o[2]),u.textContent=p.join(`
37
- `),d.appendChild(u),t.appendChild(d);continue}let a=r.match(/^(#{1,6})\s+(.*)$/);if(a){let p=`h${Math.min(6,a[1].length+2)}`,d=f(i,p);k(i,a[2],d),t.appendChild(d),n++;continue}if(/^\s*([-*_])(\s*\1){2,}\s*$/.test(r)){t.appendChild(f(i,"hr")),n++;continue}if(r.includes("|")&&n+1<e.length&&dt(e[n+1])){let p=f(i,"div","table"),d=f(i,"table"),u=f(i,"thead"),m=f(i,"tr");for(let x of gt(r)){let y=f(i,"th");k(i,x,y),m.appendChild(y)}u.appendChild(m),d.appendChild(u);let w=f(i,"tbody");for(n+=2;n<e.length&&e[n].includes("|")&&e[n].trim();){let x=f(i,"tr");for(let y of gt(e[n])){let M=f(i,"td");k(i,y,M),x.appendChild(M)}w.appendChild(x),n++}d.appendChild(w),p.appendChild(d),t.appendChild(p);continue}if(/^\s*>/.test(r)){let p=[];for(;n<e.length&&/^\s*>/.test(e[n]);)p.push(e[n++].replace(/^\s*>\s?/,""));let d=f(i,"blockquote");d.appendChild(E(i,p.join(`
38
- `))),t.appendChild(d);continue}let l=r.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(l){let p=/\d/.test(l[2]),d=f(i,p?"ol":"ul");p&&parseInt(l[2],10)!==1&&d.setAttribute("start",String(parseInt(l[2],10)));let u=l[1].length;for(;n<e.length;){let m=e[n].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(!m||m[1].length<u||/\d/.test(m[2])!==p)break;if(m[1].length>u){let x=[];for(;n<e.length&&/^\s+/.test(e[n])&&e[n].match(/^(\s*)/)[1].length>u;)x.push(e[n++].slice(u+2));d.lastElementChild?.appendChild(E(i,x.join(`
39
- `)));continue}let w=f(i,"li");k(i,m[3],w),d.appendChild(w),n++}t.appendChild(d);continue}let c=[];for(;n<e.length&&e[n].trim()&&!/^\s*(```|~~~|#{1,6}\s|>|([-*+]|\d+[.)])\s)/.test(e[n])&&!(e[n].includes("|")&&n+1<e.length&&dt(e[n+1]));)c.push(e[n++]);c.length||c.push(e[n++]);let g=f(i,"p");c.forEach((p,d)=>{d&&g.appendChild(f(i,"br")),k(i,p,g)}),t.appendChild(g)}return t}var pt=`
36
+ `),e=0;for(;e<n.length;){let r=n[e];if(!r.trim()){e++;continue}let o=r.match(/^\s*(```+|~~~+)\s*([\w+-]*)/);if(o){let g=[];for(e++;e<n.length&&!n[e].trim().startsWith(o[1]);)g.push(n[e++]);e++;let p=f(i,"pre"),u=f(i,"code");o[2]&&(u.dataset.lang=o[2]),u.textContent=g.join(`
37
+ `),p.appendChild(u),t.appendChild(p);continue}let a=r.match(/^(#{1,6})\s+(.*)$/);if(a){let g=`h${Math.min(6,a[1].length+2)}`,p=f(i,g);k(i,a[2],p),t.appendChild(p),e++;continue}if(/^\s*([-*_])(\s*\1){2,}\s*$/.test(r)){t.appendChild(f(i,"hr")),e++;continue}if(r.includes("|")&&e+1<n.length&&pt(n[e+1])){let g=f(i,"div","table"),p=f(i,"table"),u=f(i,"thead"),m=f(i,"tr");for(let b of gt(r)){let y=f(i,"th");k(i,b,y),m.appendChild(y)}u.appendChild(m),p.appendChild(u);let w=f(i,"tbody");for(e+=2;e<n.length&&n[e].includes("|")&&n[e].trim();){let b=f(i,"tr");for(let y of gt(n[e])){let M=f(i,"td");k(i,y,M),b.appendChild(M)}w.appendChild(b),e++}p.appendChild(w),g.appendChild(p),t.appendChild(g);continue}if(/^\s*>/.test(r)){let g=[];for(;e<n.length&&/^\s*>/.test(n[e]);)g.push(n[e++].replace(/^\s*>\s?/,""));let p=f(i,"blockquote");p.appendChild(S(i,g.join(`
38
+ `))),t.appendChild(p);continue}let c=r.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(c){let g=/\d/.test(c[2]),p=f(i,g?"ol":"ul");g&&parseInt(c[2],10)!==1&&p.setAttribute("start",String(parseInt(c[2],10)));let u=c[1].length;for(;e<n.length;){let m=n[e].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(!m||m[1].length<u||/\d/.test(m[2])!==g)break;if(m[1].length>u){let b=[];for(;e<n.length&&/^\s+/.test(n[e])&&n[e].match(/^(\s*)/)[1].length>u;)b.push(n[e++].slice(u+2));p.lastElementChild?.appendChild(S(i,b.join(`
39
+ `)));continue}let w=f(i,"li");k(i,m[3],w),p.appendChild(w),e++}t.appendChild(p);continue}let l=[];for(;e<n.length&&n[e].trim()&&!/^\s*(```|~~~|#{1,6}\s|>|([-*+]|\d+[.)])\s)/.test(n[e])&&!(n[e].includes("|")&&e+1<n.length&&pt(n[e+1]));)l.push(n[e++]);l.length||l.push(n[e++]);let d=f(i,"p");l.forEach((g,p)=>{p&&d.appendChild(f(i,"br")),k(i,g,d)}),t.appendChild(d)}return t}var ht=`
40
40
  :host {
41
41
  --dw-accent: #5446e8;
42
42
  --dw-accent-contrast: #fff;
@@ -301,8 +301,8 @@ textarea::placeholder { color: var(--dw-muted); }
301
301
  .agent-select { font-size: 16px; max-width: 200px; }
302
302
  }
303
303
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
304
- `;var mt=["show-reasoning","show-tool-calls","allow-agent-switch","open","persist","hide-branding","launcher-avatar"],ht=["endpoint","agent","title","subtitle","greeting","intro","placeholder","suggestions","avatar","position","mode","theme","accent-color","locale","gateway-url","gateway-token","width","height","z-index",...mt],ut=i=>i.replace(/-([a-z])/g,(s,t)=>t.toUpperCase()),Lt=i=>i!==null&&!/^(false|0|off|no)$/i.test(i),ft=()=>{let i=crypto.getRandomValues(new Uint8Array(9));return Array.from(i,s=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[s&63]).join("")},It=i=>i.replace(/^mcp_[^_]+(?:_[^_]+)*?__/,"").replace(/_/g," "),T={get(i){try{return localStorage.getItem(i)}catch{return null}},set(i,s){try{localStorage.setItem(i,s)}catch{}}},Rt=null;function $t(){return Rt??(Rt=new Promise((i,s)=>{if(window.turnstile)return i();let t=document.createElement("script");t.src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",t.async=!0,t.onload=()=>i(),t.onerror=()=>s(new Error("Could not load Turnstile")),document.head.appendChild(t)}))}var Nt=typeof HTMLElement>"u"?class{}:HTMLElement,L=class extends Nt{constructor(){super();h(this,"root");h(this,"overrides",{});h(this,"api",null);h(this,"apiKey","");h(this,"server",null);h(this,"agentId","");h(this,"conversationId","");h(this,"messages",[]);h(this,"running",null);h(this,"activated",!1);h(this,"strings",q());h(this,"mql",null);h(this,"registry",new $);h(this,"propActionNames",new Set);h(this,"hooks",{});h(this,"acks",new Map);h(this,"els");h(this,"onScheme",()=>this.applyTheme());h(this,"pendingFrame",0);h(this,"following",!0);this.root=this.attachShadow({mode:"open"})}static get observedAttributes(){return ht}get config(){let t={endpoint:"/api/dewee"};for(let e of ht){let n=this.getAttribute(e);n!==null&&(mt.includes(e)?t[ut(e)]=Lt(n):e==="suggestions"?t.suggestions=n.trim().startsWith("[")?Ht(n,[]):n.split("|").map(r=>r.trim()).filter(Boolean):e==="z-index"?t.zIndex=Number(n):t[ut(e)]=n)}return{...t,...this.overrides,...this.hooks}}configure(t){let{actions:e,confirm:n,getPageContext:r,...o}=t,a=l=>Object.prototype.hasOwnProperty.call(t,l);a("actions")&&this.setPropActions(e??[]),a("confirm")&&(this.hooks.confirm=n),a("getPageContext")&&(this.hooks.getPageContext=r),Object.keys(o).length&&(this.overrides={...this.overrides,...o},this.els&&this.applyConfig())}registerActions(t){this.registry.register(t)}unregisterAction(t){return this.propActionNames.delete(t),this.registry.unregister(t)}get actionNames(){return this.registry.names()}setPropActions(t){let e=new Set(t.map(n=>n.name));for(let n of this.propActionNames)e.has(n)||this.registry.unregister(n);t.length&&this.registry.register(t),this.propActionNames=e}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(t){let e=t.trim();if(!e||this.running)return;this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate(),this.els.input.value="",this.autosize();let n={message:e,hidden:!1};for(let r=0;n;r++){let o=await this.runTurn(n.message,n.hidden);if(n=null,o.status!=="done"||o.stopped||!this.registry.size)break;let{calls:a}=H(o.content);if(!a.length)break;o.actions=a.map(g=>({id:g.id,name:g.name,args:g.ok?g.args:void 0,status:"pending"}));let l=this.config.maxActionRounds??5;if(r>=l){for(let g of o.actions)Object.assign(g,{status:"error",error:"Automatic action limit reached"});this.updateMessage(o),this.showNotice(this.strings.actionLimit(l));break}this.updateMessage(o);let c=await this.runActions(o,a);c&&(n={message:et(c,this.server?.maxMessageLength??4e3),hidden:!0})}}async runTurn(t,e){this.pushMessage({role:"user",content:t,hidden:e});let n={role:"assistant",content:"",status:"streaming",startedAt:Date.now()};this.pushMessage(n),this.setRunning(new AbortController),this.emit("dewee:send",{message:t,agent:this.agentId,conversationId:this.conversationId,auto:e||void 0});try{await this.client().chat(this.chatBody(t),r=>this.onStreamEvent(n,r),this.running.signal),n.status==="streaming"&&(n.status="done")}catch(r){r.name==="AbortError"?(n.status="done",n.stopped=!0,n.content||(n.content=`_${this.strings.stopped}_`)):(n.status="error",this.showError(n,r))}finally{this.finishThinking(n),this.updateMessage(n),this.setRunning(null),n.status==="done"&&(n.actionsHash&&!n.stopped&&this.setAck(n.actionsHash),this.emit("dewee:reply",{content:n.content,agent:this.agentId,conversationId:this.conversationId}),this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.setAttribute("data-unread",""))}return n}chatBody(t){let e={message:t,agent:this.agentId,conversationId:this.conversationId};if(this.registry.size){e.actions=this.registry.manifest();let r=this.getAck();r&&(e.actionsHash=r)}let n=this.pageContext();return n!==void 0&&(e.pageContext=n),e}pageContext(){let t=this.hooks.getPageContext;if(t)try{let e=t();if(e==null)return;let n=new TextEncoder().encode(JSON.stringify(e)).length;return n<=C.maxPageContextBytes?e:(console.warn(`[dewee-webchat] getPageContext() returned ${n} bytes; the limit is ${C.maxPageContextBytes}. Context not sent.`),{truncated:!0})}catch(e){console.warn("[dewee-webchat] getPageContext() failed:",e);return}}ackKey(){return`dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${this.conversationId}`}getAck(){return this.acks.get(this.ackKey())??(this.config.persist!==!1?T.get(this.ackKey())??void 0:void 0)}setAck(t){this.acks.set(this.ackKey(),t),this.config.persist!==!1&&T.set(this.ackKey(),t)}async runActions(t,e){let n=new AbortController;this.setRunning(n);let r=[],o={agent:this.agentId,conversationId:this.conversationId};try{for(let[a,l]of e.entries()){let c=t.actions[a],g=l.ok?this.registry.get(l.name)?.sensitivity:void 0,p=l.ok?l.args:void 0;this.emit("dewee:action",{phase:"start",id:l.id,name:l.name,sensitivity:g,args:p,...o});let d=await ot(l,{registry:this.registry,confirm:(u,m)=>this.confirmAction(u,m,n.signal),signal:n.signal,timeoutMs:this.config.actionTimeoutMs,...o,onStatus:u=>{c.status=u,this.updateMessage(t)}});d.error&&(c.error=d.error),this.updateMessage(t),r.push(d),this.emit("dewee:action",{phase:"result",id:d.id,name:d.name,sensitivity:g,args:p,status:d.status,result:d.result,error:d.error,...o})}}finally{this.setRunning(null)}return n.signal.aborted?null:r}confirmAction(t,e,n){return this.hooks.confirm?this.hooks.confirm(t,e):(this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),ct(this.els.panel,this.strings,t,e,n))}showNotice(t){let e=document.createElement("div");e.className="notice",e.setAttribute("role","status"),e.textContent=t,this.els.log.appendChild(e),this.scrollToBottom(!0)}stop(){this.running?.abort()}newConversation(){this.stop(),this.conversationId=ft(),this.config.persist!==!1&&T.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.stop(),this.mql?.removeEventListener("change",this.onScheme)}attributeChangedCallback(){this.els&&this.applyConfig()}emit(t,e){this.dispatchEvent(new CustomEvent(t,{detail:e,bubbles:!0,composed:!0}))}client(){let t=this.config,e=[t.endpoint,t.gatewayUrl,t.gatewayToken].join("|");return(!this.api||e!==this.apiKey)&&(this.apiKey=e,this.api=new S({endpoint:t.endpoint,gatewayUrl:t.gatewayUrl,gatewayToken:t.gatewayToken,turnstile:n=>this.challenge(n)})),this.api}convKey(){return`dewee-webchat:conv:${this.config.endpoint}:${this.config.gatewayUrl||""}:${this.agentId}`}applyTheme(){let t=this.config.theme||"light",e=t==="dark"||t==="auto"&&!!this.mql?.matches;this.setAttribute("data-theme",e?"dark":"light")}applyConfig(){let t=this.config;this.strings=q(t.locale);let e=this.strings;this.setAttribute("data-position",t.position||"bottom-right"),this.setAttribute("data-mode",t.mode||"floating"),t.theme==="auto"&&!this.mql&&(this.mql=matchMedia("(prefers-color-scheme: dark)"),this.mql.addEventListener("change",this.onScheme)),this.applyTheme();let n=(o,a)=>a!==void 0&&a!==""?this.style.setProperty(o,String(a)):this.style.removeProperty(o);n("--dw-accent",t.accentColor),n("--dw-width",t.width),n("--dw-height",t.height),n("--dw-z",t.zIndex);let r=this.els;if(r.title.textContent=t.title||e.title,r.avatar.replaceChildren(),t.avatar){let o=document.createElement("img");o.src=t.avatar,o.alt="",r.avatar.appendChild(o)}else r.avatar.textContent=(t.title||e.title).trim().charAt(0).toUpperCase();if(r.launcher.innerHTML=t.avatar&&t.launcherAvatar?"":b.chat,t.avatar&&t.launcherAvatar){let o=document.createElement("img");o.src=t.avatar,o.alt="",r.launcher.appendChild(o)}r.launcher.insertAdjacentHTML("beforeend",'<span class="badge"></span>'),r.launcher.setAttribute("aria-label",e.open),r.close.setAttribute("aria-label",e.close),r.close.title=e.close,r.newChat.setAttribute("aria-label",e.newChat),r.newChat.title=e.newChat,r.input.placeholder=t.placeholder||e.placeholder,r.foot.hidden=!!t.hideBranding,r.foot.querySelector(".by").textContent=e.poweredBy+" ",this.renderSubtitle(),this.updateSendButton(),this.activated&&this.api&&this.apiKey!==[t.endpoint,t.gatewayUrl,t.gatewayToken].join("|")?(this.activated=!1,this.server=null,this.activate()):this.activated&&t.agent&&t.agent!==this.agentId&&this.server?this.switchAgent(t.agent):this.activated&&this.renderLog()}renderShell(){this.root.innerHTML=`
305
- <style>${pt}</style>
304
+ `;var wt=["show-reasoning","show-tool-calls","allow-agent-switch","open","persist","hide-branding","launcher-avatar"],ut=["endpoint","agent","title","subtitle","greeting","intro","placeholder","suggestions","avatar","position","mode","theme","accent-color","locale","gateway-url","gateway-token","width","height","z-index",...wt],ft=i=>i.replace(/-([a-z])/g,(s,t)=>t.toUpperCase()),Lt=i=>i!==null&&!/^(false|0|off|no)$/i.test(i),mt=()=>{let i=crypto.getRandomValues(new Uint8Array(9));return Array.from(i,s=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[s&63]).join("")},It=i=>i.replace(/^mcp_[^_]+(?:_[^_]+)*?__/,"").replace(/_/g," "),T={get(i){try{return localStorage.getItem(i)}catch{return null}},set(i,s){try{localStorage.setItem(i,s)}catch{}}},$t=null;function Ht(){return $t??($t=new Promise((i,s)=>{if(window.turnstile)return i();let t=document.createElement("script");t.src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",t.async=!0,t.onload=()=>i(),t.onerror=()=>s(new Error("Could not load Turnstile")),document.head.appendChild(t)}))}var Nt=typeof HTMLElement>"u"?class{}:HTMLElement,P=class extends Nt{constructor(){super();h(this,"root");h(this,"overrides",{});h(this,"api",null);h(this,"apiKey","");h(this,"server",null);h(this,"agentId","");h(this,"conversationId","");h(this,"messages",[]);h(this,"running",null);h(this,"activated",!1);h(this,"strings",U());h(this,"mql",null);h(this,"registry",new $);h(this,"propActionNames",new Set);h(this,"hooks",{});h(this,"acks",new Map);h(this,"els");h(this,"onScheme",()=>this.applyTheme());h(this,"pendingFrame",0);h(this,"following",!0);this.root=this.attachShadow({mode:"open"})}static get observedAttributes(){return ut}get config(){let t={endpoint:"/api/dewee"};for(let n of ut){let e=this.getAttribute(n);e!==null&&(wt.includes(n)?t[ft(n)]=Lt(e):n==="suggestions"?t.suggestions=e.trim().startsWith("[")?Ot(e,[]):e.split("|").map(r=>r.trim()).filter(Boolean):n==="z-index"?t.zIndex=Number(e):t[ft(n)]=e)}return{...t,...this.overrides,...this.hooks}}configure(t){let{actions:n,confirm:e,getPageContext:r,headers:o,fetch:a,...c}=t,l=d=>Object.prototype.hasOwnProperty.call(t,d);l("actions")&&this.setPropActions(n??[]),l("confirm")&&(this.hooks.confirm=e),l("getPageContext")&&(this.hooks.getPageContext=r),l("headers")&&(this.hooks.headers=o),l("fetch")&&(this.hooks.fetch=a),Object.keys(c).length&&(this.overrides={...this.overrides,...c},this.els&&this.applyConfig())}registerActions(t){this.registry.register(t)}unregisterAction(t){return this.propActionNames.delete(t),this.registry.unregister(t)}get headers(){return this.hooks.headers}set headers(t){this.hooks.headers=t}get fetch(){return this.hooks.fetch}set fetch(t){this.hooks.fetch=t}get actionNames(){return this.registry.names()}setPropActions(t){let n=new Set(t.map(e=>e.name));for(let e of this.propActionNames)n.has(e)||this.registry.unregister(e);t.length&&this.registry.register(t),this.propActionNames=n}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(t){let n=t.trim();if(!n||this.running)return;this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate(),this.els.input.value="",this.autosize();let e={message:n,hidden:!1};for(let r=0;e;r++){let o=await this.runTurn(e.message,e.hidden);if(e=null,o.status!=="done"||o.stopped||!this.registry.size)break;let{calls:a}=_(o.content);if(!a.length)break;o.actions=a.map(d=>({id:d.id,name:d.name,args:d.ok?d.args:void 0,status:"pending"}));let c=this.config.maxActionRounds??5;if(r>=c){for(let d of o.actions)Object.assign(d,{status:"error",error:"Automatic action limit reached"});this.updateMessage(o),this.showNotice(this.strings.actionLimit(c));break}this.updateMessage(o);let l=await this.runActions(o,a);l&&(e={message:st(l,this.server?.maxMessageLength??4e3),hidden:!0})}}async runTurn(t,n){this.pushMessage({role:"user",content:t,hidden:n});let e={role:"assistant",content:"",status:"streaming",startedAt:Date.now()};this.pushMessage(e),this.setRunning(new AbortController),this.emit("dewee:send",{message:t,agent:this.agentId,conversationId:this.conversationId,auto:n||void 0});try{await this.client().chat(this.chatBody(t),r=>this.onStreamEvent(e,r),this.running.signal),e.status==="streaming"&&(e.status="done")}catch(r){r.name==="AbortError"?(e.status="done",e.stopped=!0,e.content||(e.content=`_${this.strings.stopped}_`)):(e.status="error",this.showError(e,r))}finally{this.finishThinking(e),this.updateMessage(e),this.setRunning(null),e.status==="done"&&(e.actionsHash&&!e.stopped&&this.setAck(e.actionsHash),this.emit("dewee:reply",{content:e.content,agent:this.agentId,conversationId:this.conversationId}),this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.setAttribute("data-unread",""))}return e}chatBody(t){let n={message:t,agent:this.agentId,conversationId:this.conversationId};if(this.registry.size){n.actions=this.registry.manifest();let r=this.getAck();r&&(n.actionsHash=r)}let e=this.pageContext();return e!==void 0&&(n.pageContext=e),n}pageContext(){let t=this.hooks.getPageContext;if(t)try{let n=t();if(n==null)return;let e=new TextEncoder().encode(JSON.stringify(n)).length;return e<=C.maxPageContextBytes?n:(console.warn(`[dewee-webchat] getPageContext() returned ${e} bytes; the limit is ${C.maxPageContextBytes}. Context not sent.`),{truncated:!0})}catch(n){console.warn("[dewee-webchat] getPageContext() failed:",n);return}}ackKey(){return`dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${this.conversationId}`}getAck(){return this.acks.get(this.ackKey())??(this.config.persist!==!1?T.get(this.ackKey())??void 0:void 0)}setAck(t){this.acks.set(this.ackKey(),t),this.config.persist!==!1&&T.set(this.ackKey(),t)}async runActions(t,n){let e=new AbortController;this.setRunning(e);let r=[],o={agent:this.agentId,conversationId:this.conversationId};try{for(let[a,c]of n.entries()){let l=t.actions[a],d=c.ok?this.registry.get(c.name)?.sensitivity:void 0,g=c.ok?c.args:void 0;this.emit("dewee:action",{phase:"start",id:c.id,name:c.name,sensitivity:d,args:g,...o});let p=await ct(c,{registry:this.registry,confirm:(u,m)=>this.confirmAction(u,m,e.signal),signal:e.signal,timeoutMs:this.config.actionTimeoutMs,...o,onStatus:u=>{l.status=u,this.updateMessage(t)}});p.error&&(l.error=p.error),this.updateMessage(t),r.push(p),this.emit("dewee:action",{phase:"result",id:p.id,name:p.name,sensitivity:d,args:g,status:p.status,result:p.result,error:p.error,...o})}}finally{this.setRunning(null)}return e.signal.aborted?null:r}confirmAction(t,n,e){return this.hooks.confirm?this.hooks.confirm(t,n):(this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),dt(this.els.panel,this.strings,t,n,e))}showNotice(t){let n=document.createElement("div");n.className="notice",n.setAttribute("role","status"),n.textContent=t,this.els.log.appendChild(n),this.scrollToBottom(!0)}stop(){this.running?.abort()}newConversation(){this.stop(),this.conversationId=mt(),this.config.persist!==!1&&T.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.stop(),this.mql?.removeEventListener("change",this.onScheme)}attributeChangedCallback(){this.els&&this.applyConfig()}emit(t,n){this.dispatchEvent(new CustomEvent(t,{detail:n,bubbles:!0,composed:!0}))}client(){let t=this.config,n=[t.endpoint,t.gatewayUrl,t.gatewayToken].join("|");return(!this.api||n!==this.apiKey)&&(this.apiKey=n,this.api=new E({endpoint:t.endpoint,gatewayUrl:t.gatewayUrl,gatewayToken:t.gatewayToken,turnstile:e=>this.challenge(e),headers:()=>{let e=this.hooks.headers;return(typeof e=="function"?e():e)??{}},fetch:(e,r)=>(this.hooks.fetch??globalThis.fetch)(e,r)})),this.api}convKey(){return`dewee-webchat:conv:${this.config.endpoint}:${this.config.gatewayUrl||""}:${this.agentId}`}applyTheme(){let t=this.config.theme||"light",n=t==="dark"||t==="auto"&&!!this.mql?.matches;this.setAttribute("data-theme",n?"dark":"light")}applyConfig(){let t=this.config;this.strings=U(t.locale);let n=this.strings;this.setAttribute("data-position",t.position||"bottom-right"),this.setAttribute("data-mode",t.mode||"floating"),t.theme==="auto"&&!this.mql&&(this.mql=matchMedia("(prefers-color-scheme: dark)"),this.mql.addEventListener("change",this.onScheme)),this.applyTheme();let e=(o,a)=>a!==void 0&&a!==""?this.style.setProperty(o,String(a)):this.style.removeProperty(o);e("--dw-accent",t.accentColor),e("--dw-width",t.width),e("--dw-height",t.height),e("--dw-z",t.zIndex);let r=this.els;if(r.title.textContent=t.title||n.title,r.avatar.replaceChildren(),t.avatar){let o=document.createElement("img");o.src=t.avatar,o.alt="",r.avatar.appendChild(o)}else r.avatar.textContent=(t.title||n.title).trim().charAt(0).toUpperCase();if(r.launcher.innerHTML=t.avatar&&t.launcherAvatar?"":x.chat,t.avatar&&t.launcherAvatar){let o=document.createElement("img");o.src=t.avatar,o.alt="",r.launcher.appendChild(o)}r.launcher.insertAdjacentHTML("beforeend",'<span class="badge"></span>'),r.launcher.setAttribute("aria-label",n.open),r.close.setAttribute("aria-label",n.close),r.close.title=n.close,r.newChat.setAttribute("aria-label",n.newChat),r.newChat.title=n.newChat,r.input.placeholder=t.placeholder||n.placeholder,r.foot.hidden=!!t.hideBranding,r.foot.querySelector(".by").textContent=n.poweredBy+" ",this.renderSubtitle(),this.updateSendButton(),this.activated&&this.api&&this.apiKey!==[t.endpoint,t.gatewayUrl,t.gatewayToken].join("|")?(this.activated=!1,this.server=null,this.activate()):this.activated&&t.agent&&t.agent!==this.agentId&&this.server?this.switchAgent(t.agent):this.activated&&this.renderLog()}renderShell(){this.root.innerHTML=`
305
+ <style>${ht}</style>
306
306
  <button class="launcher" part="launcher" aria-expanded="false" aria-haspopup="dialog"></button>
307
307
  <section class="panel" part="panel" role="dialog" aria-label="Chat">
308
308
  <header class="head" part="header">
@@ -311,8 +311,8 @@ textarea::placeholder { color: var(--dw-muted); }
311
311
  <div class="title"></div>
312
312
  <div class="subtitle"><span class="dot"></span><span class="status"></span><select class="agent-select" hidden></select></div>
313
313
  </div>
314
- <button class="icon-btn new">${b.newChat}</button>
315
- <button class="icon-btn close">${b.close}</button>
314
+ <button class="icon-btn new">${x.newChat}</button>
315
+ <button class="icon-btn close">${x.close}</button>
316
316
  </header>
317
317
  <div class="log" part="log" role="log" aria-live="polite"></div>
318
318
  <div class="challenge"><slot name="challenge"></slot></div>
@@ -323,7 +323,7 @@ textarea::placeholder { color: var(--dw-muted); }
323
323
  </div>
324
324
  <div class="foot"><span><span class="by"></span><a href="https://dewee.sh" target="_blank" rel="noopener">dewee</a></span><span class="meta"></span></div>
325
325
  </form>
326
- </section>`;let t=n=>this.root.querySelector(n);this.els={launcher:t(".launcher"),panel:t(".panel"),log:t(".log"),title:t(".title"),subtitle:t(".subtitle"),status:t(".status"),avatar:t(".avatar"),select:t(".agent-select"),input:t("textarea"),send:t(".send"),newChat:t(".new"),close:t(".close"),foot:t(".foot")};let e=this.els;e.launcher.addEventListener("click",()=>this.toggle()),e.close.addEventListener("click",()=>this.close()),e.log.addEventListener("scroll",()=>{this.following=this.nearBottom()},{passive:!0}),e.newChat.addEventListener("click",()=>this.newConversation()),e.select.addEventListener("change",()=>this.switchAgent(e.select.value)),t("form").addEventListener("submit",n=>{n.preventDefault(),this.running?this.stop():this.send(e.input.value)}),e.input.addEventListener("keydown",n=>{n.key==="Enter"&&!n.shiftKey&&!n.isComposing&&n.keyCode!==229&&(n.preventDefault(),this.running||this.send(e.input.value))}),e.input.addEventListener("input",()=>{this.autosize(),this.updateSendButton()}),e.panel.addEventListener("keydown",n=>{n.key==="Escape"&&this.close()})}autosize(){let t=this.els.input;t.style.height="auto",t.style.height=Math.min(t.scrollHeight,140)+"px"}updateSendButton(){let t=this.els.send;t.innerHTML=this.running?b.stop:b.send,t.setAttribute("aria-label",this.running?this.strings.stop:this.strings.send),t.title=t.getAttribute("aria-label"),t.disabled=!this.running&&!this.els.input.value.trim()}setRunning(t){this.running=t,this.updateSendButton()}renderSubtitle(){let t=this.els,e=this.config,n=this.server?.agents??[],r=e.allowAgentSwitch!==!1&&!!this.server?.allowAgentSwitch&&n.length>1;t.select.hidden=!r,t.status.hidden=r,r&&(t.select.replaceChildren(...n.map(a=>new Option(a.name||a.id,a.id,!1,a.id===this.agentId))),t.select.setAttribute("aria-label","Agent"));let o=n.find(a=>a.id===this.agentId);t.status.textContent=e.subtitle||(o?.name&&o.name!==e.title?`${this.strings.online} \xB7 ${o.name}`:this.strings.online)}async activate(){if(!this.activated){this.activated=!0,this.renderLog();try{this.server=await this.client().getConfig(!0),this.els.input.maxLength=this.server.maxMessageLength;let t=this.config.agent,e=n=>!!n&&(this.server.agents.length===0||this.server.agents.some(r=>r.id===n));this.agentId=e(t)?t:this.server.defaultAgent||this.server.agents[0]?.id||t||"",await this.loadConversation()}catch(t){this.activated=!1,this.renderLog(),this.showError(null,t)}this.renderSubtitle(),this.emit("dewee:ready",{agent:this.agentId,config:this.server})}}async switchAgent(t){t!==this.agentId&&(this.stop(),this.agentId=t,this.renderSubtitle(),await this.loadConversation())}async loadConversation(){let t=this.config.persist!==!1,e=t?T.get(this.convKey()):null;if(this.conversationId=e||ft(),t&&!e&&T.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),!!e)try{let n=await this.client().history(this.agentId,this.conversationId);this.messages=_t(n.map(r=>({...r,status:"done"}))),this.renderLog()}catch{}}renderLog(){let t=this.els.log;t.replaceChildren(),this.messages.length||t.appendChild(this.renderWelcome());for(let e of this.messages)t.appendChild(this.renderMessage(e));t.scrollTop=t.scrollHeight}renderWelcome(){let t=this.config,e=this.strings,n=document.createElement("div");n.className="welcome";let r=document.createElement("h2");r.textContent=t.greeting||e.greeting;let o=document.createElement("p");if(o.textContent=t.intro||e.greetingBody,n.append(r,o),t.suggestions?.length){let a=document.createElement("div");a.className="suggestions";for(let l of t.suggestions){let c=document.createElement("button");c.className="chip",c.type="button",c.textContent=l,c.addEventListener("click",()=>this.send(l)),a.appendChild(c)}n.appendChild(a)}return n}pushMessage(t){this.messages.length||this.els.log.replaceChildren(),this.messages.push(t),this.els.log.appendChild(this.renderMessage(t)),this.scrollToBottom(!0)}renderMessage(t){let e=document.createElement("div");e.className=`msg ${t.role}${t.status==="error"?" error":""}`,e.hidden=!!t.hidden;let n=document.createElement("div");n.className="bubble",e.appendChild(n);let r=document.createElement("div");r.className="trace";let o=document.createElement("div");o.className=t.role==="assistant"?"md":"";let a=document.createElement("div");return a.className="actions",a.hidden=!0,t.role==="assistant"&&n.appendChild(r),n.appendChild(o),t.role==="assistant"&&n.appendChild(a),t.view={root:e,trace:r,md:o,tools:new Map,actions:a},this.updateMessage(t),e}updateMessage(t){let e=t.view;if(!e)return;let n=this.config;if(t.role==="user"){e.md.textContent=t.content;return}if(e.root.classList.toggle("error",t.status==="error"),n.showReasoning!==!1&&t.thinking){e.thinking||(e.thinking=document.createElement("details"),e.thinking.className="thinking",e.thinking.innerHTML='<summary></summary><div class="thinking-body"></div>',e.trace.prepend(e.thinking));let a=t.status==="streaming"&&!t.thinkingMs,l=a?this.strings.thinking:t.thinkingMs?this.strings.thoughtFor(Math.max(1,Math.round(t.thinkingMs/1e3))):this.strings.reasoning,c=e.thinking.querySelector("summary");c.innerHTML=`${a?'<span class="spin"></span>':b.brain}<span></span>${b.down.replace("<svg",'<svg class="chev"')}`,c.querySelector("span:not(.spin)").textContent=l,e.thinking.querySelector(".thinking-body").textContent=t.thinking}else e.thinking&&(e.thinking.remove(),e.thinking=void 0);if(n.showToolCalls!==!1)for(let a of t.toolCalls??[])this.renderTool(e,a);else e.tools.forEach(a=>a.remove()),e.tools.clear();e.trace.hidden=!e.trace.childElementCount;let r=this.registry.size||t.actions?X(t.content):t.content,o=t.status==="streaming"&&!r;e.md.hidden=!o&&!r&&!!t.actions?.length,o?e.md.innerHTML=`<span class="typing" aria-label="${this.strings.thinking}"><i></i><i></i><i></i></span>`:(e.md.replaceChildren(E(document,r)),e.md.classList.toggle("cursor",t.status==="streaming")),this.renderActions(e,t.actions??[])}renderActions(t,e){t.actions.hidden=!e.length,t.actions.replaceChildren(...e.map(n=>{let r=document.createElement("div");r.className="action",r.dataset.status=n.status;let o=n.status==="running"||n.status==="awaiting-confirmation";r.innerHTML=`${o?'<span class="spin"></span>':b.action}<span class="action-name"></span><span class="action-state"></span>`,r.querySelector(".action-name").textContent=n.name||"?",r.querySelector(".action-state").textContent=this.strings.actionStatus[n.status];let a=[n.args&&Object.keys(n.args).length?JSON.stringify(n.args):"",n.error??""].filter(Boolean).join(`
327
- `);return a&&(r.title=a),r}))}renderTool(t,e){let n=t.tools.get(e.id);n||(n=document.createElement("details"),n.className="tool",n.innerHTML=`<summary><span class="lead"></span><span class="tool-name"></span><span class="tool-state"></span>${b.down.replace("<svg",'<svg class="chev"')}</summary><div class="tool-body"></div>`,t.tools.set(e.id,n),t.trace.appendChild(n)),n.dataset.status=e.status,n.querySelector(".lead").innerHTML=e.status==="running"?'<span class="spin"></span>':b.tool;let r=n.querySelector(".tool-name");r.textContent=It(e.name),r.setAttribute("title",e.name),n.querySelector(".tool-state").textContent=e.status==="running"?this.strings.toolRunning:e.status==="error"?this.strings.toolError:this.strings.toolDone;let o=n.querySelector(".tool-body");o.replaceChildren();let a=(l,c)=>{if(c==null||c==="")return;let g=document.createElement("div");g.className="label",g.textContent=l;let p=document.createElement("pre");p.textContent=typeof c=="string"?Bt(c):JSON.stringify(c,null,2),o.append(g,p)};a(this.strings.args,e.arguments),a(this.strings.result,e.result)}onStreamEvent(t,e){switch(e.type){case"start":e.actionsHash&&(t.actionsHash=e.actionsHash),e.conversationId&&e.conversationId!==this.conversationId&&(this.conversationId=e.conversationId,this.config.persist!==!1&&T.set(this.convKey(),e.conversationId));break;case"thinking":t.thinking=(t.thinking||"")+e.text;break;case"delta":this.finishThinking(t),t.content+=e.text;break;case"tool_call":this.finishThinking(t),(t.toolCalls??(t.toolCalls=[])).push({id:e.id,name:e.name,arguments:e.arguments,status:"running"});break;case"tool_result":{let n=t.toolCalls?.find(r=>r.id===e.id)??(t.toolCalls??(t.toolCalls=[]))[t.toolCalls.push({id:e.id,name:e.name,status:"running"})-1];n.status=e.isError?"error":"done",n.result=e.result,n.isError=e.isError;break}case"done":this.finishThinking(t),e.content&&e.content.length>=t.content.length&&(t.content=e.content),t.status="done";break;case"error":t.status="error",this.showError(t,new v(e.message,500,e.code));break}this.scheduleUpdate(t)}scheduleUpdate(t){this.pendingFrame||(this.pendingFrame=requestAnimationFrame(()=>{this.pendingFrame=0,this.updateMessage(t),this.following&&this.scrollToBottom(!0)}))}finishThinking(t){t.thinking&&!t.thinkingMs&&t.startedAt&&(t.thinkingMs=Date.now()-t.startedAt)}nearBottom(){let t=this.els.log;return t.scrollHeight-t.scrollTop-t.clientHeight<96}scrollToBottom(t=!1){let e=this.els.log;(t||this.nearBottom())&&(e.scrollTop=e.scrollHeight,this.following=!0)}showError(t,e){let n=e.message||this.strings.errorGeneric;if(this.emit("dewee:error",{message:n,code:e.code}),t){t.content=t.content?`${t.content}
326
+ </section>`;let t=e=>this.root.querySelector(e);this.els={launcher:t(".launcher"),panel:t(".panel"),log:t(".log"),title:t(".title"),subtitle:t(".subtitle"),status:t(".status"),avatar:t(".avatar"),select:t(".agent-select"),input:t("textarea"),send:t(".send"),newChat:t(".new"),close:t(".close"),foot:t(".foot")};let n=this.els;n.launcher.addEventListener("click",()=>this.toggle()),n.close.addEventListener("click",()=>this.close()),n.log.addEventListener("scroll",()=>{this.following=this.nearBottom()},{passive:!0}),n.newChat.addEventListener("click",()=>this.newConversation()),n.select.addEventListener("change",()=>this.switchAgent(n.select.value)),t("form").addEventListener("submit",e=>{e.preventDefault(),this.running?this.stop():this.send(n.input.value)}),n.input.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&!e.isComposing&&e.keyCode!==229&&(e.preventDefault(),this.running||this.send(n.input.value))}),n.input.addEventListener("input",()=>{this.autosize(),this.updateSendButton()}),n.panel.addEventListener("keydown",e=>{e.key==="Escape"&&this.close()})}autosize(){let t=this.els.input;t.style.height="auto",t.style.height=Math.min(t.scrollHeight,140)+"px"}updateSendButton(){let t=this.els.send;t.innerHTML=this.running?x.stop:x.send,t.setAttribute("aria-label",this.running?this.strings.stop:this.strings.send),t.title=t.getAttribute("aria-label"),t.disabled=!this.running&&!this.els.input.value.trim()}setRunning(t){this.running=t,this.updateSendButton()}renderSubtitle(){let t=this.els,n=this.config,e=this.server?.agents??[],r=n.allowAgentSwitch!==!1&&!!this.server?.allowAgentSwitch&&e.length>1;t.select.hidden=!r,t.status.hidden=r,r&&(t.select.replaceChildren(...e.map(a=>new Option(a.name||a.id,a.id,!1,a.id===this.agentId))),t.select.setAttribute("aria-label","Agent"));let o=e.find(a=>a.id===this.agentId);t.status.textContent=n.subtitle||(o?.name&&o.name!==n.title?`${this.strings.online} \xB7 ${o.name}`:this.strings.online)}async activate(){if(!this.activated){this.activated=!0,this.renderLog();try{this.server=await this.client().getConfig(!0),this.els.input.maxLength=this.server.maxMessageLength;let t=this.config.agent,n=e=>!!e&&(this.server.agents.length===0||this.server.agents.some(r=>r.id===e));this.agentId=n(t)?t:this.server.defaultAgent||this.server.agents[0]?.id||t||"",await this.loadConversation()}catch(t){this.activated=!1,this.renderLog(),this.showError(null,t)}this.renderSubtitle(),this.emit("dewee:ready",{agent:this.agentId,config:this.server})}}async switchAgent(t){t!==this.agentId&&(this.stop(),this.agentId=t,this.renderSubtitle(),await this.loadConversation())}async loadConversation(){let t=this.config.persist!==!1,n=t?T.get(this.convKey()):null;if(this.conversationId=n||mt(),t&&!n&&T.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),!!n)try{let e=await this.client().history(this.agentId,this.conversationId);this.messages=_t(e.map(r=>({...r,status:"done"}))),this.renderLog()}catch{}}renderLog(){let t=this.els.log;t.replaceChildren(),this.messages.length||t.appendChild(this.renderWelcome());for(let n of this.messages)t.appendChild(this.renderMessage(n));t.scrollTop=t.scrollHeight}renderWelcome(){let t=this.config,n=this.strings,e=document.createElement("div");e.className="welcome";let r=document.createElement("h2");r.textContent=t.greeting||n.greeting;let o=document.createElement("p");if(o.textContent=t.intro||n.greetingBody,e.append(r,o),t.suggestions?.length){let a=document.createElement("div");a.className="suggestions";for(let c of t.suggestions){let l=document.createElement("button");l.className="chip",l.type="button",l.textContent=c,l.addEventListener("click",()=>this.send(c)),a.appendChild(l)}e.appendChild(a)}return e}pushMessage(t){this.messages.length||this.els.log.replaceChildren(),this.messages.push(t),this.els.log.appendChild(this.renderMessage(t)),this.scrollToBottom(!0)}renderMessage(t){let n=document.createElement("div");n.className=`msg ${t.role}${t.status==="error"?" error":""}`,n.hidden=!!t.hidden;let e=document.createElement("div");e.className="bubble",n.appendChild(e);let r=document.createElement("div");r.className="trace";let o=document.createElement("div");o.className=t.role==="assistant"?"md":"";let a=document.createElement("div");return a.className="actions",a.hidden=!0,t.role==="assistant"&&e.appendChild(r),e.appendChild(o),t.role==="assistant"&&e.appendChild(a),t.view={root:n,trace:r,md:o,tools:new Map,actions:a},this.updateMessage(t),n}updateMessage(t){let n=t.view;if(!n)return;let e=this.config;if(t.role==="user"){n.md.textContent=t.content;return}if(n.root.classList.toggle("error",t.status==="error"),e.showReasoning!==!1&&t.thinking){n.thinking||(n.thinking=document.createElement("details"),n.thinking.className="thinking",n.thinking.innerHTML='<summary></summary><div class="thinking-body"></div>',n.trace.prepend(n.thinking));let a=t.status==="streaming"&&!t.thinkingMs,c=a?this.strings.thinking:t.thinkingMs?this.strings.thoughtFor(Math.max(1,Math.round(t.thinkingMs/1e3))):this.strings.reasoning,l=n.thinking.querySelector("summary");l.innerHTML=`${a?'<span class="spin"></span>':x.brain}<span></span>${x.down.replace("<svg",'<svg class="chev"')}`,l.querySelector("span:not(.spin)").textContent=c,n.thinking.querySelector(".thinking-body").textContent=t.thinking}else n.thinking&&(n.thinking.remove(),n.thinking=void 0);if(e.showToolCalls!==!1)for(let a of t.toolCalls??[])this.renderTool(n,a);else n.tools.forEach(a=>a.remove()),n.tools.clear();n.trace.hidden=!n.trace.childElementCount;let r=this.registry.size||t.actions?nt(t.content):t.content,o=t.status==="streaming"&&!r;n.md.hidden=!o&&!r&&!!t.actions?.length,o?n.md.innerHTML=`<span class="typing" aria-label="${this.strings.thinking}"><i></i><i></i><i></i></span>`:(n.md.replaceChildren(S(document,r)),n.md.classList.toggle("cursor",t.status==="streaming")),this.renderActions(n,t.actions??[])}renderActions(t,n){t.actions.hidden=!n.length,t.actions.replaceChildren(...n.map(e=>{let r=document.createElement("div");r.className="action",r.dataset.status=e.status;let o=e.status==="running"||e.status==="awaiting-confirmation";r.innerHTML=`${o?'<span class="spin"></span>':x.action}<span class="action-name"></span><span class="action-state"></span>`,r.querySelector(".action-name").textContent=e.name||"?",r.querySelector(".action-state").textContent=this.strings.actionStatus[e.status];let a=[e.args&&Object.keys(e.args).length?JSON.stringify(e.args):"",e.error??""].filter(Boolean).join(`
327
+ `);return a&&(r.title=a),r}))}renderTool(t,n){let e=t.tools.get(n.id);e||(e=document.createElement("details"),e.className="tool",e.innerHTML=`<summary><span class="lead"></span><span class="tool-name"></span><span class="tool-state"></span>${x.down.replace("<svg",'<svg class="chev"')}</summary><div class="tool-body"></div>`,t.tools.set(n.id,e),t.trace.appendChild(e)),e.dataset.status=n.status,e.querySelector(".lead").innerHTML=n.status==="running"?'<span class="spin"></span>':x.tool;let r=e.querySelector(".tool-name");r.textContent=It(n.name),r.setAttribute("title",n.name),e.querySelector(".tool-state").textContent=n.status==="running"?this.strings.toolRunning:n.status==="error"?this.strings.toolError:this.strings.toolDone;let o=e.querySelector(".tool-body");o.replaceChildren();let a=(c,l)=>{if(l==null||l==="")return;let d=document.createElement("div");d.className="label",d.textContent=c;let g=document.createElement("pre");g.textContent=typeof l=="string"?Bt(l):JSON.stringify(l,null,2),o.append(d,g)};a(this.strings.args,n.arguments),a(this.strings.result,n.result)}onStreamEvent(t,n){switch(n.type){case"start":n.actionsHash&&(t.actionsHash=n.actionsHash),n.conversationId&&n.conversationId!==this.conversationId&&(this.conversationId=n.conversationId,this.config.persist!==!1&&T.set(this.convKey(),n.conversationId));break;case"thinking":t.thinking=(t.thinking||"")+n.text;break;case"delta":this.finishThinking(t),t.content+=n.text;break;case"tool_call":this.finishThinking(t),(t.toolCalls??(t.toolCalls=[])).push({id:n.id,name:n.name,arguments:n.arguments,status:"running"});break;case"tool_result":{let e=t.toolCalls?.find(r=>r.id===n.id)??(t.toolCalls??(t.toolCalls=[]))[t.toolCalls.push({id:n.id,name:n.name,status:"running"})-1];e.status=n.isError?"error":"done",e.result=n.result,e.isError=n.isError;break}case"done":this.finishThinking(t),n.content&&n.content.length>=t.content.length&&(t.content=n.content),t.status="done";break;case"error":t.status="error",this.showError(t,new v(n.message,500,n.code));break}this.scheduleUpdate(t)}scheduleUpdate(t){this.pendingFrame||(this.pendingFrame=requestAnimationFrame(()=>{this.pendingFrame=0,this.updateMessage(t),this.following&&this.scrollToBottom(!0)}))}finishThinking(t){t.thinking&&!t.thinkingMs&&t.startedAt&&(t.thinkingMs=Date.now()-t.startedAt)}nearBottom(){let t=this.els.log;return t.scrollHeight-t.scrollTop-t.clientHeight<96}scrollToBottom(t=!1){let n=this.els.log;(t||this.nearBottom())&&(n.scrollTop=n.scrollHeight,this.following=!0)}showError(t,n){let e=n.message||this.strings.errorGeneric;if(this.emit("dewee:error",{message:e,code:n.code}),t){t.content=t.content?`${t.content}
328
328
 
329
- > \u26A0\uFE0F ${n}`:`\u26A0\uFE0F ${n}`,t.status="error",this.updateMessage(t);return}let r=document.createElement("div");r.className="msg error",r.innerHTML='<div class="bubble"></div>',r.firstElementChild.textContent=`\u26A0\uFE0F ${n}`,this.els.log.appendChild(r)}async challenge(t){await $t();let e=document.createElement("div");e.slot="challenge",this.appendChild(e);try{return await new Promise((n,r)=>{window.turnstile.render(e,{sitekey:t,appearance:"interaction-only",theme:this.getAttribute("data-theme")==="dark"?"dark":"light",callback:n,"error-callback":()=>r(new v("Verification failed",403,"turnstile_failed"))})})}finally{e.remove()}}};function _t(i){return i.forEach((s,t)=>{if(s.role==="user"){s.content=j(s.content),B(s.content)&&(s.hidden=!0);return}if(!s.content.includes("```dewee-action"))return;let{calls:e}=H(s.content);if(!e.length)return;let n=i[t+1],r=n?.role==="user"?j(n.content):"",o=B(r)?tt(r):[];s.actions=e.map(a=>{let l=o.find(g=>g.id===a.id),c=l?l.status==="ok"?"done":l.status:"error";return{id:a.id,name:a.name,args:a.ok?a.args:void 0,status:c,error:l?l.error:"Not run"}})}),i}function Ht(i,s){try{return JSON.parse(i)}catch{return s}}function Bt(i){let s=i.trim();if(!/^[[{]/.test(s))return i;try{return JSON.stringify(JSON.parse(s),null,2)}catch{return i}}function V(i="dewee-chat"){typeof window>"u"||!window.customElements||customElements.get(i)||customElements.define(i,class extends L{})}function Ot(i={},s){V();let t=document.createElement("dewee-chat");return t.configure(i),((typeof s=="string"?document.querySelector(s):s)??document.body).appendChild(t),t}V();var zt=document.currentScript,N=zt?.dataset;if(N&&N.endpoint&&N.autoMount!=="false"){let i=()=>{let s=document.createElement("dewee-chat");for(let[t,e]of Object.entries(N))t==="autoMount"||e===void 0||s.setAttribute(t.replace(/[A-Z]/g,n=>"-"+n.toLowerCase()),e);document.body.appendChild(s)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",i):i()}window.DeweeWebchat=K;})();
329
+ > \u26A0\uFE0F ${e}`:`\u26A0\uFE0F ${e}`,t.status="error",this.updateMessage(t);return}let r=document.createElement("div");r.className="msg error",r.innerHTML='<div class="bubble"></div>',r.firstElementChild.textContent=`\u26A0\uFE0F ${e}`,this.els.log.appendChild(r)}async challenge(t){await Ht();let n=document.createElement("div");n.slot="challenge",this.appendChild(n);try{return await new Promise((e,r)=>{window.turnstile.render(n,{sitekey:t,appearance:"interaction-only",theme:this.getAttribute("data-theme")==="dark"?"dark":"light",callback:e,"error-callback":()=>r(new v("Verification failed",403,"turnstile_failed"))})})}finally{n.remove()}}};function _t(i){return i.forEach((s,t)=>{if(s.role==="user"){s.content=j(s.content),O(s.content)&&(s.hidden=!0);return}if(!s.content.includes("```dewee-action"))return;let{calls:n}=_(s.content);if(!n.length)return;let e=i[t+1],r=e?.role==="user"?j(e.content):"",o=O(r)?it(r):[];s.actions=n.map(a=>{let c=o.find(d=>d.id===a.id),l=c?c.status==="ok"?"done":c.status:"error";return{id:a.id,name:a.name,args:a.ok?a.args:void 0,status:l,error:c?c.error:"Not run"}})}),i}function Ot(i,s){try{return JSON.parse(i)}catch{return s}}function Bt(i){let s=i.trim();if(!/^[[{]/.test(s))return i;try{return JSON.stringify(JSON.parse(s),null,2)}catch{return i}}function F(i="dewee-chat"){typeof window>"u"||!window.customElements||customElements.get(i)||customElements.define(i,class extends P{})}function Dt(i={},s){F();let t=document.createElement("dewee-chat");return t.configure(i),((typeof s=="string"?document.querySelector(s):s)??document.body).appendChild(t),t}F();var zt=document.currentScript,H=zt?.dataset;if(H&&H.endpoint&&H.autoMount!=="false"){let i=()=>{let s=document.createElement("dewee-chat");for(let[t,n]of Object.entries(H))t==="autoMount"||n===void 0||s.setAttribute(t.replace(/[A-Z]/g,e=>"-"+e.toLowerCase()),n);document.body.appendChild(s)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",i):i()}window.DeweeWebchat=G;})();
package/dist/index.d.ts CHANGED
@@ -39,6 +39,54 @@ type ActionConfirmHook = (action: PageActionInfo & {
39
39
  }, args: Record<string, unknown>) => boolean | Promise<boolean>;
40
40
  type ActionCallStatus = "pending" | "awaiting-confirmation" | "running" | "done" | "denied" | "error";
41
41
 
42
+ /** Extra request headers: a static map, or a function evaluated before every request. */
43
+ type RequestHeadersOption = Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
44
+ /** Headers the widget owns; `headers` cannot override them (compared case-insensitively). */
45
+ declare const RESERVED_HEADERS: readonly string[];
46
+ interface ApiClientOptions {
47
+ /** Backend base URL, e.g. "/api/dewee" or "https://example.com/api/dewee". */
48
+ endpoint: string;
49
+ /** Bring-your-own gateway credentials (only if the backend allows it). */
50
+ gatewayUrl?: string;
51
+ gatewayToken?: string;
52
+ /** Called when the backend requires a Turnstile challenge; must resolve to a token. */
53
+ turnstile?: (siteKey: string) => Promise<string>;
54
+ /** Extra headers merged into every request (reserved headers excluded). */
55
+ headers?: RequestHeadersOption;
56
+ /** Replaces `globalThis.fetch` for every request. */
57
+ fetch?: typeof fetch;
58
+ }
59
+ declare class ApiError extends Error {
60
+ status: number;
61
+ code?: string | undefined;
62
+ constructor(message: string, status: number, code?: string | undefined);
63
+ }
64
+ declare class ApiClient {
65
+ private o;
66
+ private session;
67
+ private config;
68
+ private sessionPromise;
69
+ constructor(o: ApiClientOptions);
70
+ private url;
71
+ private get storageKey();
72
+ /** Session reuse: refresh once less than 10% of the token lifetime (at most 24 h, at least 30 s) remains. */
73
+ private fresh;
74
+ private request;
75
+ private headers;
76
+ private parse;
77
+ getConfig(force?: boolean): Promise<PublicConfig>;
78
+ /** Returns a valid visitor session, creating or refreshing it when needed. */
79
+ ensureSession(): Promise<SessionResponse>;
80
+ private createSession;
81
+ forgetSession(): void;
82
+ history(agent: string, conversationId: string): Promise<HistoryMessage[]>;
83
+ /** Sends a message and calls `onEvent` for every streamed event. Resolves when the run ends. */
84
+ chat(input: ChatRequestBody & {
85
+ agent: string;
86
+ conversationId: string;
87
+ }, onEvent: (e: ChatStreamEvent) => void, signal?: AbortSignal): Promise<void>;
88
+ }
89
+
42
90
  interface DeweeChatConfig {
43
91
  /** Backend base URL (the server handler). Default: "/api/dewee". */
44
92
  endpoint: string;
@@ -88,6 +136,13 @@ interface DeweeChatConfig {
88
136
  maxActionRounds?: number;
89
137
  /** Per-handler timeout. Default 30000 ms. */
90
138
  actionTimeoutMs?: number;
139
+ /**
140
+ * Extra headers for every request to `endpoint` (e.g. a CSRF token). A function is called before
141
+ * each request. Cannot override content-type, authorization or the gateway credential headers.
142
+ */
143
+ headers?: RequestHeadersOption;
144
+ /** Replaces `globalThis.fetch` for every widget request (e.g. to add credentials or tracing). */
145
+ fetch?: typeof fetch;
91
146
  }
92
147
  /** A page action call as shown in the transcript. */
93
148
  interface ActionCallView {
@@ -139,6 +194,12 @@ declare class DeweeChatElement extends ElementBase {
139
194
  registerActions(actions: PageAction | PageAction[]): void;
140
195
  /** Removes a page action. Returns false when it was not registered. */
141
196
  unregisterAction(name: string): boolean;
197
+ /** Extra request headers (property only; same as `configure({ headers })`). */
198
+ get headers(): RequestHeadersOption | undefined;
199
+ set headers(v: RequestHeadersOption | undefined);
200
+ /** Custom fetch for widget requests (property only; same as `configure({ fetch })`). */
201
+ get fetch(): typeof fetch | undefined;
202
+ set fetch(v: typeof fetch | undefined);
142
203
  /** Names of the currently registered page actions. */
143
204
  get actionNames(): string[];
144
205
  private setPropActions;
@@ -200,43 +261,6 @@ declare class DeweeChatElement extends ElementBase {
200
261
  private challenge;
201
262
  }
202
263
 
203
- interface ApiClientOptions {
204
- /** Backend base URL, e.g. "/api/dewee" or "https://example.com/api/dewee". */
205
- endpoint: string;
206
- /** Bring-your-own gateway credentials (only if the backend allows it). */
207
- gatewayUrl?: string;
208
- gatewayToken?: string;
209
- /** Called when the backend requires a Turnstile challenge; must resolve to a token. */
210
- turnstile?: (siteKey: string) => Promise<string>;
211
- }
212
- declare class ApiError extends Error {
213
- status: number;
214
- code?: string | undefined;
215
- constructor(message: string, status: number, code?: string | undefined);
216
- }
217
- declare class ApiClient {
218
- private o;
219
- private session;
220
- private config;
221
- private sessionPromise;
222
- constructor(o: ApiClientOptions);
223
- private url;
224
- private get storageKey();
225
- private headers;
226
- private parse;
227
- getConfig(force?: boolean): Promise<PublicConfig>;
228
- /** Returns a valid visitor session, creating or refreshing it when needed. */
229
- ensureSession(): Promise<SessionResponse>;
230
- private createSession;
231
- forgetSession(): void;
232
- history(agent: string, conversationId: string): Promise<HistoryMessage[]>;
233
- /** Sends a message and calls `onEvent` for every streamed event. Resolves when the run ends. */
234
- chat(input: ChatRequestBody & {
235
- agent: string;
236
- conversationId: string;
237
- }, onEvent: (e: ChatStreamEvent) => void, signal?: AbortSignal): Promise<void>;
238
- }
239
-
240
264
  type Doc = Document;
241
265
  declare function renderMarkdown(doc: Doc, src: string): DocumentFragment;
242
266
 
@@ -250,4 +274,4 @@ declare global {
250
274
  }
251
275
  }
252
276
 
253
- export { type ActionCallStatus, type ActionCallView, type ActionConfirmHook, type ActionEventDetail, type ActionResult, type ActionResultStatus, ActionSensitivity, ApiClient, type ApiClientOptions, ApiError, ChatRequestBody, ChatStreamEvent, type DeweeChatConfig, DeweeChatElement, HistoryMessage, type JsonSchema, type PageAction, type PageActionContext, type PageActionInfo, PublicConfig, SessionResponse, defineDeweeChat, mount, renderMarkdown };
277
+ export { type ActionCallStatus, type ActionCallView, type ActionConfirmHook, type ActionEventDetail, type ActionResult, type ActionResultStatus, ActionSensitivity, ApiClient, type ApiClientOptions, ApiError, ChatRequestBody, ChatStreamEvent, type DeweeChatConfig, DeweeChatElement, HistoryMessage, type JsonSchema, type PageAction, type PageActionContext, type PageActionInfo, PublicConfig, RESERVED_HEADERS, type RequestHeadersOption, SessionResponse, defineDeweeChat, mount, renderMarkdown };
package/dist/index.js CHANGED
@@ -2,15 +2,17 @@ import {
2
2
  ApiClient,
3
3
  ApiError,
4
4
  DeweeChatElement,
5
+ RESERVED_HEADERS,
5
6
  defineDeweeChat,
6
7
  mount,
7
8
  renderMarkdown
8
- } from "./chunk-YV6PXYJZ.js";
9
- import "./chunk-JX6AH37X.js";
9
+ } from "./chunk-ZTVGWFXL.js";
10
+ import "./chunk-W6A4HVFB.js";
10
11
  export {
11
12
  ApiClient,
12
13
  ApiError,
13
14
  DeweeChatElement,
15
+ RESERVED_HEADERS,
14
16
  defineDeweeChat,
15
17
  mount,
16
18
  renderMarkdown
package/dist/react.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties } from 'react';
3
3
  import { DeweeChatConfig, ActionEventDetail, DeweeChatElement } from './index.js';
4
- export { ActionConfirmHook, PageAction, PageActionContext } from './index.js';
4
+ export { ActionConfirmHook, PageAction, PageActionContext, RequestHeadersOption } from './index.js';
5
5
  import './protocol-B72Ry-fY.js';
6
6
 
7
7
  interface DeweeChatProps extends Partial<DeweeChatConfig> {
package/dist/react.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineDeweeChat
3
- } from "./chunk-YV6PXYJZ.js";
4
- import "./chunk-JX6AH37X.js";
3
+ } from "./chunk-ZTVGWFXL.js";
4
+ import "./chunk-W6A4HVFB.js";
5
5
 
6
6
  // src/react.ts
7
7
  import { createElement, forwardRef, useEffect, useImperativeHandle, useRef } from "react";
@@ -16,7 +16,7 @@ var EVENTS = {
16
16
  };
17
17
  var DeweeChat = forwardRef(function DeweeChat2(props, ref) {
18
18
  const el = useRef(null);
19
- const { className, style, onReady, onSend, onReply, onError, onOpen, onClose, onAction, actions, confirm, getPageContext, ...config } = props;
19
+ const { className, style, onReady, onSend, onReply, onError, onOpen, onClose, onAction, actions, confirm, getPageContext, headers, fetch, ...config } = props;
20
20
  const handlers = { onReady, onSend, onReply, onError, onOpen, onClose, onAction };
21
21
  const handlersRef = useRef(handlers);
22
22
  handlersRef.current = handlers;
@@ -28,7 +28,7 @@ var DeweeChat = forwardRef(function DeweeChat2(props, ref) {
28
28
  }, [configKey]);
29
29
  useEffect(() => {
30
30
  defineDeweeChat();
31
- el.current?.configure({ actions, confirm, getPageContext });
31
+ el.current?.configure({ actions, confirm, getPageContext, headers, fetch });
32
32
  });
33
33
  useEffect(() => {
34
34
  const node = el.current;
package/dist/server.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  normalizeManifest,
10
10
  serializePageContext,
11
11
  stripPageBlock
12
- } from "./chunk-JX6AH37X.js";
12
+ } from "./chunk-W6A4HVFB.js";
13
13
 
14
14
  // src/server/gateway-client.ts
15
15
  var GatewayError = class extends Error {
package/dist/vue.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as vue from 'vue';
2
2
  import { PropType } from 'vue';
3
- export { ActionConfirmHook, ActionEventDetail, DeweeChatConfig, DeweeChatElement, PageAction, PageActionContext } from './index.js';
3
+ export { ActionConfirmHook, ActionEventDetail, DeweeChatConfig, DeweeChatElement, PageAction, PageActionContext, RequestHeadersOption } from './index.js';
4
4
  import './protocol-B72Ry-fY.js';
5
5
 
6
6
  declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -128,6 +128,14 @@ declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
128
128
  type: PropType<any>;
129
129
  default: undefined;
130
130
  };
131
+ headers: {
132
+ type: PropType<any>;
133
+ default: undefined;
134
+ };
135
+ fetch: {
136
+ type: PropType<any>;
137
+ default: undefined;
138
+ };
131
139
  }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
132
140
  [key: string]: any;
133
141
  }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("open" | "ready" | "send" | "reply" | "error" | "close" | "action")[], "open" | "ready" | "send" | "reply" | "error" | "close" | "action", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
@@ -255,6 +263,14 @@ declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
255
263
  type: PropType<any>;
256
264
  default: undefined;
257
265
  };
266
+ headers: {
267
+ type: PropType<any>;
268
+ default: undefined;
269
+ };
270
+ fetch: {
271
+ type: PropType<any>;
272
+ default: undefined;
273
+ };
258
274
  }>> & Readonly<{
259
275
  onOpen?: ((...args: any[]) => any) | undefined;
260
276
  onReady?: ((...args: any[]) => any) | undefined;
@@ -295,6 +311,8 @@ declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
295
311
  getPageContext: any;
296
312
  maxActionRounds: any;
297
313
  actionTimeoutMs: any;
314
+ headers: any;
315
+ fetch: any;
298
316
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
299
317
 
300
318
  export { DeweeChat, DeweeChat as default };
package/dist/vue.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineDeweeChat
3
- } from "./chunk-YV6PXYJZ.js";
4
- import "./chunk-JX6AH37X.js";
3
+ } from "./chunk-ZTVGWFXL.js";
4
+ import "./chunk-W6A4HVFB.js";
5
5
 
6
6
  // src/vue.ts
7
7
  import { defineComponent, h, onMounted, ref, watch } from "vue";
@@ -36,7 +36,9 @@ var keys = [
36
36
  "confirm",
37
37
  "getPageContext",
38
38
  "maxActionRounds",
39
- "actionTimeoutMs"
39
+ "actionTimeoutMs",
40
+ "headers",
41
+ "fetch"
40
42
  ];
41
43
  var props = Object.fromEntries(keys.map((k) => [k, { type: null, default: void 0 }]));
42
44
  var EVENTS = ["ready", "send", "reply", "error", "open", "close", "action"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dewee-webchat",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
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",