dewee-webchat 0.4.1 → 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 +39 -0
- package/dist/{chunk-EVZ5K7BN.js → chunk-SAUDVPFH.js} +1 -1
- package/dist/{chunk-VU2M42GA.js → chunk-UGHJZAKL.js} +320 -25
- package/dist/dewee-webchat.js +27 -27
- package/dist/index.d.ts +23 -2
- package/dist/index.js +4 -2
- package/dist/react.d.ts +1 -1
- package/dist/react.js +4 -4
- package/dist/server.js +1 -1
- package/dist/vue.d.ts +21 -3
- package/dist/vue.js +5 -3
- package/package.json +1 -1
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.
|
|
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-
|
|
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
|
|
1790
|
+
/** Names of the page actions offered to the agent (host actions, then enabled built-ins). */
|
|
1527
1791
|
get actionNames() {
|
|
1528
|
-
return this.
|
|
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.
|
|
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
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
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
|
-
|
|
1641
|
-
|
|
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
|
-
|
|
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.
|
|
1648
|
-
|
|
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 ?
|
|
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
|
|
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
|
-
|
|
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,
|
|
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.
|
|
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)
|
|
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,
|
package/dist/dewee-webchat.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
"use strict";(()=>{var
|
|
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(
|
|
4
|
+
`).trim()};function K(t){let s=t.replace(/\r\n?/g,`
|
|
5
5
|
`).split(`
|
|
6
|
-
`),e=
|
|
7
|
-
`),
|
|
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
|
|
10
|
-
`,1)[0]??"")}function
|
|
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
|
|
13
|
-
`));
|
|
14
|
-
`+JSON.stringify(
|
|
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
|
|
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
|
|
20
|
-
`).filter(
|
|
21
|
-
`);if(!
|
|
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
|
-
`),
|
|
37
|
-
`),
|
|
38
|
-
`))),e.appendChild(
|
|
39
|
-
`)));continue}let m=
|
|
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>${
|
|
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">${
|
|
319
|
-
<button class="icon-btn job-access" hidden>${
|
|
320
|
-
<button class="icon-btn close">${
|
|
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=
|
|
334
|
-
`);return a&&(o.title=a),o}))}renderTool(e,
|
|
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 ${
|
|
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
|
|
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-
|
|
10
|
-
import "./chunk-
|
|
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-
|
|
4
|
-
import "./chunk-
|
|
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
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,9 +136,17 @@ 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
|
-
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("open" | "
|
|
149
|
+
}>, {}, {}, {}, vue.ComponentOptionsMixin, vue.ComponentOptionsMixin, ("open" | "ready" | "send" | "reply" | "error" | "close" | "action")[], "open" | "ready" | "send" | "reply" | "error" | "close" | "action", vue.PublicProps, Readonly<vue.ExtractPropTypes<{
|
|
142
150
|
endpoint: {
|
|
143
151
|
type: PropType<any>;
|
|
144
152
|
default: undefined;
|
|
@@ -271,12 +279,20 @@ 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<{
|
|
291
|
+
onOpen?: ((...args: any[]) => any) | undefined;
|
|
275
292
|
onReady?: ((...args: any[]) => any) | undefined;
|
|
276
293
|
onSend?: ((...args: any[]) => any) | undefined;
|
|
277
294
|
onReply?: ((...args: any[]) => any) | undefined;
|
|
278
295
|
onError?: ((...args: any[]) => any) | undefined;
|
|
279
|
-
onOpen?: ((...args: any[]) => any) | undefined;
|
|
280
296
|
onClose?: ((...args: any[]) => any) | undefined;
|
|
281
297
|
onAction?: ((...args: any[]) => any) | undefined;
|
|
282
298
|
}>, {
|
|
@@ -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-
|
|
4
|
-
import "./chunk-
|
|
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.
|
|
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",
|