@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/react.cjs CHANGED
@@ -386,13 +386,15 @@ function emitWorkItem(item, handlers, workItems) {
386
386
  handlers.onWork?.(item);
387
387
  handlers.onStep?.(item.label, item.detail);
388
388
  }
389
- function completePlanning(handlers, workItems) {
389
+ function completePlanning(handlers, workItems, nextItems = []) {
390
390
  const planning = workItems.get("planning");
391
391
  if (!planning || planning.state !== "active") return;
392
+ const specialists = nextItems.filter((item) => item.kind === "specialist");
393
+ 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";
392
394
  emitWorkItem(
393
395
  {
394
396
  ...planning,
395
- detail: "Picked the best way to help",
397
+ detail,
396
398
  state: "completed"
397
399
  },
398
400
  handlers,
@@ -442,11 +444,12 @@ function applyWorkEvent(event, handlers, workItems) {
442
444
  return;
443
445
  }
444
446
  if (event.type === "actions.requested") {
445
- completePlanning(handlers, workItems);
446
- for (const action of event.data.actions) {
447
+ const nextItems = event.data.actions.flatMap((action) => {
447
448
  const item = requestedWorkItem(action);
448
- if (item) emitWorkItem(item, handlers, workItems);
449
- }
449
+ return item ? [item] : [];
450
+ });
451
+ completePlanning(handlers, workItems, nextItems);
452
+ for (const item of nextItems) emitWorkItem(item, handlers, workItems);
450
453
  return;
451
454
  }
452
455
  if (event.type === "subagent.called") {
@@ -1168,6 +1171,37 @@ function useAgentChat({
1168
1171
  },
1169
1172
  [runTurn]
1170
1173
  );
1174
+ const retry = (0, import_react2.useCallback)(async () => {
1175
+ const visitorMessage = [...state.messages].reverse().find((message) => message.role === "visitor");
1176
+ if (!visitorMessage) return;
1177
+ if (runRef.current) {
1178
+ runRef.current.abort();
1179
+ clientRef.current.cancelActive();
1180
+ }
1181
+ const controller = new AbortController();
1182
+ runRef.current = controller;
1183
+ setState((prev) => ({
1184
+ ...prev,
1185
+ phase: "thinking",
1186
+ toolSteps: [
1187
+ {
1188
+ id: "planning",
1189
+ kind: "planning",
1190
+ label: "Understanding your question",
1191
+ state: "active"
1192
+ }
1193
+ ],
1194
+ journey: null,
1195
+ followUps: [],
1196
+ streamingText: "",
1197
+ error: null
1198
+ }));
1199
+ await runTurn({
1200
+ controller,
1201
+ resume: false,
1202
+ visitorText: visitorMessage.text
1203
+ });
1204
+ }, [runTurn, state.messages]);
1171
1205
  (0, import_react2.useEffect)(() => {
1172
1206
  const conversation = loadPersistedAgentConversation(
1173
1207
  resolvedStorageKeyPrefix,
@@ -1200,6 +1234,7 @@ function useAgentChat({
1200
1234
  return {
1201
1235
  state,
1202
1236
  reset,
1237
+ retry,
1203
1238
  submit,
1204
1239
  visitorSessionId: visitorId,
1205
1240
  sessionId: clientRef.current.getActiveSessionId()
@@ -1286,11 +1321,12 @@ var defaultAgentRailTheme = {
1286
1321
 
1287
1322
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1288
1323
  var import_jsx_runtime = require("react/jsx-runtime");
1289
- function workSummary(steps, failed) {
1324
+ function workSummary(steps, failed, brandLabel) {
1290
1325
  const active = [...steps].reverse().find((step) => step.state === "active");
1291
- if (active?.kind === "specialist") return `Working with ${active.label}`;
1326
+ if (active?.kind === "specialist")
1327
+ return `${active.label} is reviewing your question`;
1292
1328
  if (active?.kind === "search") return "Searching this site";
1293
- if (active) return active.label;
1329
+ if (active) return `${brandLabel} is choosing the best way to help`;
1294
1330
  if (failed) return "Couldn\u2019t complete this request";
1295
1331
  const hasError = steps.some((step) => step.state === "error");
1296
1332
  const specialists = steps.filter(
@@ -1301,16 +1337,60 @@ function workSummary(steps, failed) {
1301
1337
  );
1302
1338
  if (hasError) return "Answered with available information";
1303
1339
  if (specialists.length > 1)
1304
- return `Consulted ${specialists.length} specialists`;
1305
- if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1306
- if (searched) return "Searched this site";
1307
- return "Prepared a response";
1340
+ return `Answer prepared with ${specialists.length} specialists`;
1341
+ if (specialists.length === 1)
1342
+ return `Answer prepared with ${specialists[0]?.label}`;
1343
+ if (searched) return "Answer prepared from this site";
1344
+ return "Answer ready";
1345
+ }
1346
+ function stepLabel(step, brandLabel) {
1347
+ return step.kind === "planning" ? brandLabel : step.label;
1348
+ }
1349
+ function stepDetail(step, steps) {
1350
+ if (step.kind !== "planning" || step.state !== "completed") {
1351
+ return step.detail;
1352
+ }
1353
+ const specialists = steps.filter((item) => item.kind === "specialist");
1354
+ if (specialists.length === 1) return `Delegated to ${specialists[0]?.label}`;
1355
+ if (specialists.length > 1)
1356
+ return `Delegated to ${specialists.length} specialists`;
1357
+ if (steps.some((item) => item.kind === "search"))
1358
+ return "Used built-in Search & Discovery";
1359
+ return step.detail;
1360
+ }
1361
+ function SearchIcon() {
1362
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
1363
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7", cy: "7", r: "3.75", stroke: "currentColor", strokeWidth: "1.4" }),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1365
+ "path",
1366
+ {
1367
+ d: "m10 10 3 3",
1368
+ stroke: "currentColor",
1369
+ strokeWidth: "1.4",
1370
+ strokeLinecap: "round"
1371
+ }
1372
+ )
1373
+ ] });
1374
+ }
1375
+ function PlanningIcon() {
1376
+ 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)(
1377
+ "path",
1378
+ {
1379
+ d: "M4 4.5h8M4 8h5.5M4 11.5h7",
1380
+ stroke: "currentColor",
1381
+ strokeWidth: "1.4",
1382
+ strokeLinecap: "round"
1383
+ }
1384
+ ) });
1308
1385
  }
1309
1386
  function AgentActivityBubble({
1387
+ brandLabel = "Webless Guide",
1388
+ brandLogoUrl,
1310
1389
  failed = false,
1311
1390
  steps
1312
1391
  }) {
1313
1392
  const active = steps.some((step) => step.state === "active");
1393
+ const delegated = steps.some((step) => step.kind === "specialist");
1314
1394
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1315
1395
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1316
1396
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1320,33 +1400,59 @@ function AgentActivityBubble({
1320
1400
  "aria-hidden": "true"
1321
1401
  }
1322
1402
  ),
1323
- workSummary(steps, failed)
1403
+ workSummary(steps, failed, brandLabel)
1324
1404
  ] }),
1325
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "agent-activity-bubble__details", open: active, children: [
1326
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Work details" }),
1327
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1328
- "li",
1329
- {
1330
- className: "agent-activity-bubble__step",
1331
- "data-state": step.state,
1332
- children: [
1333
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1334
- "span",
1405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1406
+ "details",
1407
+ {
1408
+ className: "agent-activity-bubble__details",
1409
+ open: active || delegated,
1410
+ children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: failed ? "What happened" : active ? "Working" : "How this answer was prepared" }),
1412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
1413
+ const detail = stepDetail(step, steps);
1414
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1415
+ "li",
1335
1416
  {
1336
- className: "agent-activity-bubble__step-icon",
1337
- "aria-hidden": "true",
1338
- children: step.state === "completed" ? "\u2713" : step.state === "error" ? "!" : ""
1339
- }
1340
- ),
1341
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1342
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: step.label }),
1343
- step.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.detail }) : null
1344
- ] })
1345
- ]
1346
- },
1347
- step.id
1348
- )) })
1349
- ] })
1417
+ className: "agent-activity-bubble__step",
1418
+ "data-kind": step.kind,
1419
+ "data-state": step.state,
1420
+ children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1422
+ "span",
1423
+ {
1424
+ className: "agent-activity-bubble__step-icon",
1425
+ "aria-hidden": "true",
1426
+ children: step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1427
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanningIcon, {}),
1428
+ brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1429
+ "img",
1430
+ {
1431
+ src: brandLogoUrl,
1432
+ alt: "",
1433
+ onError: (event) => {
1434
+ event.currentTarget.hidden = true;
1435
+ }
1436
+ }
1437
+ ) : null
1438
+ ] }) : step.kind === "search" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
1439
+ }
1440
+ ),
1441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-heading", children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }),
1444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
1445
+ ] }),
1446
+ detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1447
+ ] })
1448
+ ]
1449
+ },
1450
+ step.id
1451
+ );
1452
+ }) })
1453
+ ]
1454
+ }
1455
+ )
1350
1456
  ] });
1351
1457
  }
1352
1458
 
@@ -1485,6 +1591,29 @@ function MinimizeIcon() {
1485
1591
  }
1486
1592
  ) });
1487
1593
  }
1594
+ function CloseIcon() {
1595
+ 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)(
1596
+ "path",
1597
+ {
1598
+ d: "M4 4l8 8M12 4l-8 8",
1599
+ stroke: "currentColor",
1600
+ strokeWidth: "1.5",
1601
+ strokeLinecap: "round"
1602
+ }
1603
+ ) });
1604
+ }
1605
+ function NewChatIcon() {
1606
+ 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)(
1607
+ "path",
1608
+ {
1609
+ 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",
1610
+ stroke: "currentColor",
1611
+ strokeWidth: "1.4",
1612
+ strokeLinecap: "round",
1613
+ strokeLinejoin: "round"
1614
+ }
1615
+ ) });
1616
+ }
1488
1617
  function ExpandIcon() {
1489
1618
  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)(
1490
1619
  "path",
@@ -1521,10 +1650,13 @@ function AgentRail({
1521
1650
  onCollapse,
1522
1651
  onClose,
1523
1652
  onExpandToggle,
1653
+ onReset,
1654
+ onRetry,
1524
1655
  onSubmit,
1525
1656
  onFollowUpSelect
1526
1657
  }) {
1527
1658
  const transcriptRef = (0, import_react5.useRef)(null);
1659
+ const welcomeTitleId = (0, import_react5.useId)();
1528
1660
  const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
1529
1661
  const railStyle = {
1530
1662
  "--rail-width": resolvedTheme.railMaxWidth,
@@ -1551,7 +1683,13 @@ function AgentRail({
1551
1683
  (message) => message.role === "visitor"
1552
1684
  );
1553
1685
  const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
1554
- const showDockFollowUps = expanded && showIdleFollowUps;
1686
+ const greeting = state.messages.find(
1687
+ (message) => message.role === "agent" && message.id === "greeting"
1688
+ );
1689
+ const visibleMessages = hasVisitorMessages2 ? state.messages.filter((message) => message.id !== "greeting") : [];
1690
+ const lastMessage = visibleMessages.at(-1);
1691
+ const completedAnswer = showActivity && state.phase === "complete" && lastMessage?.role === "agent" ? lastMessage : null;
1692
+ const transcriptMessages = completedAnswer ? visibleMessages.slice(0, -1) : visibleMessages;
1555
1693
  const streamingMessage = state.phase === "streaming" && state.streamingText ? {
1556
1694
  createdAt: 0,
1557
1695
  id: "streaming-response",
@@ -1576,6 +1714,10 @@ function AgentRail({
1576
1714
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
1577
1715
  style: railStyle,
1578
1716
  "aria-label": "Agent conversation",
1717
+ "aria-modal": mobileFullscreen || expanded ? true : void 0,
1718
+ autoFocus: mobileFullscreen || expanded,
1719
+ role: mobileFullscreen || expanded ? "dialog" : void 0,
1720
+ tabIndex: mobileFullscreen || expanded ? -1 : void 0,
1579
1721
  children: [
1580
1722
  /* @__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: [
1581
1723
  onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -1594,7 +1736,7 @@ function AgentRail({
1594
1736
  className: "agent-rail__close",
1595
1737
  "aria-label": "Close agent",
1596
1738
  onClick: onClose,
1597
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MinimizeIcon, {})
1739
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {})
1598
1740
  }
1599
1741
  ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1600
1742
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__identity", children: [
@@ -1614,81 +1756,102 @@ function AgentRail({
1614
1756
  ] }),
1615
1757
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
1616
1758
  ] }),
1617
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1618
- "button",
1759
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__actions", children: [
1760
+ onReset ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1761
+ "button",
1762
+ {
1763
+ type: "button",
1764
+ className: "agent-rail__new-chat",
1765
+ "aria-label": "Start a new conversation",
1766
+ disabled: !hasVisitorMessages2,
1767
+ onClick: onReset,
1768
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NewChatIcon, {})
1769
+ }
1770
+ ) : null,
1771
+ onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1772
+ "button",
1773
+ {
1774
+ type: "button",
1775
+ className: "agent-rail__expand",
1776
+ "aria-label": expanded ? "Exit focus view" : "Open focus view",
1777
+ onClick: onExpandToggle,
1778
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
1779
+ }
1780
+ ) : null
1781
+ ] })
1782
+ ] }) }),
1783
+ /* @__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: [
1784
+ !hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1785
+ "section",
1619
1786
  {
1620
- type: "button",
1621
- className: "agent-rail__expand",
1622
- "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
1623
- onClick: onExpandToggle,
1624
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
1787
+ className: "agent-rail__welcome",
1788
+ "aria-labelledby": welcomeTitleId,
1789
+ children: [
1790
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__welcome-mark", "aria-hidden": "true", children: [
1791
+ brandLabel.slice(0, 1).toUpperCase(),
1792
+ brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1793
+ "img",
1794
+ {
1795
+ className: "agent-rail__welcome-logo",
1796
+ src: brandLogoUrl,
1797
+ alt: "",
1798
+ onError: (event) => {
1799
+ event.currentTarget.hidden = true;
1800
+ }
1801
+ }
1802
+ ) : null
1803
+ ] }),
1804
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__welcome-copy", children: [
1805
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: welcomeTitleId, children: "What can I help you find?" }),
1806
+ greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: greeting.text }) : null
1807
+ ] }),
1808
+ showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1809
+ FollowUpChips,
1810
+ {
1811
+ suggestions: state.followUps,
1812
+ disabled: isBusy,
1813
+ label: "Start here",
1814
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1815
+ }
1816
+ ) }) : null
1817
+ ]
1625
1818
  }
1626
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
1627
- ] }) }),
1628
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
1629
- state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1630
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1819
+ ) : null,
1820
+ transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1631
1821
  showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1632
1822
  AgentActivityBubble,
1633
1823
  {
1824
+ brandLabel,
1825
+ brandLogoUrl,
1634
1826
  failed: state.phase === "error",
1635
1827
  steps: state.toolSteps
1636
1828
  }
1637
1829
  ) : null,
1638
- !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1639
- FollowUpChips,
1640
- {
1641
- suggestions: state.followUps,
1642
- disabled: isBusy,
1643
- label: "Try asking",
1644
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1645
- }
1646
- ) }) : null,
1647
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1648
- "p",
1649
- {
1650
- role: "alert",
1651
- style: { fontSize: 13, color: "var(--as-danger)", margin: 0 },
1652
- children: state.error
1653
- }
1654
- ) : null
1655
- ] }),
1656
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
1657
- showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1658
- FollowUpChips,
1659
- {
1660
- variant: "dock",
1661
- suggestions: state.followUps,
1662
- disabled: isBusy,
1663
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1664
- }
1665
- ) }) : null,
1666
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1667
- Composer,
1668
- {
1669
- variant: "dock",
1670
- disabled: isBusy,
1671
- placeholder: composerPlaceholder,
1672
- onSubmit
1673
- }
1674
- ) }),
1675
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1676
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1677
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1678
- ] })
1679
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1830
+ streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1831
+ completedAnswer ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: completedAnswer }) : null,
1832
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
1833
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1834
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "Something went wrong" }),
1835
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: state.error })
1836
+ ] }),
1837
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
1838
+ ] }) : null
1839
+ ] }) }),
1840
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1680
1841
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1681
1842
  Composer,
1682
1843
  {
1844
+ variant: expanded || mobileFullscreen ? "dock" : "default",
1683
1845
  disabled: isBusy,
1684
1846
  placeholder: composerPlaceholder,
1685
1847
  onSubmit
1686
1848
  }
1687
1849
  ),
1688
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1689
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1690
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1691
- ] })
1850
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
1851
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "AI can make mistakes." }),
1852
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", children: " \xB7 " }),
1853
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: poweredByLabel })
1854
+ ] }) })
1692
1855
  ] })
1693
1856
  ]
1694
1857
  }
@@ -1772,7 +1935,12 @@ function AssistEdgeTab({
1772
1935
  label,
1773
1936
  logoUrl,
1774
1937
  brandColor,
1938
+ brandForeground,
1939
+ borderColor,
1775
1940
  fontFamily,
1941
+ mobile = false,
1942
+ surfaceColor,
1943
+ textColor,
1776
1944
  onOpen
1777
1945
  }) {
1778
1946
  const copy = VARIANT_COPY[variant];
@@ -1781,20 +1949,50 @@ function AssistEdgeTab({
1781
1949
  "--tab-along": `${along}%`,
1782
1950
  "--tab-inset": `${inset}px`,
1783
1951
  ...brandColor ? { "--as-brand": brandColor } : {},
1784
- ...fontFamily ? { "--as-font-display": fontFamily } : {}
1952
+ ...brandForeground ? { "--as-visitor-text": brandForeground } : {},
1953
+ ...borderColor ? { "--as-border": borderColor } : {},
1954
+ ...fontFamily ? { "--as-font-display": fontFamily } : {},
1955
+ ...surfaceColor ? { "--as-surface": surfaceColor } : {},
1956
+ ...textColor ? { "--as-text": textColor } : {}
1785
1957
  };
1786
1958
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1787
1959
  "button",
1788
1960
  {
1789
1961
  type: "button",
1790
- className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${visible ? " is-visible" : ""}`,
1962
+ className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
1791
1963
  style,
1792
1964
  "aria-label": `Open ${visibleLabel}`,
1793
1965
  "aria-hidden": !visible,
1794
1966
  tabIndex: visible ? 0 : -1,
1795
1967
  onClick: onOpen,
1796
1968
  children: [
1797
- variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1969
+ mobile ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1970
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1971
+ "span",
1972
+ {
1973
+ className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
1974
+ "aria-hidden": "true",
1975
+ children: [
1976
+ visibleLabel.slice(0, 1).toUpperCase(),
1977
+ logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1978
+ "img",
1979
+ {
1980
+ className: "assist-edge-tab__logo",
1981
+ src: logoUrl,
1982
+ alt: "",
1983
+ onError: (event) => {
1984
+ event.currentTarget.hidden = true;
1985
+ }
1986
+ }
1987
+ ) : null
1988
+ ]
1989
+ }
1990
+ ),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__label", children: [
1992
+ "Ask ",
1993
+ visibleLabel
1994
+ ] })
1995
+ ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1798
1996
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1799
1997
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
1800
1998
  logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -1870,7 +2068,7 @@ function AgentWidget({
1870
2068
  active: pageShiftActive,
1871
2069
  railSlotRef
1872
2070
  });
1873
- const { state, submit } = useAgentChat({
2071
+ const { state, reset, retry, submit } = useAgentChat({
1874
2072
  customerId,
1875
2073
  getUnpublishedPreviewGrant,
1876
2074
  indexId,
@@ -1913,6 +2111,40 @@ function AgentWidget({
1913
2111
  if (isMobile) setRailCollapsed(false);
1914
2112
  await submit(message);
1915
2113
  }
2114
+ (0, import_react6.useEffect)(() => {
2115
+ if (railCollapsed) return;
2116
+ const handleKeyDown = (event) => {
2117
+ if (event.key === "Tab" && (isMobile || railExpanded)) {
2118
+ const focusable = railSlotRef.current?.querySelectorAll(
2119
+ 'button:not(:disabled), a[href], textarea:not(:disabled), input:not(:disabled), [tabindex]:not([tabindex="-1"])'
2120
+ );
2121
+ if (!focusable || focusable.length === 0) return;
2122
+ const first = focusable.item(0);
2123
+ const last = focusable.item(focusable.length - 1);
2124
+ const dialog = railSlotRef.current?.querySelector(".agent-rail");
2125
+ if (document.activeElement === dialog) {
2126
+ event.preventDefault();
2127
+ (event.shiftKey ? last : first).focus();
2128
+ } else if (event.shiftKey && document.activeElement === first) {
2129
+ event.preventDefault();
2130
+ last.focus();
2131
+ } else if (!event.shiftKey && document.activeElement === last) {
2132
+ event.preventDefault();
2133
+ first.focus();
2134
+ }
2135
+ return;
2136
+ }
2137
+ if (event.key === "Escape") {
2138
+ if (railExpanded) {
2139
+ setRailExpanded(false);
2140
+ return;
2141
+ }
2142
+ setRailCollapsed(true);
2143
+ }
2144
+ };
2145
+ window.addEventListener("keydown", handleKeyDown);
2146
+ return () => window.removeEventListener("keydown", handleKeyDown);
2147
+ }, [isMobile, railCollapsed, railExpanded]);
1916
2148
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "webless-agent-root", children: [
1917
2149
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1918
2150
  "div",
@@ -1944,19 +2176,27 @@ function AgentWidget({
1944
2176
  onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1945
2177
  onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1946
2178
  onSubmit: handleSubmit,
2179
+ onReset: reset,
2180
+ onRetry: () => void retry(),
1947
2181
  onFollowUpSelect: (label) => void handleSubmit(label)
1948
2182
  }
1949
2183
  )
1950
2184
  }
1951
2185
  ),
1952
- !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2186
+ !railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1953
2187
  "button",
1954
2188
  {
1955
2189
  type: "button",
1956
- className: "webless-agent-root__backdrop",
2190
+ className: `webless-agent-root__backdrop${isMobile ? " webless-agent-root__backdrop--mobile" : ""}`,
1957
2191
  tabIndex: -1,
1958
- "aria-label": "Close expanded assist",
1959
- onClick: () => setRailExpanded(false)
2192
+ "aria-label": isMobile ? "Close agent" : "Exit focus view",
2193
+ onClick: () => {
2194
+ if (isMobile) {
2195
+ setRailCollapsed(true);
2196
+ } else {
2197
+ setRailExpanded(false);
2198
+ }
2199
+ }
1960
2200
  }
1961
2201
  ) : null
1962
2202
  ]
@@ -1973,7 +2213,12 @@ function AgentWidget({
1973
2213
  label: agentName,
1974
2214
  logoUrl: branding?.logoUrl,
1975
2215
  brandColor: branding?.colors?.primary,
2216
+ brandForeground: branding?.colors?.primaryForeground,
2217
+ borderColor: branding?.colors?.border,
1976
2218
  fontFamily: branding?.fontFamily,
2219
+ mobile: isMobile,
2220
+ surfaceColor: branding?.colors?.surface,
2221
+ textColor: branding?.colors?.text,
1977
2222
  onOpen: () => setRailCollapsed(false)
1978
2223
  }
1979
2224
  ) : null