@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/embed.cjs CHANGED
@@ -178,11 +178,12 @@ function createAgentRuntimeCapability(options) {
178
178
  let pendingBootstrap;
179
179
  const bootstrap = async () => {
180
180
  let previewGrant;
181
+ const previewBuildId = options.previewBuildId?.trim();
181
182
  if (options.version === "unpublished") {
182
- previewGrant = (await options.getUnpublishedPreviewGrant?.())?.trim();
183
- if (!previewGrant) {
183
+ previewGrant = previewBuildId ? void 0 : (await options.getUnpublishedPreviewGrant?.())?.trim();
184
+ if (!previewGrant && !previewBuildId) {
184
185
  throw new Error(
185
- "An unpublished preview grant is required to use the Agent Runtime preview."
186
+ "Unpublished preview authorization is required to use the Agent Runtime preview."
186
187
  );
187
188
  }
188
189
  }
@@ -192,6 +193,7 @@ function createAgentRuntimeCapability(options) {
192
193
  body: JSON.stringify({
193
194
  clientSessionId: options.visitorSessionId,
194
195
  indexId: options.indexId,
196
+ ...previewBuildId ? { previewBuildId } : {},
195
197
  ...previewGrant ? { previewGrant } : {},
196
198
  version: options.version
197
199
  }),
@@ -400,13 +402,15 @@ function emitWorkItem(item, handlers, workItems) {
400
402
  handlers.onWork?.(item);
401
403
  handlers.onStep?.(item.label, item.detail);
402
404
  }
403
- function completePlanning(handlers, workItems) {
405
+ function completePlanning(handlers, workItems, nextItems = []) {
404
406
  const planning = workItems.get("planning");
405
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";
406
410
  emitWorkItem(
407
411
  {
408
412
  ...planning,
409
- detail: "Picked the best way to help",
413
+ detail,
410
414
  state: "completed"
411
415
  },
412
416
  handlers,
@@ -456,11 +460,12 @@ function applyWorkEvent(event, handlers, workItems) {
456
460
  return;
457
461
  }
458
462
  if (event.type === "actions.requested") {
459
- completePlanning(handlers, workItems);
460
- for (const action of event.data.actions) {
463
+ const nextItems = event.data.actions.flatMap((action) => {
461
464
  const item = requestedWorkItem(action);
462
- if (item) emitWorkItem(item, handlers, workItems);
463
- }
465
+ return item ? [item] : [];
466
+ });
467
+ completePlanning(handlers, workItems, nextItems);
468
+ for (const item of nextItems) emitWorkItem(item, handlers, workItems);
464
469
  return;
465
470
  }
466
471
  if (event.type === "subagent.called") {
@@ -511,7 +516,7 @@ function applyWorkEvent(event, handlers, workItems) {
511
516
  );
512
517
  }
513
518
  var AgentSession = class {
514
- constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, getUnpublishedPreviewGrant) {
519
+ constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions, previewBuildId, getUnpublishedPreviewGrant) {
515
520
  this.indexId = indexId;
516
521
  this.version = version;
517
522
  this.runtimeOrigin = runtimeOrigin;
@@ -520,6 +525,7 @@ var AgentSession = class {
520
525
  this.capability = createAgentRuntimeCapability({
521
526
  getUnpublishedPreviewGrant,
522
527
  indexId,
528
+ previewBuildId,
523
529
  runtimeOrigin,
524
530
  version,
525
531
  visitorSessionId
@@ -764,6 +770,7 @@ function createAgentClient(options) {
764
770
  runtimeOrigin,
765
771
  visitorSessionId,
766
772
  storeOptions,
773
+ options.previewBuildId,
767
774
  options.getUnpublishedPreviewGrant
768
775
  );
769
776
  return {
@@ -791,10 +798,15 @@ function createAgentClient(options) {
791
798
  // src/runtime/errors.ts
792
799
  var import_client3 = require("eve/client");
793
800
  var TRANSIENT_AGENT_ERROR_MESSAGE = "I couldn\u2019t finish that answer. Please try again.";
801
+ var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
794
802
  function isTransientRuntimeMessage(message) {
795
803
  const normalized = message.trim().toLowerCase();
796
804
  return normalized.includes("empty response from runtime") || normalized.includes("failed to fetch") || normalized.includes("networkerror") || normalized.includes("load failed");
797
805
  }
806
+ function isPreviewAuthorizationMessage(message) {
807
+ const normalized = message.trim().toLowerCase();
808
+ return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
809
+ }
798
810
  function formatAgentError(error) {
799
811
  if (error instanceof import_client3.ClientError) {
800
812
  if (error.status === 401 && error.code === "index_required") {
@@ -806,6 +818,9 @@ function formatAgentError(error) {
806
818
  if (error.status === 409 && error.code === "session_not_active") {
807
819
  return "Session expired \u2014 send a new message to start again.";
808
820
  }
821
+ if (error.message && isPreviewAuthorizationMessage(error.message)) {
822
+ return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
823
+ }
809
824
  if (error.status >= 500 || error.message && isTransientRuntimeMessage(error.message)) {
810
825
  return TRANSIENT_AGENT_ERROR_MESSAGE;
811
826
  }
@@ -815,6 +830,9 @@ function formatAgentError(error) {
815
830
  return "";
816
831
  }
817
832
  if (error instanceof Error) {
833
+ if (isPreviewAuthorizationMessage(error.message)) {
834
+ return PREVIEW_AUTHORIZATION_ERROR_MESSAGE;
835
+ }
818
836
  return isTransientRuntimeMessage(error.message) ? TRANSIENT_AGENT_ERROR_MESSAGE : error.message;
819
837
  }
820
838
  return TRANSIENT_AGENT_ERROR_MESSAGE;
@@ -926,6 +944,7 @@ function useAgentChat({
926
944
  customerId,
927
945
  getUnpublishedPreviewGrant,
928
946
  indexId,
947
+ previewBuildId,
929
948
  version,
930
949
  runtimeOrigin,
931
950
  visitorSessionId,
@@ -976,13 +995,14 @@ function useAgentChat({
976
995
  customerId,
977
996
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
978
997
  indexId,
998
+ previewBuildId,
979
999
  version,
980
1000
  runtimeOrigin,
981
1001
  visitorSessionId: visitorId,
982
1002
  storageKeyPrefix: resolvedStorageKeyPrefix
983
1003
  })
984
1004
  );
985
- const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
1005
+ const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${previewBuildId ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}|${greeting ?? ""}`;
986
1006
  const identityRef = (0, import_react2.useRef)(identityKey);
987
1007
  (0, import_react2.useEffect)(() => {
988
1008
  if (identityRef.current === identityKey) {
@@ -995,6 +1015,7 @@ function useAgentChat({
995
1015
  customerId,
996
1016
  getUnpublishedPreviewGrant: resolveUnpublishedPreviewGrant,
997
1017
  indexId,
1018
+ previewBuildId,
998
1019
  version,
999
1020
  runtimeOrigin,
1000
1021
  visitorSessionId: visitorId,
@@ -1011,6 +1032,7 @@ function useAgentChat({
1011
1032
  identityKey,
1012
1033
  indexId,
1013
1034
  initialState,
1035
+ previewBuildId,
1014
1036
  runtimeOrigin,
1015
1037
  resolvedStorageKeyPrefix,
1016
1038
  version,
@@ -1114,8 +1136,8 @@ function useAgentChat({
1114
1136
  if (!message) return;
1115
1137
  setState((prev) => ({
1116
1138
  ...prev,
1117
- phase: "complete",
1118
- toolSteps: prev.toolSteps.map(
1139
+ phase: "error",
1140
+ toolSteps: prev.toolSteps.length === 1 && prev.toolSteps[0]?.kind === "planning" ? [] : prev.toolSteps.map(
1119
1141
  (step) => step.state === "active" ? {
1120
1142
  ...step,
1121
1143
  detail: "Couldn\u2019t complete this step",
@@ -1165,6 +1187,37 @@ function useAgentChat({
1165
1187
  },
1166
1188
  [runTurn]
1167
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]);
1168
1221
  (0, import_react2.useEffect)(() => {
1169
1222
  const conversation = loadPersistedAgentConversation(
1170
1223
  resolvedStorageKeyPrefix,
@@ -1197,6 +1250,7 @@ function useAgentChat({
1197
1250
  return {
1198
1251
  state,
1199
1252
  reset,
1253
+ retry,
1200
1254
  submit,
1201
1255
  visitorSessionId: visitorId,
1202
1256
  sessionId: clientRef.current.getActiveSessionId()
@@ -1274,11 +1328,13 @@ var defaultAgentRailTheme = {
1274
1328
 
1275
1329
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1276
1330
  var import_jsx_runtime = require("react/jsx-runtime");
1277
- function workSummary(steps) {
1331
+ function workSummary(steps, failed, brandLabel) {
1278
1332
  const active = [...steps].reverse().find((step) => step.state === "active");
1279
- if (active?.kind === "specialist") return `Working with ${active.label}`;
1333
+ if (active?.kind === "specialist")
1334
+ return `${active.label} is reviewing your question`;
1280
1335
  if (active?.kind === "search") return "Searching this site";
1281
- if (active) return active.label;
1336
+ if (active) return `${brandLabel} is choosing the best way to help`;
1337
+ if (failed) return "Couldn\u2019t complete this request";
1282
1338
  const hasError = steps.some((step) => step.state === "error");
1283
1339
  const specialists = steps.filter(
1284
1340
  (step) => step.kind === "specialist" && step.state === "completed"
@@ -1288,49 +1344,122 @@ function workSummary(steps) {
1288
1344
  );
1289
1345
  if (hasError) return "Answered with available information";
1290
1346
  if (specialists.length > 1)
1291
- return `Consulted ${specialists.length} specialists`;
1292
- if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1293
- if (searched) return "Searched this site";
1294
- 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
+ ) });
1295
1392
  }
1296
- function AgentActivityBubble({ steps }) {
1393
+ function AgentActivityBubble({
1394
+ brandLabel = "Webless Guide",
1395
+ brandLogoUrl,
1396
+ failed = false,
1397
+ steps
1398
+ }) {
1297
1399
  const active = steps.some((step) => step.state === "active");
1400
+ const delegated = steps.some((step) => step.kind === "specialist");
1298
1401
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1299
1402
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1300
1403
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1301
1404
  "span",
1302
1405
  {
1303
- className: `agent-activity-bubble__pulse${active ? " is-active" : ""}`,
1406
+ className: `agent-activity-bubble__pulse${active ? " is-active" : failed ? " is-error" : ""}`,
1304
1407
  "aria-hidden": "true"
1305
1408
  }
1306
1409
  ),
1307
- workSummary(steps)
1410
+ workSummary(steps, failed, brandLabel)
1308
1411
  ] }),
1309
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "agent-activity-bubble__details", open: active, children: [
1310
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Work details" }),
1311
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1312
- "li",
1313
- {
1314
- className: "agent-activity-bubble__step",
1315
- "data-state": step.state,
1316
- children: [
1317
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1318
- "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",
1319
1423
  {
1320
- className: "agent-activity-bubble__step-icon",
1321
- "aria-hidden": "true",
1322
- children: step.state === "completed" ? "\u2713" : step.state === "error" ? "!" : ""
1323
- }
1324
- ),
1325
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1326
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: step.label }),
1327
- step.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.detail }) : null
1328
- ] })
1329
- ]
1330
- },
1331
- step.id
1332
- )) })
1333
- ] })
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
+ )
1334
1463
  ] });
1335
1464
  }
1336
1465
 
@@ -1469,6 +1598,29 @@ function MinimizeIcon() {
1469
1598
  }
1470
1599
  ) });
1471
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
+ }
1472
1624
  function ExpandIcon() {
1473
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)(
1474
1626
  "path",
@@ -1505,10 +1657,13 @@ function AgentRail({
1505
1657
  onCollapse,
1506
1658
  onClose,
1507
1659
  onExpandToggle,
1660
+ onReset,
1661
+ onRetry,
1508
1662
  onSubmit,
1509
1663
  onFollowUpSelect
1510
1664
  }) {
1511
1665
  const transcriptRef = (0, import_react5.useRef)(null);
1666
+ const welcomeTitleId = (0, import_react5.useId)();
1512
1667
  const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
1513
1668
  const railStyle = {
1514
1669
  "--rail-width": resolvedTheme.railMaxWidth,
@@ -1535,7 +1690,13 @@ function AgentRail({
1535
1690
  (message) => message.role === "visitor"
1536
1691
  );
1537
1692
  const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
1538
- 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;
1539
1700
  const streamingMessage = state.phase === "streaming" && state.streamingText ? {
1540
1701
  createdAt: 0,
1541
1702
  id: "streaming-response",
@@ -1560,6 +1721,10 @@ function AgentRail({
1560
1721
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
1561
1722
  style: railStyle,
1562
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,
1563
1728
  children: [
1564
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: [
1565
1730
  onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -1578,7 +1743,7 @@ function AgentRail({
1578
1743
  className: "agent-rail__close",
1579
1744
  "aria-label": "Close agent",
1580
1745
  onClick: onClose,
1581
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MinimizeIcon, {})
1746
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {})
1582
1747
  }
1583
1748
  ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1584
1749
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__identity", children: [
@@ -1598,75 +1763,102 @@ function AgentRail({
1598
1763
  ] }),
1599
1764
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
1600
1765
  ] }),
1601
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1602
- "button",
1603
- {
1604
- type: "button",
1605
- className: "agent-rail__expand",
1606
- "aria-label": expanded ? "Restore assist panel" : "Expand assist panel",
1607
- onClick: onExpandToggle,
1608
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
1609
- }
1610
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" })
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
+ ] })
1611
1789
  ] }) }),
1612
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
1613
- state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1614
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1615
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AgentActivityBubble, { steps: state.toolSteps }) : null,
1616
- !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1617
- FollowUpChips,
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",
1618
1793
  {
1619
- suggestions: state.followUps,
1620
- disabled: isBusy,
1621
- label: "Try asking",
1622
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1623
- }
1624
- ) }) : null,
1625
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1626
- "p",
1627
- {
1628
- role: "alert",
1629
- style: { fontSize: 13, color: "var(--as-danger)", margin: 0 },
1630
- children: state.error
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
+ ]
1631
1825
  }
1632
- ) : null
1633
- ] }),
1634
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
1635
- showDockFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock-followups", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1636
- FollowUpChips,
1826
+ ) : null,
1827
+ transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1828
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1829
+ AgentActivityBubble,
1637
1830
  {
1638
- variant: "dock",
1639
- suggestions: state.followUps,
1640
- disabled: isBusy,
1641
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1831
+ brandLabel,
1832
+ brandLogoUrl,
1833
+ failed: state.phase === "error",
1834
+ steps: state.toolSteps
1642
1835
  }
1643
- ) }) : null,
1644
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__dock", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1645
- Composer,
1646
- {
1647
- variant: "dock",
1648
- disabled: isBusy,
1649
- placeholder: composerPlaceholder,
1650
- onSubmit
1651
- }
1652
- ) }),
1653
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1654
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1655
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1656
- ] })
1657
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1836
+ ) : null,
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: [
1658
1848
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1659
1849
  Composer,
1660
1850
  {
1851
+ variant: expanded || mobileFullscreen ? "dock" : "default",
1661
1852
  disabled: isBusy,
1662
1853
  placeholder: composerPlaceholder,
1663
1854
  onSubmit
1664
1855
  }
1665
1856
  ),
1666
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__footer", children: [
1667
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-disclaimer", children: "AI can make mistakes. Check important info." }),
1668
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "agent-rail__footer-note", children: poweredByLabel })
1669
- ] })
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
+ ] }) })
1670
1862
  ] })
1671
1863
  ]
1672
1864
  }
@@ -1750,7 +1942,12 @@ function AssistEdgeTab({
1750
1942
  label,
1751
1943
  logoUrl,
1752
1944
  brandColor,
1945
+ brandForeground,
1946
+ borderColor,
1753
1947
  fontFamily,
1948
+ mobile = false,
1949
+ surfaceColor,
1950
+ textColor,
1754
1951
  onOpen
1755
1952
  }) {
1756
1953
  const copy = VARIANT_COPY[variant];
@@ -1759,20 +1956,50 @@ function AssistEdgeTab({
1759
1956
  "--tab-along": `${along}%`,
1760
1957
  "--tab-inset": `${inset}px`,
1761
1958
  ...brandColor ? { "--as-brand": brandColor } : {},
1762
- ...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 } : {}
1763
1964
  };
1764
1965
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1765
1966
  "button",
1766
1967
  {
1767
1968
  type: "button",
1768
- 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" : ""}`,
1769
1970
  style,
1770
1971
  "aria-label": `Open ${visibleLabel}`,
1771
1972
  "aria-hidden": !visible,
1772
1973
  tabIndex: visible ? 0 : -1,
1773
1974
  onClick: onOpen,
1774
1975
  children: [
1775
- 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: [
1776
2003
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1777
2004
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
1778
2005
  logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -1824,6 +2051,7 @@ function AgentWidget({
1824
2051
  indexId,
1825
2052
  customerId,
1826
2053
  getUnpublishedPreviewGrant,
2054
+ previewBuildId,
1827
2055
  version,
1828
2056
  runtimeOrigin,
1829
2057
  placement: placementInput,
@@ -1847,10 +2075,11 @@ function AgentWidget({
1847
2075
  active: pageShiftActive,
1848
2076
  railSlotRef
1849
2077
  });
1850
- const { state, submit } = useAgentChat({
2078
+ const { state, reset, retry, submit } = useAgentChat({
1851
2079
  customerId,
1852
2080
  getUnpublishedPreviewGrant,
1853
2081
  indexId,
2082
+ previewBuildId,
1854
2083
  version,
1855
2084
  runtimeOrigin,
1856
2085
  greeting: branding?.greeting
@@ -1889,6 +2118,40 @@ function AgentWidget({
1889
2118
  if (isMobile) setRailCollapsed(false);
1890
2119
  await submit(message);
1891
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]);
1892
2155
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "webless-agent-root", children: [
1893
2156
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1894
2157
  "div",
@@ -1920,19 +2183,27 @@ function AgentWidget({
1920
2183
  onClose: isMobile ? () => setRailCollapsed(true) : void 0,
1921
2184
  onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
1922
2185
  onSubmit: handleSubmit,
2186
+ onReset: reset,
2187
+ onRetry: () => void retry(),
1923
2188
  onFollowUpSelect: (label) => void handleSubmit(label)
1924
2189
  }
1925
2190
  )
1926
2191
  }
1927
2192
  ),
1928
- !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2193
+ !railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1929
2194
  "button",
1930
2195
  {
1931
2196
  type: "button",
1932
- className: "webless-agent-root__backdrop",
2197
+ className: `webless-agent-root__backdrop${isMobile ? " webless-agent-root__backdrop--mobile" : ""}`,
1933
2198
  tabIndex: -1,
1934
- "aria-label": "Close expanded assist",
1935
- 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
+ }
1936
2207
  }
1937
2208
  ) : null
1938
2209
  ]
@@ -1949,13 +2220,31 @@ function AgentWidget({
1949
2220
  label: agentName,
1950
2221
  logoUrl: branding?.logoUrl,
1951
2222
  brandColor: branding?.colors?.primary,
2223
+ brandForeground: branding?.colors?.primaryForeground,
2224
+ borderColor: branding?.colors?.border,
1952
2225
  fontFamily: branding?.fontFamily,
2226
+ mobile: isMobile,
2227
+ surfaceColor: branding?.colors?.surface,
2228
+ textColor: branding?.colors?.text,
1953
2229
  onOpen: () => setRailCollapsed(false)
1954
2230
  }
1955
2231
  ) : null
1956
2232
  ] });
1957
2233
  }
1958
2234
 
2235
+ // src/embed/preview-build.ts
2236
+ var PREVIEW_BUILD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
2237
+ function readUnpublishedPreviewBuildId(href) {
2238
+ const source = href ?? (typeof window === "undefined" ? void 0 : window.location.href);
2239
+ if (!source) return void 0;
2240
+ try {
2241
+ const buildId = new URL(source).searchParams.get("v")?.trim();
2242
+ return buildId && PREVIEW_BUILD_ID_PATTERN.test(buildId) ? buildId : void 0;
2243
+ } catch {
2244
+ return void 0;
2245
+ }
2246
+ }
2247
+
1959
2248
  // src/embed/AgentWidget.tsx
1960
2249
  var import_jsx_runtime8 = require("react/jsx-runtime");
1961
2250
  function AgentWidget2({
@@ -1968,6 +2257,7 @@ function AgentWidget2({
1968
2257
  customerId: manifest.customerId,
1969
2258
  version: manifest.version,
1970
2259
  runtimeOrigin: manifest.runtimeOrigin,
2260
+ previewBuildId: manifest.version === "unpublished" ? readUnpublishedPreviewBuildId() : void 0,
1971
2261
  placement: manifest.placement,
1972
2262
  pageShift: manifest.pageShift,
1973
2263
  branding: manifest.branding,