@webless/agent 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs CHANGED
@@ -162,11 +162,12 @@ function createAgentRuntimeCapability(options) {
162
162
  let pendingBootstrap;
163
163
  const bootstrap = async () => {
164
164
  let previewGrant;
165
+ const previewBuildId = options.previewBuildId?.trim();
165
166
  if (options.version === "unpublished") {
166
- previewGrant = (await options.getUnpublishedPreviewGrant?.())?.trim();
167
- if (!previewGrant) {
167
+ previewGrant = previewBuildId ? void 0 : (await options.getUnpublishedPreviewGrant?.())?.trim();
168
+ if (!previewGrant && !previewBuildId) {
168
169
  throw new Error(
169
- "An unpublished preview grant is required to use the Agent Runtime preview."
170
+ "Unpublished preview authorization is required to use the Agent Runtime preview."
170
171
  );
171
172
  }
172
173
  }
@@ -176,6 +177,7 @@ function createAgentRuntimeCapability(options) {
176
177
  body: JSON.stringify({
177
178
  clientSessionId: options.visitorSessionId,
178
179
  indexId: options.indexId,
180
+ ...previewBuildId ? { previewBuildId } : {},
179
181
  ...previewGrant ? { previewGrant } : {},
180
182
  version: options.version
181
183
  }),
@@ -384,13 +386,15 @@ function emitWorkItem(item, handlers, workItems) {
384
386
  handlers.onWork?.(item);
385
387
  handlers.onStep?.(item.label, item.detail);
386
388
  }
387
- function completePlanning(handlers, workItems) {
389
+ function completePlanning(handlers, workItems, nextItems = []) {
388
390
  const planning = workItems.get("planning");
389
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";
390
394
  emitWorkItem(
391
395
  {
392
396
  ...planning,
393
- detail: "Picked the best way to help",
397
+ detail,
394
398
  state: "completed"
395
399
  },
396
400
  handlers,
@@ -440,11 +444,12 @@ function applyWorkEvent(event, handlers, workItems) {
440
444
  return;
441
445
  }
442
446
  if (event.type === "actions.requested") {
443
- completePlanning(handlers, workItems);
444
- for (const action of event.data.actions) {
447
+ const nextItems = event.data.actions.flatMap((action) => {
445
448
  const item = requestedWorkItem(action);
446
- if (item) emitWorkItem(item, handlers, workItems);
447
- }
449
+ return item ? [item] : [];
450
+ });
451
+ completePlanning(handlers, workItems, nextItems);
452
+ for (const item of nextItems) emitWorkItem(item, handlers, workItems);
448
453
  return;
449
454
  }
450
455
  if (event.type === "subagent.called") {
@@ -495,7 +500,7 @@ function applyWorkEvent(event, handlers, workItems) {
495
500
  );
496
501
  }
497
502
  var AgentSession = class {
498
- constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, getUnpublishedPreviewGrant) {
503
+ constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, previewBuildId, getUnpublishedPreviewGrant) {
499
504
  this.indexId = indexId;
500
505
  this.version = version;
501
506
  this.runtimeOrigin = runtimeOrigin;
@@ -504,6 +509,7 @@ var AgentSession = class {
504
509
  this.capability = createAgentRuntimeCapability({
505
510
  getUnpublishedPreviewGrant,
506
511
  indexId,
512
+ previewBuildId,
507
513
  runtimeOrigin,
508
514
  version,
509
515
  visitorSessionId
@@ -748,6 +754,7 @@ function createAgentClient(options) {
748
754
  runtimeOrigin,
749
755
  visitorSessionId,
750
756
  storeOptions,
757
+ options.previewBuildId,
751
758
  options.getUnpublishedPreviewGrant
752
759
  );
753
760
  return {
@@ -775,10 +782,15 @@ function createAgentClient(options) {
775
782
  // src/runtime/errors.ts
776
783
  var import_client3 = require("eve/client");
777
784
  var TRANSIENT_AGENT_ERROR_MESSAGE = "I couldn\u2019t finish that answer. Please try again.";
785
+ var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
778
786
  function isTransientRuntimeMessage(message) {
779
787
  const normalized = message.trim().toLowerCase();
780
788
  return normalized.includes("empty response from runtime") || normalized.includes("failed to fetch") || normalized.includes("networkerror") || normalized.includes("load failed");
781
789
  }
790
+ function isPreviewAuthorizationMessage(message) {
791
+ const normalized = message.trim().toLowerCase();
792
+ return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
793
+ }
782
794
  function formatAgentError(error) {
783
795
  if (error instanceof import_client3.ClientError) {
784
796
  if (error.status === 401 && error.code === "index_required") {
@@ -790,6 +802,9 @@ function formatAgentError(error) {
790
802
  if (error.status === 409 && error.code === "session_not_active") {
791
803
  return "Session expired \u2014 send a new message to start again.";
792
804
  }
805
+ if (error.message && isPreviewAuthorizationMessage(error.message)) {
806
+ return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
807
+ }
793
808
  if (error.status >= 500 || error.message && isTransientRuntimeMessage(error.message)) {
794
809
  return TRANSIENT_AGENT_ERROR_MESSAGE;
795
810
  }
@@ -799,6 +814,9 @@ function formatAgentError(error) {
799
814
  return "";
800
815
  }
801
816
  if (error instanceof Error) {
817
+ if (isPreviewAuthorizationMessage(error.message)) {
818
+ return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
819
+ }
802
820
  return isTransientRuntimeMessage(error.message) ? TRANSIENT_AGENT_ERROR_MESSAGE : error.message;
803
821
  }
804
822
  return TRANSIENT_AGENT_ERROR_MESSAGE;
@@ -910,6 +928,7 @@ function useAgentChat({
910
928
  customerId,
911
929
  getUnpublishedPreviewGrant,
912
930
  indexId,
931
+ previewBuildId,
913
932
  version,
914
933
  runtimeOrigin,
915
934
  visitorSessionId,
@@ -960,13 +979,14 @@ function useAgentChat({
960
979
  customerId,
961
980
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
962
981
  indexId,
982
+ previewBuildId,
963
983
  version,
964
984
  runtimeOrigin,
965
985
  visitorSessionId: visitorId,
966
986
  storageKeyPrefix: resolvedStorageKeyPrefix
967
987
  })
968
988
  );
969
- const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
989
+ const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${previewBuildId ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
970
990
  const identityRef = (0, import_react2.useRef)(identityKey);
971
991
  (0, import_react2.useEffect)(() => {
972
992
  if (identityRef.current === identityKey) {
@@ -979,6 +999,7 @@ function useAgentChat({
979
999
  customerId,
980
1000
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
981
1001
  indexId,
1002
+ previewBuildId,
982
1003
  version,
983
1004
  runtimeOrigin,
984
1005
  visitorSessionId: visitorId,
@@ -995,6 +1016,7 @@ function useAgentChat({
995
1016
  identityKey,
996
1017
  indexId,
997
1018
  initialState,
1019
+ previewBuildId,
998
1020
  runtimeOrigin,
999
1021
  resolvedStorageKeyPrefix,
1000
1022
  version,
@@ -1098,8 +1120,8 @@ function useAgentChat({
1098
1120
  if (!message) return;
1099
1121
  setState((prev) => ({
1100
1122
  ...prev,
1101
- phase: "complete",
1102
- toolSteps: prev.toolSteps.map(
1123
+ phase: "error",
1124
+ toolSteps: prev.toolSteps.length === 1 && prev.toolSteps[0]?.kind === "planning" ? [] : prev.toolSteps.map(
1103
1125
  (step) => step.state === "active" ? {
1104
1126
  ...step,
1105
1127
  detail: "Couldn\u2019t complete this step",
@@ -1149,6 +1171,37 @@ function useAgentChat({
1149
1171
  },
1150
1172
  [runTurn]
1151
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]);
1152
1205
  (0, import_react2.useEffect)(() => {
1153
1206
  const conversation = loadPersistedAgentConversation(
1154
1207
  resolvedStorageKeyPrefix,
@@ -1181,6 +1234,7 @@ function useAgentChat({
1181
1234
  return {
1182
1235
  state,
1183
1236
  reset,
1237
+ retry,
1184
1238
  submit,
1185
1239
  visitorSessionId: visitorId,
1186
1240
  sessionId: clientRef.current.getActiveSessionId()
@@ -1267,11 +1321,13 @@ var defaultAgentRailTheme = {
1267
1321
 
1268
1322
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1269
1323
  var import_jsx_runtime = require("react/jsx-runtime");
1270
- function workSummary(steps) {
1324
+ function workSummary(steps, failed, brandLabel) {
1271
1325
  const active = [...steps].reverse().find((step) => step.state === "active");
1272
- if (active?.kind === "specialist") return `Working with ${active.label}`;
1326
+ if (active?.kind === "specialist")
1327
+ return `${active.label} is reviewing your question`;
1273
1328
  if (active?.kind === "search") return "Searching this site";
1274
- if (active) return active.label;
1329
+ if (active) return `${brandLabel} is choosing the best way to help`;
1330
+ if (failed) return "Couldn\u2019t complete this request";
1275
1331
  const hasError = steps.some((step) => step.state === "error");
1276
1332
  const specialists = steps.filter(
1277
1333
  (step) => step.kind === "specialist" && step.state === "completed"
@@ -1281,49 +1337,122 @@ function workSummary(steps) {
1281
1337
  );
1282
1338
  if (hasError) return "Answered with available information";
1283
1339
  if (specialists.length > 1)
1284
- return `Consulted ${specialists.length} specialists`;
1285
- if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1286
- if (searched) return "Searched this site";
1287
- 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
+ ] });
1288
1374
  }
1289
- function AgentActivityBubble({ steps }) {
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
+ ) });
1385
+ }
1386
+ function AgentActivityBubble({
1387
+ brandLabel = "Webless Guide",
1388
+ brandLogoUrl,
1389
+ failed = false,
1390
+ steps
1391
+ }) {
1290
1392
  const active = steps.some((step) => step.state === "active");
1393
+ const delegated = steps.some((step) => step.kind === "specialist");
1291
1394
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1292
1395
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1293
1396
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1294
1397
  "span",
1295
1398
  {
1296
- className: `agent-activity-bubble__pulse${active ? " is-active" : ""}`,
1399
+ className: `agent-activity-bubble__pulse${active ? " is-active" : failed ? " is-error" : ""}`,
1297
1400
  "aria-hidden": "true"
1298
1401
  }
1299
1402
  ),
1300
- workSummary(steps)
1403
+ workSummary(steps, failed, brandLabel)
1301
1404
  ] }),
1302
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "agent-activity-bubble__details", open: active, children: [
1303
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Work details" }),
1304
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1305
- "li",
1306
- {
1307
- className: "agent-activity-bubble__step",
1308
- "data-state": step.state,
1309
- children: [
1310
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1311
- "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",
1312
1416
  {
1313
- className: "agent-activity-bubble__step-icon",
1314
- "aria-hidden": "true",
1315
- children: step.state === "completed" ? "\u2713" : step.state === "error" ? "!" : ""
1316
- }
1317
- ),
1318
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1319
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: step.label }),
1320
- step.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.detail }) : null
1321
- ] })
1322
- ]
1323
- },
1324
- step.id
1325
- )) })
1326
- ] })
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
+ )
1327
1456
  ] });
1328
1457
  }
1329
1458
 
@@ -1462,6 +1591,29 @@ function MinimizeIcon() {
1462
1591
  }
1463
1592
  ) });
1464
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
+ }
1465
1617
  function ExpandIcon() {
1466
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)(
1467
1619
  "path",
@@ -1498,10 +1650,13 @@ function AgentRail({
1498
1650
  onCollapse,
1499
1651
  onClose,
1500
1652
  onExpandToggle,
1653
+ onReset,
1654
+ onRetry,
1501
1655
  onSubmit,
1502
1656
  onFollowUpSelect
1503
1657
  }) {
1504
1658
  const transcriptRef = (0, import_react5.useRef)(null);
1659
+ const welcomeTitleId = (0, import_react5.useId)();
1505
1660
  const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
1506
1661
  const railStyle = {
1507
1662
  "--rail-width": resolvedTheme.railMaxWidth,
@@ -1528,7 +1683,13 @@ function AgentRail({
1528
1683
  (message) => message.role === "visitor"
1529
1684
  );
1530
1685
  const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
1531
- 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;
1532
1693
  const streamingMessage = state.phase === "streaming" && state.streamingText ? {
1533
1694
  createdAt: 0,
1534
1695
  id: "streaming-response",
@@ -1553,6 +1714,10 @@ function AgentRail({
1553
1714
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
1554
1715
  style: railStyle,
1555
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,
1556
1721
  children: [
1557
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: [
1558
1723
  onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -1571,7 +1736,7 @@ function AgentRail({
1571
1736
  className: "agent-rail__close",
1572
1737
  "aria-label": "Close agent",
1573
1738
  onClick: onClose,
1574
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MinimizeIcon, {})
1739
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {})
1575
1740
  }
1576
1741
  ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1577
1742
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__identity", children: [
@@ -1591,75 +1756,102 @@ function AgentRail({
1591
1756
  ] }),
1592
1757
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
1593
1758
  ] }),
1594
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1595
- "button",
1596
- {
1597
- type: "button",
1598
- className: "agent-rail__expand",
1599
- "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
1600
- onClick: onExpandToggle,
1601
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
1602
- }
1603
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
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
+ ] })
1604
1782
  ] }) }),
1605
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
1606
- state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1607
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1608
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AgentActivityBubble, { steps: state.toolSteps }) : null,
1609
- !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1610
- FollowUpChips,
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",
1611
1786
  {
1612
- suggestions: state.followUps,
1613
- disabled: isBusy,
1614
- label: "Try asking",
1615
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1616
- }
1617
- ) }) : null,
1618
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1619
- "p",
1620
- {
1621
- role: "alert",
1622
- style: { fontSize: 13, color: "var(--as-danger)", margin: 0 },
1623
- children: state.error
1624
- }
1625
- ) : null
1626
- ] }),
1627
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
1628
- showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1629
- FollowUpChips,
1630
- {
1631
- variant: "dock",
1632
- suggestions: state.followUps,
1633
- disabled: isBusy,
1634
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
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
+ ]
1635
1818
  }
1636
- ) }) : null,
1637
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1638
- Composer,
1819
+ ) : null,
1820
+ transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1821
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1822
+ AgentActivityBubble,
1639
1823
  {
1640
- variant: "dock",
1641
- disabled: isBusy,
1642
- placeholder: composerPlaceholder,
1643
- onSubmit
1824
+ brandLabel,
1825
+ brandLogoUrl,
1826
+ failed: state.phase === "error",
1827
+ steps: state.toolSteps
1644
1828
  }
1645
- ) }),
1646
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1647
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1648
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1649
- ] })
1650
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1829
+ ) : null,
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: [
1651
1841
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1652
1842
  Composer,
1653
1843
  {
1844
+ variant: expanded || mobileFullscreen ? "dock" : "default",
1654
1845
  disabled: isBusy,
1655
1846
  placeholder: composerPlaceholder,
1656
1847
  onSubmit
1657
1848
  }
1658
1849
  ),
1659
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1660
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1661
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1662
- ] })
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
+ ] }) })
1663
1855
  ] })
1664
1856
  ]
1665
1857
  }
@@ -1743,7 +1935,12 @@ function AssistEdgeTab({
1743
1935
  label,
1744
1936
  logoUrl,
1745
1937
  brandColor,
1938
+ brandForeground,
1939
+ borderColor,
1746
1940
  fontFamily,
1941
+ mobile = false,
1942
+ surfaceColor,
1943
+ textColor,
1747
1944
  onOpen
1748
1945
  }) {
1749
1946
  const copy = VARIANT_COPY[variant];
@@ -1752,20 +1949,50 @@ function AssistEdgeTab({
1752
1949
  "--tab-along": `${along}%`,
1753
1950
  "--tab-inset": `${inset}px`,
1754
1951
  ...brandColor ? { "--as-brand": brandColor } : {},
1755
- ...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 } : {}
1756
1957
  };
1757
1958
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1758
1959
  "button",
1759
1960
  {
1760
1961
  type: "button",
1761
- 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" : ""}`,
1762
1963
  style,
1763
1964
  "aria-label": `Open ${visibleLabel}`,
1764
1965
  "aria-hidden": !visible,
1765
1966
  tabIndex: visible ? 0 : -1,
1766
1967
  onClick: onOpen,
1767
1968
  children: [
1768
- 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: [
1769
1996
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1770
1997
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
1771
1998
  logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -1817,6 +2044,7 @@ function AgentWidget({
1817
2044
  indexId,
1818
2045
  customerId,
1819
2046
  getUnpublishedPreviewGrant,
2047
+ previewBuildId,
1820
2048
  version,
1821
2049
  runtimeOrigin,
1822
2050
  placement: placementInput,
@@ -1840,10 +2068,11 @@ function AgentWidget({
1840
2068
  active: pageShiftActive,
1841
2069
  railSlotRef
1842
2070
  });
1843
- const { state, submit } = useAgentChat({
2071
+ const { state, reset, retry, submit } = useAgentChat({
1844
2072
  customerId,
1845
2073
  getUnpublishedPreviewGrant,
1846
2074
  indexId,
2075
+ previewBuildId,
1847
2076
  version,
1848
2077
  runtimeOrigin,
1849
2078
  greeting: branding?.greeting
@@ -1882,6 +2111,40 @@ function AgentWidget({
1882
2111
  if (isMobile) setRailCollapsed(false);
1883
2112
  await submit(message);
1884
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]);
1885
2148
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "webless-agent-root", children: [
1886
2149
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1887
2150
  "div",
@@ -1913,19 +2176,27 @@ function AgentWidget({
1913
2176
  onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1914
2177
  onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1915
2178
  onSubmit: handleSubmit,
2179
+ onReset: reset,
2180
+ onRetry: () => void retry(),
1916
2181
  onFollowUpSelect: (label) => void handleSubmit(label)
1917
2182
  }
1918
2183
  )
1919
2184
  }
1920
2185
  ),
1921
- !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2186
+ !railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1922
2187
  "button",
1923
2188
  {
1924
2189
  type: "button",
1925
- className: "webless-agent-root__backdrop",
2190
+ className: `webless-agent-root__backdrop${isMobile ? " webless-agent-root__backdrop--mobile" : ""}`,
1926
2191
  tabIndex: -1,
1927
- "aria-label": "Close expanded assist",
1928
- 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
+ }
1929
2200
  }
1930
2201
  ) : null
1931
2202
  ]
@@ -1942,7 +2213,12 @@ function AgentWidget({
1942
2213
  label: agentName,
1943
2214
  logoUrl: branding?.logoUrl,
1944
2215
  brandColor: branding?.colors?.primary,
2216
+ brandForeground: branding?.colors?.primaryForeground,
2217
+ borderColor: branding?.colors?.border,
1945
2218
  fontFamily: branding?.fontFamily,
2219
+ mobile: isMobile,
2220
+ surfaceColor: branding?.colors?.surface,
2221
+ textColor: branding?.colors?.text,
1946
2222
  onOpen: () => setRailCollapsed(false)
1947
2223
  }
1948
2224
  ) : null