@webless/agent 0.3.2 → 0.4.1

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/dist/embed.cjs CHANGED
@@ -402,13 +402,15 @@ function emitWorkItem(item, handlers, workItems) {
402
402
  handlers.onWork?.(item);
403
403
  handlers.onStep?.(item.label, item.detail);
404
404
  }
405
- function completePlanning(handlers, workItems) {
405
+ function completePlanning(handlers, workItems, nextItems = []) {
406
406
  const planning = workItems.get("planning");
407
407
  if (!planning || planning.state !== "active") return;
408
+ const specialists = nextItems.filter((item) => item.kind === "specialist");
409
+ const detail = specialists.length === 1 ? `Delegating to ${specialists[0]?.label}` : specialists.length > 1 ? `Delegating to ${specialists.length} specialists` : nextItems.some((item) => item.kind === "search") ? "Using built-in Search & Discovery" : "Picked the best way to help";
408
410
  emitWorkItem(
409
411
  {
410
412
  ...planning,
411
- detail: "Picked the best way to help",
413
+ detail,
412
414
  state: "completed"
413
415
  },
414
416
  handlers,
@@ -458,11 +460,12 @@ function applyWorkEvent(event, handlers, workItems) {
458
460
  return;
459
461
  }
460
462
  if (event.type === "actions.requested") {
461
- completePlanning(handlers, workItems);
462
- for (const action of event.data.actions) {
463
+ const nextItems = event.data.actions.flatMap((action) => {
463
464
  const item = requestedWorkItem(action);
464
- if (item) emitWorkItem(item, handlers, workItems);
465
- }
465
+ return item ? [item] : [];
466
+ });
467
+ completePlanning(handlers, workItems, nextItems);
468
+ for (const item of nextItems) emitWorkItem(item, handlers, workItems);
466
469
  return;
467
470
  }
468
471
  if (event.type === "subagent.called") {
@@ -1184,6 +1187,37 @@ function useAgentChat({
1184
1187
  },
1185
1188
  [runTurn]
1186
1189
  );
1190
+ const retry = (0, import_react2.useCallback)(async () => {
1191
+ const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
1192
+ if (!visitorMessage) return;
1193
+ if (runRef.current) {
1194
+ runRef.current.abort();
1195
+ clientRef.current.cancelActive();
1196
+ }
1197
+ const controller = new AbortController();
1198
+ runRef.current = controller;
1199
+ setState((prev) => ({
1200
+ ...prev,
1201
+ phase: "thinking",
1202
+ toolSteps: [
1203
+ {
1204
+ id: "planning",
1205
+ kind: "planning",
1206
+ label: "Understanding your question",
1207
+ state: "active"
1208
+ }
1209
+ ],
1210
+ journey: null,
1211
+ followUps: [],
1212
+ streamingText: "",
1213
+ error: null
1214
+ }));
1215
+ await runTurn({
1216
+ controller,
1217
+ resume: false,
1218
+ visitorText: visitorMessage.text
1219
+ });
1220
+ }, [runTurn, state.messages]);
1187
1221
  (0, import_react2.useEffect)(() => {
1188
1222
  const conversation = loadPersistedAgentConversation(
1189
1223
  resolvedStorageKeyPrefix,
@@ -1216,6 +1250,7 @@ function useAgentChat({
1216
1250
  return {
1217
1251
  state,
1218
1252
  reset,
1253
+ retry,
1219
1254
  submit,
1220
1255
  visitorSessionId: visitorId,
1221
1256
  sessionId: clientRef.current.getActiveSessionId()
@@ -1293,11 +1328,12 @@ var defaultAgentRailTheme = {
1293
1328
 
1294
1329
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1295
1330
  var import_jsx_runtime = require("react/jsx-runtime");
1296
- function workSummary(steps, failed) {
1331
+ function workSummary(steps, failed, brandLabel) {
1297
1332
  const active = [...steps].reverse().find((step) => step.state === "active");
1298
- if (active?.kind === "specialist") return `Working with ${active.label}`;
1333
+ if (active?.kind === "specialist")
1334
+ return `${active.label} is reviewing your question`;
1299
1335
  if (active?.kind === "search") return "Searching this site";
1300
- if (active) return active.label;
1336
+ if (active) return `${brandLabel} is choosing the best way to help`;
1301
1337
  if (failed) return "Couldn\u2019t complete this request";
1302
1338
  const hasError = steps.some((step) => step.state === "error");
1303
1339
  const specialists = steps.filter(
@@ -1308,16 +1344,60 @@ function workSummary(steps, failed) {
1308
1344
  );
1309
1345
  if (hasError) return "Answered with available information";
1310
1346
  if (specialists.length > 1)
1311
- return `Consulted ${specialists.length} specialists`;
1312
- if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1313
- if (searched) return "Searched this site";
1314
- return "Prepared a response";
1347
+ return `Answer prepared with ${specialists.length} specialists`;
1348
+ if (specialists.length === 1)
1349
+ return `Answer prepared with ${specialists[0]?.label}`;
1350
+ if (searched) return "Answer prepared from this site";
1351
+ return "Answer ready";
1352
+ }
1353
+ function stepLabel(step, brandLabel) {
1354
+ return step.kind === "planning" ? brandLabel : step.label;
1355
+ }
1356
+ function stepDetail(step, steps) {
1357
+ if (step.kind !== "planning" || step.state !== "completed") {
1358
+ return step.detail;
1359
+ }
1360
+ const specialists = steps.filter((item) => item.kind === "specialist");
1361
+ if (specialists.length === 1) return `Delegated to ${specialists[0]?.label}`;
1362
+ if (specialists.length > 1)
1363
+ return `Delegated to ${specialists.length} specialists`;
1364
+ if (steps.some((item) => item.kind === "search"))
1365
+ return "Used built-in Search & Discovery";
1366
+ return step.detail;
1367
+ }
1368
+ function SearchIcon() {
1369
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
1370
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7", cy: "7", r: "3.75", stroke: "currentColor", strokeWidth: "1.4" }),
1371
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1372
+ "path",
1373
+ {
1374
+ d: "m10 10 3 3",
1375
+ stroke: "currentColor",
1376
+ strokeWidth: "1.4",
1377
+ strokeLinecap: "round"
1378
+ }
1379
+ )
1380
+ ] });
1381
+ }
1382
+ function PlanningIcon() {
1383
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1384
+ "path",
1385
+ {
1386
+ d: "M4 4.5h8M4 8h5.5M4 11.5h7",
1387
+ stroke: "currentColor",
1388
+ strokeWidth: "1.4",
1389
+ strokeLinecap: "round"
1390
+ }
1391
+ ) });
1315
1392
  }
1316
1393
  function AgentActivityBubble({
1394
+ brandLabel = "Webless Guide",
1395
+ brandLogoUrl,
1317
1396
  failed = false,
1318
1397
  steps
1319
1398
  }) {
1320
1399
  const active = steps.some((step) => step.state === "active");
1400
+ const delegated = steps.some((step) => step.kind === "specialist");
1321
1401
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1322
1402
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1323
1403
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1327,33 +1407,59 @@ function AgentActivityBubble({
1327
1407
  "aria-hidden": "true"
1328
1408
  }
1329
1409
  ),
1330
- workSummary(steps, failed)
1410
+ workSummary(steps, failed, brandLabel)
1331
1411
  ] }),
1332
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "agent-activity-bubble__details", open: active, children: [
1333
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Work details" }),
1334
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1335
- "li",
1336
- {
1337
- className: "agent-activity-bubble__step",
1338
- "data-state": step.state,
1339
- children: [
1340
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1341
- "span",
1412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1413
+ "details",
1414
+ {
1415
+ className: "agent-activity-bubble__details",
1416
+ open: active || delegated,
1417
+ children: [
1418
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: failed ? "What happened" : active ? "Working" : "How this answer was prepared" }),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
1420
+ const detail = stepDetail(step, steps);
1421
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1422
+ "li",
1342
1423
  {
1343
- className: "agent-activity-bubble__step-icon",
1344
- "aria-hidden": "true",
1345
- children: step.state === "completed" ? "\u2713" : step.state === "error" ? "!" : ""
1346
- }
1347
- ),
1348
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1349
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: step.label }),
1350
- step.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.detail }) : null
1351
- ] })
1352
- ]
1353
- },
1354
- step.id
1355
- )) })
1356
- ] })
1424
+ className: "agent-activity-bubble__step",
1425
+ "data-kind": step.kind,
1426
+ "data-state": step.state,
1427
+ children: [
1428
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1429
+ "span",
1430
+ {
1431
+ className: "agent-activity-bubble__step-icon",
1432
+ "aria-hidden": "true",
1433
+ children: step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanningIcon, {}),
1435
+ brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1436
+ "img",
1437
+ {
1438
+ src: brandLogoUrl,
1439
+ alt: "",
1440
+ onError: (event) => {
1441
+ event.currentTarget.hidden = true;
1442
+ }
1443
+ }
1444
+ ) : null
1445
+ ] }) : step.kind === "search" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
1446
+ }
1447
+ ),
1448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-heading", children: [
1450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
1452
+ ] }),
1453
+ detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1454
+ ] })
1455
+ ]
1456
+ },
1457
+ step.id
1458
+ );
1459
+ }) })
1460
+ ]
1461
+ }
1462
+ )
1357
1463
  ] });
1358
1464
  }
1359
1465
 
@@ -1492,6 +1598,29 @@ function MinimizeIcon() {
1492
1598
  }
1493
1599
  ) });
1494
1600
  }
1601
+ function CloseIcon() {
1602
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1603
+ "path",
1604
+ {
1605
+ d: "M4 4l8 8M12 4l-8 8",
1606
+ stroke: "currentColor",
1607
+ strokeWidth: "1.5",
1608
+ strokeLinecap: "round"
1609
+ }
1610
+ ) });
1611
+ }
1612
+ function NewChatIcon() {
1613
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1614
+ "path",
1615
+ {
1616
+ d: "M9.5 3.5h3v3M12.25 3.75 8 8M7 4H4.5A1.5 1.5 0 0 0 3 5.5v6A1.5 1.5 0 0 0 4.5 13h6a1.5 1.5 0 0 0 1.5-1.5V9",
1617
+ stroke: "currentColor",
1618
+ strokeWidth: "1.4",
1619
+ strokeLinecap: "round",
1620
+ strokeLinejoin: "round"
1621
+ }
1622
+ ) });
1623
+ }
1495
1624
  function ExpandIcon() {
1496
1625
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1497
1626
  "path",
@@ -1528,10 +1657,13 @@ function AgentRail({
1528
1657
  onCollapse,
1529
1658
  onClose,
1530
1659
  onExpandToggle,
1660
+ onReset,
1661
+ onRetry,
1531
1662
  onSubmit,
1532
1663
  onFollowUpSelect
1533
1664
  }) {
1534
1665
  const transcriptRef = (0, import_react5.useRef)(null);
1666
+ const welcomeTitleId = (0, import_react5.useId)();
1535
1667
  const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
1536
1668
  const railStyle = {
1537
1669
  "--rail-width": resolvedTheme.railMaxWidth,
@@ -1558,7 +1690,13 @@ function AgentRail({
1558
1690
  (message) => message.role === "visitor"
1559
1691
  );
1560
1692
  const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
1561
- const showDockFollowUps = expanded && showIdleFollowUps;
1693
+ const greeting = state.messages.find(
1694
+ (message) => message.role === "agent" && message.id === "greeting"
1695
+ );
1696
+ const visibleMessages = hasVisitorMessages2 ? state.messages.filter((message) => message.id !== "greeting") : [];
1697
+ const lastMessage = visibleMessages.at(-1);
1698
+ const completedAnswer = showActivity && state.phase === "complete" && lastMessage?.role === "agent" ? lastMessage : null;
1699
+ const transcriptMessages = completedAnswer ? visibleMessages.slice(0, -1) : visibleMessages;
1562
1700
  const streamingMessage = state.phase === "streaming" && state.streamingText ? {
1563
1701
  createdAt: 0,
1564
1702
  id: "streaming-response",
@@ -1583,6 +1721,10 @@ function AgentRail({
1583
1721
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
1584
1722
  style: railStyle,
1585
1723
  "aria-label": "Agent conversation",
1724
+ "aria-modal": mobileFullscreen || expanded ? true : void 0,
1725
+ autoFocus: mobileFullscreen || expanded,
1726
+ role: mobileFullscreen || expanded ? "dialog" : void 0,
1727
+ tabIndex: mobileFullscreen || expanded ? -1 : void 0,
1586
1728
  children: [
1587
1729
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__brand-row", children: [
1588
1730
  onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -1601,7 +1743,7 @@ function AgentRail({
1601
1743
  className: "agent-rail__close",
1602
1744
  "aria-label": "Close agent",
1603
1745
  onClick: onClose,
1604
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MinimizeIcon, {})
1746
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {})
1605
1747
  }
1606
1748
  ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1607
1749
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__identity", children: [
@@ -1621,81 +1763,102 @@ function AgentRail({
1621
1763
  ] }),
1622
1764
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
1623
1765
  ] }),
1624
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1625
- "button",
1766
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__actions", children: [
1767
+ onReset ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1768
+ "button",
1769
+ {
1770
+ type: "button",
1771
+ className: "agent-rail__new-chat",
1772
+ "aria-label": "Start a new conversation",
1773
+ disabled: !hasVisitorMessages2,
1774
+ onClick: onReset,
1775
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NewChatIcon, {})
1776
+ }
1777
+ ) : null,
1778
+ onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1779
+ "button",
1780
+ {
1781
+ type: "button",
1782
+ className: "agent-rail__expand",
1783
+ "aria-label": expanded ? "Exit focus view" : "Open focus view",
1784
+ onClick: onExpandToggle,
1785
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
1786
+ }
1787
+ ) : null
1788
+ ] })
1789
+ ] }) }),
1790
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__thread", children: [
1791
+ !hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1792
+ "section",
1626
1793
  {
1627
- type: "button",
1628
- className: "agent-rail__expand",
1629
- "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
1630
- onClick: onExpandToggle,
1631
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
1794
+ className: "agent-rail__welcome",
1795
+ "aria-labelledby": welcomeTitleId,
1796
+ children: [
1797
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__welcome-mark", "aria-hidden": "true", children: [
1798
+ brandLabel.slice(0, 1).toUpperCase(),
1799
+ brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1800
+ "img",
1801
+ {
1802
+ className: "agent-rail__welcome-logo",
1803
+ src: brandLogoUrl,
1804
+ alt: "",
1805
+ onError: (event) => {
1806
+ event.currentTarget.hidden = true;
1807
+ }
1808
+ }
1809
+ ) : null
1810
+ ] }),
1811
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__welcome-copy", children: [
1812
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: welcomeTitleId, children: "What can I help you find?" }),
1813
+ greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: greeting.text }) : null
1814
+ ] }),
1815
+ showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1816
+ FollowUpChips,
1817
+ {
1818
+ suggestions: state.followUps,
1819
+ disabled: isBusy,
1820
+ label: "Start here",
1821
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1822
+ }
1823
+ ) }) : null
1824
+ ]
1632
1825
  }
1633
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
1634
- ] }) }),
1635
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
1636
- state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1637
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1826
+ ) : null,
1827
+ transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1638
1828
  showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1639
1829
  AgentActivityBubble,
1640
1830
  {
1831
+ brandLabel,
1832
+ brandLogoUrl,
1641
1833
  failed: state.phase === "error",
1642
1834
  steps: state.toolSteps
1643
1835
  }
1644
1836
  ) : null,
1645
- !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1646
- FollowUpChips,
1647
- {
1648
- suggestions: state.followUps,
1649
- disabled: isBusy,
1650
- label: "Try asking",
1651
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1652
- }
1653
- ) }) : null,
1654
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1655
- "p",
1656
- {
1657
- role: "alert",
1658
- style: { fontSize: 13, color: "var(--as-danger)", margin: 0 },
1659
- children: state.error
1660
- }
1661
- ) : null
1662
- ] }),
1663
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
1664
- showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1665
- FollowUpChips,
1666
- {
1667
- variant: "dock",
1668
- suggestions: state.followUps,
1669
- disabled: isBusy,
1670
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1671
- }
1672
- ) }) : null,
1673
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1674
- Composer,
1675
- {
1676
- variant: "dock",
1677
- disabled: isBusy,
1678
- placeholder: composerPlaceholder,
1679
- onSubmit
1680
- }
1681
- ) }),
1682
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1683
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1684
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1685
- ] })
1686
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1837
+ streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1838
+ completedAnswer ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: completedAnswer }) : null,
1839
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
1840
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1841
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "Something went wrong" }),
1842
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: state.error })
1843
+ ] }),
1844
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
1845
+ ] }) : null
1846
+ ] }) }),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1687
1848
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1688
1849
  Composer,
1689
1850
  {
1851
+ variant: expanded || mobileFullscreen ? "dock" : "default",
1690
1852
  disabled: isBusy,
1691
1853
  placeholder: composerPlaceholder,
1692
1854
  onSubmit
1693
1855
  }
1694
1856
  ),
1695
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1696
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1697
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1698
- ] })
1857
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
1858
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "AI can make mistakes." }),
1859
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", children: " \xB7 " }),
1860
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: poweredByLabel })
1861
+ ] }) })
1699
1862
  ] })
1700
1863
  ]
1701
1864
  }
@@ -1779,7 +1942,12 @@ function AssistEdgeTab({
1779
1942
  label,
1780
1943
  logoUrl,
1781
1944
  brandColor,
1945
+ brandForeground,
1946
+ borderColor,
1782
1947
  fontFamily,
1948
+ mobile = false,
1949
+ surfaceColor,
1950
+ textColor,
1783
1951
  onOpen
1784
1952
  }) {
1785
1953
  const copy = VARIANT_COPY[variant];
@@ -1788,20 +1956,50 @@ function AssistEdgeTab({
1788
1956
  "--tab-along": `${along}%`,
1789
1957
  "--tab-inset": `${inset}px`,
1790
1958
  ...brandColor ? { "--as-brand": brandColor } : {},
1791
- ...fontFamily ? { "--as-font-display": fontFamily } : {}
1959
+ ...brandForeground ? { "--as-visitor-text": brandForeground } : {},
1960
+ ...borderColor ? { "--as-border": borderColor } : {},
1961
+ ...fontFamily ? { "--as-font-display": fontFamily } : {},
1962
+ ...surfaceColor ? { "--as-surface": surfaceColor } : {},
1963
+ ...textColor ? { "--as-text": textColor } : {}
1792
1964
  };
1793
1965
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1794
1966
  "button",
1795
1967
  {
1796
1968
  type: "button",
1797
- className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${visible ? " is-visible" : ""}`,
1969
+ className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
1798
1970
  style,
1799
1971
  "aria-label": `Open ${visibleLabel}`,
1800
1972
  "aria-hidden": !visible,
1801
1973
  tabIndex: visible ? 0 : -1,
1802
1974
  onClick: onOpen,
1803
1975
  children: [
1804
- variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1976
+ mobile ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1977
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1978
+ "span",
1979
+ {
1980
+ className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
1981
+ "aria-hidden": "true",
1982
+ children: [
1983
+ visibleLabel.slice(0, 1).toUpperCase(),
1984
+ logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1985
+ "img",
1986
+ {
1987
+ className: "assist-edge-tab__logo",
1988
+ src: logoUrl,
1989
+ alt: "",
1990
+ onError: (event) => {
1991
+ event.currentTarget.hidden = true;
1992
+ }
1993
+ }
1994
+ ) : null
1995
+ ]
1996
+ }
1997
+ ),
1998
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__label", children: [
1999
+ "Ask ",
2000
+ visibleLabel
2001
+ ] })
2002
+ ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1805
2003
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1806
2004
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
1807
2005
  logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -1877,7 +2075,7 @@ function AgentWidget({
1877
2075
  active: pageShiftActive,
1878
2076
  railSlotRef
1879
2077
  });
1880
- const { state, submit } = useAgentChat({
2078
+ const { state, reset, retry, submit } = useAgentChat({
1881
2079
  customerId,
1882
2080
  getUnpublishedPreviewGrant,
1883
2081
  indexId,
@@ -1920,6 +2118,40 @@ function AgentWidget({
1920
2118
  if (isMobile) setRailCollapsed(false);
1921
2119
  await submit(message);
1922
2120
  }
2121
+ (0, import_react6.useEffect)(() => {
2122
+ if (railCollapsed) return;
2123
+ const handleKeyDown = (event) => {
2124
+ if (event.key === "Tab" && (isMobile || railExpanded)) {
2125
+ const focusable = railSlotRef.current?.querySelectorAll(
2126
+ 'button:not(:disabled), a[href], textarea:not(:disabled), input:not(:disabled), [tabindex]:not([tabindex="-1"])'
2127
+ );
2128
+ if (!focusable || focusable.length === 0) return;
2129
+ const first = focusable.item(0);
2130
+ const last = focusable.item(focusable.length - 1);
2131
+ const dialog = railSlotRef.current?.querySelector(".agent-rail");
2132
+ if (document.activeElement === dialog) {
2133
+ event.preventDefault();
2134
+ (event.shiftKey ? last : first).focus();
2135
+ } else if (event.shiftKey && document.activeElement === first) {
2136
+ event.preventDefault();
2137
+ last.focus();
2138
+ } else if (!event.shiftKey && document.activeElement === last) {
2139
+ event.preventDefault();
2140
+ first.focus();
2141
+ }
2142
+ return;
2143
+ }
2144
+ if (event.key === "Escape") {
2145
+ if (railExpanded) {
2146
+ setRailExpanded(false);
2147
+ return;
2148
+ }
2149
+ setRailCollapsed(true);
2150
+ }
2151
+ };
2152
+ window.addEventListener("keydown", handleKeyDown);
2153
+ return () => window.removeEventListener("keydown", handleKeyDown);
2154
+ }, [isMobile, railCollapsed, railExpanded]);
1923
2155
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "webless-agent-root", children: [
1924
2156
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1925
2157
  "div",
@@ -1951,19 +2183,27 @@ function AgentWidget({
1951
2183
  onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1952
2184
  onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1953
2185
  onSubmit: handleSubmit,
2186
+ onReset: reset,
2187
+ onRetry: () => void retry(),
1954
2188
  onFollowUpSelect: (label) => void handleSubmit(label)
1955
2189
  }
1956
2190
  )
1957
2191
  }
1958
2192
  ),
1959
- !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2193
+ !railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1960
2194
  "button",
1961
2195
  {
1962
2196
  type: "button",
1963
- className: "webless-agent-root__backdrop",
2197
+ className: `webless-agent-root__backdrop${isMobile ? " webless-agent-root__backdrop--mobile" : ""}`,
1964
2198
  tabIndex: -1,
1965
- "aria-label": "Close expanded assist",
1966
- onClick: () => setRailExpanded(false)
2199
+ "aria-label": isMobile ? "Close agent" : "Exit focus view",
2200
+ onClick: () => {
2201
+ if (isMobile) {
2202
+ setRailCollapsed(true);
2203
+ } else {
2204
+ setRailExpanded(false);
2205
+ }
2206
+ }
1967
2207
  }
1968
2208
  ) : null
1969
2209
  ]
@@ -1980,7 +2220,12 @@ function AgentWidget({
1980
2220
  label: agentName,
1981
2221
  logoUrl: branding?.logoUrl,
1982
2222
  brandColor: branding?.colors?.primary,
2223
+ brandForeground: branding?.colors?.primaryForeground,
2224
+ borderColor: branding?.colors?.border,
1983
2225
  fontFamily: branding?.fontFamily,
2226
+ mobile: isMobile,
2227
+ surfaceColor: branding?.colors?.surface,
2228
+ textColor: branding?.colors?.text,
1984
2229
  onOpen: () => setRailCollapsed(false)
1985
2230
  }
1986
2231
  ) : null