dewee-webchat 0.4.1-beta.2 → 0.4.2

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
@@ -153,6 +153,8 @@ Attributes use kebab-case (`show-reasoning="false"`), `data-*` on the script tag
153
153
  | `actions`, `confirm`, `getPageContext` | | [Page actions](#agent-page-actions) (JS/props only) |
154
154
  | `maxActionRounds` | `5` | Automatic action turns per visitor message |
155
155
  | `actionTimeoutMs` | `30000` | Per-handler timeout |
156
+ | `domActions` | `false` | [Built-in DOM actions](#built-in-dom-actions): snapshot, scroll, click, fill |
157
+ | `domActionsRoot` | `document.body` | Selector or element the built-in DOM actions are limited to |
156
158
  | `headers` | | Extra request headers, object or `() => object \| Promise<object>` called before every request (JS/props only) |
157
159
  | `fetch` | `globalThis.fetch` | Custom fetch for every widget request (JS/props only) |
158
160
 
@@ -253,6 +255,43 @@ or inserts it as HTML, caps calls per answer and automatic turns per message, an
253
255
  `mutate`/`destructive` handler without approval. Page context and results are labelled as data for the
254
256
  agent, but prompt injection through page content is still possible — do not put secrets in either.
255
257
 
258
+ ### Built-in DOM actions
259
+
260
+ Without writing handlers, a page can let the agent scroll, click and type on it. They are off by
261
+ default:
262
+
263
+ ```html
264
+ <script src="https://webchat-cdn.dewee.sh/v/latest/dewee-webchat.js"
265
+ data-endpoint="/api/dewee" data-dom-actions="true" data-dom-actions-root="#app" defer></script>
266
+ ```
267
+
268
+ ```tsx
269
+ <DeweeChat endpoint="/api/dewee" domActions domActionsRoot="#app" />
270
+ ```
271
+
272
+ | Action | Sensitivity | What it does |
273
+ |---|---|---|
274
+ | `page.snapshot` | `read` | Lists visible links, buttons and form fields as `e3 textbox "Email" value="…"`, with a `generation` |
275
+ | `page.scroll` | `read` | Scrolls one screen up/down, to the top/bottom, or an element (`ref`) into view |
276
+ | `page.click` | `mutate` | Clicks an element by `ref` + `generation` |
277
+ | `page.fill` | `mutate` | Sets the value of a text field, textarea, select or editable area, then fires `input` and `change` |
278
+
279
+ - `page.click` and `page.fill` always ask the visitor first; the dialog (or your `confirm` hook, as
280
+ `args.target`) names the element, e.g. `button "Delete run"`.
281
+ - Refs come only from the latest snapshot. A ref from an older snapshot, or an element that is gone,
282
+ hidden or now sensitive, is refused, and the agent is told to take a new snapshot.
283
+ - Never listed or touched: password, payment card and one-time-code fields (by `type`, `autocomplete`
284
+ and name), hidden elements, the widget itself, and anything inside `[data-dewee-ignore]`. Put
285
+ `data-dewee-ignore` on whatever else the agent must not reach.
286
+ - `page.fill` uses the native value setter, so React and Vue controlled inputs see the change. Events are
287
+ synthetic (`isTrusted` is false): pages that require trusted events, file pickers and popups opened by a
288
+ click will not react.
289
+ - Your own actions keep working; registering an action with a built-in name (say `page.click`)
290
+ replaces the built-in one.
291
+ - The built-ins operate the DOM, not your app's permissions: the agent can do whatever the visitor's
292
+ clicks can, after approval. Narrow it with `domActionsRoot` and `data-dewee-ignore`, and keep the server
293
+ as the authority.
294
+
256
295
  ## Signed-in users
257
296
 
258
297
  When your site knows who the visitor is, the agent can know it too, with private tools that act for
@@ -21,7 +21,7 @@ function isLinkErrorCode(code) {
21
21
  }
22
22
  var HEADER_GATEWAY_URL = "x-dewee-gateway-url";
23
23
  var HEADER_GATEWAY_TOKEN = "x-dewee-gateway-token";
24
- var VERSION = "0.4.1-beta.2";
24
+ var VERSION = "0.4.2";
25
25
 
26
26
  // src/actions/page-block.ts
27
27
  var ACTION_NAME_RE = /^[a-z][a-z0-9_.-]{0,63}$/;
@@ -9,7 +9,7 @@ import {
9
9
  isLinkErrorCode,
10
10
  parseJobAccessLaunch,
11
11
  stripPageBlock
12
- } from "./chunk-PUIN5ZCQ.js";
12
+ } from "./chunk-SAUDVPFH.js";
13
13
 
14
14
  // src/actions/directives.ts
15
15
  var DIRECTIVE_LANG = "dewee-action";
@@ -279,6 +279,257 @@ function raceAbort(p, signal) {
279
279
  });
280
280
  }
281
281
 
282
+ // src/actions/dom-actions.ts
283
+ var DOM_ACTION_NAMES = ["page.snapshot", "page.scroll", "page.click", "page.fill"];
284
+ var INTERACTIVE = [
285
+ "a[href]",
286
+ "button",
287
+ "input",
288
+ "select",
289
+ "textarea",
290
+ "summary",
291
+ "[contenteditable]:not([contenteditable=false])",
292
+ ...["button", "link", "checkbox", "radio", "switch", "tab", "menuitem", "option", "combobox", "textbox", "searchbox"].map((r) => `[role=${r}]`)
293
+ ].join(",");
294
+ var UNFILLABLE_INPUTS = /* @__PURE__ */ new Set(["checkbox", "radio", "file", "submit", "button", "image", "reset", "hidden", "password"]);
295
+ var SENSITIVE_AUTOCOMPLETE = /\b(cc-[a-z-]+|one-time-code|current-password|new-password)\b/i;
296
+ var SENSITIVE_NAME = /(passw|passcode|\bpin\b|otp|one.?time|cvv|cvc|card.?(num|no)|cc.?num|security.?code|\bssn\b)/i;
297
+ var MAX_NAME = 80;
298
+ var MAX_VALUE = 60;
299
+ var DEFAULT_LIMIT = 40;
300
+ var clip = (s, n) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
301
+ var squash = (s) => (s ?? "").replace(/\s+/g, " ").trim();
302
+ var refSchema = { type: "string", pattern: "^e[0-9]{1,4}$" };
303
+ var genSchema = { type: "integer", minimum: 1 };
304
+ function createDomActions(o) {
305
+ let generation = 0;
306
+ let refs = /* @__PURE__ */ new Map();
307
+ const rootEl = () => {
308
+ const r = o.root();
309
+ const el2 = typeof r === "string" ? document.querySelector(r) : r;
310
+ if (r !== void 0 && !el2) throw new Error(`domActionsRoot ${JSON.stringify(r)} matches no element`);
311
+ return el2 ?? document.body;
312
+ };
313
+ const allowed = (el2, root) => el2.isConnected && root.contains(el2) && !o.exclude?.(el2) && !el2.closest("[data-dewee-ignore]") && !isSensitive(el2) && isVisible(el2);
314
+ const resolve = (args) => {
315
+ if (args.generation !== generation) {
316
+ throw new Error(`Stale ref: generation ${args.generation} is not the latest snapshot (${generation || "none"}). Call page.snapshot again.`);
317
+ }
318
+ const el2 = refs.get(args.ref);
319
+ if (!el2) throw new Error(`Unknown ref "${args.ref}". Call page.snapshot again.`);
320
+ if (!allowed(el2, rootEl())) throw new Error(`Ref "${args.ref}" is no longer available. Call page.snapshot again.`);
321
+ return el2;
322
+ };
323
+ const snapshot = {
324
+ name: "page.snapshot",
325
+ description: 'List the interactive elements visible on the page (links, buttons, form fields) as lines `ref role "name" [state]`. Use the ref together with the returned generation in page.scroll, page.click and page.fill. Every snapshot invalidates older refs. Use query to filter by visible name, offset/limit to page through long lists.',
326
+ sensitivity: "read",
327
+ parameters: {
328
+ type: "object",
329
+ properties: { query: { type: "string", maxLength: 100 }, offset: { type: "integer", minimum: 0 }, limit: { type: "integer", minimum: 1, maximum: 100 } },
330
+ additionalProperties: false
331
+ },
332
+ handler: ({ query, offset = 0, limit = DEFAULT_LIMIT }) => {
333
+ const root = rootEl();
334
+ generation++;
335
+ refs = /* @__PURE__ */ new Map();
336
+ const q = squash(query).toLowerCase();
337
+ const lines = [];
338
+ let total = 0;
339
+ for (const el2 of root.querySelectorAll(INTERACTIVE)) {
340
+ if (!allowed(el2, root)) continue;
341
+ const line = describe(el2);
342
+ if (q && !line.toLowerCase().includes(q)) continue;
343
+ total++;
344
+ if (total <= offset || lines.length >= limit) continue;
345
+ const ref = `e${total}`;
346
+ refs.set(ref, el2);
347
+ lines.push(`${ref} ${line}`);
348
+ }
349
+ return { generation, url: location.pathname + location.search, title: clip(document.title, MAX_NAME), total, offset, elements: lines, ...scrollState(root) };
350
+ }
351
+ };
352
+ const scroll = {
353
+ name: "page.scroll",
354
+ description: "Scroll the page by about one screen (direction up/down) or to the top/bottom, or scroll an element from page.snapshot into view (ref + generation).",
355
+ sensitivity: "read",
356
+ parameters: {
357
+ type: "object",
358
+ properties: { direction: { type: "string", enum: ["up", "down", "top", "bottom"] }, ref: refSchema, generation: genSchema },
359
+ additionalProperties: false
360
+ },
361
+ handler: ({ direction, ref, generation: gen }) => {
362
+ const root = rootEl();
363
+ if (ref !== void 0) {
364
+ if (gen === void 0) throw new Error("generation is required with ref");
365
+ const el2 = resolve({ ref, generation: gen });
366
+ el2.scrollIntoView?.({ block: "center", inline: "nearest" });
367
+ return { ref, ...scrollState(root) };
368
+ }
369
+ if (!direction) throw new Error("Give a direction or a ref");
370
+ const box = scroller(root);
371
+ const page = box === null ? window.innerHeight : box.clientHeight;
372
+ const current = box === null ? window.scrollY : box.scrollTop;
373
+ const max = box === null ? document.documentElement.scrollHeight : box.scrollHeight;
374
+ const top = direction === "top" ? 0 : direction === "bottom" ? max : current + (direction === "down" ? 0.8 : -0.8) * page;
375
+ if (box === null) window.scrollTo({ top });
376
+ else box.scrollTop = top;
377
+ return { direction, ...scrollState(root) };
378
+ }
379
+ };
380
+ const click = {
381
+ name: "page.click",
382
+ description: "Click an element from page.snapshot (ref + generation). The visitor approves every click.",
383
+ sensitivity: "mutate",
384
+ parameters: { type: "object", properties: { ref: refSchema, generation: genSchema }, required: ["ref", "generation"], additionalProperties: false },
385
+ handler: (args) => {
386
+ const el2 = resolve(args);
387
+ if (isDisabled(el2)) throw new Error(`Ref "${args.ref}" is disabled`);
388
+ el2.scrollIntoView?.({ block: "center", inline: "nearest" });
389
+ if (el2 instanceof HTMLElement) {
390
+ el2.focus({ preventScroll: true });
391
+ el2.click();
392
+ } else {
393
+ el2.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, composed: true }));
394
+ }
395
+ return { ref: args.ref, clicked: describe(el2) };
396
+ }
397
+ };
398
+ const fill = {
399
+ name: "page.fill",
400
+ description: "Replace the value of a text field, textarea, select (option value or label) or editable area from page.snapshot (ref + generation). The visitor approves every fill.",
401
+ sensitivity: "mutate",
402
+ parameters: {
403
+ type: "object",
404
+ properties: { ref: refSchema, generation: genSchema, text: { type: "string", maxLength: 1e4 } },
405
+ required: ["ref", "generation", "text"],
406
+ additionalProperties: false
407
+ },
408
+ handler: (args) => {
409
+ const el2 = resolve(args);
410
+ if (isDisabled(el2) || el2.readOnly) throw new Error(`Ref "${args.ref}" is not editable`);
411
+ setValue(el2, args.text);
412
+ return { ref: args.ref, value: clip(currentValue(el2), MAX_VALUE) };
413
+ }
414
+ };
415
+ return {
416
+ actions: [snapshot, scroll, click, fill],
417
+ target: (args) => {
418
+ try {
419
+ return describe(resolve(args));
420
+ } catch {
421
+ return void 0;
422
+ }
423
+ }
424
+ };
425
+ }
426
+ function setValue(el2, text) {
427
+ if (el2 instanceof HTMLInputElement) {
428
+ if (UNFILLABLE_INPUTS.has(el2.type)) throw new Error(`Inputs of type "${el2.type}" cannot be filled; use page.click`);
429
+ nativeSet(HTMLInputElement.prototype, el2, text);
430
+ } else if (el2 instanceof HTMLTextAreaElement) {
431
+ nativeSet(HTMLTextAreaElement.prototype, el2, text);
432
+ } else if (el2 instanceof HTMLSelectElement) {
433
+ const opt = [...el2.options].find((op) => op.value === text) ?? [...el2.options].find((op) => squash(op.textContent).toLowerCase() === squash(text).toLowerCase());
434
+ if (!opt) throw new Error(`No option "${clip(text, MAX_VALUE)}"`);
435
+ nativeSet(HTMLSelectElement.prototype, el2, opt.value);
436
+ } else if (el2 instanceof HTMLElement && el2.isContentEditable) {
437
+ el2.focus({ preventScroll: true });
438
+ el2.textContent = text;
439
+ el2.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true, inputType: "insertReplacementText", data: text }));
440
+ return;
441
+ } else {
442
+ throw new Error("This element cannot be filled; use page.click");
443
+ }
444
+ el2.focus({ preventScroll: true });
445
+ el2.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
446
+ el2.dispatchEvent(new Event("change", { bubbles: true }));
447
+ }
448
+ function nativeSet(proto, el2, value) {
449
+ const set = Object.getOwnPropertyDescriptor(proto, "value")?.set;
450
+ if (set) set.call(el2, value);
451
+ else el2.value = value;
452
+ }
453
+ function currentValue(el2) {
454
+ if (el2 instanceof HTMLInputElement || el2 instanceof HTMLTextAreaElement || el2 instanceof HTMLSelectElement) return el2.value;
455
+ return squash(el2.textContent);
456
+ }
457
+ function isDisabled(el2) {
458
+ return el2.disabled === true || el2.closest("fieldset[disabled]") !== null || el2.getAttribute("aria-disabled") === "true";
459
+ }
460
+ function isSensitive(el2) {
461
+ if (el2 instanceof HTMLInputElement && el2.type === "password") return true;
462
+ if (!(el2 instanceof HTMLInputElement || el2 instanceof HTMLTextAreaElement || el2 instanceof HTMLSelectElement)) return false;
463
+ if (SENSITIVE_AUTOCOMPLETE.test(el2.getAttribute("autocomplete") ?? "")) return true;
464
+ const hint = [el2.name, el2.id, el2.getAttribute("aria-label"), el2.placeholder].filter(Boolean).join(" ");
465
+ return SENSITIVE_NAME.test(hint);
466
+ }
467
+ function isVisible(el2) {
468
+ if (el2 instanceof HTMLInputElement && el2.type === "hidden") return false;
469
+ if (el2.closest("[hidden],[inert],[aria-hidden=true]")) return false;
470
+ for (let n = el2; n; n = n.parentElement) {
471
+ const cs = getComputedStyle(n);
472
+ if (cs.display === "none" || n === el2 && (cs.visibility === "hidden" || cs.visibility === "collapse")) return false;
473
+ }
474
+ return true;
475
+ }
476
+ function role(el2) {
477
+ const explicit = el2.getAttribute("role");
478
+ if (explicit) return explicit;
479
+ const tag = el2.tagName.toLowerCase();
480
+ if (tag === "a") return "link";
481
+ if (tag === "select") return "combobox";
482
+ if (tag === "textarea") return "textbox";
483
+ if (tag === "summary") return "button";
484
+ if (el2 instanceof HTMLInputElement) {
485
+ const t = el2.type;
486
+ if (t === "checkbox" || t === "radio") return t;
487
+ if (t === "submit" || t === "button" || t === "reset" || t === "image") return "button";
488
+ if (t === "search") return "searchbox";
489
+ if (t === "range") return "slider";
490
+ return t === "text" ? "textbox" : `textbox:${t}`;
491
+ }
492
+ if (el2.isContentEditable) return "textbox";
493
+ return tag;
494
+ }
495
+ function accessibleName(el2) {
496
+ const labelledBy = el2.getAttribute("aria-labelledby");
497
+ if (labelledBy) {
498
+ const text2 = labelledBy.split(/\s+/).map((id) => squash(document.getElementById(id)?.textContent)).filter(Boolean).join(" ");
499
+ if (text2) return text2;
500
+ }
501
+ const aria = squash(el2.getAttribute("aria-label"));
502
+ if (aria) return aria;
503
+ const labels = el2.labels;
504
+ if (labels?.length) {
505
+ const text2 = squash([...labels].map((l) => l.textContent).join(" "));
506
+ if (text2) return text2;
507
+ }
508
+ if (el2 instanceof HTMLInputElement && ["submit", "button", "reset"].includes(el2.type)) return squash(el2.value);
509
+ const text = el2 instanceof HTMLSelectElement || el2 instanceof HTMLTextAreaElement || el2 instanceof HTMLInputElement ? "" : squash(el2.textContent);
510
+ return text || squash(el2.getAttribute("placeholder")) || squash(el2.getAttribute("title")) || squash(el2.querySelector("img[alt]")?.getAttribute("alt")) || squash(el2.getAttribute("name"));
511
+ }
512
+ function describe(el2) {
513
+ const parts = [role(el2), JSON.stringify(clip(accessibleName(el2), MAX_NAME))];
514
+ if (el2 instanceof HTMLInputElement && (el2.type === "checkbox" || el2.type === "radio")) parts.push(el2.checked ? "checked" : "unchecked");
515
+ else if (el2 instanceof HTMLInputElement || el2 instanceof HTMLTextAreaElement || el2 instanceof HTMLSelectElement) {
516
+ const v = currentValue(el2);
517
+ if (v && !(el2 instanceof HTMLInputElement && ["submit", "button", "reset"].includes(el2.type))) parts.push(`value=${JSON.stringify(clip(v, MAX_VALUE))}`);
518
+ }
519
+ if (el2 instanceof HTMLAnchorElement) parts.push(`href=${JSON.stringify(clip(el2.getAttribute("href") ?? "", MAX_NAME))}`);
520
+ if (isDisabled(el2)) parts.push("disabled");
521
+ return parts.join(" ");
522
+ }
523
+ function scroller(root) {
524
+ if (root === document.body || root === document.documentElement) return null;
525
+ return root.scrollHeight > root.clientHeight ? root : null;
526
+ }
527
+ function scrollState(root) {
528
+ const box = scroller(root);
529
+ if (box === null) return { scrollY: Math.round(window.scrollY), maxScrollY: Math.max(0, Math.round(document.documentElement.scrollHeight - window.innerHeight)) };
530
+ return { scrollY: Math.round(box.scrollTop), maxScrollY: Math.max(0, Math.round(box.scrollHeight - box.clientHeight)) };
531
+ }
532
+
282
533
  // src/widget/api-client.ts
283
534
  var RENEW_CODES = /* @__PURE__ */ new Set(["LINK_EXPIRED", "LINK_REVOKED"]);
284
535
  var VERIFIED_REFRESH_MS = 6e4;
@@ -422,6 +673,10 @@ var ApiClient = class {
422
673
  return null;
423
674
  }
424
675
  }
676
+ /** The visitor of the session in use, or null before one is held. Backend conversations are keyed by it. */
677
+ visitorId() {
678
+ return this.session?.visitorId ?? null;
679
+ }
425
680
  /** Returns a valid visitor session, creating or refreshing it when needed. */
426
681
  ensureSession() {
427
682
  if (this.session && this.fresh(this.session)) return Promise.resolve(this.session);
@@ -547,8 +802,10 @@ var ApiClient = class {
547
802
  await this.ensureSession();
548
803
  let res = await this.request(this.url("chat"), { method: "POST", headers: await this.headers(true), body: JSON.stringify(input), signal });
549
804
  if (res.status === 401) {
805
+ const before = this.visitorId();
550
806
  this.forgetSession();
551
807
  await this.ensureSession();
808
+ if (input.actionsHash && this.visitorId() !== before) input = { ...input, actionsHash: void 0 };
552
809
  res = await this.request(this.url("chat"), { method: "POST", headers: await this.headers(true), body: JSON.stringify(input), signal });
553
810
  }
554
811
  if (!res.ok || !res.body) await this.parse(res);
@@ -1386,7 +1643,7 @@ textarea::placeholder { color: var(--dw-muted); }
1386
1643
 
1387
1644
  // src/widget/element.ts
1388
1645
  var DEFAULT_COOLDOWN_MS = 5e3;
1389
- var BOOL_ATTRS = ["show-reasoning", "show-tool-calls", "allow-agent-switch", "open", "persist", "hide-branding", "launcher-avatar"];
1646
+ var BOOL_ATTRS = ["show-reasoning", "show-tool-calls", "allow-agent-switch", "open", "persist", "hide-branding", "launcher-avatar", "dom-actions"];
1390
1647
  var ATTRS = [
1391
1648
  "endpoint",
1392
1649
  "agent",
@@ -1407,6 +1664,7 @@ var ATTRS = [
1407
1664
  "width",
1408
1665
  "height",
1409
1666
  "z-index",
1667
+ "dom-actions-root",
1410
1668
  ...BOOL_ATTRS
1411
1669
  ];
1412
1670
  var camel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
@@ -1465,6 +1723,8 @@ var DeweeChatElement = class extends ElementBase {
1465
1723
  registry = new ActionRegistry();
1466
1724
  propActionNames = /* @__PURE__ */ new Set();
1467
1725
  hooks = {};
1726
+ /** Built-in DOM actions, created on first use when `domActions` is on (keeps the snapshot refs). */
1727
+ dom = null;
1468
1728
  acks = /* @__PURE__ */ new Map();
1469
1729
  /** Pause after a RATE_LIMITED refusal; send stays disabled until it ends. */
1470
1730
  cooldown = new SendCooldown((s) => this.renderCooldown(s), () => this.endCooldown());
@@ -1489,13 +1749,17 @@ var DeweeChatElement = class extends ElementBase {
1489
1749
  }
1490
1750
  /** Updates configuration at runtime (wins over attributes). */
1491
1751
  configure(partial) {
1492
- const { actions, confirm, getPageContext, headers, fetch, ...rest } = partial;
1752
+ const { actions, confirm, getPageContext, headers, fetch, domActionsRoot, ...rest } = partial;
1493
1753
  const has = (k) => Object.prototype.hasOwnProperty.call(partial, k);
1494
1754
  if (has("actions")) this.setPropActions(actions ?? []);
1495
1755
  if (has("confirm")) this.hooks.confirm = confirm;
1496
1756
  if (has("getPageContext")) this.hooks.getPageContext = getPageContext;
1497
1757
  if (has("headers")) this.hooks.headers = headers;
1498
1758
  if (has("fetch")) this.hooks.fetch = fetch;
1759
+ if (has("domActionsRoot")) {
1760
+ if (domActionsRoot === void 0) delete this.hooks.domActionsRoot;
1761
+ else this.hooks.domActionsRoot = domActionsRoot;
1762
+ }
1499
1763
  if (!Object.keys(rest).length) return;
1500
1764
  this.overrides = { ...this.overrides, ...rest };
1501
1765
  if (this.els) this.applyConfig();
@@ -1523,9 +1787,23 @@ var DeweeChatElement = class extends ElementBase {
1523
1787
  set fetch(v) {
1524
1788
  this.hooks.fetch = v;
1525
1789
  }
1526
- /** Names of the currently registered page actions. */
1790
+ /** Names of the page actions offered to the agent (host actions, then enabled built-ins). */
1527
1791
  get actionNames() {
1528
- return this.registry.names();
1792
+ return this.actionRegistry().names();
1793
+ }
1794
+ /** Host actions plus the enabled built-in DOM actions whose names the host did not take. */
1795
+ actionRegistry() {
1796
+ if (!this.config.domActions) return this.registry;
1797
+ this.dom ??= createDomActions({
1798
+ root: () => this.hooks.domActionsRoot ?? this.getAttribute("dom-actions-root") ?? void 0,
1799
+ exclude: (el2) => el2 === this || this.contains(el2) || el2.closest("dewee-chat") !== null
1800
+ });
1801
+ const host = this.registry.names().map((n) => this.registry.get(n));
1802
+ const room = ACTION_LIMITS.maxActions - host.length;
1803
+ const builtins = this.dom.actions.filter((a) => !this.registry.get(a.name)).slice(0, Math.max(0, room));
1804
+ const all = new ActionRegistry();
1805
+ all.register([...host, ...builtins]);
1806
+ return all;
1529
1807
  }
1530
1808
  setPropActions(actions) {
1531
1809
  const next = new Set(actions.map((a) => a.name));
@@ -1563,7 +1841,7 @@ var DeweeChatElement = class extends ElementBase {
1563
1841
  for (let round = 0; next; round++) {
1564
1842
  const reply = await this.runTurn(next.message, next.hidden);
1565
1843
  next = null;
1566
- if (reply.status !== "done" || reply.stopped || !this.registry.size) break;
1844
+ if (reply.status !== "done" || reply.stopped || !this.actionRegistry().size) break;
1567
1845
  const { calls } = parseDirectives(reply.content);
1568
1846
  if (!calls.length) break;
1569
1847
  reply.actions = calls.map((c) => ({ id: c.id, name: c.name, args: c.ok ? c.args : void 0, status: "pending" }));
@@ -1587,6 +1865,7 @@ var DeweeChatElement = class extends ElementBase {
1587
1865
  this.setRunning(new AbortController());
1588
1866
  this.emit("dewee:send", { message, agent: this.agentId, conversationId: this.conversationId, auto: hidden || void 0 });
1589
1867
  try {
1868
+ await this.client().ensureSession();
1590
1869
  await this.client().chat(this.chatBody(message), (e) => this.onStreamEvent(reply, e), this.running.signal);
1591
1870
  if (reply.status === "streaming") reply.status = "done";
1592
1871
  } catch (e) {
@@ -1603,7 +1882,7 @@ var DeweeChatElement = class extends ElementBase {
1603
1882
  this.updateMessage(reply);
1604
1883
  this.setRunning(null);
1605
1884
  if (reply.status === "done") {
1606
- if (reply.actionsHash && !reply.stopped) this.setAck(reply.actionsHash);
1885
+ if (reply.actionsHash && reply.ackVisitor && !reply.stopped) this.setAck(reply.actionsHash, reply.ackVisitor);
1607
1886
  this.emit("dewee:reply", { content: reply.content, agent: this.agentId, conversationId: this.conversationId });
1608
1887
  if (this.config.mode !== "inline" && !this.hasAttribute("data-open")) this.setAttribute("data-unread", "");
1609
1888
  }
@@ -1612,9 +1891,11 @@ var DeweeChatElement = class extends ElementBase {
1612
1891
  }
1613
1892
  chatBody(message) {
1614
1893
  const body = { message, agent: this.agentId, conversationId: this.conversationId };
1615
- if (this.registry.size) {
1616
- body.actions = this.registry.manifest();
1617
- const ack = this.getAck();
1894
+ const registry = this.actionRegistry();
1895
+ if (registry.size) {
1896
+ body.actions = registry.manifest();
1897
+ const visitor = this.client().visitorId();
1898
+ const ack = visitor ? this.getAck(visitor) : void 0;
1618
1899
  if (ack) body.actionsHash = ack;
1619
1900
  }
1620
1901
  const ctx = this.pageContext();
@@ -1637,15 +1918,21 @@ var DeweeChatElement = class extends ElementBase {
1637
1918
  return void 0;
1638
1919
  }
1639
1920
  }
1640
- ackKey() {
1641
- return `dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${this.conversationId}`;
1921
+ /**
1922
+ * The backend keys a conversation by visitor as well as by id, so a sign-in, a sign-out or another
1923
+ * user starts a conversation that has not seen the manifest, even with the same conversation id.
1924
+ */
1925
+ ackKey(visitor) {
1926
+ return `dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${visitor}:${this.conversationId}`;
1642
1927
  }
1643
- getAck() {
1644
- return this.acks.get(this.ackKey()) ?? (this.config.persist !== false ? store2.get(this.ackKey()) ?? void 0 : void 0);
1928
+ getAck(visitor) {
1929
+ const key = this.ackKey(visitor);
1930
+ return this.acks.get(key) ?? (this.config.persist !== false ? store2.get(key) ?? void 0 : void 0);
1645
1931
  }
1646
- setAck(hash) {
1647
- this.acks.set(this.ackKey(), hash);
1648
- if (this.config.persist !== false) store2.set(this.ackKey(), hash);
1932
+ setAck(hash, visitor) {
1933
+ const key = this.ackKey(visitor);
1934
+ this.acks.set(key, hash);
1935
+ if (this.config.persist !== false) store2.set(key, hash);
1649
1936
  }
1650
1937
  /** Executes the calls of one answer in order. Resolves null when the visitor stopped the chain. */
1651
1938
  async runActions(reply, calls) {
@@ -1653,15 +1940,16 @@ var DeweeChatElement = class extends ElementBase {
1653
1940
  this.setRunning(ctrl);
1654
1941
  const results = [];
1655
1942
  const base = { agent: this.agentId, conversationId: this.conversationId };
1943
+ const registry = this.actionRegistry();
1656
1944
  try {
1657
1945
  for (const [i, call] of calls.entries()) {
1658
1946
  const view = reply.actions[i];
1659
- const sensitivity = call.ok ? this.registry.get(call.name)?.sensitivity : void 0;
1947
+ const sensitivity = call.ok ? registry.get(call.name)?.sensitivity : void 0;
1660
1948
  const args = call.ok ? call.args : void 0;
1661
1949
  this.emit("dewee:action", { phase: "start", id: call.id, name: call.name, sensitivity, args, ...base });
1662
1950
  const r = await runActionCall(call, {
1663
- registry: this.registry,
1664
- confirm: (action, a) => this.confirmAction(action, a, ctrl.signal),
1951
+ registry,
1952
+ confirm: (action, a) => this.confirmAction(registry, action, a, ctrl.signal),
1665
1953
  signal: ctrl.signal,
1666
1954
  timeoutMs: this.config.actionTimeoutMs,
1667
1955
  ...base,
@@ -1680,10 +1968,13 @@ var DeweeChatElement = class extends ElementBase {
1680
1968
  }
1681
1969
  return ctrl.signal.aborted ? null : results;
1682
1970
  }
1683
- confirmAction(action, args, signal) {
1684
- if (this.hooks.confirm) return this.hooks.confirm(action, args);
1971
+ confirmAction(registry, action, args, signal) {
1972
+ const builtin = this.dom?.actions.includes(registry.get(action.name));
1973
+ const target = builtin ? this.dom.target(args) : void 0;
1974
+ const shown = target ? { ...args, target } : args;
1975
+ if (this.hooks.confirm) return this.hooks.confirm(action, shown);
1685
1976
  if (this.config.mode !== "inline" && !this.hasAttribute("data-open")) this.open();
1686
- return showConfirmDialog(this.els.panel, this.strings, action, args, signal);
1977
+ return showConfirmDialog(this.els.panel, this.strings, action, shown, signal);
1687
1978
  }
1688
1979
  showNotice(text) {
1689
1980
  const div = document.createElement("div");
@@ -2173,7 +2464,7 @@ var DeweeChatElement = class extends ElementBase {
2173
2464
  v.tools.clear();
2174
2465
  }
2175
2466
  v.trace.hidden = !v.trace.childElementCount;
2176
- const shown = this.registry.size || m.actions ? stripDirectives(m.content) : m.content;
2467
+ const shown = this.actionRegistry().size || m.actions ? stripDirectives(m.content) : m.content;
2177
2468
  const typing = m.status === "streaming" && !shown;
2178
2469
  v.md.hidden = !typing && !shown && !!m.actions?.length;
2179
2470
  if (typing) {
@@ -2233,7 +2524,10 @@ var DeweeChatElement = class extends ElementBase {
2233
2524
  onStreamEvent(m, e) {
2234
2525
  switch (e.type) {
2235
2526
  case "start":
2236
- if (e.actionsHash) m.actionsHash = e.actionsHash;
2527
+ if (e.actionsHash) {
2528
+ m.actionsHash = e.actionsHash;
2529
+ m.ackVisitor = this.client().visitorId() ?? void 0;
2530
+ }
2237
2531
  if (e.conversationId && e.conversationId !== this.conversationId) {
2238
2532
  this.conversationId = e.conversationId;
2239
2533
  if (this.config.persist !== false) store2.set(this.convKey(), e.conversationId);
@@ -2416,6 +2710,7 @@ function mount(config = {}, target) {
2416
2710
  defineDeweeChat();
2417
2711
 
2418
2712
  export {
2713
+ DOM_ACTION_NAMES,
2419
2714
  RESERVED_HEADERS,
2420
2715
  ApiError,
2421
2716
  ApiClient,
@@ -1,24 +1,24 @@
1
- "use strict";(()=>{var te=Object.defineProperty;var Te=(i,s,e)=>s in i?te(i,s,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[s]=e;var Ie=(i,s)=>{for(var e in s)te(i,e,{get:s[e],enumerable:!0})};var u=(i,s,e)=>Te(i,typeof s!="symbol"?s+"":s,e);var ee={};Ie(ee,{ApiClient:()=>I,ApiError:()=>x,DeweeChatElement:()=>M,RESERVED_HEADERS:()=>Q,defineDeweeChat:()=>X,mount:()=>it,renderMarkdown:()=>L});var Le=["IDENTITY_REQUIRED","IDENTITY_LINK_REQUIRED","IDENTITY_CONFLICT","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"];function j(i){return typeof i=="string"&&Le.includes(i)}var Re=["POLICY_DENIED","RATE_LIMITED","CHALLENGE_REQUIRED"];function B(i){return typeof i=="string"&&Re.includes(i)}var _e=["idempotency_key_reused","link_in_progress","linking_unavailable"];function ne(i){return typeof i=="string"&&_e.includes(i)}var z="x-dewee-gateway-url",J="x-dewee-gateway-token";var ie="dewee-action",se="dewee-action-result",P={maxCalls:8,maxBlockBytes:8192},Me=/^[A-Za-z0-9_.:-]{1,64}$/,Pe=/^\s{0,3}```\s*$/,oe=i=>new RegExp("^\\s{0,3}```"+i.replace(/[.-]/g,"\\$&")+"\\s*$"),re=oe(ie),ae=oe(se);function q(i,s){let e=[];for(let t=0;t<i.length;t++){if(!s.test(i[t]))continue;let n=t,o=[],r=!1;for(t++;t<i.length;t++){if(Pe.test(i[t])){r=!0;break}o.push(i[t])}e.push({start:n,end:Math.min(t,i.length-1),body:o,closed:r})}return e}var ce=(i,s)=>{let e=new Set;for(let t of s)for(let n=t.start;n<=t.end;n++)e.add(n);return i.filter((t,n)=>!e.has(n)).join(`
1
+ "use strict";(()=>{var ue=Object.defineProperty;var ze=(t,s,e)=>s in t?ue(t,s,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[s]=e;var qe=(t,s)=>{for(var e in s)ue(t,e,{get:s[e],enumerable:!0})};var g=(t,s,e)=>ze(t,typeof s!="symbol"?s+"":s,e);var de={};qe(de,{ApiClient:()=>R,ApiError:()=>E,DOM_ACTION_NAMES:()=>Te,DeweeChatElement:()=>H,RESERVED_HEADERS:()=>ae,defineDeweeChat:()=>le,mount:()=>Tt,renderMarkdown:()=>M});var Je=["IDENTITY_REQUIRED","IDENTITY_LINK_REQUIRED","IDENTITY_CONFLICT","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"];function z(t){return typeof t=="string"&&Je.includes(t)}var Ve=["POLICY_DENIED","RATE_LIMITED","CHALLENGE_REQUIRED"];function q(t){return typeof t=="string"&&Ve.includes(t)}var Ue=["idempotency_key_reused","link_in_progress","linking_unavailable"];function pe(t){return typeof t=="string"&&Ue.includes(t)}var J="x-dewee-gateway-url",V="x-dewee-gateway-token";var he="dewee-action",ge="dewee-action-result",D={maxCalls:8,maxBlockBytes:8192},Ke=/^[A-Za-z0-9_.:-]{1,64}$/,Ye=/^\s{0,3}```\s*$/,fe=t=>new RegExp("^\\s{0,3}```"+t.replace(/[.-]/g,"\\$&")+"\\s*$"),me=fe(he),be=fe(ge);function U(t,s){let e=[];for(let n=0;n<t.length;n++){if(!s.test(t[n]))continue;let i=n,o=[],r=!1;for(n++;n<t.length;n++){if(Ye.test(t[n])){r=!0;break}o.push(t[n])}e.push({start:i,end:Math.min(n,t.length-1),body:o,closed:r})}return e}var we=(t,s)=>{let e=new Set;for(let n of s)for(let i=n.start;i<=n.end;i++)e.add(i);return t.filter((n,i)=>!e.has(i)).join(`
2
2
  `).replace(/\n{3,}/g,`
3
3
 
4
- `).trim()};function K(i){let s=i.replace(/\r\n?/g,`
4
+ `).trim()};function K(t){let s=t.replace(/\r\n?/g,`
5
5
  `).split(`
6
- `),e=q(s,re),t=[],n=new Set;return e.forEach((o,r)=>{let a=`call-${r+1}`,l=o.body.join(`
7
- `),c=(f,m=a,w="")=>t.push({ok:!1,id:m,name:w,error:f});if(r>=P.maxCalls)return c(`Too many action calls in one reply (max ${P.maxCalls})`);if(!o.closed)return c("Unclosed dewee-action block");if(new TextEncoder().encode(l).length>P.maxBlockBytes)return c(`Action block exceeds ${P.maxBlockBytes} bytes`);let d;try{d=JSON.parse(l)}catch{return c("Action block is not valid JSON")}if(typeof d!="object"||d===null||Array.isArray(d))return c("Action block must be a JSON object");let h=typeof d.id=="string"&&Me.test(d.id)?d.id:a,p=typeof d.name=="string"?d.name.slice(0,64):"";if(n.has(h))return c(`Duplicate action call id "${h}"`,`${h}-${r+1}`,p);if(n.add(h),!p)return c("Action name is required",h);let g=d.args??{};if(typeof g!="object"||g===null||Array.isArray(g))return c("args must be a JSON object",h,p);t.push({ok:!0,id:h,name:p,args:g})}),{text:e.length?ce(s,e):i,calls:t}}function le(i){if(!i.includes("```"+ie))return i;let s=i.replace(/\r\n?/g,`
6
+ `),e=U(s,me),n=[],i=new Set;return e.forEach((o,r)=>{let a=`call-${r+1}`,d=o.body.join(`
7
+ `),l=(h,m=a,b="")=>n.push({ok:!1,id:m,name:b,error:h});if(r>=D.maxCalls)return l(`Too many action calls in one reply (max ${D.maxCalls})`);if(!o.closed)return l("Unclosed dewee-action block");if(new TextEncoder().encode(d).length>D.maxBlockBytes)return l(`Action block exceeds ${D.maxBlockBytes} bytes`);let c;try{c=JSON.parse(d)}catch{return l("Action block is not valid JSON")}if(typeof c!="object"||c===null||Array.isArray(c))return l("Action block must be a JSON object");let u=typeof c.id=="string"&&Ke.test(c.id)?c.id:a,f=typeof c.name=="string"?c.name.slice(0,64):"";if(i.has(u))return l(`Duplicate action call id "${u}"`,`${u}-${r+1}`,f);if(i.add(u),!f)return l("Action name is required",u);let p=c.args??{};if(typeof p!="object"||p===null||Array.isArray(p))return l("args must be a JSON object",u,f);n.push({ok:!0,id:u,name:f,args:p})}),{text:e.length?we(s,e):t,calls:n}}function ye(t){if(!t.includes("```"+he))return t;let s=t.replace(/\r\n?/g,`
8
8
  `).split(`
9
- `);return ce(s,q(s,re))}function V(i){return ae.test(i.trimStart().split(`
10
- `,1)[0]??"")}function de(i){let s=i.replace(/\r\n?/g,`
9
+ `);return we(s,U(s,me))}function Y(t){return be.test(t.trimStart().split(`
10
+ `,1)[0]??"")}function xe(t){let s=t.replace(/\r\n?/g,`
11
11
  `).split(`
12
- `),e=[];for(let t of q(s,ae))try{let n=JSON.parse(t.body.join(`
13
- `));n&&typeof n.id=="string"&&["ok","denied","error"].includes(n.status)&&e.push(n)}catch{}return e}var _=i=>"```"+se+`
14
- `+JSON.stringify(i)+"\n```";function he(i,s=4e3){let e=Math.max(64,Math.floor((s-16)/Math.max(1,i.length)));return i.map(n=>{let o={...n,result:U(n.result)};if(o.result===void 0&&delete o.result,_(o).length<=e)return _(o);let r=JSON.stringify(o.result??null),a=e-_({...o,result:{truncated:!0,preview:""}}).length-8,l=_({...o,result:{truncated:!0,preview:r.slice(0,Math.max(0,a))}});return l.length<=e?l:_({id:n.id,name:n.name.slice(0,64),status:n.status,result:{truncated:!0}})}).join(`
12
+ `),e=[];for(let n of U(s,be))try{let i=JSON.parse(n.body.join(`
13
+ `));i&&typeof i.id=="string"&&["ok","denied","error"].includes(i.status)&&e.push(i)}catch{}return e}var P=t=>"```"+ge+`
14
+ `+JSON.stringify(t)+"\n```";function ve(t,s=4e3){let e=Math.max(64,Math.floor((s-16)/Math.max(1,t.length)));return t.map(i=>{let o={...i,result:F(i.result)};if(o.result===void 0&&delete o.result,P(o).length<=e)return P(o);let r=JSON.stringify(o.result??null),a=e-P({...o,result:{truncated:!0,preview:""}}).length-8,d=P({...o,result:{truncated:!0,preview:r.slice(0,Math.max(0,a))}});return d.length<=e?d:P({id:i.id,name:i.name.slice(0,64),status:i.status,result:{truncated:!0}})}).join(`
15
15
 
16
- `).slice(0,s)}function U(i){if(i!==void 0)try{let s=JSON.stringify(i);return s===void 0?null:JSON.parse(s)}catch{return"[unserializable result]"}}var Y=i=>i===null?"null":Array.isArray(i)?"array":typeof i=="number"?Number.isInteger(i)?"integer":"number":typeof i,Ne=(i,s)=>{let e=Y(i);return e===s||s==="number"&&e==="integer"};function N(i,s,e="args",t=0){if(!s||typeof s!="object")return null;if(t>32)return`${e}: schema is nested too deeply`;let n=s;if(n.type!==void 0){let o=Array.isArray(n.type)?n.type:[n.type];if(!o.some(r=>Ne(i,r)))return`${e}: expected ${o.join(" or ")}, got ${Y(i)}`}if(Array.isArray(n.enum)&&!n.enum.some(o=>pe(o,i)))return`${e}: must be one of ${n.enum.map(o=>JSON.stringify(o)).join(", ")}`;if("const"in n&&!pe(n.const,i))return`${e}: must equal ${JSON.stringify(n.const)}`;if(typeof i=="string"){if(typeof n.minLength=="number"&&i.length<n.minLength)return`${e}: shorter than ${n.minLength} characters`;if(typeof n.maxLength=="number"&&i.length>n.maxLength)return`${e}: longer than ${n.maxLength} characters`;if(typeof n.pattern=="string"){let o=null;try{o=new RegExp(n.pattern,"u")}catch{return`${e}: schema pattern is invalid`}if(!o.test(i))return`${e}: does not match ${n.pattern}`}}if(typeof i=="number"){if(typeof n.minimum=="number"&&i<n.minimum)return`${e}: must be >= ${n.minimum}`;if(typeof n.maximum=="number"&&i>n.maximum)return`${e}: must be <= ${n.maximum}`}if(Array.isArray(i)){if(typeof n.minItems=="number"&&i.length<n.minItems)return`${e}: needs at least ${n.minItems} items`;if(typeof n.maxItems=="number"&&i.length>n.maxItems)return`${e}: allows at most ${n.maxItems} items`;if(n.items&&typeof n.items=="object")for(let o=0;o<i.length;o++){let r=N(i[o],n.items,`${e}[${o}]`,t+1);if(r)return r}}if(Y(i)==="object"){let o=i,r=n.properties&&typeof n.properties=="object"?n.properties:{};for(let a of Array.isArray(n.required)?n.required:[])if(!(a in o))return`${e}.${a}: is required`;for(let[a,l]of Object.entries(o))if(Object.prototype.hasOwnProperty.call(r,a)){let c=N(l,r[a],`${e}.${a}`,t+1);if(c)return c}else if(n.additionalProperties===!1)return`${e}.${a}: is not allowed`}return null}function pe(i,s){return JSON.stringify(i)===JSON.stringify(s)}var ge=/^[a-z][a-z0-9_.-]{0,63}$/,F=["read","navigate","mutate","destructive"],S={maxActions:32,maxDescription:1e3,maxParametersBytes:4096,maxManifestBytes:16384,maxPageContextBytes:4096},De="[[dewee-page v1]]",ue="[[/dewee-page]]";function W(i){if(!i.startsWith(De))return i;let s=i.indexOf(`
17
- `+ue);return s<0?i:i.slice(s+ue.length+1).replace(/^\n+/,"")}var Oe=i=>i==="mutate"||i==="destructive",D=class{constructor(){u(this,"map",new Map)}get size(){return this.map.size}get(s){return this.map.get(s)}names(){return[...this.map.keys()]}register(s){let e=Array.isArray(s)?s:[s];for(let t of e)He(t);if(new Set([...this.map.keys(),...e.map(t=>t.name)]).size>S.maxActions)throw new TypeError(`dewee-webchat: at most ${S.maxActions} page actions`);for(let t of e)this.map.set(t.name,t)}unregister(s){return this.map.delete(s)}manifest(){return[...this.map.values()].map(s=>({name:s.name,description:s.description,sensitivity:s.sensitivity,parameters:s.parameters??{type:"object",properties:{}}}))}};function He(i){let s=e=>{throw new TypeError(`dewee-webchat: invalid page action ${JSON.stringify(i?.name)}: ${e}`)};(!i||typeof i!="object")&&s("not an object"),(typeof i.name!="string"||!ge.test(i.name))&&s("name must match ^[a-z][a-z0-9_.-]{0,63}$"),(typeof i.description!="string"||i.description.length>S.maxDescription)&&s(`description must be a string up to ${S.maxDescription} chars`),F.includes(i.sensitivity)||s(`sensitivity must be one of ${F.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 me(i,s){let e=l=>(s.onStatus?.("error"),{id:i.id,name:i.name,status:"error",error:l});if(!i.ok)return e(i.error);if(s.signal.aborted)return e("Cancelled");let t=s.registry.get(i.name);if(!t)return e(`Unknown action "${i.name}". Available: ${s.registry.names().join(", ")||"none"}`);let n=N(i.args,t.parameters??{type:"object"});if(n)return e(`Invalid arguments: ${n}`);if(Oe(t.sensitivity)){s.onStatus?.("awaiting-confirmation");let{handler:l,...c}=t,d=!1;try{d=await fe(Promise.resolve(s.confirm({...c,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 o=new AbortController,r=()=>o.abort();s.signal.addEventListener("abort",r,{once:!0});let a=setTimeout(r,s.timeoutMs??3e4);try{let l=await fe(Promise.resolve().then(()=>t.handler(i.args,{callId:i.id,conversationId:s.conversationId,agent:s.agent,signal:o.signal})),o.signal);return s.onStatus?.("done"),{id:i.id,name:i.name,status:"ok",result:U(l)??null}}catch(l){return e(o.signal.aborted?s.signal.aborted?"Cancelled":"Timed out":String(l?.message??l).slice(0,500)||"Action failed")}finally{clearTimeout(a),s.signal.removeEventListener("abort",r)}}function fe(i,s){return s.aborted?Promise.reject(new Error("aborted")):new Promise((e,t)=>{let n=()=>t(new Error("aborted"));s.addEventListener("abort",n,{once:!0}),i.then(e,t).finally(()=>s.removeEventListener("abort",n))})}function O(i){let s=()=>{throw new Error("Job access is unavailable")};if(!i||typeof i!="object"||Array.isArray(i))return s();let e=i;if(typeof e.bootstrap_url!="string"||typeof e.launch_token!="string"||!/^[A-Za-z0-9_.-]{16,16384}$/.test(e.launch_token)||typeof e.expires_at!="string")return s();let t;try{t=new URL(e.bootstrap_url)}catch{return s()}if(t.protocol!=="https:"||t.username||t.password||t.search||t.hash||t.pathname!=="/identity/job-access/launch"||t.href!==e.bootstrap_url)return s();let n=Date.parse(e.expires_at);return!Number.isFinite(n)||n<=Date.now()||n>Date.now()+6e4?s():{bootstrap_url:t.href,launch_token:e.launch_token,expires_at:e.expires_at}}var $e=new Set(["LINK_EXPIRED","LINK_REVOKED"]),je=6e4,Q=["content-type","authorization",z,J],x=class extends Error{constructor(e,t,n,o,r){super(e);u(this,"status",t);u(this,"code",n);u(this,"remediation",o);u(this,"retryAfterMs",r)}};function Be(i,s){if(typeof i=="number"&&Number.isFinite(i)&&i>0)return i;let e=Number(s.headers.get("retry-after"));return Number.isFinite(e)&&e>0?e*1e3:void 0}var be=i=>({get(s){try{return i().getItem(s)}catch{return null}},set(s,e){try{i().setItem(s,e)}catch{}},del(s){try{i().removeItem(s)}catch{}}}),G=be(()=>localStorage),T=be(()=>sessionStorage),I=class{constructor(s){u(this,"o",s);u(this,"session",null);u(this,"config",null);u(this,"sessionPromise",null);u(this,"refreshAt",new WeakMap)}url(s,e){let t=this.o.endpoint.replace(/\/+$/,""),n=e?"?"+new URLSearchParams(e).toString():"";return`${t}/${s}${n}`}get storageKey(){return`dewee-webchat:session:${this.o.endpoint}`}get reuseKey(){return`dewee-webchat:reuse:${this.o.endpoint}`}reuseHint(){return T.get(this.reuseKey)}request(s,e){return(this.o.fetch??globalThis.fetch)(s,e)}async headers(s=!1){let e={},t=typeof this.o.headers=="function"?await this.o.headers():this.o.headers;for(let[n,o]of Object.entries(t??{}))typeof o=="string"&&!Q.includes(n.toLowerCase())&&(e[n.toLowerCase()]=o);if(e["content-type"]="application/json",this.o.gatewayUrl&&this.o.gatewayToken&&(e[z]=this.o.gatewayUrl,e[J]=this.o.gatewayToken),s){let n=this.session&&this.fresh(this.session)?this.session:await this.ensureSession();e.authorization=`Bearer ${n.token}`}return e}async parse(s){let e=await s.json().catch(()=>({}));if(!s.ok){let t=e?.error?.remediation,n=t&&typeof t.action=="string"&&typeof t.audience=="string"?{action:t.action,audience:t.audience}:void 0;throw new x(e?.error?.message||`HTTP ${s.status}`,s.status,e?.error?.code,n,Be(e?.error?.retryAfterMs,s))}return e}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)}fresh(s){if(s.verified)return Date.now()<(this.refreshAt.get(s)??0);let e=typeof s.expiresIn=="number"&&s.expiresIn>=0&&s.obtainedAt,t=e?s.obtainedAt+s.expiresIn:s.expiresAt,n=e?s.expiresIn:s.obtainedAt?s.expiresAt-s.obtainedAt:1/0,o=Math.min(864e5,Math.max(3e4,n*.1));return t-Date.now()>o}scheduleRefresh(s,e){let t=typeof s.expiresIn=="number"&&s.expiresIn>0?s.expiresIn:s.expiresAt-e;this.refreshAt.set(s,e+t-Math.min(je,t/2))}stored(){let s=G.get(this.storageKey);if(!s)return null;try{let e=JSON.parse(s);return e.verified?null:e}catch{return null}}ensureSession(){return this.session&&this.fresh(this.session)?Promise.resolve(this.session):this.sessionPromise??(this.sessionPromise=this.loadSession().finally(()=>this.sessionPromise=null))}async loadSession(){if(!this.session){let s=this.stored();if(s){if(this.fresh(s)&&!(await this.getConfig()).identity)return this.session=s;this.session=s}}return this.createSession()}async createSession(){let s=await this.getConfig(),e={},t=this.session&&!this.session.verified?this.session:this.stored();if(t?.token&&(e.token=t.token),s.identity){let c=this.session?.verified?this.session.reuseHint:T.get(this.reuseKey);c&&(e.reuseHint=c)}let n=async()=>this.parse(await this.request(this.url("session"),{method:"POST",headers:await this.headers(),body:JSON.stringify(e)})),o=async()=>{if(!this.o.turnstile)throw new x("Verification is required but no Turnstile handler is set",403,"turnstile_required");e.turnstileToken=await this.o.turnstile(s.turnstileSiteKey)},r;try{s.turnstileSiteKey&&!(s.refreshWithoutTurnstile&&!s.identity&&e.token&&!e.reuseHint)&&await o();try{r=await n()}catch(c){if(!(s.turnstileSiteKey&&!e.turnstileToken&&c instanceof x&&c.code==="turnstile_failed"))throw c;await o(),r=await n()}}catch(c){throw c instanceof x&&c.status===401&&T.del(this.reuseKey),c}let a=Date.now(),l={...r,obtainedAt:a};if(this.session=l,l.verified)this.scheduleRefresh(l,a),typeof l.reuseHint=="string"&&l.reuseHint!==""?T.set(this.reuseKey,l.reuseHint):T.del(this.reuseKey);else{T.del(this.reuseKey);let{reuseHint:c,...d}=l;G.set(this.storageKey,JSON.stringify(d))}return l}forgetSession(){if(this.session?.verified){T.del(this.reuseKey),this.session=null;return}this.session=null,G.del(this.storageKey)}async renewSession(){return this.session=null,this.ensureSession()}async jobAccess(s){try{if(!(await this.ensureSession()).verified)throw new x("Sign in to open job access",401,"IDENTITY_REQUIRED");if(s.aborted)throw new x("Job access was cancelled",0,"job_access_unavailable");let t=await this.request(this.url("job-access"),{method:"POST",headers:await this.headers(!0),body:"{}",signal:s,redirect:"error"});return O(await this.parse(t))}catch(e){let t=e instanceof x&&typeof e.code=="string"&&["IDENTITY_REQUIRED","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"].includes(e.code)?e.code:"job_access_unavailable";throw new x("Job access is unavailable. Sign in again or retry.",e instanceof x?e.status:0,t)}}async link(){let s=await this.signedInRequest(async()=>this.request(this.url("link"),{method:"POST",headers:await this.headers(!0),body:"{}"}));return this.parse(s)}async history(s,e){let t=await this.signedInRequest(async()=>this.request(this.url("history",{agent:s,conversationId:e}),{headers:await this.headers(!0)}));return(await this.parse(t)).messages}async signedInRequest(s){await this.ensureSession();let e=await s();return e.status!==401||!this.session?.verified?e:(this.forgetSession(),await this.ensureSession(),s())}async chat(s,e,t){await this.ensureSession();let n=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:t});n.status===401&&(this.forgetSession(),await this.ensureSession(),n=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:t})),(!n.ok||!n.body)&&await this.parse(n);let o=n.body.pipeThrough(new TextDecoderStream).getReader(),r="",a=!1;for(;;){let{done:l,value:c}=await o.read();if(l)break;r+=c;let d;for(;(d=r.indexOf(`
16
+ `).slice(0,s)}function F(t){if(t!==void 0)try{let s=JSON.stringify(t);return s===void 0?null:JSON.parse(s)}catch{return"[unserializable result]"}}var W=t=>t===null?"null":Array.isArray(t)?"array":typeof t=="number"?Number.isInteger(t)?"integer":"number":typeof t,Fe=(t,s)=>{let e=W(t);return e===s||s==="number"&&e==="integer"};function O(t,s,e="args",n=0){if(!s||typeof s!="object")return null;if(n>32)return`${e}: schema is nested too deeply`;let i=s;if(i.type!==void 0){let o=Array.isArray(i.type)?i.type:[i.type];if(!o.some(r=>Fe(t,r)))return`${e}: expected ${o.join(" or ")}, got ${W(t)}`}if(Array.isArray(i.enum)&&!i.enum.some(o=>Ee(o,t)))return`${e}: must be one of ${i.enum.map(o=>JSON.stringify(o)).join(", ")}`;if("const"in i&&!Ee(i.const,t))return`${e}: must equal ${JSON.stringify(i.const)}`;if(typeof t=="string"){if(typeof i.minLength=="number"&&t.length<i.minLength)return`${e}: shorter than ${i.minLength} characters`;if(typeof i.maxLength=="number"&&t.length>i.maxLength)return`${e}: longer than ${i.maxLength} characters`;if(typeof i.pattern=="string"){let o=null;try{o=new RegExp(i.pattern,"u")}catch{return`${e}: schema pattern is invalid`}if(!o.test(t))return`${e}: does not match ${i.pattern}`}}if(typeof t=="number"){if(typeof i.minimum=="number"&&t<i.minimum)return`${e}: must be >= ${i.minimum}`;if(typeof i.maximum=="number"&&t>i.maximum)return`${e}: must be <= ${i.maximum}`}if(Array.isArray(t)){if(typeof i.minItems=="number"&&t.length<i.minItems)return`${e}: needs at least ${i.minItems} items`;if(typeof i.maxItems=="number"&&t.length>i.maxItems)return`${e}: allows at most ${i.maxItems} items`;if(i.items&&typeof i.items=="object")for(let o=0;o<t.length;o++){let r=O(t[o],i.items,`${e}[${o}]`,n+1);if(r)return r}}if(W(t)==="object"){let o=t,r=i.properties&&typeof i.properties=="object"?i.properties:{};for(let a of Array.isArray(i.required)?i.required:[])if(!(a in o))return`${e}.${a}: is required`;for(let[a,d]of Object.entries(o))if(Object.prototype.hasOwnProperty.call(r,a)){let l=O(d,r[a],`${e}.${a}`,n+1);if(l)return l}else if(i.additionalProperties===!1)return`${e}.${a}: is not allowed`}return null}function Ee(t,s){return JSON.stringify(t)===JSON.stringify(s)}var Ae=/^[a-z][a-z0-9_.-]{0,63}$/,G=["read","navigate","mutate","destructive"],S={maxActions:32,maxDescription:1e3,maxParametersBytes:4096,maxManifestBytes:16384,maxPageContextBytes:4096},We="[[dewee-page v1]]",ke="[[/dewee-page]]";function Q(t){if(!t.startsWith(We))return t;let s=t.indexOf(`
17
+ `+ke);return s<0?t:t.slice(s+ke.length+1).replace(/^\n+/,"")}var Ge=t=>t==="mutate"||t==="destructive",N=class{constructor(){g(this,"map",new Map)}get size(){return this.map.size}get(s){return this.map.get(s)}names(){return[...this.map.keys()]}register(s){let e=Array.isArray(s)?s:[s];for(let n of e)Qe(n);if(new Set([...this.map.keys(),...e.map(n=>n.name)]).size>S.maxActions)throw new TypeError(`dewee-webchat: at most ${S.maxActions} page actions`);for(let n of e)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 Qe(t){let s=e=>{throw new TypeError(`dewee-webchat: invalid page action ${JSON.stringify(t?.name)}: ${e}`)};(!t||typeof t!="object")&&s("not an object"),(typeof t.name!="string"||!Ae.test(t.name))&&s("name must match ^[a-z][a-z0-9_.-]{0,63}$"),(typeof t.description!="string"||t.description.length>S.maxDescription)&&s(`description must be a string up to ${S.maxDescription} chars`),G.includes(t.sensitivity)||s(`sensitivity must be one of ${G.join(", ")}`),typeof t.handler!="function"&&s("handler must be a function"),t.parameters!==void 0&&(typeof t.parameters!="object"||t.parameters===null||Array.isArray(t.parameters))&&s("parameters must be a JSON Schema object")}async function Se(t,s){let e=d=>(s.onStatus?.("error"),{id:t.id,name:t.name,status:"error",error:d});if(!t.ok)return e(t.error);if(s.signal.aborted)return e("Cancelled");let n=s.registry.get(t.name);if(!n)return e(`Unknown action "${t.name}". Available: ${s.registry.names().join(", ")||"none"}`);let i=O(t.args,n.parameters??{type:"object"});if(i)return e(`Invalid arguments: ${i}`);if(Ge(n.sensitivity)){s.onStatus?.("awaiting-confirmation");let{handler:d,...l}=n,c=!1;try{c=await Ce(Promise.resolve(s.confirm({...l,callId:t.id},t.args)),s.signal)===!0}catch{c=!1}if(!c||s.signal.aborted)return s.onStatus?.("denied"),{id:t.id,name:t.name,status:"denied",error:"The user declined this action."}}s.onStatus?.("running");let o=new AbortController,r=()=>o.abort();s.signal.addEventListener("abort",r,{once:!0});let a=setTimeout(r,s.timeoutMs??3e4);try{let d=await Ce(Promise.resolve().then(()=>n.handler(t.args,{callId:t.id,conversationId:s.conversationId,agent:s.agent,signal:o.signal})),o.signal);return s.onStatus?.("done"),{id:t.id,name:t.name,status:"ok",result:F(d)??null}}catch(d){return e(o.signal.aborted?s.signal.aborted?"Cancelled":"Timed out":String(d?.message??d).slice(0,500)||"Action failed")}finally{clearTimeout(a),s.signal.removeEventListener("abort",r)}}function Ce(t,s){return s.aborted?Promise.reject(new Error("aborted")):new Promise((e,n)=>{let i=()=>n(new Error("aborted"));s.addEventListener("abort",i,{once:!0}),t.then(e,n).finally(()=>s.removeEventListener("abort",i))})}var Te=["page.snapshot","page.scroll","page.click","page.fill"],Ze=["a[href]","button","input","select","textarea","summary","[contenteditable]:not([contenteditable=false])",...["button","link","checkbox","radio","switch","tab","menuitem","option","combobox","textbox","searchbox"].map(t=>`[role=${t}]`)].join(","),Xe=new Set(["checkbox","radio","file","submit","button","image","reset","hidden","password"]),et=/\b(cc-[a-z-]+|one-time-code|current-password|new-password)\b/i,tt=/(passw|passcode|\bpin\b|otp|one.?time|cvv|cvc|card.?(num|no)|cc.?num|security.?code|\bssn\b)/i,ie=80,oe=60,nt=40,L=(t,s)=>t.length>s?t.slice(0,s-1)+"\u2026":t,k=t=>(t??"").replace(/\s+/g," ").trim(),Z={type:"string",pattern:"^e[0-9]{1,4}$"},X={type:"integer",minimum:1};function Ie(t){let s=0,e=new Map,n=()=>{let c=t.root(),u=typeof c=="string"?document.querySelector(c):c;if(c!==void 0&&!u)throw new Error(`domActionsRoot ${JSON.stringify(c)} matches no element`);return u??document.body},i=(c,u)=>c.isConnected&&u.contains(c)&&!t.exclude?.(c)&&!c.closest("[data-dewee-ignore]")&&!st(c)&&ot(c),o=c=>{if(c.generation!==s)throw new Error(`Stale ref: generation ${c.generation} is not the latest snapshot (${s||"none"}). Call page.snapshot again.`);let u=e.get(c.ref);if(!u)throw new Error(`Unknown ref "${c.ref}". Call page.snapshot again.`);if(!i(u,n()))throw new Error(`Ref "${c.ref}" is no longer available. Call page.snapshot again.`);return u};return{actions:[{name:"page.snapshot",description:'List the interactive elements visible on the page (links, buttons, form fields) as lines `ref role "name" [state]`. Use the ref together with the returned generation in page.scroll, page.click and page.fill. Every snapshot invalidates older refs. Use query to filter by visible name, offset/limit to page through long lists.',sensitivity:"read",parameters:{type:"object",properties:{query:{type:"string",maxLength:100},offset:{type:"integer",minimum:0},limit:{type:"integer",minimum:1,maximum:100}},additionalProperties:!1},handler:({query:c,offset:u=0,limit:f=nt})=>{let p=n();s++,e=new Map;let h=k(c).toLowerCase(),m=[],b=0;for(let v of p.querySelectorAll(Ze)){if(!i(v,p))continue;let w=te(v);if(h&&!w.toLowerCase().includes(h)||(b++,b<=u||m.length>=f))continue;let x=`e${b}`;e.set(x,v),m.push(`${x} ${w}`)}return{generation:s,url:location.pathname+location.search,title:L(document.title,ie),total:b,offset:u,elements:m,...ne(p)}}},{name:"page.scroll",description:"Scroll the page by about one screen (direction up/down) or to the top/bottom, or scroll an element from page.snapshot into view (ref + generation).",sensitivity:"read",parameters:{type:"object",properties:{direction:{type:"string",enum:["up","down","top","bottom"]},ref:Z,generation:X},additionalProperties:!1},handler:({direction:c,ref:u,generation:f})=>{let p=n();if(u!==void 0){if(f===void 0)throw new Error("generation is required with ref");return o({ref:u,generation:f}).scrollIntoView?.({block:"center",inline:"nearest"}),{ref:u,...ne(p)}}if(!c)throw new Error("Give a direction or a ref");let h=Re(p),m=h===null?window.innerHeight:h.clientHeight,b=h===null?window.scrollY:h.scrollTop,v=h===null?document.documentElement.scrollHeight:h.scrollHeight,w=c==="top"?0:c==="bottom"?v:b+(c==="down"?.8:-.8)*m;return h===null?window.scrollTo({top:w}):h.scrollTop=w,{direction:c,...ne(p)}}},{name:"page.click",description:"Click an element from page.snapshot (ref + generation). The visitor approves every click.",sensitivity:"mutate",parameters:{type:"object",properties:{ref:Z,generation:X},required:["ref","generation"],additionalProperties:!1},handler:c=>{let u=o(c);if(se(u))throw new Error(`Ref "${c.ref}" is disabled`);return u.scrollIntoView?.({block:"center",inline:"nearest"}),u instanceof HTMLElement?(u.focus({preventScroll:!0}),u.click()):u.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,composed:!0})),{ref:c.ref,clicked:te(u)}}},{name:"page.fill",description:"Replace the value of a text field, textarea, select (option value or label) or editable area from page.snapshot (ref + generation). The visitor approves every fill.",sensitivity:"mutate",parameters:{type:"object",properties:{ref:Z,generation:X,text:{type:"string",maxLength:1e4}},required:["ref","generation","text"],additionalProperties:!1},handler:c=>{let u=o(c);if(se(u)||u.readOnly)throw new Error(`Ref "${c.ref}" is not editable`);return it(u,c.text),{ref:c.ref,value:L(Le(u),oe)}}}],target:c=>{try{return te(o(c))}catch{return}}}}function it(t,s){if(t instanceof HTMLInputElement){if(Xe.has(t.type))throw new Error(`Inputs of type "${t.type}" cannot be filled; use page.click`);ee(HTMLInputElement.prototype,t,s)}else if(t instanceof HTMLTextAreaElement)ee(HTMLTextAreaElement.prototype,t,s);else if(t instanceof HTMLSelectElement){let e=[...t.options].find(n=>n.value===s)??[...t.options].find(n=>k(n.textContent).toLowerCase()===k(s).toLowerCase());if(!e)throw new Error(`No option "${L(s,oe)}"`);ee(HTMLSelectElement.prototype,t,e.value)}else if(t instanceof HTMLElement&&t.isContentEditable){t.focus({preventScroll:!0}),t.textContent=s,t.dispatchEvent(new InputEvent("input",{bubbles:!0,composed:!0,inputType:"insertReplacementText",data:s}));return}else throw new Error("This element cannot be filled; use page.click");t.focus({preventScroll:!0}),t.dispatchEvent(new Event("input",{bubbles:!0,composed:!0})),t.dispatchEvent(new Event("change",{bubbles:!0}))}function ee(t,s,e){let n=Object.getOwnPropertyDescriptor(t,"value")?.set;n?n.call(s,e):s.value=e}function Le(t){return t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement?t.value:k(t.textContent)}function se(t){return t.disabled===!0||t.closest("fieldset[disabled]")!==null||t.getAttribute("aria-disabled")==="true"}function st(t){if(t instanceof HTMLInputElement&&t.type==="password")return!0;if(!(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement))return!1;if(et.test(t.getAttribute("autocomplete")??""))return!0;let s=[t.name,t.id,t.getAttribute("aria-label"),t.placeholder].filter(Boolean).join(" ");return tt.test(s)}function ot(t){if(t instanceof HTMLInputElement&&t.type==="hidden"||t.closest("[hidden],[inert],[aria-hidden=true]"))return!1;for(let s=t;s;s=s.parentElement){let e=getComputedStyle(s);if(e.display==="none"||s===t&&(e.visibility==="hidden"||e.visibility==="collapse"))return!1}return!0}function rt(t){let s=t.getAttribute("role");if(s)return s;let e=t.tagName.toLowerCase();if(e==="a")return"link";if(e==="select")return"combobox";if(e==="textarea")return"textbox";if(e==="summary")return"button";if(t instanceof HTMLInputElement){let n=t.type;return n==="checkbox"||n==="radio"?n:n==="submit"||n==="button"||n==="reset"||n==="image"?"button":n==="search"?"searchbox":n==="range"?"slider":n==="text"?"textbox":`textbox:${n}`}return t.isContentEditable?"textbox":e}function at(t){let s=t.getAttribute("aria-labelledby");if(s){let o=s.split(/\s+/).map(r=>k(document.getElementById(r)?.textContent)).filter(Boolean).join(" ");if(o)return o}let e=k(t.getAttribute("aria-label"));if(e)return e;let n=t.labels;if(n?.length){let o=k([...n].map(r=>r.textContent).join(" "));if(o)return o}return t instanceof HTMLInputElement&&["submit","button","reset"].includes(t.type)?k(t.value):(t instanceof HTMLSelectElement||t instanceof HTMLTextAreaElement||t instanceof HTMLInputElement?"":k(t.textContent))||k(t.getAttribute("placeholder"))||k(t.getAttribute("title"))||k(t.querySelector("img[alt]")?.getAttribute("alt"))||k(t.getAttribute("name"))}function te(t){let s=[rt(t),JSON.stringify(L(at(t),ie))];if(t instanceof HTMLInputElement&&(t.type==="checkbox"||t.type==="radio"))s.push(t.checked?"checked":"unchecked");else if(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement){let e=Le(t);e&&!(t instanceof HTMLInputElement&&["submit","button","reset"].includes(t.type))&&s.push(`value=${JSON.stringify(L(e,oe))}`)}return t instanceof HTMLAnchorElement&&s.push(`href=${JSON.stringify(L(t.getAttribute("href")??"",ie))}`),se(t)&&s.push("disabled"),s.join(" ")}function Re(t){return t===document.body||t===document.documentElement?null:t.scrollHeight>t.clientHeight?t:null}function ne(t){let s=Re(t);return s===null?{scrollY:Math.round(window.scrollY),maxScrollY:Math.max(0,Math.round(document.documentElement.scrollHeight-window.innerHeight))}:{scrollY:Math.round(s.scrollTop),maxScrollY:Math.max(0,Math.round(s.scrollHeight-s.clientHeight))}}function $(t){let s=()=>{throw new Error("Job access is unavailable")};if(!t||typeof t!="object"||Array.isArray(t))return s();let e=t;if(typeof e.bootstrap_url!="string"||typeof e.launch_token!="string"||!/^[A-Za-z0-9_.-]{16,16384}$/.test(e.launch_token)||typeof e.expires_at!="string")return s();let n;try{n=new URL(e.bootstrap_url)}catch{return s()}if(n.protocol!=="https:"||n.username||n.password||n.search||n.hash||n.pathname!=="/identity/job-access/launch"||n.href!==e.bootstrap_url)return s();let i=Date.parse(e.expires_at);return!Number.isFinite(i)||i<=Date.now()||i>Date.now()+6e4?s():{bootstrap_url:n.href,launch_token:e.launch_token,expires_at:e.expires_at}}var ct=new Set(["LINK_EXPIRED","LINK_REVOKED"]),lt=6e4,ae=["content-type","authorization",J,V],E=class extends Error{constructor(e,n,i,o,r){super(e);g(this,"status",n);g(this,"code",i);g(this,"remediation",o);g(this,"retryAfterMs",r)}};function dt(t,s){if(typeof t=="number"&&Number.isFinite(t)&&t>0)return t;let e=Number(s.headers.get("retry-after"));return Number.isFinite(e)&&e>0?e*1e3:void 0}var Me=t=>({get(s){try{return t().getItem(s)}catch{return null}},set(s,e){try{t().setItem(s,e)}catch{}},del(s){try{t().removeItem(s)}catch{}}}),re=Me(()=>localStorage),I=Me(()=>sessionStorage),R=class{constructor(s){g(this,"o",s);g(this,"session",null);g(this,"config",null);g(this,"sessionPromise",null);g(this,"refreshAt",new WeakMap)}url(s,e){let n=this.o.endpoint.replace(/\/+$/,""),i=e?"?"+new URLSearchParams(e).toString():"";return`${n}/${s}${i}`}get storageKey(){return`dewee-webchat:session:${this.o.endpoint}`}get reuseKey(){return`dewee-webchat:reuse:${this.o.endpoint}`}reuseHint(){return I.get(this.reuseKey)}request(s,e){return(this.o.fetch??globalThis.fetch)(s,e)}async headers(s=!1){let e={},n=typeof this.o.headers=="function"?await this.o.headers():this.o.headers;for(let[i,o]of Object.entries(n??{}))typeof o=="string"&&!ae.includes(i.toLowerCase())&&(e[i.toLowerCase()]=o);if(e["content-type"]="application/json",this.o.gatewayUrl&&this.o.gatewayToken&&(e[J]=this.o.gatewayUrl,e[V]=this.o.gatewayToken),s){let i=this.session&&this.fresh(this.session)?this.session:await this.ensureSession();e.authorization=`Bearer ${i.token}`}return e}async parse(s){let e=await s.json().catch(()=>({}));if(!s.ok){let n=e?.error?.remediation,i=n&&typeof n.action=="string"&&typeof n.audience=="string"?{action:n.action,audience:n.audience}:void 0;throw new E(e?.error?.message||`HTTP ${s.status}`,s.status,e?.error?.code,i,dt(e?.error?.retryAfterMs,s))}return e}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)}fresh(s){if(s.verified)return Date.now()<(this.refreshAt.get(s)??0);let e=typeof s.expiresIn=="number"&&s.expiresIn>=0&&s.obtainedAt,n=e?s.obtainedAt+s.expiresIn:s.expiresAt,i=e?s.expiresIn:s.obtainedAt?s.expiresAt-s.obtainedAt:1/0,o=Math.min(864e5,Math.max(3e4,i*.1));return n-Date.now()>o}scheduleRefresh(s,e){let n=typeof s.expiresIn=="number"&&s.expiresIn>0?s.expiresIn:s.expiresAt-e;this.refreshAt.set(s,e+n-Math.min(lt,n/2))}stored(){let s=re.get(this.storageKey);if(!s)return null;try{let e=JSON.parse(s);return e.verified?null:e}catch{return null}}visitorId(){return this.session?.visitorId??null}ensureSession(){return this.session&&this.fresh(this.session)?Promise.resolve(this.session):this.sessionPromise??(this.sessionPromise=this.loadSession().finally(()=>this.sessionPromise=null))}async loadSession(){if(!this.session){let s=this.stored();if(s){if(this.fresh(s)&&!(await this.getConfig()).identity)return this.session=s;this.session=s}}return this.createSession()}async createSession(){let s=await this.getConfig(),e={},n=this.session&&!this.session.verified?this.session:this.stored();if(n?.token&&(e.token=n.token),s.identity){let l=this.session?.verified?this.session.reuseHint:I.get(this.reuseKey);l&&(e.reuseHint=l)}let i=async()=>this.parse(await this.request(this.url("session"),{method:"POST",headers:await this.headers(),body:JSON.stringify(e)})),o=async()=>{if(!this.o.turnstile)throw new E("Verification is required but no Turnstile handler is set",403,"turnstile_required");e.turnstileToken=await this.o.turnstile(s.turnstileSiteKey)},r;try{s.turnstileSiteKey&&!(s.refreshWithoutTurnstile&&!s.identity&&e.token&&!e.reuseHint)&&await o();try{r=await i()}catch(l){if(!(s.turnstileSiteKey&&!e.turnstileToken&&l instanceof E&&l.code==="turnstile_failed"))throw l;await o(),r=await i()}}catch(l){throw l instanceof E&&l.status===401&&I.del(this.reuseKey),l}let a=Date.now(),d={...r,obtainedAt:a};if(this.session=d,d.verified)this.scheduleRefresh(d,a),typeof d.reuseHint=="string"&&d.reuseHint!==""?I.set(this.reuseKey,d.reuseHint):I.del(this.reuseKey);else{I.del(this.reuseKey);let{reuseHint:l,...c}=d;re.set(this.storageKey,JSON.stringify(c))}return d}forgetSession(){if(this.session?.verified){I.del(this.reuseKey),this.session=null;return}this.session=null,re.del(this.storageKey)}async renewSession(){return this.session=null,this.ensureSession()}async jobAccess(s){try{if(!(await this.ensureSession()).verified)throw new E("Sign in to open job access",401,"IDENTITY_REQUIRED");if(s.aborted)throw new E("Job access was cancelled",0,"job_access_unavailable");let n=await this.request(this.url("job-access"),{method:"POST",headers:await this.headers(!0),body:"{}",signal:s,redirect:"error"});return $(await this.parse(n))}catch(e){let n=e instanceof E&&typeof e.code=="string"&&["IDENTITY_REQUIRED","LINK_EXPIRED","LINK_REVOKED","PERMISSION_DENIED","SOURCE_UNAVAILABLE"].includes(e.code)?e.code:"job_access_unavailable";throw new E("Job access is unavailable. Sign in again or retry.",e instanceof E?e.status:0,n)}}async link(){let s=await this.signedInRequest(async()=>this.request(this.url("link"),{method:"POST",headers:await this.headers(!0),body:"{}"}));return this.parse(s)}async history(s,e){let n=await this.signedInRequest(async()=>this.request(this.url("history",{agent:s,conversationId:e}),{headers:await this.headers(!0)}));return(await this.parse(n)).messages}async signedInRequest(s){await this.ensureSession();let e=await s();return e.status!==401||!this.session?.verified?e:(this.forgetSession(),await this.ensureSession(),s())}async chat(s,e,n){await this.ensureSession();let i=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:n});if(i.status===401){let d=this.visitorId();this.forgetSession(),await this.ensureSession(),s.actionsHash&&this.visitorId()!==d&&(s={...s,actionsHash:void 0}),i=await this.request(this.url("chat"),{method:"POST",headers:await this.headers(!0),body:JSON.stringify(s),signal:n})}(!i.ok||!i.body)&&await this.parse(i);let o=i.body.pipeThrough(new TextDecoderStream).getReader(),r="",a=!1;for(;;){let{done:d,value:l}=await o.read();if(d)break;r+=l;let c;for(;(c=r.indexOf(`
18
18
 
19
- `))>=0;){let h=r.slice(0,d);r=r.slice(d+2);let p=h.split(`
20
- `).filter(f=>f.startsWith("data:")).map(f=>f.slice(5).trimStart()).join(`
21
- `);if(!p)continue;let g;try{g=JSON.parse(p)}catch{continue}g.type==="error"&&g.code&&$e.has(g.code)&&(a=!0);try{e(g)}catch{}}}a&&this.session?.verified&&(this.forgetSession(),await this.ensureSession().catch(()=>{}))}};var E=class extends Error{constructor(e){super("Job access could not be opened");u(this,"code",e)}};async function we(i,s){let e=`dewee-job-access-${crypto.randomUUID()}`,t;try{t=window.open("about:blank",e,"popup,width=720,height=800")}catch{throw new E("popup_blocked")}if(!t)throw new E("popup_blocked");let n=new AbortController,o=()=>n.abort(),r,a=new Promise((y,v)=>{r=()=>v(new E(t.closed?"popup_closed":"job_access_unavailable"))});n.signal.addEventListener("abort",r,{once:!0}),s?.addEventListener("abort",o,{once:!0});let l=setTimeout(o,3e4),c=!1,d,h,p=new Promise((y,v)=>{d=y,h=()=>v(new E("job_access_unavailable"))}),g=setInterval(()=>{if(t.closed){o();return}if(c)try{t.location.href!=="about:blank"&&d()}catch{d()}},200);window.addEventListener("pagehide",o,{once:!0});let f,m,w,k=y=>{y.disposition==="enforce"&&(y.effectiveDirective==="form-action"||y.violatedDirective==="form-action")&&h()};try{t.opener=null,s?.aborted&&o();let y=O(await Promise.race([i(n.signal),a]));if(t.closed)throw new E("popup_closed");if(n.signal.aborted)throw new E("job_access_unavailable");w=t.document;let v=w.createElement("meta");v.name="referrer",v.content="strict-origin",w.head.append(v),f=w.createElement("form"),f.method="POST",f.action=y.bootstrap_url,f.target="_self",f.hidden=!0,m=w.createElement("input"),m.type="hidden",m.name="launch_token",m.value=y.launch_token,f.append(m),w.body.append(f),w.addEventListener("securitypolicyviolation",k),HTMLFormElement.prototype.submit.call(f),c=!0,m.value="",y.launch_token="",f.remove(),await Promise.race([p,a])}catch(y){let v=t.closed;try{t.close()}catch{}throw v?new E("popup_closed"):(y instanceof E,y)}finally{m&&(m.value=""),f?.remove(),w?.removeEventListener("securitypolicyviolation",k),clearTimeout(l),clearInterval(g),window.removeEventListener("pagehide",o),s?.removeEventListener("abort",o),n.signal.removeEventListener("abort",r),n.abort()}}var ze=0;function ye(i,s,e,t,n){let o=`dw-confirm-${++ze}`,r=i.getRootNode(),a=r.activeElement,l=document.createElement("div");l.className="confirm-backdrop",l.innerHTML=`
19
+ `))>=0;){let u=r.slice(0,c);r=r.slice(c+2);let f=u.split(`
20
+ `).filter(h=>h.startsWith("data:")).map(h=>h.slice(5).trimStart()).join(`
21
+ `);if(!f)continue;let p;try{p=JSON.parse(f)}catch{continue}p.type==="error"&&p.code&&ct.has(p.code)&&(a=!0);try{e(p)}catch{}}}a&&this.session?.verified&&(this.forgetSession(),await this.ensureSession().catch(()=>{}))}};var A=class extends Error{constructor(e){super("Job access could not be opened");g(this,"code",e)}};async function _e(t,s){let e=`dewee-job-access-${crypto.randomUUID()}`,n;try{n=window.open("about:blank",e,"popup,width=720,height=800")}catch{throw new A("popup_blocked")}if(!n)throw new A("popup_blocked");let i=new AbortController,o=()=>i.abort(),r,a=new Promise((w,x)=>{r=()=>x(new A(n.closed?"popup_closed":"job_access_unavailable"))});i.signal.addEventListener("abort",r,{once:!0}),s?.addEventListener("abort",o,{once:!0});let d=setTimeout(o,3e4),l=!1,c,u,f=new Promise((w,x)=>{c=w,u=()=>x(new A("job_access_unavailable"))}),p=setInterval(()=>{if(n.closed){o();return}if(l)try{n.location.href!=="about:blank"&&c()}catch{c()}},200);window.addEventListener("pagehide",o,{once:!0});let h,m,b,v=w=>{w.disposition==="enforce"&&(w.effectiveDirective==="form-action"||w.violatedDirective==="form-action")&&u()};try{n.opener=null,s?.aborted&&o();let w=$(await Promise.race([t(i.signal),a]));if(n.closed)throw new A("popup_closed");if(i.signal.aborted)throw new A("job_access_unavailable");b=n.document;let x=b.createElement("meta");x.name="referrer",x.content="strict-origin",b.head.append(x),h=b.createElement("form"),h.method="POST",h.action=w.bootstrap_url,h.target="_self",h.hidden=!0,m=b.createElement("input"),m.type="hidden",m.name="launch_token",m.value=w.launch_token,h.append(m),b.body.append(h),b.addEventListener("securitypolicyviolation",v),HTMLFormElement.prototype.submit.call(h),l=!0,m.value="",w.launch_token="",h.remove(),await Promise.race([f,a])}catch(w){let x=n.closed;try{n.close()}catch{}throw x?new A("popup_closed"):(w instanceof A,w)}finally{m&&(m.value=""),h?.remove(),b?.removeEventListener("securitypolicyviolation",v),clearTimeout(d),clearInterval(p),window.removeEventListener("pagehide",o),s?.removeEventListener("abort",o),i.signal.removeEventListener("abort",r),i.abort()}}var ut=0;function Pe(t,s,e,n,i){let o=`dw-confirm-${++ut}`,r=t.getRootNode(),a=r.activeElement,d=document.createElement("div");d.className="confirm-backdrop",d.innerHTML=`
22
22
  <div class="confirm" role="alertdialog" aria-modal="true" aria-labelledby="${o}-t" aria-describedby="${o}-d">
23
23
  <h3 id="${o}-t"></h3>
24
24
  <div id="${o}-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=m=>l.querySelector(m);c("h3").textContent=s.confirmTitle,c("code").textContent=e.name;let d=c(".sens");if(d.textContent=e.sensitivity,d.dataset.level=e.sensitivity,c(".confirm-text").textContent=e.description,e.sensitivity==="destructive"){let m=c(".confirm-warn");m.hidden=!1,m.textContent=s.confirmDestructive}let h=JSON.stringify(t,null,2),p=c(".confirm-args");h&&h!=="{}"?p.textContent=h:p.remove();let g=c(".deny"),f=c(".allow");return g.textContent=s.deny,f.textContent=s.allow,e.sensitivity==="destructive"&&f.classList.add("danger"),new Promise(m=>{let w=!1,k=v=>{w||(w=!0,n?.removeEventListener("abort",y),l.remove(),a?.focus?.(),m(v))},y=()=>k(!1);if(n?.aborted)return k(!1);n?.addEventListener("abort",y,{once:!0}),g.addEventListener("click",()=>k(!1)),f.addEventListener("click",()=>k(!0)),l.addEventListener("keydown",v=>{v.key==="Escape"?(v.preventDefault(),v.stopPropagation(),k(!1)):v.key==="Tab"&&(v.preventDefault(),(r.activeElement===g?f:g).focus())}),i.appendChild(l),g.focus()})}var Je={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.`,identityErrors:{IDENTITY_REQUIRED:"Please sign in to chat.",IDENTITY_LINK_REQUIRED:"Link your chat account to continue.",IDENTITY_CONFLICT:"This chat account is already linked to another user. Unlink it there first.",LINK_EXPIRED:"Your session expired. Send your message again.",LINK_REVOKED:"Your access has ended. Sign in again to continue.",PERMISSION_DENIED:"You don't have access to this.",SOURCE_UNAVAILABLE:"We couldn't check your sign-in right now. Please try again shortly."},admissionErrors:{POLICY_DENIED:"This message can't be sent. Try shortening or rephrasing it.",RATE_LIMITED:"You're sending messages too quickly.",CHALLENGE_REQUIRED:"Extra verification is needed. Please try again later."},cooldown:i=>`You can send again in ${i}s`,cooldownStart:i=>`Sending is paused. You can send again in ${i} second${i===1?"":"s"}.`,cooldownOver:"You can send messages again.",linkErrors:{idempotency_key_reused:"This link request was already used. Please try again.",link_in_progress:"A link is already in progress. Use the code you got, or wait for it to expire.",linking_unavailable:"Linking is not available right now. Please try again later."},linkIntro:"Send this code in a direct message from the chat account you want to link:",linkContinueOn:i=>`Continue on ${i}`,linkExpires:i=>`The code expires in ${i} min.`,linkNoTargets:"No chat app is set up for linking yet.",jobAccess:"Job access",jobAccessOpened:"Job access opened in a new window. Review and confirm any permissions there.",jobAccessUnavailable:"Job access could not be opened. Sign in again or retry.",jobAccessPopupBlocked:"Allow popups for this site, then choose Job access again.",jobAccessPopupClosed:"The window was closed. Choose Job access again to retry."},qe={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.`,identityErrors:{IDENTITY_REQUIRED:"B\u1EA1n \u0111\u0103ng nh\u1EADp \u0111\u1EC3 tr\xF2 chuy\u1EC7n nh\xE9.",IDENTITY_LINK_REQUIRED:"B\u1EA1n c\u1EA7n li\xEAn k\u1EBFt t\xE0i kho\u1EA3n chat \u0111\u1EC3 ti\u1EBFp t\u1EE5c.",IDENTITY_CONFLICT:"T\xE0i kho\u1EA3n chat n\xE0y \u0111\xE3 li\xEAn k\u1EBFt v\u1EDBi ng\u01B0\u1EDDi d\xF9ng kh\xE1c. H\xE3y h\u1EE7y li\xEAn k\u1EBFt \u1EDF \u0111\xF3 tr\u01B0\u1EDBc.",LINK_EXPIRED:"Phi\xEAn \u0111\u0103ng nh\u1EADp \u0111\xE3 h\u1EBFt h\u1EA1n. B\u1EA1n g\u1EEDi l\u1EA1i tin nh\u1EAFn gi\xFAp m\xECnh nh\xE9.",LINK_REVOKED:"Quy\u1EC1n truy c\u1EADp c\u1EE7a b\u1EA1n \u0111\xE3 k\u1EBFt th\xFAc. H\xE3y \u0111\u0103ng nh\u1EADp l\u1EA1i \u0111\u1EC3 ti\u1EBFp t\u1EE5c.",PERMISSION_DENIED:"B\u1EA1n kh\xF4ng c\xF3 quy\u1EC1n th\u1EF1c hi\u1EC7n vi\u1EC7c n\xE0y.",SOURCE_UNAVAILABLE:"Hi\u1EC7n ch\u01B0a ki\u1EC3m tra \u0111\u01B0\u1EE3c \u0111\u0103ng nh\u1EADp c\u1EE7a b\u1EA1n. B\u1EA1n th\u1EED l\u1EA1i sau \xEDt ph\xFAt nh\xE9."},admissionErrors:{POLICY_DENIED:"Kh\xF4ng g\u1EEDi \u0111\u01B0\u1EE3c tin nh\u1EAFn n\xE0y. B\u1EA1n th\u1EED r\xFAt g\u1ECDn ho\u1EB7c di\u1EC5n \u0111\u1EA1t l\u1EA1i nh\xE9.",RATE_LIMITED:"B\u1EA1n \u0111ang g\u1EEDi tin nh\u1EAFn h\u01A1i nhanh.",CHALLENGE_REQUIRED:"C\u1EA7n x\xE1c minh th\xEAm. B\u1EA1n th\u1EED l\u1EA1i sau nh\xE9."},cooldown:i=>`B\u1EA1n c\xF3 th\u1EC3 g\u1EEDi l\u1EA1i sau ${i} gi\xE2y`,cooldownStart:i=>`T\u1EA1m d\u1EEBng g\u1EEDi tin nh\u1EAFn. B\u1EA1n c\xF3 th\u1EC3 g\u1EEDi l\u1EA1i sau ${i} gi\xE2y.`,cooldownOver:"B\u1EA1n c\xF3 th\u1EC3 g\u1EEDi tin nh\u1EAFn ti\u1EBFp r\u1ED3i.",linkErrors:{idempotency_key_reused:"Y\xEAu c\u1EA7u li\xEAn k\u1EBFt n\xE0y \u0111\xE3 \u0111\u01B0\u1EE3c d\xF9ng. B\u1EA1n th\u1EED l\u1EA1i nh\xE9.",link_in_progress:"\u0110ang c\xF3 m\u1ED9t li\xEAn k\u1EBFt ch\u01B0a ho\xE0n t\u1EA5t. H\xE3y d\xF9ng m\xE3 \u0111\xE3 nh\u1EADn ho\u1EB7c ch\u1EDD m\xE3 h\u1EBFt h\u1EA1n.",linking_unavailable:"Hi\u1EC7n ch\u01B0a th\u1EC3 li\xEAn k\u1EBFt. B\u1EA1n th\u1EED l\u1EA1i sau nh\xE9."},linkIntro:"G\u1EEDi m\xE3 n\xE0y trong tin nh\u1EAFn ri\xEAng t\u1EEB t\xE0i kho\u1EA3n chat b\u1EA1n mu\u1ED1n li\xEAn k\u1EBFt:",linkContinueOn:i=>`Ti\u1EBFp t\u1EE5c tr\xEAn ${i}`,linkExpires:i=>`M\xE3 h\u1EBFt h\u1EA1n sau ${i} ph\xFAt.`,linkNoTargets:"Ch\u01B0a c\xF3 \u1EE9ng d\u1EE5ng chat n\xE0o \u0111\u01B0\u1EE3c thi\u1EBFt l\u1EADp \u0111\u1EC3 li\xEAn k\u1EBFt.",jobAccess:"Quy\u1EC1n c\xF4ng vi\u1EC7c",jobAccessOpened:"\u0110\xE3 m\u1EDF c\u1EEDa s\u1ED5 quy\u1EC1n c\xF4ng vi\u1EC7c. H\xE3y xem l\u1EA1i v\xE0 x\xE1c nh\u1EADn quy\u1EC1n t\u1EA1i \u0111\xF3.",jobAccessUnavailable:"Ch\u01B0a m\u1EDF \u0111\u01B0\u1EE3c quy\u1EC1n c\xF4ng vi\u1EC7c. H\xE3y \u0111\u0103ng nh\u1EADp l\u1EA1i ho\u1EB7c th\u1EED l\u1EA1i.",jobAccessPopupBlocked:"Cho ph\xE9p c\u1EEDa s\u1ED5 b\u1EADt l\xEAn cho trang n\xE0y r\u1ED3i ch\u1ECDn Quy\u1EC1n c\xF4ng vi\u1EC7c l\u1EA7n n\u1EEFa.",jobAccessPopupClosed:"C\u1EEDa s\u1ED5 \u0111\xE3 \u0111\xF3ng. Ch\u1ECDn Quy\u1EC1n c\xF4ng vi\u1EC7c l\u1EA7n n\u1EEFa \u0111\u1EC3 th\u1EED l\u1EA1i."};function Z(i){return(i||(typeof document<"u"?document.documentElement.lang:"")||(typeof navigator<"u"?navigator.language:"")||"en").toLowerCase().startsWith("vi")?qe:Je}var A={jobAccess:'<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 3 8 4v5c0 5-8 9-8 9s-8-4-8-9V7l8-4Z"/><path d="m8 12 3 3 5-6"/></svg>',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 b(i,s,e){let t=i.createElement(s);return e&&(t.className=e),t}function Ke(i){let s=i.trim();return/^(https?:|mailto:|tel:)/i.test(s)||/^[/#?]/.test(s)&&!s.startsWith("//")?s:null}var Ve=/(`+)([\s\S]*?[^`])\1(?!`)|\*\*([^*]+?)\*\*|__([^_]+?)__|~~([^~]+?)~~|\*([^*\s][^*]*?)\*|(?<![\w])_([^_\s][^_]*?)_(?![\w])|\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/;function C(i,s,e){let t=new RegExp(Ve.source,"g"),n=0;for(let o;o=t.exec(s);)if(o.index>n&&e.appendChild(i.createTextNode(s.slice(n,o.index))),n=o.index+o[0].length,o[2]!==void 0){let r=b(i,"code");r.textContent=o[2],e.appendChild(r)}else if(o[3]!==void 0||o[4]!==void 0){let r=b(i,"strong");C(i,o[3]??o[4],r),e.appendChild(r)}else if(o[5]!==void 0){let r=b(i,"del");C(i,o[5],r),e.appendChild(r)}else if(o[6]!==void 0||o[7]!==void 0){let r=b(i,"em");C(i,o[6]??o[7],r),e.appendChild(r)}else if(o[8]!==void 0){let r=Ke(o[9]);if(r){let a=b(i,"a");a.href=r,a.target="_blank",a.rel="noopener noreferrer nofollow",C(i,o[8],a),e.appendChild(a)}else e.appendChild(i.createTextNode(o[8]))}else if(o[10]!==void 0){let r=b(i,"a");r.href=o[10],r.target="_blank",r.rel="noopener noreferrer nofollow",r.textContent=o[10],e.appendChild(r)}n<s.length&&e.appendChild(i.createTextNode(s.slice(n)))}var ve=i=>/^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(i),xe=i=>i.trim().replace(/^\||\|$/g,"").split("|").map(s=>s.trim());function L(i,s){let e=i.createDocumentFragment(),t=s.replace(/\r\n?/g,`
34
+ </div>`;let l=m=>d.querySelector(m);l("h3").textContent=s.confirmTitle,l("code").textContent=e.name;let c=l(".sens");if(c.textContent=e.sensitivity,c.dataset.level=e.sensitivity,l(".confirm-text").textContent=e.description,e.sensitivity==="destructive"){let m=l(".confirm-warn");m.hidden=!1,m.textContent=s.confirmDestructive}let u=JSON.stringify(n,null,2),f=l(".confirm-args");u&&u!=="{}"?f.textContent=u:f.remove();let p=l(".deny"),h=l(".allow");return p.textContent=s.deny,h.textContent=s.allow,e.sensitivity==="destructive"&&h.classList.add("danger"),new Promise(m=>{let b=!1,v=x=>{b||(b=!0,i?.removeEventListener("abort",w),d.remove(),a?.focus?.(),m(x))},w=()=>v(!1);if(i?.aborted)return v(!1);i?.addEventListener("abort",w,{once:!0}),p.addEventListener("click",()=>v(!1)),h.addEventListener("click",()=>v(!0)),d.addEventListener("keydown",x=>{x.key==="Escape"?(x.preventDefault(),x.stopPropagation(),v(!1)):x.key==="Tab"&&(x.preventDefault(),(r.activeElement===p?h:p).focus())}),t.appendChild(d),p.focus()})}var pt={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:t=>`Thought for ${t}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:t=>`Stopped after ${t} automatic action rounds. Send a message to continue.`,identityErrors:{IDENTITY_REQUIRED:"Please sign in to chat.",IDENTITY_LINK_REQUIRED:"Link your chat account to continue.",IDENTITY_CONFLICT:"This chat account is already linked to another user. Unlink it there first.",LINK_EXPIRED:"Your session expired. Send your message again.",LINK_REVOKED:"Your access has ended. Sign in again to continue.",PERMISSION_DENIED:"You don't have access to this.",SOURCE_UNAVAILABLE:"We couldn't check your sign-in right now. Please try again shortly."},admissionErrors:{POLICY_DENIED:"This message can't be sent. Try shortening or rephrasing it.",RATE_LIMITED:"You're sending messages too quickly.",CHALLENGE_REQUIRED:"Extra verification is needed. Please try again later."},cooldown:t=>`You can send again in ${t}s`,cooldownStart:t=>`Sending is paused. You can send again in ${t} second${t===1?"":"s"}.`,cooldownOver:"You can send messages again.",linkErrors:{idempotency_key_reused:"This link request was already used. Please try again.",link_in_progress:"A link is already in progress. Use the code you got, or wait for it to expire.",linking_unavailable:"Linking is not available right now. Please try again later."},linkIntro:"Send this code in a direct message from the chat account you want to link:",linkContinueOn:t=>`Continue on ${t}`,linkExpires:t=>`The code expires in ${t} min.`,linkNoTargets:"No chat app is set up for linking yet.",jobAccess:"Job access",jobAccessOpened:"Job access opened in a new window. Review and confirm any permissions there.",jobAccessUnavailable:"Job access could not be opened. Sign in again or retry.",jobAccessPopupBlocked:"Allow popups for this site, then choose Job access again.",jobAccessPopupClosed:"The window was closed. Choose Job access again to retry."},ht={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:t=>`\u0110\xE3 suy ngh\u0129 ${t} 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:t=>`\u0110\xE3 d\u1EEBng sau ${t} l\u01B0\u1EE3t thao t\xE1c t\u1EF1 \u0111\u1ED9ng. G\u1EEDi tin nh\u1EAFn \u0111\u1EC3 ti\u1EBFp t\u1EE5c.`,identityErrors:{IDENTITY_REQUIRED:"B\u1EA1n \u0111\u0103ng nh\u1EADp \u0111\u1EC3 tr\xF2 chuy\u1EC7n nh\xE9.",IDENTITY_LINK_REQUIRED:"B\u1EA1n c\u1EA7n li\xEAn k\u1EBFt t\xE0i kho\u1EA3n chat \u0111\u1EC3 ti\u1EBFp t\u1EE5c.",IDENTITY_CONFLICT:"T\xE0i kho\u1EA3n chat n\xE0y \u0111\xE3 li\xEAn k\u1EBFt v\u1EDBi ng\u01B0\u1EDDi d\xF9ng kh\xE1c. H\xE3y h\u1EE7y li\xEAn k\u1EBFt \u1EDF \u0111\xF3 tr\u01B0\u1EDBc.",LINK_EXPIRED:"Phi\xEAn \u0111\u0103ng nh\u1EADp \u0111\xE3 h\u1EBFt h\u1EA1n. B\u1EA1n g\u1EEDi l\u1EA1i tin nh\u1EAFn gi\xFAp m\xECnh nh\xE9.",LINK_REVOKED:"Quy\u1EC1n truy c\u1EADp c\u1EE7a b\u1EA1n \u0111\xE3 k\u1EBFt th\xFAc. H\xE3y \u0111\u0103ng nh\u1EADp l\u1EA1i \u0111\u1EC3 ti\u1EBFp t\u1EE5c.",PERMISSION_DENIED:"B\u1EA1n kh\xF4ng c\xF3 quy\u1EC1n th\u1EF1c hi\u1EC7n vi\u1EC7c n\xE0y.",SOURCE_UNAVAILABLE:"Hi\u1EC7n ch\u01B0a ki\u1EC3m tra \u0111\u01B0\u1EE3c \u0111\u0103ng nh\u1EADp c\u1EE7a b\u1EA1n. B\u1EA1n th\u1EED l\u1EA1i sau \xEDt ph\xFAt nh\xE9."},admissionErrors:{POLICY_DENIED:"Kh\xF4ng g\u1EEDi \u0111\u01B0\u1EE3c tin nh\u1EAFn n\xE0y. B\u1EA1n th\u1EED r\xFAt g\u1ECDn ho\u1EB7c di\u1EC5n \u0111\u1EA1t l\u1EA1i nh\xE9.",RATE_LIMITED:"B\u1EA1n \u0111ang g\u1EEDi tin nh\u1EAFn h\u01A1i nhanh.",CHALLENGE_REQUIRED:"C\u1EA7n x\xE1c minh th\xEAm. B\u1EA1n th\u1EED l\u1EA1i sau nh\xE9."},cooldown:t=>`B\u1EA1n c\xF3 th\u1EC3 g\u1EEDi l\u1EA1i sau ${t} gi\xE2y`,cooldownStart:t=>`T\u1EA1m d\u1EEBng g\u1EEDi tin nh\u1EAFn. B\u1EA1n c\xF3 th\u1EC3 g\u1EEDi l\u1EA1i sau ${t} gi\xE2y.`,cooldownOver:"B\u1EA1n c\xF3 th\u1EC3 g\u1EEDi tin nh\u1EAFn ti\u1EBFp r\u1ED3i.",linkErrors:{idempotency_key_reused:"Y\xEAu c\u1EA7u li\xEAn k\u1EBFt n\xE0y \u0111\xE3 \u0111\u01B0\u1EE3c d\xF9ng. B\u1EA1n th\u1EED l\u1EA1i nh\xE9.",link_in_progress:"\u0110ang c\xF3 m\u1ED9t li\xEAn k\u1EBFt ch\u01B0a ho\xE0n t\u1EA5t. H\xE3y d\xF9ng m\xE3 \u0111\xE3 nh\u1EADn ho\u1EB7c ch\u1EDD m\xE3 h\u1EBFt h\u1EA1n.",linking_unavailable:"Hi\u1EC7n ch\u01B0a th\u1EC3 li\xEAn k\u1EBFt. B\u1EA1n th\u1EED l\u1EA1i sau nh\xE9."},linkIntro:"G\u1EEDi m\xE3 n\xE0y trong tin nh\u1EAFn ri\xEAng t\u1EEB t\xE0i kho\u1EA3n chat b\u1EA1n mu\u1ED1n li\xEAn k\u1EBFt:",linkContinueOn:t=>`Ti\u1EBFp t\u1EE5c tr\xEAn ${t}`,linkExpires:t=>`M\xE3 h\u1EBFt h\u1EA1n sau ${t} ph\xFAt.`,linkNoTargets:"Ch\u01B0a c\xF3 \u1EE9ng d\u1EE5ng chat n\xE0o \u0111\u01B0\u1EE3c thi\u1EBFt l\u1EADp \u0111\u1EC3 li\xEAn k\u1EBFt.",jobAccess:"Quy\u1EC1n c\xF4ng vi\u1EC7c",jobAccessOpened:"\u0110\xE3 m\u1EDF c\u1EEDa s\u1ED5 quy\u1EC1n c\xF4ng vi\u1EC7c. H\xE3y xem l\u1EA1i v\xE0 x\xE1c nh\u1EADn quy\u1EC1n t\u1EA1i \u0111\xF3.",jobAccessUnavailable:"Ch\u01B0a m\u1EDF \u0111\u01B0\u1EE3c quy\u1EC1n c\xF4ng vi\u1EC7c. H\xE3y \u0111\u0103ng nh\u1EADp l\u1EA1i ho\u1EB7c th\u1EED l\u1EA1i.",jobAccessPopupBlocked:"Cho ph\xE9p c\u1EEDa s\u1ED5 b\u1EADt l\xEAn cho trang n\xE0y r\u1ED3i ch\u1ECDn Quy\u1EC1n c\xF4ng vi\u1EC7c l\u1EA7n n\u1EEFa.",jobAccessPopupClosed:"C\u1EEDa s\u1ED5 \u0111\xE3 \u0111\xF3ng. Ch\u1ECDn Quy\u1EC1n c\xF4ng vi\u1EC7c l\u1EA7n n\u1EEFa \u0111\u1EC3 th\u1EED l\u1EA1i."};function ce(t){return(t||(typeof document<"u"?document.documentElement.lang:"")||(typeof navigator<"u"?navigator.language:"")||"en").toLowerCase().startsWith("vi")?ht:pt}var C={jobAccess:'<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 3 8 4v5c0 5-8 9-8 9s-8-4-8-9V7l8-4Z"/><path d="m8 12 3 3 5-6"/></svg>',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 y(t,s,e){let n=t.createElement(s);return e&&(n.className=e),n}function gt(t){let s=t.trim();return/^(https?:|mailto:|tel:)/i.test(s)||/^[/#?]/.test(s)&&!s.startsWith("//")?s:null}var ft=/(`+)([\s\S]*?[^`])\1(?!`)|\*\*([^*]+?)\*\*|__([^_]+?)__|~~([^~]+?)~~|\*([^*\s][^*]*?)\*|(?<![\w])_([^_\s][^_]*?)_(?![\w])|\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/;function T(t,s,e){let n=new RegExp(ft.source,"g"),i=0;for(let o;o=n.exec(s);)if(o.index>i&&e.appendChild(t.createTextNode(s.slice(i,o.index))),i=o.index+o[0].length,o[2]!==void 0){let r=y(t,"code");r.textContent=o[2],e.appendChild(r)}else if(o[3]!==void 0||o[4]!==void 0){let r=y(t,"strong");T(t,o[3]??o[4],r),e.appendChild(r)}else if(o[5]!==void 0){let r=y(t,"del");T(t,o[5],r),e.appendChild(r)}else if(o[6]!==void 0||o[7]!==void 0){let r=y(t,"em");T(t,o[6]??o[7],r),e.appendChild(r)}else if(o[8]!==void 0){let r=gt(o[9]);if(r){let a=y(t,"a");a.href=r,a.target="_blank",a.rel="noopener noreferrer nofollow",T(t,o[8],a),e.appendChild(a)}else e.appendChild(t.createTextNode(o[8]))}else if(o[10]!==void 0){let r=y(t,"a");r.href=o[10],r.target="_blank",r.rel="noopener noreferrer nofollow",r.textContent=o[10],e.appendChild(r)}i<s.length&&e.appendChild(t.createTextNode(s.slice(i)))}var Ne=t=>/^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(t),He=t=>t.trim().replace(/^\||\|$/g,"").split("|").map(s=>s.trim());function M(t,s){let e=t.createDocumentFragment(),n=s.replace(/\r\n?/g,`
35
35
  `).split(`
36
- `),n=0;for(;n<t.length;){let o=t[n];if(!o.trim()){n++;continue}let r=o.match(/^\s*(```+|~~~+)\s*([\w+-]*)/);if(r){let h=[];for(n++;n<t.length&&!t[n].trim().startsWith(r[1]);)h.push(t[n++]);n++;let p=b(i,"pre"),g=b(i,"code");r[2]&&(g.dataset.lang=r[2]),g.textContent=h.join(`
37
- `),p.appendChild(g),e.appendChild(p);continue}let a=o.match(/^(#{1,6})\s+(.*)$/);if(a){let h=`h${Math.min(6,a[1].length+2)}`,p=b(i,h);C(i,a[2],p),e.appendChild(p),n++;continue}if(/^\s*([-*_])(\s*\1){2,}\s*$/.test(o)){e.appendChild(b(i,"hr")),n++;continue}if(o.includes("|")&&n+1<t.length&&ve(t[n+1])){let h=b(i,"div","table"),p=b(i,"table"),g=b(i,"thead"),f=b(i,"tr");for(let w of xe(o)){let k=b(i,"th");C(i,w,k),f.appendChild(k)}g.appendChild(f),p.appendChild(g);let m=b(i,"tbody");for(n+=2;n<t.length&&t[n].includes("|")&&t[n].trim();){let w=b(i,"tr");for(let k of xe(t[n])){let y=b(i,"td");C(i,k,y),w.appendChild(y)}m.appendChild(w),n++}p.appendChild(m),h.appendChild(p),e.appendChild(h);continue}if(/^\s*>/.test(o)){let h=[];for(;n<t.length&&/^\s*>/.test(t[n]);)h.push(t[n++].replace(/^\s*>\s?/,""));let p=b(i,"blockquote");p.appendChild(L(i,h.join(`
38
- `))),e.appendChild(p);continue}let l=o.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(l){let h=/\d/.test(l[2]),p=b(i,h?"ol":"ul");h&&parseInt(l[2],10)!==1&&p.setAttribute("start",String(parseInt(l[2],10)));let g=l[1].length;for(;n<t.length;){let f=t[n].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(!f||f[1].length<g||/\d/.test(f[2])!==h)break;if(f[1].length>g){let w=[];for(;n<t.length&&/^\s+/.test(t[n])&&t[n].match(/^(\s*)/)[1].length>g;)w.push(t[n++].slice(g+2));p.lastElementChild?.appendChild(L(i,w.join(`
39
- `)));continue}let m=b(i,"li");C(i,f[3],m),p.appendChild(m),n++}e.appendChild(p);continue}let c=[];for(;n<t.length&&t[n].trim()&&!/^\s*(```|~~~|#{1,6}\s|>|([-*+]|\d+[.)])\s)/.test(t[n])&&!(t[n].includes("|")&&n+1<t.length&&ve(t[n+1]));)c.push(t[n++]);c.length||c.push(t[n++]);let d=b(i,"p");c.forEach((h,p)=>{p&&d.appendChild(b(i,"br")),C(i,h,d)}),e.appendChild(d)}return e}var H=class{constructor(s,e){u(this,"onTick",s);u(this,"onEnd",e);u(this,"until",0);u(this,"tick");u(this,"done")}get active(){return Date.now()<this.until}get secondsLeft(){return Math.max(0,Math.ceil((this.until-Date.now())/1e3))}start(s){let e=Date.now()+Math.max(0,s);e<=this.until||(this.until=e,this.clear(),this.tick=setInterval(()=>this.onTick(this.secondsLeft),1e3),this.done=setTimeout(()=>{this.until=0,this.clear(),this.onEnd()},e-Date.now()))}clear(){clearInterval(this.tick),clearTimeout(this.done)}};var ke=`
36
+ `),i=0;for(;i<n.length;){let o=n[i];if(!o.trim()){i++;continue}let r=o.match(/^\s*(```+|~~~+)\s*([\w+-]*)/);if(r){let u=[];for(i++;i<n.length&&!n[i].trim().startsWith(r[1]);)u.push(n[i++]);i++;let f=y(t,"pre"),p=y(t,"code");r[2]&&(p.dataset.lang=r[2]),p.textContent=u.join(`
37
+ `),f.appendChild(p),e.appendChild(f);continue}let a=o.match(/^(#{1,6})\s+(.*)$/);if(a){let u=`h${Math.min(6,a[1].length+2)}`,f=y(t,u);T(t,a[2],f),e.appendChild(f),i++;continue}if(/^\s*([-*_])(\s*\1){2,}\s*$/.test(o)){e.appendChild(y(t,"hr")),i++;continue}if(o.includes("|")&&i+1<n.length&&Ne(n[i+1])){let u=y(t,"div","table"),f=y(t,"table"),p=y(t,"thead"),h=y(t,"tr");for(let b of He(o)){let v=y(t,"th");T(t,b,v),h.appendChild(v)}p.appendChild(h),f.appendChild(p);let m=y(t,"tbody");for(i+=2;i<n.length&&n[i].includes("|")&&n[i].trim();){let b=y(t,"tr");for(let v of He(n[i])){let w=y(t,"td");T(t,v,w),b.appendChild(w)}m.appendChild(b),i++}f.appendChild(m),u.appendChild(f),e.appendChild(u);continue}if(/^\s*>/.test(o)){let u=[];for(;i<n.length&&/^\s*>/.test(n[i]);)u.push(n[i++].replace(/^\s*>\s?/,""));let f=y(t,"blockquote");f.appendChild(M(t,u.join(`
38
+ `))),e.appendChild(f);continue}let d=o.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(d){let u=/\d/.test(d[2]),f=y(t,u?"ol":"ul");u&&parseInt(d[2],10)!==1&&f.setAttribute("start",String(parseInt(d[2],10)));let p=d[1].length;for(;i<n.length;){let h=n[i].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(!h||h[1].length<p||/\d/.test(h[2])!==u)break;if(h[1].length>p){let b=[];for(;i<n.length&&/^\s+/.test(n[i])&&n[i].match(/^(\s*)/)[1].length>p;)b.push(n[i++].slice(p+2));f.lastElementChild?.appendChild(M(t,b.join(`
39
+ `)));continue}let m=y(t,"li");T(t,h[3],m),f.appendChild(m),i++}e.appendChild(f);continue}let l=[];for(;i<n.length&&n[i].trim()&&!/^\s*(```|~~~|#{1,6}\s|>|([-*+]|\d+[.)])\s)/.test(n[i])&&!(n[i].includes("|")&&i+1<n.length&&Ne(n[i+1]));)l.push(n[i++]);l.length||l.push(n[i++]);let c=y(t,"p");l.forEach((u,f)=>{f&&c.appendChild(y(t,"br")),T(t,u,c)}),e.appendChild(c)}return e}var j=class{constructor(s,e){g(this,"onTick",s);g(this,"onEnd",e);g(this,"until",0);g(this,"tick");g(this,"done")}get active(){return Date.now()<this.until}get secondsLeft(){return Math.max(0,Math.ceil((this.until-Date.now())/1e3))}start(s){let e=Date.now()+Math.max(0,s);e<=this.until||(this.until=e,this.clear(),this.tick=setInterval(()=>this.onTick(this.secondsLeft),1e3),this.done=setTimeout(()=>{this.until=0,this.clear(),this.onEnd()},e-Date.now()))}clear(){clearInterval(this.tick),clearTimeout(this.done)}};var De=`
40
40
  :host {
41
41
  --dw-accent: #5446e8;
42
42
  --dw-accent-contrast: #fff;
@@ -305,8 +305,8 @@ textarea::placeholder { color: var(--dw-muted); }
305
305
  .agent-select { font-size: 16px; max-width: 200px; }
306
306
  }
307
307
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
308
- `;var Ue=5e3,Se=["show-reasoning","show-tool-calls","allow-agent-switch","open","persist","hide-branding","launcher-avatar"],Ee=["endpoint","agent","title","subtitle","greeting","intro","placeholder","suggestions","avatar","position","mode","theme","accent-color","locale","gateway-url","gateway-token","width","height","z-index",...Se],Ae=i=>i.replace(/-([a-z])/g,(s,e)=>e.toUpperCase()),Ye=i=>i!==null&&!/^(false|0|off|no)$/i.test(i),Ce=()=>{let i=crypto.getRandomValues(new Uint8Array(9));return Array.from(i,s=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[s&63]).join("")},Fe=i=>i.replace(/^mcp_[^_]+(?:_[^_]+)*?__/,"").replace(/_/g," "),R={get(i){try{return localStorage.getItem(i)}catch{return null}},set(i,s){try{localStorage.setItem(i,s)}catch{}}},We=null;function Ge(){return We??(We=new Promise((i,s)=>{if(window.turnstile)return i();let e=document.createElement("script");e.src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",e.async=!0,e.onload=()=>i(),e.onerror=()=>s(new Error("Could not load Turnstile")),document.head.appendChild(e)}))}var Qe=typeof HTMLElement>"u"?class{}:HTMLElement,M=class extends Qe{constructor(){super();u(this,"root");u(this,"overrides",{});u(this,"api",null);u(this,"apiKey","");u(this,"server",null);u(this,"agentId","");u(this,"conversationId","");u(this,"messages",[]);u(this,"running",null);u(this,"jobAccessLaunch",null);u(this,"activated",!1);u(this,"strings",Z());u(this,"mql",null);u(this,"registry",new D);u(this,"propActionNames",new Set);u(this,"hooks",{});u(this,"acks",new Map);u(this,"cooldown",new H(e=>this.renderCooldown(e),()=>this.endCooldown()));u(this,"els");u(this,"onScheme",()=>this.applyTheme());u(this,"pendingFrame",0);u(this,"following",!0);this.root=this.attachShadow({mode:"open"})}static get observedAttributes(){return Ee}get config(){let e={endpoint:"/api/dewee"};for(let t of Ee){let n=this.getAttribute(t);n!==null&&(Se.includes(t)?e[Ae(t)]=Ye(n):t==="suggestions"?e.suggestions=n.trim().startsWith("[")?tt(n,[]):n.split("|").map(o=>o.trim()).filter(Boolean):t==="z-index"?e.zIndex=Number(n):e[Ae(t)]=n)}return{...e,...this.overrides,...this.hooks}}configure(e){let{actions:t,confirm:n,getPageContext:o,headers:r,fetch:a,...l}=e,c=d=>Object.prototype.hasOwnProperty.call(e,d);c("actions")&&this.setPropActions(t??[]),c("confirm")&&(this.hooks.confirm=n),c("getPageContext")&&(this.hooks.getPageContext=o),c("headers")&&(this.hooks.headers=r),c("fetch")&&(this.hooks.fetch=a),Object.keys(l).length&&(this.overrides={...this.overrides,...l},this.els&&this.applyConfig())}registerActions(e){this.registry.register(e)}unregisterAction(e){return this.propActionNames.delete(e),this.registry.unregister(e)}get headers(){return this.hooks.headers}set headers(e){this.hooks.headers=e}get fetch(){return this.hooks.fetch}set fetch(e){this.hooks.fetch=e}get actionNames(){return this.registry.names()}setPropActions(e){let t=new Set(e.map(n=>n.name));for(let n of this.propActionNames)t.has(n)||this.registry.unregister(n);e.length&&this.registry.register(e),this.propActionNames=t}open(){this.config.mode!=="inline"&&(this.setAttribute("data-open",""),this.removeAttribute("data-unread"),this.els.launcher.setAttribute("aria-expanded","true"),this.activate(),setTimeout(()=>this.els.input.focus(),120),this.emit("dewee:open"))}close(){this.config.mode!=="inline"&&(this.removeAttribute("data-open"),this.els.launcher.setAttribute("aria-expanded","false"),this.emit("dewee:close"))}toggle(){this.hasAttribute("data-open")?this.close():this.open()}async send(e){let t=e.trim();if(!t||this.running||this.cooldown.active)return;this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate(),this.els.input.value="",this.autosize();let n={message:t,hidden:!1};for(let o=0;n;o++){let r=await this.runTurn(n.message,n.hidden);if(n=null,r.status!=="done"||r.stopped||!this.registry.size)break;let{calls:a}=K(r.content);if(!a.length)break;r.actions=a.map(d=>({id:d.id,name:d.name,args:d.ok?d.args:void 0,status:"pending"}));let l=this.config.maxActionRounds??5;if(o>=l){for(let d of r.actions)Object.assign(d,{status:"error",error:"Automatic action limit reached"});this.updateMessage(r),this.showNotice(this.strings.actionLimit(l));break}this.updateMessage(r);let c=await this.runActions(r,a);c&&(n={message:he(c,this.server?.maxMessageLength??4e3),hidden:!0})}}async runTurn(e,t){this.pushMessage({role:"user",content:e,hidden:t});let n={role:"assistant",content:"",status:"streaming",startedAt:Date.now()};this.pushMessage(n),this.setRunning(new AbortController),this.emit("dewee:send",{message:e,agent:this.agentId,conversationId:this.conversationId,auto:t||void 0});try{await this.client().chat(this.chatBody(e),o=>this.onStreamEvent(n,o),this.running.signal),n.status==="streaming"&&(n.status="done")}catch(o){o.name==="AbortError"?(n.status="done",n.stopped=!0,n.content||(n.content=`_${this.strings.stopped}_`)):(n.status="error",this.showError(n,o))}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(e){let t={message:e,agent:this.agentId,conversationId:this.conversationId};if(this.registry.size){t.actions=this.registry.manifest();let o=this.getAck();o&&(t.actionsHash=o)}let n=this.pageContext();return n!==void 0&&(t.pageContext=n),t}pageContext(){let e=this.hooks.getPageContext;if(e)try{let t=e();if(t==null)return;let n=new TextEncoder().encode(JSON.stringify(t)).length;return n<=S.maxPageContextBytes?t:(console.warn(`[dewee-webchat] getPageContext() returned ${n} bytes; the limit is ${S.maxPageContextBytes}. Context not sent.`),{truncated:!0})}catch(t){console.warn("[dewee-webchat] getPageContext() failed:",t);return}}ackKey(){return`dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${this.conversationId}`}getAck(){return this.acks.get(this.ackKey())??(this.config.persist!==!1?R.get(this.ackKey())??void 0:void 0)}setAck(e){this.acks.set(this.ackKey(),e),this.config.persist!==!1&&R.set(this.ackKey(),e)}async runActions(e,t){let n=new AbortController;this.setRunning(n);let o=[],r={agent:this.agentId,conversationId:this.conversationId};try{for(let[a,l]of t.entries()){let c=e.actions[a],d=l.ok?this.registry.get(l.name)?.sensitivity:void 0,h=l.ok?l.args:void 0;this.emit("dewee:action",{phase:"start",id:l.id,name:l.name,sensitivity:d,args:h,...r});let p=await me(l,{registry:this.registry,confirm:(g,f)=>this.confirmAction(g,f,n.signal),signal:n.signal,timeoutMs:this.config.actionTimeoutMs,...r,onStatus:g=>{c.status=g,this.updateMessage(e)}});p.error&&(c.error=p.error),this.updateMessage(e),o.push(p),this.emit("dewee:action",{phase:"result",id:p.id,name:p.name,sensitivity:d,args:h,status:p.status,result:p.result,error:p.error,...r})}}finally{this.setRunning(null)}return n.signal.aborted?null:o}confirmAction(e,t,n){return this.hooks.confirm?this.hooks.confirm(e,t):(this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),ye(this.els.panel,this.strings,e,t,n))}showNotice(e){let t=document.createElement("div");t.className="notice",t.setAttribute("role","status"),t.textContent=e,this.els.log.appendChild(t),this.scrollToBottom(!0)}stop(){this.running?.abort()}async renewSession(){this.stop();try{await this.client().renewSession()}catch(e){this.showError(null,e);return}this.activated&&await this.loadConversation()}reuseHint(){return this.client().reuseHint()}async startLink(){this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate();try{let e=await this.client().link();return this.renderLink(e),this.emit("dewee:link",{expiresAt:e.expiresAt,deepLinks:e.deepLinks}),e}catch(e){return this.showError(null,e),null}}async openJobAccess(){if(this.jobAccessLaunch)return;let e=new AbortController;this.jobAccessLaunch=e,this.els.jobAccess.disabled=!0;try{await we(t=>this.client().jobAccess(t),e.signal),this.showNotice(this.strings.jobAccessOpened)}catch(t){if(e.signal.aborted)return;t instanceof E?this.showNotice(t.code==="popup_blocked"?this.strings.jobAccessPopupBlocked:t.code==="popup_closed"?this.strings.jobAccessPopupClosed:this.strings.jobAccessUnavailable):t instanceof x&&j(t.code)?this.showError(null,t):this.showNotice(this.strings.jobAccessUnavailable)}finally{this.jobAccessLaunch=null,this.els.jobAccess.disabled=!1}}renderLink(e){let t=this.strings,n=document.createElement("div");n.className="msg assistant link";let o=document.createElement("div");o.className="bubble";let r=document.createElement("div");r.className="md";let a=d=>{let h=document.createElement("p");return h.textContent=d,h},l=document.createElement("code");l.textContent=e.proofCode;let c=document.createElement("p");if(c.appendChild(l),r.append(a(t.linkIntro),c),e.deepLinks.length){let d=document.createElement("ul");for(let h of e.deepLinks){let p=document.createElement("li"),g=h.displayName||h.name;p.textContent=t.linkContinueOn(et(h.channelType))+(g?` \xB7 ${g}`:""),d.appendChild(p)}r.appendChild(d)}else r.appendChild(a(t.linkNoTargets));r.appendChild(a(t.linkExpires(Math.max(1,Math.round((e.expiresAt-Date.now())/6e4))))),o.appendChild(r),n.appendChild(o),this.messages.length||this.els.log.replaceChildren(),this.els.log.appendChild(n),this.scrollToBottom(!0)}newConversation(){this.stop(),this.conversationId=Ce(),this.config.persist!==!1&&R.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.jobAccessLaunch?.abort(),this.stop(),this.mql?.removeEventListener("change",this.onScheme)}attributeChangedCallback(){this.els&&this.applyConfig()}emit(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}client(){let e=this.config,t=[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|");return(!this.api||t!==this.apiKey)&&(this.apiKey=t,this.api=new I({endpoint:e.endpoint,gatewayUrl:e.gatewayUrl,gatewayToken:e.gatewayToken,turnstile:n=>this.challenge(n),headers:()=>{let n=this.hooks.headers;return(typeof n=="function"?n():n)??{}},fetch:(n,o)=>(this.hooks.fetch??globalThis.fetch)(n,o)})),this.api}convKey(){return`dewee-webchat:conv:${this.config.endpoint}:${this.config.gatewayUrl||""}:${this.agentId}`}applyTheme(){let e=this.config.theme||"light",t=e==="dark"||e==="auto"&&!!this.mql?.matches;this.setAttribute("data-theme",t?"dark":"light")}applyConfig(){let e=this.config;this.strings=Z(e.locale);let t=this.strings;this.setAttribute("data-position",e.position||"bottom-right"),this.setAttribute("data-mode",e.mode||"floating"),e.theme==="auto"&&!this.mql&&(this.mql=matchMedia("(prefers-color-scheme: dark)"),this.mql.addEventListener("change",this.onScheme)),this.applyTheme();let n=(r,a)=>a!==void 0&&a!==""?this.style.setProperty(r,String(a)):this.style.removeProperty(r);n("--dw-accent",e.accentColor),n("--dw-width",e.width),n("--dw-height",e.height),n("--dw-z",e.zIndex);let o=this.els;if(o.title.textContent=e.title||t.title,o.avatar.replaceChildren(),e.avatar){let r=document.createElement("img");r.src=e.avatar,r.alt="",o.avatar.appendChild(r)}else o.avatar.textContent=(e.title||t.title).trim().charAt(0).toUpperCase();if(o.launcher.innerHTML=e.avatar&&e.launcherAvatar?"":A.chat,e.avatar&&e.launcherAvatar){let r=document.createElement("img");r.src=e.avatar,r.alt="",o.launcher.appendChild(r)}o.launcher.insertAdjacentHTML("beforeend",'<span class="badge"></span>'),o.launcher.setAttribute("aria-label",t.open),o.close.setAttribute("aria-label",t.close),o.close.title=t.close,o.newChat.setAttribute("aria-label",t.newChat),o.newChat.title=t.newChat,o.jobAccess.setAttribute("aria-label",t.jobAccess),o.jobAccess.title=t.jobAccess,o.input.placeholder=e.placeholder||t.placeholder,o.foot.hidden=!!e.hideBranding,o.foot.querySelector(".by").textContent=t.poweredBy+" ",this.renderSubtitle(),this.updateSendButton(),this.activated&&this.api&&this.apiKey!==[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|")?(this.activated=!1,this.server=null,this.activate()):this.activated&&e.agent&&e.agent!==this.agentId&&this.server?this.switchAgent(e.agent):this.activated&&this.renderLog()}renderShell(){this.root.innerHTML=`
309
- <style>${ke}</style>
308
+ `;var mt=5e3,Be=["show-reasoning","show-tool-calls","allow-agent-switch","open","persist","hide-branding","launcher-avatar","dom-actions"],Oe=["endpoint","agent","title","subtitle","greeting","intro","placeholder","suggestions","avatar","position","mode","theme","accent-color","locale","gateway-url","gateway-token","width","height","z-index","dom-actions-root",...Be],$e=t=>t.replace(/-([a-z])/g,(s,e)=>e.toUpperCase()),bt=t=>t!==null&&!/^(false|0|off|no)$/i.test(t),je=()=>{let t=crypto.getRandomValues(new Uint8Array(9));return Array.from(t,s=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[s&63]).join("")},wt=t=>t.replace(/^mcp_[^_]+(?:_[^_]+)*?__/,"").replace(/_/g," "),_={get(t){try{return localStorage.getItem(t)}catch{return null}},set(t,s){try{localStorage.setItem(t,s)}catch{}}},yt=null;function xt(){return yt??(yt=new Promise((t,s)=>{if(window.turnstile)return t();let e=document.createElement("script");e.src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",e.async=!0,e.onload=()=>t(),e.onerror=()=>s(new Error("Could not load Turnstile")),document.head.appendChild(e)}))}var vt=typeof HTMLElement>"u"?class{}:HTMLElement,H=class extends vt{constructor(){super();g(this,"root");g(this,"overrides",{});g(this,"api",null);g(this,"apiKey","");g(this,"server",null);g(this,"agentId","");g(this,"conversationId","");g(this,"messages",[]);g(this,"running",null);g(this,"jobAccessLaunch",null);g(this,"activated",!1);g(this,"strings",ce());g(this,"mql",null);g(this,"registry",new N);g(this,"propActionNames",new Set);g(this,"hooks",{});g(this,"dom",null);g(this,"acks",new Map);g(this,"cooldown",new j(e=>this.renderCooldown(e),()=>this.endCooldown()));g(this,"els");g(this,"onScheme",()=>this.applyTheme());g(this,"pendingFrame",0);g(this,"following",!0);this.root=this.attachShadow({mode:"open"})}static get observedAttributes(){return Oe}get config(){let e={endpoint:"/api/dewee"};for(let n of Oe){let i=this.getAttribute(n);i!==null&&(Be.includes(n)?e[$e(n)]=bt(i):n==="suggestions"?e.suggestions=i.trim().startsWith("[")?Ct(i,[]):i.split("|").map(o=>o.trim()).filter(Boolean):n==="z-index"?e.zIndex=Number(i):e[$e(n)]=i)}return{...e,...this.overrides,...this.hooks}}configure(e){let{actions:n,confirm:i,getPageContext:o,headers:r,fetch:a,domActionsRoot:d,...l}=e,c=u=>Object.prototype.hasOwnProperty.call(e,u);c("actions")&&this.setPropActions(n??[]),c("confirm")&&(this.hooks.confirm=i),c("getPageContext")&&(this.hooks.getPageContext=o),c("headers")&&(this.hooks.headers=r),c("fetch")&&(this.hooks.fetch=a),c("domActionsRoot")&&(d===void 0?delete this.hooks.domActionsRoot:this.hooks.domActionsRoot=d),Object.keys(l).length&&(this.overrides={...this.overrides,...l},this.els&&this.applyConfig())}registerActions(e){this.registry.register(e)}unregisterAction(e){return this.propActionNames.delete(e),this.registry.unregister(e)}get headers(){return this.hooks.headers}set headers(e){this.hooks.headers=e}get fetch(){return this.hooks.fetch}set fetch(e){this.hooks.fetch=e}get actionNames(){return this.actionRegistry().names()}actionRegistry(){if(!this.config.domActions)return this.registry;this.dom??(this.dom=Ie({root:()=>this.hooks.domActionsRoot??this.getAttribute("dom-actions-root")??void 0,exclude:r=>r===this||this.contains(r)||r.closest("dewee-chat")!==null}));let e=this.registry.names().map(r=>this.registry.get(r)),n=S.maxActions-e.length,i=this.dom.actions.filter(r=>!this.registry.get(r.name)).slice(0,Math.max(0,n)),o=new N;return o.register([...e,...i]),o}setPropActions(e){let n=new Set(e.map(i=>i.name));for(let i of this.propActionNames)n.has(i)||this.registry.unregister(i);e.length&&this.registry.register(e),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(e){let n=e.trim();if(!n||this.running||this.cooldown.active)return;this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate(),this.els.input.value="",this.autosize();let i={message:n,hidden:!1};for(let o=0;i;o++){let r=await this.runTurn(i.message,i.hidden);if(i=null,r.status!=="done"||r.stopped||!this.actionRegistry().size)break;let{calls:a}=K(r.content);if(!a.length)break;r.actions=a.map(c=>({id:c.id,name:c.name,args:c.ok?c.args:void 0,status:"pending"}));let d=this.config.maxActionRounds??5;if(o>=d){for(let c of r.actions)Object.assign(c,{status:"error",error:"Automatic action limit reached"});this.updateMessage(r),this.showNotice(this.strings.actionLimit(d));break}this.updateMessage(r);let l=await this.runActions(r,a);l&&(i={message:ve(l,this.server?.maxMessageLength??4e3),hidden:!0})}}async runTurn(e,n){this.pushMessage({role:"user",content:e,hidden:n});let i={role:"assistant",content:"",status:"streaming",startedAt:Date.now()};this.pushMessage(i),this.setRunning(new AbortController),this.emit("dewee:send",{message:e,agent:this.agentId,conversationId:this.conversationId,auto:n||void 0});try{await this.client().ensureSession(),await this.client().chat(this.chatBody(e),o=>this.onStreamEvent(i,o),this.running.signal),i.status==="streaming"&&(i.status="done")}catch(o){o.name==="AbortError"?(i.status="done",i.stopped=!0,i.content||(i.content=`_${this.strings.stopped}_`)):(i.status="error",this.showError(i,o))}finally{this.finishThinking(i),this.updateMessage(i),this.setRunning(null),i.status==="done"&&(i.actionsHash&&i.ackVisitor&&!i.stopped&&this.setAck(i.actionsHash,i.ackVisitor),this.emit("dewee:reply",{content:i.content,agent:this.agentId,conversationId:this.conversationId}),this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.setAttribute("data-unread",""))}return i}chatBody(e){let n={message:e,agent:this.agentId,conversationId:this.conversationId},i=this.actionRegistry();if(i.size){n.actions=i.manifest();let r=this.client().visitorId(),a=r?this.getAck(r):void 0;a&&(n.actionsHash=a)}let o=this.pageContext();return o!==void 0&&(n.pageContext=o),n}pageContext(){let e=this.hooks.getPageContext;if(e)try{let n=e();if(n==null)return;let i=new TextEncoder().encode(JSON.stringify(n)).length;return i<=S.maxPageContextBytes?n:(console.warn(`[dewee-webchat] getPageContext() returned ${i} bytes; the limit is ${S.maxPageContextBytes}. Context not sent.`),{truncated:!0})}catch(n){console.warn("[dewee-webchat] getPageContext() failed:",n);return}}ackKey(e){return`dewee-webchat:actions:${this.config.endpoint}:${this.agentId}:${e}:${this.conversationId}`}getAck(e){let n=this.ackKey(e);return this.acks.get(n)??(this.config.persist!==!1?_.get(n)??void 0:void 0)}setAck(e,n){let i=this.ackKey(n);this.acks.set(i,e),this.config.persist!==!1&&_.set(i,e)}async runActions(e,n){let i=new AbortController;this.setRunning(i);let o=[],r={agent:this.agentId,conversationId:this.conversationId},a=this.actionRegistry();try{for(let[d,l]of n.entries()){let c=e.actions[d],u=l.ok?a.get(l.name)?.sensitivity:void 0,f=l.ok?l.args:void 0;this.emit("dewee:action",{phase:"start",id:l.id,name:l.name,sensitivity:u,args:f,...r});let p=await Se(l,{registry:a,confirm:(h,m)=>this.confirmAction(a,h,m,i.signal),signal:i.signal,timeoutMs:this.config.actionTimeoutMs,...r,onStatus:h=>{c.status=h,this.updateMessage(e)}});p.error&&(c.error=p.error),this.updateMessage(e),o.push(p),this.emit("dewee:action",{phase:"result",id:p.id,name:p.name,sensitivity:u,args:f,status:p.status,result:p.result,error:p.error,...r})}}finally{this.setRunning(null)}return i.signal.aborted?null:o}confirmAction(e,n,i,o){let a=this.dom?.actions.includes(e.get(n.name))?this.dom.target(i):void 0,d=a?{...i,target:a}:i;return this.hooks.confirm?this.hooks.confirm(n,d):(this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),Pe(this.els.panel,this.strings,n,d,o))}showNotice(e){let n=document.createElement("div");n.className="notice",n.setAttribute("role","status"),n.textContent=e,this.els.log.appendChild(n),this.scrollToBottom(!0)}stop(){this.running?.abort()}async renewSession(){this.stop();try{await this.client().renewSession()}catch(e){this.showError(null,e);return}this.activated&&await this.loadConversation()}reuseHint(){return this.client().reuseHint()}async startLink(){this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate();try{let e=await this.client().link();return this.renderLink(e),this.emit("dewee:link",{expiresAt:e.expiresAt,deepLinks:e.deepLinks}),e}catch(e){return this.showError(null,e),null}}async openJobAccess(){if(this.jobAccessLaunch)return;let e=new AbortController;this.jobAccessLaunch=e,this.els.jobAccess.disabled=!0;try{await _e(n=>this.client().jobAccess(n),e.signal),this.showNotice(this.strings.jobAccessOpened)}catch(n){if(e.signal.aborted)return;n instanceof A?this.showNotice(n.code==="popup_blocked"?this.strings.jobAccessPopupBlocked:n.code==="popup_closed"?this.strings.jobAccessPopupClosed:this.strings.jobAccessUnavailable):n instanceof E&&z(n.code)?this.showError(null,n):this.showNotice(this.strings.jobAccessUnavailable)}finally{this.jobAccessLaunch=null,this.els.jobAccess.disabled=!1}}renderLink(e){let n=this.strings,i=document.createElement("div");i.className="msg assistant link";let o=document.createElement("div");o.className="bubble";let r=document.createElement("div");r.className="md";let a=c=>{let u=document.createElement("p");return u.textContent=c,u},d=document.createElement("code");d.textContent=e.proofCode;let l=document.createElement("p");if(l.appendChild(d),r.append(a(n.linkIntro),l),e.deepLinks.length){let c=document.createElement("ul");for(let u of e.deepLinks){let f=document.createElement("li"),p=u.displayName||u.name;f.textContent=n.linkContinueOn(At(u.channelType))+(p?` \xB7 ${p}`:""),c.appendChild(f)}r.appendChild(c)}else r.appendChild(a(n.linkNoTargets));r.appendChild(a(n.linkExpires(Math.max(1,Math.round((e.expiresAt-Date.now())/6e4))))),o.appendChild(r),i.appendChild(o),this.messages.length||this.els.log.replaceChildren(),this.els.log.appendChild(i),this.scrollToBottom(!0)}newConversation(){this.stop(),this.conversationId=je(),this.config.persist!==!1&&_.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.jobAccessLaunch?.abort(),this.stop(),this.mql?.removeEventListener("change",this.onScheme)}attributeChangedCallback(){this.els&&this.applyConfig()}emit(e,n){this.dispatchEvent(new CustomEvent(e,{detail:n,bubbles:!0,composed:!0}))}client(){let e=this.config,n=[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|");return(!this.api||n!==this.apiKey)&&(this.apiKey=n,this.api=new R({endpoint:e.endpoint,gatewayUrl:e.gatewayUrl,gatewayToken:e.gatewayToken,turnstile:i=>this.challenge(i),headers:()=>{let i=this.hooks.headers;return(typeof i=="function"?i():i)??{}},fetch:(i,o)=>(this.hooks.fetch??globalThis.fetch)(i,o)})),this.api}convKey(){return`dewee-webchat:conv:${this.config.endpoint}:${this.config.gatewayUrl||""}:${this.agentId}`}applyTheme(){let e=this.config.theme||"light",n=e==="dark"||e==="auto"&&!!this.mql?.matches;this.setAttribute("data-theme",n?"dark":"light")}applyConfig(){let e=this.config;this.strings=ce(e.locale);let n=this.strings;this.setAttribute("data-position",e.position||"bottom-right"),this.setAttribute("data-mode",e.mode||"floating"),e.theme==="auto"&&!this.mql&&(this.mql=matchMedia("(prefers-color-scheme: dark)"),this.mql.addEventListener("change",this.onScheme)),this.applyTheme();let i=(r,a)=>a!==void 0&&a!==""?this.style.setProperty(r,String(a)):this.style.removeProperty(r);i("--dw-accent",e.accentColor),i("--dw-width",e.width),i("--dw-height",e.height),i("--dw-z",e.zIndex);let o=this.els;if(o.title.textContent=e.title||n.title,o.avatar.replaceChildren(),e.avatar){let r=document.createElement("img");r.src=e.avatar,r.alt="",o.avatar.appendChild(r)}else o.avatar.textContent=(e.title||n.title).trim().charAt(0).toUpperCase();if(o.launcher.innerHTML=e.avatar&&e.launcherAvatar?"":C.chat,e.avatar&&e.launcherAvatar){let r=document.createElement("img");r.src=e.avatar,r.alt="",o.launcher.appendChild(r)}o.launcher.insertAdjacentHTML("beforeend",'<span class="badge"></span>'),o.launcher.setAttribute("aria-label",n.open),o.close.setAttribute("aria-label",n.close),o.close.title=n.close,o.newChat.setAttribute("aria-label",n.newChat),o.newChat.title=n.newChat,o.jobAccess.setAttribute("aria-label",n.jobAccess),o.jobAccess.title=n.jobAccess,o.input.placeholder=e.placeholder||n.placeholder,o.foot.hidden=!!e.hideBranding,o.foot.querySelector(".by").textContent=n.poweredBy+" ",this.renderSubtitle(),this.updateSendButton(),this.activated&&this.api&&this.apiKey!==[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|")?(this.activated=!1,this.server=null,this.activate()):this.activated&&e.agent&&e.agent!==this.agentId&&this.server?this.switchAgent(e.agent):this.activated&&this.renderLog()}renderShell(){this.root.innerHTML=`
309
+ <style>${De}</style>
310
310
  <button class="launcher" part="launcher" aria-expanded="false" aria-haspopup="dialog"></button>
311
311
  <section class="panel" part="panel" role="dialog" aria-label="Chat">
312
312
  <header class="head" part="header">
@@ -315,9 +315,9 @@ textarea::placeholder { color: var(--dw-muted); }
315
315
  <div class="title"></div>
316
316
  <div class="subtitle"><span class="dot"></span><span class="status"></span><select class="agent-select" hidden></select></div>
317
317
  </div>
318
- <button class="icon-btn new">${A.newChat}</button>
319
- <button class="icon-btn job-access" hidden>${A.jobAccess}</button>
320
- <button class="icon-btn close">${A.close}</button>
318
+ <button class="icon-btn new">${C.newChat}</button>
319
+ <button class="icon-btn job-access" hidden>${C.jobAccess}</button>
320
+ <button class="icon-btn close">${C.close}</button>
321
321
  </header>
322
322
  <div class="log" part="log" role="log" aria-live="polite"></div>
323
323
  <div class="challenge"><slot name="challenge"></slot></div>
@@ -330,7 +330,7 @@ textarea::placeholder { color: var(--dw-muted); }
330
330
  </div>
331
331
  <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>
332
332
  </form>
333
- </section>`;let e=n=>this.root.querySelector(n);this.els={launcher:e(".launcher"),panel:e(".panel"),log:e(".log"),title:e(".title"),subtitle:e(".subtitle"),status:e(".status"),avatar:e(".avatar"),select:e(".agent-select"),input:e("textarea"),send:e(".send"),newChat:e(".new"),jobAccess:e(".job-access"),close:e(".close"),foot:e(".foot"),cooldown:e(".cooldown"),announcer:e(".announcer")};let t=this.els;t.launcher.addEventListener("click",()=>this.toggle()),t.close.addEventListener("click",()=>this.close()),t.log.addEventListener("scroll",()=>{this.following=this.nearBottom()},{passive:!0}),t.newChat.addEventListener("click",()=>this.newConversation()),t.jobAccess.addEventListener("click",()=>{this.openJobAccess()}),t.select.addEventListener("change",()=>this.switchAgent(t.select.value)),e("form").addEventListener("submit",n=>{n.preventDefault(),this.running?this.stop():this.send(t.input.value)}),t.input.addEventListener("keydown",n=>{n.key==="Enter"&&!n.shiftKey&&!n.isComposing&&n.keyCode!==229&&(n.preventDefault(),this.running||this.send(t.input.value))}),t.input.addEventListener("input",()=>{this.autosize(),this.updateSendButton()}),t.panel.addEventListener("keydown",n=>{n.key==="Escape"&&this.close()})}autosize(){let e=this.els.input;e.style.height="auto",e.style.height=Math.min(e.scrollHeight,140)+"px"}updateSendButton(){let e=this.els.send;e.innerHTML=this.running?A.stop:A.send,e.setAttribute("aria-label",this.running?this.strings.stop:this.strings.send),e.title=e.getAttribute("aria-label"),e.disabled=!this.running&&(this.cooldown.active||!this.els.input.value.trim())}startCooldown(e){this.cooldown.start(typeof e=="number"&&Number.isFinite(e)&&e>0?e:Ue),this.cooldown.active&&(this.renderCooldown(this.cooldown.secondsLeft),this.els.announcer.textContent=this.strings.cooldownStart(this.cooldown.secondsLeft),this.updateSendButton())}renderCooldown(e){this.els.cooldown.hidden=!1,this.els.cooldown.textContent=this.strings.cooldown(e)}endCooldown(){this.els.cooldown.hidden=!0,this.els.cooldown.textContent="",this.els.announcer.textContent=this.strings.cooldownOver,this.updateSendButton()}setRunning(e){this.running=e,this.updateSendButton()}renderSubtitle(){this.els.jobAccess.hidden=this.server?.identity!==!0;let e=this.els,t=this.config,n=this.server?.agents??[],o=t.allowAgentSwitch!==!1&&!!this.server?.allowAgentSwitch&&n.length>1;e.select.hidden=!o,e.status.hidden=o,o&&(e.select.replaceChildren(...n.map(a=>new Option(a.name||a.id,a.id,!1,a.id===this.agentId))),e.select.setAttribute("aria-label","Agent"));let r=n.find(a=>a.id===this.agentId);e.status.textContent=t.subtitle||(r?.name&&r.name!==t.title?`${this.strings.online} \xB7 ${r.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 e=this.config.agent,t=n=>!!n&&(this.server.agents.length===0||this.server.agents.some(o=>o.id===n));this.agentId=t(e)?e:this.server.defaultAgent||this.server.agents[0]?.id||e||"",await this.loadConversation()}catch(e){this.activated=!1,this.renderLog(),this.showError(null,e)}this.renderSubtitle(),this.emit("dewee:ready",{agent:this.agentId,config:this.server})}}async switchAgent(e){e!==this.agentId&&(this.stop(),this.agentId=e,this.renderSubtitle(),await this.loadConversation())}async loadConversation(){let e=this.config.persist!==!1,t=e?R.get(this.convKey()):null;if(this.conversationId=t||Ce(),e&&!t&&R.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),!!t)try{let n=await this.client().history(this.agentId,this.conversationId);this.messages=Ze(n.map(o=>({...o,status:"done"}))),this.renderLog()}catch{}}renderLog(){let e=this.els.log;e.replaceChildren(),this.messages.length||e.appendChild(this.renderWelcome());for(let t of this.messages)e.appendChild(this.renderMessage(t));e.scrollTop=e.scrollHeight}renderWelcome(){let e=this.config,t=this.strings,n=document.createElement("div");n.className="welcome";let o=document.createElement("h2");o.textContent=e.greeting||t.greeting;let r=document.createElement("p");if(r.textContent=e.intro||t.greetingBody,n.append(o,r),e.suggestions?.length){let a=document.createElement("div");a.className="suggestions";for(let l of e.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(e){this.messages.length||this.els.log.replaceChildren(),this.messages.push(e),this.els.log.appendChild(this.renderMessage(e)),this.scrollToBottom(!0)}renderMessage(e){let t=document.createElement("div");t.className=`msg ${e.role}${e.status==="error"?" error":""}`,t.hidden=!!e.hidden;let n=document.createElement("div");n.className="bubble",t.appendChild(n);let o=document.createElement("div");o.className="trace";let r=document.createElement("div");r.className=e.role==="assistant"?"md":"";let a=document.createElement("div");return a.className="actions",a.hidden=!0,e.role==="assistant"&&n.appendChild(o),n.appendChild(r),e.role==="assistant"&&n.appendChild(a),e.view={root:t,trace:o,md:r,tools:new Map,actions:a},this.updateMessage(e),t}updateMessage(e){let t=e.view;if(!t)return;let n=this.config;if(e.role==="user"){t.md.textContent=e.content,t.root.classList.toggle("refused",!!e.refusal);let a=t.root.querySelector(".refusal");e.refusal&&!a&&(a=document.createElement("div"),a.className="refusal",t.root.appendChild(a)),a&&e.refusal?a.textContent=`\u26A0\uFE0F ${e.refusal}`:a?.remove();return}if(t.root.classList.toggle("error",e.status==="error"),n.showReasoning!==!1&&e.thinking){t.thinking||(t.thinking=document.createElement("details"),t.thinking.className="thinking",t.thinking.innerHTML='<summary></summary><div class="thinking-body"></div>',t.trace.prepend(t.thinking));let a=e.status==="streaming"&&!e.thinkingMs,l=a?this.strings.thinking:e.thinkingMs?this.strings.thoughtFor(Math.max(1,Math.round(e.thinkingMs/1e3))):this.strings.reasoning,c=t.thinking.querySelector("summary");c.innerHTML=`${a?'<span class="spin"></span>':A.brain}<span></span>${A.down.replace("<svg",'<svg class="chev"')}`,c.querySelector("span:not(.spin)").textContent=l,t.thinking.querySelector(".thinking-body").textContent=e.thinking}else t.thinking&&(t.thinking.remove(),t.thinking=void 0);if(n.showToolCalls!==!1)for(let a of e.toolCalls??[])this.renderTool(t,a);else t.tools.forEach(a=>a.remove()),t.tools.clear();t.trace.hidden=!t.trace.childElementCount;let o=this.registry.size||e.actions?le(e.content):e.content,r=e.status==="streaming"&&!o;t.md.hidden=!r&&!o&&!!e.actions?.length,r?t.md.innerHTML=`<span class="typing" aria-label="${this.strings.thinking}"><i></i><i></i><i></i></span>`:(t.md.replaceChildren(L(document,o)),t.md.classList.toggle("cursor",e.status==="streaming")),this.renderActions(t,e.actions??[])}renderActions(e,t){e.actions.hidden=!t.length,e.actions.replaceChildren(...t.map(n=>{let o=document.createElement("div");o.className="action",o.dataset.status=n.status;let r=n.status==="running"||n.status==="awaiting-confirmation";o.innerHTML=`${r?'<span class="spin"></span>':A.action}<span class="action-name"></span><span class="action-state"></span>`,o.querySelector(".action-name").textContent=n.name||"?",o.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(`
334
- `);return a&&(o.title=a),o}))}renderTool(e,t){let n=e.tools.get(t.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>${A.down.replace("<svg",'<svg class="chev"')}</summary><div class="tool-body"></div>`,e.tools.set(t.id,n),e.trace.appendChild(n)),n.dataset.status=t.status,n.querySelector(".lead").innerHTML=t.status==="running"?'<span class="spin"></span>':A.tool;let o=n.querySelector(".tool-name");o.textContent=Fe(t.name),o.setAttribute("title",t.name),n.querySelector(".tool-state").textContent=t.status==="running"?this.strings.toolRunning:t.status==="error"?this.strings.toolError:this.strings.toolDone;let r=n.querySelector(".tool-body");r.replaceChildren();let a=(l,c)=>{if(c==null||c==="")return;let d=document.createElement("div");d.className="label",d.textContent=l;let h=document.createElement("pre");h.textContent=typeof c=="string"?nt(c):JSON.stringify(c,null,2),r.append(d,h)};a(this.strings.args,t.arguments),a(this.strings.result,t.result)}onStreamEvent(e,t){switch(t.type){case"start":t.actionsHash&&(e.actionsHash=t.actionsHash),t.conversationId&&t.conversationId!==this.conversationId&&(this.conversationId=t.conversationId,this.config.persist!==!1&&R.set(this.convKey(),t.conversationId));break;case"thinking":e.thinking=(e.thinking||"")+t.text;break;case"delta":this.finishThinking(e),e.content+=t.text;break;case"tool_call":this.finishThinking(e),(e.toolCalls??(e.toolCalls=[])).push({id:t.id,name:t.name,arguments:t.arguments,status:"running"});break;case"tool_result":{let n=e.toolCalls?.find(o=>o.id===t.id)??(e.toolCalls??(e.toolCalls=[]))[e.toolCalls.push({id:t.id,name:t.name,status:"running"})-1];n.status=t.isError?"error":"done",n.result=t.result,n.isError=t.isError;break}case"done":this.finishThinking(e),t.content&&t.content.length>=e.content.length&&(e.content=t.content),e.status="done";break;case"error":e.status="error",this.showError(e,new x(t.message,500,t.code,t.remediation,t.retryAfterMs));break}this.scheduleUpdate(e)}scheduleUpdate(e){this.pendingFrame||(this.pendingFrame=requestAnimationFrame(()=>{this.pendingFrame=0,this.updateMessage(e),this.following&&this.scrollToBottom(!0)}))}finishThinking(e){e.thinking&&!e.thinkingMs&&e.startedAt&&(e.thinkingMs=Date.now()-e.startedAt)}nearBottom(){let e=this.els.log;return e.scrollHeight-e.scrollTop-e.clientHeight<96}scrollToBottom(e=!1){let t=this.els.log;(e||this.nearBottom())&&(t.scrollTop=t.scrollHeight,this.following=!0)}showError(e,t){let{code:n,remediation:o,retryAfterMs:r}=t,l=(j(n)?this.strings.identityErrors[n]:B(n)?this.strings.admissionErrors[n]:ne(n)?this.strings.linkErrors[n]:"")||t.message||this.strings.errorGeneric;if(this.emit("dewee:error",{message:l,code:n,...o&&{remediation:o},...r&&{retryAfterMs:r}}),n==="RATE_LIMITED"&&this.startCooldown(r),e&&B(n)&&this.refuseInline(e,n,l))return;if(e){e.content=e.content?`${e.content}
333
+ </section>`;let e=i=>this.root.querySelector(i);this.els={launcher:e(".launcher"),panel:e(".panel"),log:e(".log"),title:e(".title"),subtitle:e(".subtitle"),status:e(".status"),avatar:e(".avatar"),select:e(".agent-select"),input:e("textarea"),send:e(".send"),newChat:e(".new"),jobAccess:e(".job-access"),close:e(".close"),foot:e(".foot"),cooldown:e(".cooldown"),announcer:e(".announcer")};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.jobAccess.addEventListener("click",()=>{this.openJobAccess()}),n.select.addEventListener("change",()=>this.switchAgent(n.select.value)),e("form").addEventListener("submit",i=>{i.preventDefault(),this.running?this.stop():this.send(n.input.value)}),n.input.addEventListener("keydown",i=>{i.key==="Enter"&&!i.shiftKey&&!i.isComposing&&i.keyCode!==229&&(i.preventDefault(),this.running||this.send(n.input.value))}),n.input.addEventListener("input",()=>{this.autosize(),this.updateSendButton()}),n.panel.addEventListener("keydown",i=>{i.key==="Escape"&&this.close()})}autosize(){let e=this.els.input;e.style.height="auto",e.style.height=Math.min(e.scrollHeight,140)+"px"}updateSendButton(){let e=this.els.send;e.innerHTML=this.running?C.stop:C.send,e.setAttribute("aria-label",this.running?this.strings.stop:this.strings.send),e.title=e.getAttribute("aria-label"),e.disabled=!this.running&&(this.cooldown.active||!this.els.input.value.trim())}startCooldown(e){this.cooldown.start(typeof e=="number"&&Number.isFinite(e)&&e>0?e:mt),this.cooldown.active&&(this.renderCooldown(this.cooldown.secondsLeft),this.els.announcer.textContent=this.strings.cooldownStart(this.cooldown.secondsLeft),this.updateSendButton())}renderCooldown(e){this.els.cooldown.hidden=!1,this.els.cooldown.textContent=this.strings.cooldown(e)}endCooldown(){this.els.cooldown.hidden=!0,this.els.cooldown.textContent="",this.els.announcer.textContent=this.strings.cooldownOver,this.updateSendButton()}setRunning(e){this.running=e,this.updateSendButton()}renderSubtitle(){this.els.jobAccess.hidden=this.server?.identity!==!0;let e=this.els,n=this.config,i=this.server?.agents??[],o=n.allowAgentSwitch!==!1&&!!this.server?.allowAgentSwitch&&i.length>1;e.select.hidden=!o,e.status.hidden=o,o&&(e.select.replaceChildren(...i.map(a=>new Option(a.name||a.id,a.id,!1,a.id===this.agentId))),e.select.setAttribute("aria-label","Agent"));let r=i.find(a=>a.id===this.agentId);e.status.textContent=n.subtitle||(r?.name&&r.name!==n.title?`${this.strings.online} \xB7 ${r.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 e=this.config.agent,n=i=>!!i&&(this.server.agents.length===0||this.server.agents.some(o=>o.id===i));this.agentId=n(e)?e:this.server.defaultAgent||this.server.agents[0]?.id||e||"",await this.loadConversation()}catch(e){this.activated=!1,this.renderLog(),this.showError(null,e)}this.renderSubtitle(),this.emit("dewee:ready",{agent:this.agentId,config:this.server})}}async switchAgent(e){e!==this.agentId&&(this.stop(),this.agentId=e,this.renderSubtitle(),await this.loadConversation())}async loadConversation(){let e=this.config.persist!==!1,n=e?_.get(this.convKey()):null;if(this.conversationId=n||je(),e&&!n&&_.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),!!n)try{let i=await this.client().history(this.agentId,this.conversationId);this.messages=Et(i.map(o=>({...o,status:"done"}))),this.renderLog()}catch{}}renderLog(){let e=this.els.log;e.replaceChildren(),this.messages.length||e.appendChild(this.renderWelcome());for(let n of this.messages)e.appendChild(this.renderMessage(n));e.scrollTop=e.scrollHeight}renderWelcome(){let e=this.config,n=this.strings,i=document.createElement("div");i.className="welcome";let o=document.createElement("h2");o.textContent=e.greeting||n.greeting;let r=document.createElement("p");if(r.textContent=e.intro||n.greetingBody,i.append(o,r),e.suggestions?.length){let a=document.createElement("div");a.className="suggestions";for(let d of e.suggestions){let l=document.createElement("button");l.className="chip",l.type="button",l.textContent=d,l.addEventListener("click",()=>this.send(d)),a.appendChild(l)}i.appendChild(a)}return i}pushMessage(e){this.messages.length||this.els.log.replaceChildren(),this.messages.push(e),this.els.log.appendChild(this.renderMessage(e)),this.scrollToBottom(!0)}renderMessage(e){let n=document.createElement("div");n.className=`msg ${e.role}${e.status==="error"?" error":""}`,n.hidden=!!e.hidden;let i=document.createElement("div");i.className="bubble",n.appendChild(i);let o=document.createElement("div");o.className="trace";let r=document.createElement("div");r.className=e.role==="assistant"?"md":"";let a=document.createElement("div");return a.className="actions",a.hidden=!0,e.role==="assistant"&&i.appendChild(o),i.appendChild(r),e.role==="assistant"&&i.appendChild(a),e.view={root:n,trace:o,md:r,tools:new Map,actions:a},this.updateMessage(e),n}updateMessage(e){let n=e.view;if(!n)return;let i=this.config;if(e.role==="user"){n.md.textContent=e.content,n.root.classList.toggle("refused",!!e.refusal);let a=n.root.querySelector(".refusal");e.refusal&&!a&&(a=document.createElement("div"),a.className="refusal",n.root.appendChild(a)),a&&e.refusal?a.textContent=`\u26A0\uFE0F ${e.refusal}`:a?.remove();return}if(n.root.classList.toggle("error",e.status==="error"),i.showReasoning!==!1&&e.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=e.status==="streaming"&&!e.thinkingMs,d=a?this.strings.thinking:e.thinkingMs?this.strings.thoughtFor(Math.max(1,Math.round(e.thinkingMs/1e3))):this.strings.reasoning,l=n.thinking.querySelector("summary");l.innerHTML=`${a?'<span class="spin"></span>':C.brain}<span></span>${C.down.replace("<svg",'<svg class="chev"')}`,l.querySelector("span:not(.spin)").textContent=d,n.thinking.querySelector(".thinking-body").textContent=e.thinking}else n.thinking&&(n.thinking.remove(),n.thinking=void 0);if(i.showToolCalls!==!1)for(let a of e.toolCalls??[])this.renderTool(n,a);else n.tools.forEach(a=>a.remove()),n.tools.clear();n.trace.hidden=!n.trace.childElementCount;let o=this.actionRegistry().size||e.actions?ye(e.content):e.content,r=e.status==="streaming"&&!o;n.md.hidden=!r&&!o&&!!e.actions?.length,r?n.md.innerHTML=`<span class="typing" aria-label="${this.strings.thinking}"><i></i><i></i><i></i></span>`:(n.md.replaceChildren(M(document,o)),n.md.classList.toggle("cursor",e.status==="streaming")),this.renderActions(n,e.actions??[])}renderActions(e,n){e.actions.hidden=!n.length,e.actions.replaceChildren(...n.map(i=>{let o=document.createElement("div");o.className="action",o.dataset.status=i.status;let r=i.status==="running"||i.status==="awaiting-confirmation";o.innerHTML=`${r?'<span class="spin"></span>':C.action}<span class="action-name"></span><span class="action-state"></span>`,o.querySelector(".action-name").textContent=i.name||"?",o.querySelector(".action-state").textContent=this.strings.actionStatus[i.status];let a=[i.args&&Object.keys(i.args).length?JSON.stringify(i.args):"",i.error??""].filter(Boolean).join(`
334
+ `);return a&&(o.title=a),o}))}renderTool(e,n){let i=e.tools.get(n.id);i||(i=document.createElement("details"),i.className="tool",i.innerHTML=`<summary><span class="lead"></span><span class="tool-name"></span><span class="tool-state"></span>${C.down.replace("<svg",'<svg class="chev"')}</summary><div class="tool-body"></div>`,e.tools.set(n.id,i),e.trace.appendChild(i)),i.dataset.status=n.status,i.querySelector(".lead").innerHTML=n.status==="running"?'<span class="spin"></span>':C.tool;let o=i.querySelector(".tool-name");o.textContent=wt(n.name),o.setAttribute("title",n.name),i.querySelector(".tool-state").textContent=n.status==="running"?this.strings.toolRunning:n.status==="error"?this.strings.toolError:this.strings.toolDone;let r=i.querySelector(".tool-body");r.replaceChildren();let a=(d,l)=>{if(l==null||l==="")return;let c=document.createElement("div");c.className="label",c.textContent=d;let u=document.createElement("pre");u.textContent=typeof l=="string"?St(l):JSON.stringify(l,null,2),r.append(c,u)};a(this.strings.args,n.arguments),a(this.strings.result,n.result)}onStreamEvent(e,n){switch(n.type){case"start":n.actionsHash&&(e.actionsHash=n.actionsHash,e.ackVisitor=this.client().visitorId()??void 0),n.conversationId&&n.conversationId!==this.conversationId&&(this.conversationId=n.conversationId,this.config.persist!==!1&&_.set(this.convKey(),n.conversationId));break;case"thinking":e.thinking=(e.thinking||"")+n.text;break;case"delta":this.finishThinking(e),e.content+=n.text;break;case"tool_call":this.finishThinking(e),(e.toolCalls??(e.toolCalls=[])).push({id:n.id,name:n.name,arguments:n.arguments,status:"running"});break;case"tool_result":{let i=e.toolCalls?.find(o=>o.id===n.id)??(e.toolCalls??(e.toolCalls=[]))[e.toolCalls.push({id:n.id,name:n.name,status:"running"})-1];i.status=n.isError?"error":"done",i.result=n.result,i.isError=n.isError;break}case"done":this.finishThinking(e),n.content&&n.content.length>=e.content.length&&(e.content=n.content),e.status="done";break;case"error":e.status="error",this.showError(e,new E(n.message,500,n.code,n.remediation,n.retryAfterMs));break}this.scheduleUpdate(e)}scheduleUpdate(e){this.pendingFrame||(this.pendingFrame=requestAnimationFrame(()=>{this.pendingFrame=0,this.updateMessage(e),this.following&&this.scrollToBottom(!0)}))}finishThinking(e){e.thinking&&!e.thinkingMs&&e.startedAt&&(e.thinkingMs=Date.now()-e.startedAt)}nearBottom(){let e=this.els.log;return e.scrollHeight-e.scrollTop-e.clientHeight<96}scrollToBottom(e=!1){let n=this.els.log;(e||this.nearBottom())&&(n.scrollTop=n.scrollHeight,this.following=!0)}showError(e,n){let{code:i,remediation:o,retryAfterMs:r}=n,d=(z(i)?this.strings.identityErrors[i]:q(i)?this.strings.admissionErrors[i]:pe(i)?this.strings.linkErrors[i]:"")||n.message||this.strings.errorGeneric;if(this.emit("dewee:error",{message:d,code:i,...o&&{remediation:o},...r&&{retryAfterMs:r}}),i==="RATE_LIMITED"&&this.startCooldown(r),e&&q(i)&&this.refuseInline(e,i,d))return;if(e){e.content=e.content?`${e.content}
335
335
 
336
- > \u26A0\uFE0F ${l}`:`\u26A0\uFE0F ${l}`,e.status="error",this.updateMessage(e);return}let c=document.createElement("div");c.className="msg error",c.innerHTML='<div class="bubble"></div>',c.firstElementChild.textContent=`\u26A0\uFE0F ${l}`,this.els.log.appendChild(c)}refuseInline(e,t,n){let o=this.messages.indexOf(e),r=o>0?this.messages[o-1]:void 0;return r?.role!=="user"||r.hidden||(this.els.input.value.trim()||(this.els.input.value=r.content,this.autosize()),t!=="POLICY_DENIED"||e.content)?!1:(e.status="error",e.hidden=!0,e.view&&(e.view.root.hidden=!0),r.refusal=n,this.updateMessage(r),!0)}async challenge(e){await Ge();let t=document.createElement("div");t.slot="challenge",this.appendChild(t);try{return await new Promise((n,o)=>{window.turnstile.render(t,{sitekey:e,appearance:"interaction-only",theme:this.getAttribute("data-theme")==="dark"?"dark":"light",callback:n,"error-callback":()=>o(new x("Verification failed",403,"turnstile_failed"))})})}finally{t.remove()}}};function Ze(i){return i.forEach((s,e)=>{if(s.role==="user"){s.content=W(s.content),V(s.content)&&(s.hidden=!0);return}if(!s.content.includes("```dewee-action"))return;let{calls:t}=K(s.content);if(!t.length)return;let n=i[e+1],o=n?.role==="user"?W(n.content):"",r=V(o)?de(o):[];s.actions=t.map(a=>{let l=r.find(d=>d.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}var Xe={telegram:"Telegram",discord:"Discord",zalo:"Zalo",slack:"Slack",whatsapp:"WhatsApp",messenger:"Messenger",feishu:"Feishu",line:"LINE"};function et(i){let s=i.toLowerCase().split(/[_-]/)[0];return Xe[s]??(i?i.charAt(0).toUpperCase()+i.slice(1):"chat")}function tt(i,s){try{return JSON.parse(i)}catch{return s}}function nt(i){let s=i.trim();if(!/^[[{]/.test(s))return i;try{return JSON.stringify(JSON.parse(s),null,2)}catch{return i}}function X(i="dewee-chat"){typeof window>"u"||!window.customElements||customElements.get(i)||customElements.define(i,class extends M{})}function it(i={},s){X();let e=document.createElement("dewee-chat");return e.configure(i),((typeof s=="string"?document.querySelector(s):s)??document.body).appendChild(e),e}X();var st=document.currentScript,$=st?.dataset;if($&&$.endpoint&&$.autoMount!=="false"){let i=()=>{let s=document.createElement("dewee-chat");for(let[e,t]of Object.entries($))e==="autoMount"||t===void 0||s.setAttribute(e.replace(/[A-Z]/g,n=>"-"+n.toLowerCase()),t);document.body.appendChild(s)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",i):i()}window.DeweeWebchat=ee;})();
336
+ > \u26A0\uFE0F ${d}`:`\u26A0\uFE0F ${d}`,e.status="error",this.updateMessage(e);return}let l=document.createElement("div");l.className="msg error",l.innerHTML='<div class="bubble"></div>',l.firstElementChild.textContent=`\u26A0\uFE0F ${d}`,this.els.log.appendChild(l)}refuseInline(e,n,i){let o=this.messages.indexOf(e),r=o>0?this.messages[o-1]:void 0;return r?.role!=="user"||r.hidden||(this.els.input.value.trim()||(this.els.input.value=r.content,this.autosize()),n!=="POLICY_DENIED"||e.content)?!1:(e.status="error",e.hidden=!0,e.view&&(e.view.root.hidden=!0),r.refusal=i,this.updateMessage(r),!0)}async challenge(e){await xt();let n=document.createElement("div");n.slot="challenge",this.appendChild(n);try{return await new Promise((i,o)=>{window.turnstile.render(n,{sitekey:e,appearance:"interaction-only",theme:this.getAttribute("data-theme")==="dark"?"dark":"light",callback:i,"error-callback":()=>o(new E("Verification failed",403,"turnstile_failed"))})})}finally{n.remove()}}};function Et(t){return t.forEach((s,e)=>{if(s.role==="user"){s.content=Q(s.content),Y(s.content)&&(s.hidden=!0);return}if(!s.content.includes("```dewee-action"))return;let{calls:n}=K(s.content);if(!n.length)return;let i=t[e+1],o=i?.role==="user"?Q(i.content):"",r=Y(o)?xe(o):[];s.actions=n.map(a=>{let d=r.find(c=>c.id===a.id),l=d?d.status==="ok"?"done":d.status:"error";return{id:a.id,name:a.name,args:a.ok?a.args:void 0,status:l,error:d?d.error:"Not run"}})}),t}var kt={telegram:"Telegram",discord:"Discord",zalo:"Zalo",slack:"Slack",whatsapp:"WhatsApp",messenger:"Messenger",feishu:"Feishu",line:"LINE"};function At(t){let s=t.toLowerCase().split(/[_-]/)[0];return kt[s]??(t?t.charAt(0).toUpperCase()+t.slice(1):"chat")}function Ct(t,s){try{return JSON.parse(t)}catch{return s}}function St(t){let s=t.trim();if(!/^[[{]/.test(s))return t;try{return JSON.stringify(JSON.parse(s),null,2)}catch{return t}}function le(t="dewee-chat"){typeof window>"u"||!window.customElements||customElements.get(t)||customElements.define(t,class extends H{})}function Tt(t={},s){le();let e=document.createElement("dewee-chat");return e.configure(t),((typeof s=="string"?document.querySelector(s):s)??document.body).appendChild(e),e}le();var It=document.currentScript,B=It?.dataset;if(B&&B.endpoint&&B.autoMount!=="false"){let t=()=>{let s=document.createElement("dewee-chat");for(let[e,n]of Object.entries(B))e==="autoMount"||n===void 0||s.setAttribute(e.replace(/[A-Z]/g,i=>"-"+i.toLowerCase()),n);document.body.appendChild(s)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t):t()}window.DeweeWebchat=de;})();
package/dist/index.d.ts CHANGED
@@ -39,6 +39,10 @@ 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
+ declare const DOM_ACTION_NAMES: readonly ["page.snapshot", "page.scroll", "page.click", "page.fill"];
43
+ /** Where the agent may act: a selector, an element, or (default) the whole document body. */
44
+ type DomActionsRoot = string | Element;
45
+
42
46
  /** The only launch data allowed to reach the browser. Never an identity session. */
43
47
  interface JobAccessLaunch {
44
48
  bootstrap_url: string;
@@ -110,6 +114,8 @@ declare class ApiClient {
110
114
  private scheduleRefresh;
111
115
  /** The stored anonymous visitor session, if any. Signed-in sessions are never stored. */
112
116
  private stored;
117
+ /** The visitor of the session in use, or null before one is held. Backend conversations are keyed by it. */
118
+ visitorId(): string | null;
113
119
  /** Returns a valid visitor session, creating or refreshing it when needed. */
114
120
  ensureSession(): Promise<SessionResponse>;
115
121
  private loadSession;
@@ -188,6 +194,13 @@ interface DeweeChatConfig {
188
194
  maxActionRounds?: number;
189
195
  /** Per-handler timeout. Default 30000 ms. */
190
196
  actionTimeoutMs?: number;
197
+ /**
198
+ * Built-in page actions that read and operate the page DOM (page.snapshot, page.scroll, page.click,
199
+ * page.fill). Off by default. Host actions with the same name win. click/fill always ask the visitor.
200
+ */
201
+ domActions?: boolean;
202
+ /** Limits the built-in DOM actions to this element or selector. Default: document.body. */
203
+ domActionsRoot?: DomActionsRoot;
191
204
  /**
192
205
  * Extra headers for every request to `endpoint` (e.g. a CSRF token). A function is called before
193
206
  * each request. Cannot override content-type, authorization or the gateway credential headers.
@@ -236,6 +249,8 @@ declare class DeweeChatElement extends ElementBase {
236
249
  private registry;
237
250
  private propActionNames;
238
251
  private hooks;
252
+ /** Built-in DOM actions, created on first use when `domActions` is on (keeps the snapshot refs). */
253
+ private dom;
239
254
  private acks;
240
255
  /** Pause after a RATE_LIMITED refusal; send stays disabled until it ends. */
241
256
  private cooldown;
@@ -255,8 +270,10 @@ declare class DeweeChatElement extends ElementBase {
255
270
  /** Custom fetch for widget requests (property only; same as `configure({ fetch })`). */
256
271
  get fetch(): typeof fetch | undefined;
257
272
  set fetch(v: typeof fetch | undefined);
258
- /** Names of the currently registered page actions. */
273
+ /** Names of the page actions offered to the agent (host actions, then enabled built-ins). */
259
274
  get actionNames(): string[];
275
+ /** Host actions plus the enabled built-in DOM actions whose names the host did not take. */
276
+ private actionRegistry;
260
277
  private setPropActions;
261
278
  open(): void;
262
279
  close(): void;
@@ -268,6 +285,10 @@ declare class DeweeChatElement extends ElementBase {
268
285
  private chatBody;
269
286
  /** Host snapshot, dropped (with a warning) when it fails or exceeds the backend cap. */
270
287
  private pageContext;
288
+ /**
289
+ * The backend keys a conversation by visitor as well as by id, so a sign-in, a sign-out or another
290
+ * user starts a conversation that has not seen the manifest, even with the same conversation id.
291
+ */
271
292
  private ackKey;
272
293
  private getAck;
273
294
  private setAck;
@@ -358,4 +379,4 @@ declare global {
358
379
  }
359
380
  }
360
381
 
361
- 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, LinkResponse, type PageAction, type PageActionContext, type PageActionInfo, PublicConfig, RESERVED_HEADERS, Remediation, type RequestHeadersOption, SessionResponse, defineDeweeChat, mount, renderMarkdown };
382
+ export { type ActionCallStatus, type ActionCallView, type ActionConfirmHook, type ActionEventDetail, type ActionResult, type ActionResultStatus, ActionSensitivity, ApiClient, type ApiClientOptions, ApiError, ChatRequestBody, ChatStreamEvent, DOM_ACTION_NAMES, type DeweeChatConfig, DeweeChatElement, type DomActionsRoot, HistoryMessage, type JsonSchema, LinkResponse, type PageAction, type PageActionContext, type PageActionInfo, PublicConfig, RESERVED_HEADERS, Remediation, type RequestHeadersOption, SessionResponse, defineDeweeChat, mount, renderMarkdown };
package/dist/index.js CHANGED
@@ -1,16 +1,18 @@
1
1
  import {
2
2
  ApiClient,
3
3
  ApiError,
4
+ DOM_ACTION_NAMES,
4
5
  DeweeChatElement,
5
6
  RESERVED_HEADERS,
6
7
  defineDeweeChat,
7
8
  mount,
8
9
  renderMarkdown
9
- } from "./chunk-2NRPUV6T.js";
10
- import "./chunk-PUIN5ZCQ.js";
10
+ } from "./chunk-UGHJZAKL.js";
11
+ import "./chunk-SAUDVPFH.js";
11
12
  export {
12
13
  ApiClient,
13
14
  ApiError,
15
+ DOM_ACTION_NAMES,
14
16
  DeweeChatElement,
15
17
  RESERVED_HEADERS,
16
18
  defineDeweeChat,
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, RequestHeadersOption } from './index.js';
4
+ export { ActionConfirmHook, DomActionsRoot, PageAction, PageActionContext, RequestHeadersOption } from './index.js';
5
5
  import './protocol-CLbGE9Ns.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-2NRPUV6T.js";
4
- import "./chunk-PUIN5ZCQ.js";
3
+ } from "./chunk-UGHJZAKL.js";
4
+ import "./chunk-SAUDVPFH.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, headers, fetch, ...config } = props;
19
+ const { className, style, onReady, onSend, onReply, onError, onOpen, onClose, onAction, actions, confirm, getPageContext, headers, fetch, domActionsRoot, ...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, headers, fetch });
31
+ el.current?.configure({ actions, confirm, getPageContext, headers, fetch, domActionsRoot });
32
32
  });
33
33
  useEffect(() => {
34
34
  const node = el.current;
package/dist/server.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  parseJobAccessLaunch,
14
14
  serializePageContext,
15
15
  stripPageBlock
16
- } from "./chunk-PUIN5ZCQ.js";
16
+ } from "./chunk-SAUDVPFH.js";
17
17
 
18
18
  // src/server/admission-errors.ts
19
19
  var MESSAGES = {
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, RequestHeadersOption } from './index.js';
3
+ export { ActionConfirmHook, ActionEventDetail, DeweeChatConfig, DeweeChatElement, DomActionsRoot, PageAction, PageActionContext, RequestHeadersOption } from './index.js';
4
4
  import './protocol-CLbGE9Ns.js';
5
5
 
6
6
  declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
@@ -136,6 +136,14 @@ declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
136
136
  type: PropType<any>;
137
137
  default: undefined;
138
138
  };
139
+ domActions: {
140
+ type: PropType<any>;
141
+ default: undefined;
142
+ };
143
+ domActionsRoot: {
144
+ type: PropType<any>;
145
+ default: undefined;
146
+ };
139
147
  }>, () => vue.VNode<vue.RendererNode, vue.RendererElement, {
140
148
  [key: string]: any;
141
149
  }>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("open" | "ready" | "send" | "reply" | "error" | "close" | "action")[], "open" | "ready" | "send" | "reply" | "error" | "close" | "action", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
@@ -271,6 +279,14 @@ declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
271
279
  type: PropType<any>;
272
280
  default: undefined;
273
281
  };
282
+ domActions: {
283
+ type: PropType<any>;
284
+ default: undefined;
285
+ };
286
+ domActionsRoot: {
287
+ type: PropType<any>;
288
+ default: undefined;
289
+ };
274
290
  }>> & Readonly<{
275
291
  onOpen?: ((...args: any[]) => any) | undefined;
276
292
  onReady?: ((...args: any[]) => any) | undefined;
@@ -313,6 +329,8 @@ declare const DeweeChat: vue.DefineComponent<vue.ExtractPropTypes<{
313
329
  actionTimeoutMs: any;
314
330
  headers: any;
315
331
  fetch: any;
332
+ domActions: any;
333
+ domActionsRoot: any;
316
334
  }, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
317
335
 
318
336
  export { DeweeChat, DeweeChat as default };
package/dist/vue.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineDeweeChat
3
- } from "./chunk-2NRPUV6T.js";
4
- import "./chunk-PUIN5ZCQ.js";
3
+ } from "./chunk-UGHJZAKL.js";
4
+ import "./chunk-SAUDVPFH.js";
5
5
 
6
6
  // src/vue.ts
7
7
  import { defineComponent, h, onMounted, ref, watch } from "vue";
@@ -38,7 +38,9 @@ var keys = [
38
38
  "maxActionRounds",
39
39
  "actionTimeoutMs",
40
40
  "headers",
41
- "fetch"
41
+ "fetch",
42
+ "domActions",
43
+ "domActionsRoot"
42
44
  ];
43
45
  var props = Object.fromEntries(keys.map((k) => [k, { type: null, default: void 0 }]));
44
46
  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.4.1-beta.2",
3
+ "version": "0.4.2",
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",