@webless/agent 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/embed.cjs CHANGED
@@ -50,7 +50,7 @@ function closeAgentPanel(customerId) {
50
50
  }
51
51
 
52
52
  // src/react/components/AgentWidget/AgentWidget.tsx
53
- var import_react7 = require("react");
53
+ var import_react10 = require("react");
54
54
 
55
55
  // src/react/page-shift.ts
56
56
  var import_react = require("react");
@@ -1116,7 +1116,7 @@ function visitorBookingPrefix(booking) {
1116
1116
  }
1117
1117
 
1118
1118
  // src/react/persisted-conversation.ts
1119
- var CONVERSATION_VERSION = 1;
1119
+ var CONVERSATION_VERSION = 2;
1120
1120
  function conversationKey(storageKeyPrefix, visitorSessionId) {
1121
1121
  return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
1122
1122
  }
@@ -1142,6 +1142,20 @@ function parseMessage(value) {
1142
1142
  createdAt: record.createdAt
1143
1143
  };
1144
1144
  }
1145
+ function parseToolStep(value) {
1146
+ if (typeof value !== "object" || value === null) return null;
1147
+ const record = value;
1148
+ if (typeof record.id !== "string" || record.kind !== "planning" && record.kind !== "search" && record.kind !== "specialist" || typeof record.label !== "string" || record.state !== "completed" && record.state !== "active" && record.state !== "pending" && record.state !== "error") {
1149
+ return null;
1150
+ }
1151
+ return {
1152
+ id: record.id,
1153
+ kind: record.kind,
1154
+ label: record.label,
1155
+ state: record.state,
1156
+ ...typeof record.detail === "string" && record.detail ? { detail: record.detail } : {}
1157
+ };
1158
+ }
1145
1159
  function visitorTurnText(message) {
1146
1160
  return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
1147
1161
  }
@@ -1153,15 +1167,19 @@ function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
1153
1167
  const value = JSON.parse(raw);
1154
1168
  if (typeof value !== "object" || value === null) return null;
1155
1169
  const record = value;
1156
- if (record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string") {
1170
+ if (record.version !== 1 && record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string" || record.version === CONVERSATION_VERSION && !Array.isArray(record.toolSteps)) {
1157
1171
  return null;
1158
1172
  }
1159
1173
  const messages = record.messages.map(parseMessage);
1160
1174
  if (messages.some((message) => message === null)) return null;
1175
+ const storedToolSteps = record.version === CONVERSATION_VERSION && Array.isArray(record.toolSteps) ? record.toolSteps : [];
1176
+ const toolSteps = storedToolSteps.map(parseToolStep);
1177
+ if (toolSteps.some((step) => step === null)) return null;
1161
1178
  return {
1162
1179
  messages: messages.filter((message) => message !== null),
1163
1180
  pending: record.pending,
1164
- streamingText: record.streamingText
1181
+ streamingText: record.streamingText,
1182
+ toolSteps: toolSteps.filter((step) => step !== null)
1165
1183
  };
1166
1184
  } catch {
1167
1185
  return null;
@@ -1242,7 +1260,8 @@ function stateFromConversation(conversation, initialState) {
1242
1260
  ...initialState,
1243
1261
  messages: conversation.messages,
1244
1262
  phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
1245
- streamingText: conversation.streamingText
1263
+ streamingText: conversation.streamingText,
1264
+ toolSteps: conversation.toolSteps
1246
1265
  };
1247
1266
  }
1248
1267
  function upsertToolStep(steps, item) {
@@ -1376,13 +1395,15 @@ function useAgentChat({
1376
1395
  savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
1377
1396
  messages: state.messages,
1378
1397
  pending: isAgentBusy(state.phase),
1379
- streamingText: state.streamingText
1398
+ streamingText: state.streamingText,
1399
+ toolSteps: state.toolSteps
1380
1400
  });
1381
1401
  }, [
1382
1402
  resolvedStorageKeyPrefix,
1383
1403
  state.messages,
1384
1404
  state.phase,
1385
1405
  state.streamingText,
1406
+ state.toolSteps,
1386
1407
  visitorId
1387
1408
  ]);
1388
1409
  const reset = (0, import_react2.useCallback)(() => {
@@ -1687,7 +1708,7 @@ function normalizeAgentPlacement(placement) {
1687
1708
  }
1688
1709
 
1689
1710
  // src/react/components/AgentRail/AgentRail.tsx
1690
- var import_react6 = require("react");
1711
+ var import_react9 = require("react");
1691
1712
 
1692
1713
  // src/react/types/conversation.ts
1693
1714
  var defaultAgentRailTheme = {
@@ -1708,15 +1729,62 @@ var defaultAgentRailTheme = {
1708
1729
  fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
1709
1730
  fontDisplay: '"Space Grotesk", sans-serif'
1710
1731
  };
1732
+ var defaultDarkAgentRailTheme = {
1733
+ ...defaultAgentRailTheme,
1734
+ brand: "#a77bff",
1735
+ brandSoft: "#2b2140",
1736
+ brandDeep: "#f5f0ff",
1737
+ surface: "#101218",
1738
+ surfaceMuted: "#1a1e27",
1739
+ text: "#f5f7fb",
1740
+ textMuted: "#b6bfce",
1741
+ textSubtle: "#919cad",
1742
+ border: "rgb(226 232 240 / 0.16)",
1743
+ visitorBubble: "#7c3aed",
1744
+ success: "#55cf91",
1745
+ danger: "#ff8da1"
1746
+ };
1747
+
1748
+ // src/react/hooks/useAgentColorScheme.ts
1749
+ var import_react4 = require("react");
1750
+ var DARK_MODE_QUERY = "(prefers-color-scheme: dark)";
1751
+ function subscribeToDarkMode(onChange) {
1752
+ if (typeof window === "undefined" || !window.matchMedia) {
1753
+ return () => void 0;
1754
+ }
1755
+ const mediaQuery = window.matchMedia(DARK_MODE_QUERY);
1756
+ if (typeof mediaQuery.addEventListener === "function") {
1757
+ mediaQuery.addEventListener("change", onChange);
1758
+ return () => mediaQuery.removeEventListener("change", onChange);
1759
+ }
1760
+ mediaQuery.addListener(onChange);
1761
+ return () => mediaQuery.removeListener(onChange);
1762
+ }
1763
+ function getPrefersDarkMode() {
1764
+ return typeof window !== "undefined" && Boolean(window.matchMedia?.(DARK_MODE_QUERY).matches);
1765
+ }
1766
+ function useAgentColorScheme(colorScheme = "auto") {
1767
+ const prefersDarkMode = (0, import_react4.useSyncExternalStore)(
1768
+ subscribeToDarkMode,
1769
+ getPrefersDarkMode,
1770
+ () => false
1771
+ );
1772
+ return resolveAgentColorScheme(colorScheme, prefersDarkMode);
1773
+ }
1774
+ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
1775
+ return colorScheme === "auto" ? prefersDarkMode ? "dark" : "light" : colorScheme;
1776
+ }
1711
1777
 
1712
1778
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1779
+ var import_react5 = require("react");
1713
1780
  var import_jsx_runtime = require("react/jsx-runtime");
1714
1781
  function workSummary(steps, failed, brandLabel) {
1715
1782
  const active = [...steps].reverse().find((step) => step.state === "active");
1716
1783
  if (active?.kind === "specialist")
1717
1784
  return `${active.label} is reviewing your question`;
1718
1785
  if (active?.kind === "search") return "Searching this site";
1719
- if (active) return `${brandLabel} is choosing the best way to help`;
1786
+ if (active)
1787
+ return brandLabel ? `${brandLabel} is choosing the best way to help` : "Choosing the best way to help";
1720
1788
  if (failed) return "Couldn\u2019t complete this request";
1721
1789
  const hasError = steps.some((step) => step.state === "error");
1722
1790
  const specialists = steps.filter(
@@ -1727,8 +1795,9 @@ function workSummary(steps, failed, brandLabel) {
1727
1795
  );
1728
1796
  if (hasError) return "Answered with available information";
1729
1797
  if (specialists.length > 1)
1730
- return `Consulted ${specialists.length} specialists`;
1731
- if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1798
+ return `Brought in ${specialists.length} specialists`;
1799
+ if (specialists.length === 1)
1800
+ return `Brought in ${specialists[0]?.label}`;
1732
1801
  if (searched) return "Searched this site";
1733
1802
  return "Answer ready";
1734
1803
  }
@@ -1740,11 +1809,12 @@ function stepDetail(step, steps) {
1740
1809
  return step.detail;
1741
1810
  }
1742
1811
  const specialists = steps.filter((item) => item.kind === "specialist");
1743
- if (specialists.length === 1) return `Delegated to ${specialists[0]?.label}`;
1812
+ if (specialists.length === 1)
1813
+ return `Brought in ${specialists[0]?.label}`;
1744
1814
  if (specialists.length > 1)
1745
- return `Delegated to ${specialists.length} specialists`;
1815
+ return `Brought in ${specialists.length} specialists`;
1746
1816
  if (steps.some((item) => item.kind === "search"))
1747
- return "Used built-in Search & Discovery";
1817
+ return "Searched this site";
1748
1818
  return step.detail;
1749
1819
  }
1750
1820
  function SearchIcon() {
@@ -1773,12 +1843,20 @@ function PlanningIcon() {
1773
1843
  ) });
1774
1844
  }
1775
1845
  function AgentActivityBubble({
1776
- brandLabel = "Webless Guide",
1846
+ brandLabel = "",
1777
1847
  brandLogoUrl,
1778
1848
  failed = false,
1779
1849
  steps
1780
1850
  }) {
1781
1851
  const active = steps.some((step) => step.state === "active");
1852
+ const receiptId = steps.map((step) => step.id).join(":");
1853
+ const [expandedReceiptId, setExpandedReceiptId] = (0, import_react5.useState)(
1854
+ null
1855
+ );
1856
+ const detailsOpen = active || expandedReceiptId === receiptId;
1857
+ const visibleSteps = steps.filter(
1858
+ (step) => step.kind !== "planning" || Boolean(brandLabel)
1859
+ );
1782
1860
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1783
1861
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1784
1862
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1790,9 +1868,23 @@ function AgentActivityBubble({
1790
1868
  ),
1791
1869
  workSummary(steps, failed, brandLabel)
1792
1870
  ] }),
1793
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "agent-activity-bubble__details", open: active, children: [
1794
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: "Work details" }),
1795
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
1871
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-activity-bubble__details", children: [
1872
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1873
+ "button",
1874
+ {
1875
+ type: "button",
1876
+ className: "agent-activity-bubble__summary",
1877
+ "aria-expanded": detailsOpen,
1878
+ onClick: () => {
1879
+ if (active) return;
1880
+ setExpandedReceiptId(
1881
+ (current) => current === receiptId ? null : receiptId
1882
+ );
1883
+ },
1884
+ children: "How this answer was made"
1885
+ }
1886
+ ),
1887
+ detailsOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: visibleSteps.map((step) => {
1796
1888
  const detail = stepDetail(step, steps);
1797
1889
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1798
1890
  "li",
@@ -1822,23 +1914,20 @@ function AgentActivityBubble({
1822
1914
  }
1823
1915
  ),
1824
1916
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1825
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-heading", children: [
1826
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }),
1827
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
1828
- ] }),
1917
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-heading", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }) }),
1829
1918
  detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1830
1919
  ] })
1831
1920
  ]
1832
1921
  },
1833
1922
  step.id
1834
1923
  );
1835
- }) })
1924
+ }) }) : null
1836
1925
  ] })
1837
1926
  ] });
1838
1927
  }
1839
1928
 
1840
1929
  // src/react/components/Composer/Composer.tsx
1841
- var import_react4 = require("react");
1930
+ var import_react6 = require("react");
1842
1931
  var import_jsx_runtime2 = require("react/jsx-runtime");
1843
1932
  function SendIcon() {
1844
1933
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 12V4M8 4l-3 3M8 4l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
@@ -1849,8 +1938,8 @@ function Composer({
1849
1938
  variant = "default",
1850
1939
  onSubmit
1851
1940
  }) {
1852
- const [value, setValue] = (0, import_react4.useState)("");
1853
- const inputRef = (0, import_react4.useRef)(null);
1941
+ const [value, setValue] = (0, import_react6.useState)("");
1942
+ const inputRef = (0, import_react6.useRef)(null);
1854
1943
  function submitCurrent() {
1855
1944
  const trimmed = value.trim();
1856
1945
  if (!trimmed || disabled) return;
@@ -1935,8 +2024,11 @@ function FollowUpChips({
1935
2024
  ] });
1936
2025
  }
1937
2026
 
2027
+ // src/react/components/MessageBubble/MessageBubble.tsx
2028
+ var import_react8 = require("react");
2029
+
1938
2030
  // src/react/components/BookingCard/BookingCard.tsx
1939
- var import_react5 = require("react");
2031
+ var import_react7 = require("react");
1940
2032
  var import_jsx_runtime4 = require("react/jsx-runtime");
1941
2033
  function monthFromKey(key) {
1942
2034
  const [year, month] = key.split("-").map(Number);
@@ -1965,19 +2057,19 @@ function BookingCard({
1965
2057
  offer,
1966
2058
  onBook
1967
2059
  }) {
1968
- const fieldId = (0, import_react5.useId)();
2060
+ const fieldId = (0, import_react7.useId)();
1969
2061
  const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
1970
- const [step, setStep] = (0, import_react5.useState)("date");
1971
- const [eventTypeUri, setEventTypeUri] = (0, import_react5.useState)(defaultType);
1972
- const [selectedDate, setSelectedDate] = (0, import_react5.useState)("");
1973
- const [startTime, setStartTime] = (0, import_react5.useState)("");
1974
- const [name, setName] = (0, import_react5.useState)("");
1975
- const [email, setEmail] = (0, import_react5.useState)("");
1976
- const slots = (0, import_react5.useMemo)(
2062
+ const [step, setStep] = (0, import_react7.useState)("date");
2063
+ const [eventTypeUri, setEventTypeUri] = (0, import_react7.useState)(defaultType);
2064
+ const [selectedDate, setSelectedDate] = (0, import_react7.useState)("");
2065
+ const [startTime, setStartTime] = (0, import_react7.useState)("");
2066
+ const [name, setName] = (0, import_react7.useState)("");
2067
+ const [email, setEmail] = (0, import_react7.useState)("");
2068
+ const slots = (0, import_react7.useMemo)(
1977
2069
  () => bookingSlotsForEventType(offer.slots, eventTypeUri),
1978
2070
  [eventTypeUri, offer.slots]
1979
2071
  );
1980
- const availableByDate = (0, import_react5.useMemo)(() => {
2072
+ const availableByDate = (0, import_react7.useMemo)(() => {
1981
2073
  const next = /* @__PURE__ */ new Map();
1982
2074
  for (const slot of slots) {
1983
2075
  const key = slotDateKey(slot.startTime);
@@ -1985,7 +2077,7 @@ function BookingCard({
1985
2077
  }
1986
2078
  return next;
1987
2079
  }, [slots]);
1988
- const [visibleMonth, setVisibleMonth] = (0, import_react5.useState)(
2080
+ const [visibleMonth, setVisibleMonth] = (0, import_react7.useState)(
1989
2081
  () => firstAvailableBookingMonth(slots)
1990
2082
  );
1991
2083
  function selectEventType(nextType) {
@@ -1996,14 +2088,14 @@ function BookingCard({
1996
2088
  firstAvailableBookingMonth(bookingSlotsForEventType(offer.slots, nextType))
1997
2089
  );
1998
2090
  }
1999
- const daySlots = (0, import_react5.useMemo)(
2091
+ const daySlots = (0, import_react7.useMemo)(
2000
2092
  () => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
2001
2093
  [selectedDate, slots]
2002
2094
  );
2003
2095
  const selectedType = offer.eventTypes.find((item) => item.uri === eventTypeUri);
2004
2096
  const selectedSample = availableByDate.get(selectedDate) ?? startTime;
2005
2097
  const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
2006
- const weekdays = (0, import_react5.useMemo)(() => weekdayLabels(), []);
2098
+ const weekdays = (0, import_react7.useMemo)(() => weekdayLabels(), []);
2007
2099
  const cells = calendarCells(visibleMonth.year, visibleMonth.month);
2008
2100
  const canPrevMonth = [...availableByDate.keys()].some((key) => {
2009
2101
  const month = monthFromKey(key);
@@ -2203,9 +2295,13 @@ var import_styles = require("streamdown/styles.css");
2203
2295
  var import_jsx_runtime5 = require("react/jsx-runtime");
2204
2296
  function MessageBubble({
2205
2297
  message,
2298
+ brandLogoUrl,
2206
2299
  offer,
2207
2300
  onBook
2208
2301
  }) {
2302
+ const resolvedLogoUrl = brandLogoUrl?.trim();
2303
+ const [failedLogoUrl, setFailedLogoUrl] = (0, import_react8.useState)(null);
2304
+ const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
2209
2305
  const cards = message.role === "agent" ? extractToolCards(message.text) : [];
2210
2306
  const extractedOffers = cards.filter(
2211
2307
  (card) => card.type === "booking_offer"
@@ -2217,21 +2313,34 @@ function MessageBubble({
2217
2313
  if (message.role === "visitor") {
2218
2314
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "message-bubble__text", children: message.text }) });
2219
2315
  }
2316
+ const agentText = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2317
+ import_streamdown.Streamdown,
2318
+ {
2319
+ animated: true,
2320
+ caret: "circle",
2321
+ className: "message-bubble__markdown",
2322
+ controls: false,
2323
+ isAnimating: isStreaming,
2324
+ linkSafety: { enabled: false },
2325
+ mode: isStreaming ? "streaming" : "static",
2326
+ skipHtml: true,
2327
+ children: displayText
2328
+ }
2329
+ ) });
2220
2330
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
2221
- displayText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2222
- import_streamdown.Streamdown,
2223
- {
2224
- animated: true,
2225
- caret: "circle",
2226
- className: "message-bubble__markdown",
2227
- controls: false,
2228
- isAnimating: isStreaming,
2229
- linkSafety: { enabled: false },
2230
- mode: isStreaming ? "streaming" : "static",
2231
- skipHtml: true,
2232
- children: displayText
2233
- }
2234
- ) }) : null,
2331
+ displayText ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__agent-row", children: [
2332
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2333
+ "img",
2334
+ {
2335
+ src: resolvedLogoUrl,
2336
+ alt: "",
2337
+ onError: () => {
2338
+ setFailedLogoUrl(resolvedLogoUrl ?? null);
2339
+ }
2340
+ }
2341
+ ) }),
2342
+ agentText
2343
+ ] }) : agentText : null,
2235
2344
  offers.map((nextOffer, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2236
2345
  BookingCard,
2237
2346
  {
@@ -2306,7 +2415,8 @@ function RestoreIcon() {
2306
2415
  function AgentRail({
2307
2416
  state,
2308
2417
  theme,
2309
- brandLabel = "Webless Assist",
2418
+ colorScheme = "auto",
2419
+ brandLabel = "",
2310
2420
  brandLogoUrl,
2311
2421
  poweredByLabel = "Powered by Webless",
2312
2422
  composerPlaceholder = "Ask anything\u2026",
@@ -2321,8 +2431,28 @@ function AgentRail({
2321
2431
  onFollowUpSelect,
2322
2432
  onBook
2323
2433
  }) {
2324
- const transcriptRef = (0, import_react6.useRef)(null);
2325
- const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
2434
+ const transcriptRef = (0, import_react9.useRef)(null);
2435
+ const resolvedBrandLabel = brandLabel.trim();
2436
+ const resolvedBrandLogoUrl = brandLogoUrl?.trim();
2437
+ const [failedLogoUrl, setFailedLogoUrl] = (0, import_react9.useState)(null);
2438
+ const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
2439
+ const resolvedColorScheme = useAgentColorScheme(colorScheme);
2440
+ const brandedTheme = { ...defaultAgentRailTheme, ...theme };
2441
+ const resolvedTheme = resolvedColorScheme === "dark" ? {
2442
+ ...brandedTheme,
2443
+ brand: theme?.brand ?? defaultDarkAgentRailTheme.brand,
2444
+ brandDeep: defaultDarkAgentRailTheme.brandDeep,
2445
+ brandSoft: `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
2446
+ border: defaultDarkAgentRailTheme.border,
2447
+ danger: defaultDarkAgentRailTheme.danger,
2448
+ success: defaultDarkAgentRailTheme.success,
2449
+ surface: defaultDarkAgentRailTheme.surface,
2450
+ surfaceMuted: defaultDarkAgentRailTheme.surfaceMuted,
2451
+ text: defaultDarkAgentRailTheme.text,
2452
+ textMuted: defaultDarkAgentRailTheme.textMuted,
2453
+ textSubtle: defaultDarkAgentRailTheme.textSubtle,
2454
+ visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
2455
+ } : brandedTheme;
2326
2456
  const railStyle = {
2327
2457
  "--rail-width": resolvedTheme.railMaxWidth,
2328
2458
  "--as-rail-max-width": resolvedTheme.railMaxWidth,
@@ -2340,7 +2470,8 @@ function AgentRail({
2340
2470
  "--as-success": resolvedTheme.success,
2341
2471
  "--as-danger": resolvedTheme.danger,
2342
2472
  "--as-font-body": resolvedTheme.fontBody,
2343
- "--as-font-display": resolvedTheme.fontDisplay
2473
+ "--as-font-display": resolvedTheme.fontDisplay,
2474
+ colorScheme: resolvedColorScheme
2344
2475
  };
2345
2476
  const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
2346
2477
  const showActivity = state.toolSteps.length > 0;
@@ -2351,7 +2482,7 @@ function AgentRail({
2351
2482
  const greeting = state.messages.find(
2352
2483
  (message) => message.role === "agent" && message.id === "greeting"
2353
2484
  );
2354
- const visibleMessages = hasVisitorMessages2 ? state.messages.filter((message) => message.id !== "greeting") : [];
2485
+ const visibleMessages = hasVisitorMessages2 ? state.messages : [];
2355
2486
  const lastMessage = visibleMessages.at(-1);
2356
2487
  const completedAnswer = showActivity && state.phase === "complete" && lastMessage?.role === "agent" ? lastMessage : null;
2357
2488
  const transcriptMessages = completedAnswer ? visibleMessages.slice(0, -1) : visibleMessages;
@@ -2368,7 +2499,7 @@ function AgentRail({
2368
2499
  streaming: false,
2369
2500
  text: "Pick a date and time that works for you."
2370
2501
  } : null;
2371
- (0, import_react6.useEffect)(() => {
2502
+ (0, import_react9.useEffect)(() => {
2372
2503
  const node = transcriptRef.current;
2373
2504
  if (!node) return;
2374
2505
  node.scrollTop = node.scrollHeight;
@@ -2383,6 +2514,7 @@ function AgentRail({
2383
2514
  "aside",
2384
2515
  {
2385
2516
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
2517
+ "data-color-scheme": resolvedColorScheme,
2386
2518
  style: railStyle,
2387
2519
  "aria-label": "Agent conversation",
2388
2520
  "aria-modal": mobileFullscreen || expanded ? true : void 0,
@@ -2410,23 +2542,20 @@ function AgentRail({
2410
2542
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon, {})
2411
2543
  }
2412
2544
  ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
2413
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__identity", children: [
2414
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: [
2415
- brandLabel.slice(0, 1).toUpperCase(),
2416
- brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2417
- "img",
2418
- {
2419
- className: "agent-rail__brand-logo",
2420
- src: brandLogoUrl,
2421
- alt: "",
2422
- onError: (event) => {
2423
- event.currentTarget.hidden = true;
2424
- }
2545
+ resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__identity", children: [
2546
+ showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2547
+ "img",
2548
+ {
2549
+ className: "agent-rail__brand-logo",
2550
+ src: resolvedBrandLogoUrl,
2551
+ alt: "",
2552
+ onError: () => {
2553
+ setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
2425
2554
  }
2426
- ) : null
2427
- ] }),
2428
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
2429
- ] }),
2555
+ }
2556
+ ) }) : null,
2557
+ resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
2558
+ ] }) : null,
2430
2559
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__actions", children: [
2431
2560
  onReset ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2432
2561
  "button",
@@ -2444,7 +2573,7 @@ function AgentRail({
2444
2573
  {
2445
2574
  type: "button",
2446
2575
  className: "agent-rail__expand",
2447
- "aria-label": expanded ? "Exit focus view" : "Open focus view",
2576
+ "aria-label": expanded ? "Exit full screen" : "Open full screen",
2448
2577
  onClick: onExpandToggle,
2449
2578
  children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
2450
2579
  }
@@ -2453,7 +2582,14 @@ function AgentRail({
2453
2582
  ] }) }),
2454
2583
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__thread", children: [
2455
2584
  !hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
2456
- greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message: greeting, onBook }) : null,
2585
+ greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2586
+ MessageBubble,
2587
+ {
2588
+ message: greeting,
2589
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2590
+ onBook
2591
+ }
2592
+ ) : null,
2457
2593
  showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2458
2594
  FollowUpChips,
2459
2595
  {
@@ -2464,21 +2600,37 @@ function AgentRail({
2464
2600
  }
2465
2601
  ) }) : null
2466
2602
  ] }) : null,
2467
- transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message, onBook }, message.id)),
2468
- completedAnswer ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message: completedAnswer, onBook }) : null,
2603
+ transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2604
+ MessageBubble,
2605
+ {
2606
+ message,
2607
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2608
+ onBook
2609
+ },
2610
+ message.id
2611
+ )),
2469
2612
  showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2470
2613
  AgentActivityBubble,
2471
2614
  {
2472
- brandLabel,
2473
- brandLogoUrl,
2615
+ brandLabel: resolvedBrandLabel,
2616
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2474
2617
  failed: state.phase === "error",
2475
2618
  steps: state.toolSteps
2476
2619
  }
2477
2620
  ) : null,
2621
+ completedAnswer ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2622
+ MessageBubble,
2623
+ {
2624
+ message: completedAnswer,
2625
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2626
+ onBook
2627
+ }
2628
+ ) : null,
2478
2629
  streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2479
2630
  MessageBubble,
2480
2631
  {
2481
2632
  message: streamingMessage,
2633
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2482
2634
  offer: state.pendingOffer,
2483
2635
  onBook
2484
2636
  }
@@ -2590,9 +2742,9 @@ function DragDots() {
2590
2742
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("i", {}, index)) });
2591
2743
  }
2592
2744
  var VARIANT_COPY = {
2593
- outline: { label: "Assist", aria: "Open Assist" },
2745
+ outline: { label: "Ask anything", aria: "Ask anything" },
2594
2746
  ask: { label: "Ask anything", aria: "Ask anything" },
2595
- fill: { label: "Assist", aria: "Open Assist" }
2747
+ fill: { label: "Ask anything", aria: "Ask anything" }
2596
2748
  };
2597
2749
  function AssistEdgeTab({
2598
2750
  variant,
@@ -2610,26 +2762,34 @@ function AssistEdgeTab({
2610
2762
  mobile = false,
2611
2763
  surfaceColor,
2612
2764
  textColor,
2765
+ colorScheme = "auto",
2613
2766
  onOpen
2614
2767
  }) {
2768
+ const resolvedColorScheme = useAgentColorScheme(colorScheme);
2615
2769
  const copy = VARIANT_COPY[variant];
2616
2770
  const visibleLabel = label?.trim() || copy.label;
2617
2771
  const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
2772
+ const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
2773
+ const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
2774
+ const resolvedSurfaceColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.surface : surfaceColor;
2775
+ const resolvedTextColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.text : textColor;
2618
2776
  const style = {
2619
2777
  "--tab-along": `${along}%`,
2620
2778
  "--tab-inset": `${inset}px`,
2621
- ...brandColor ? { "--as-brand": brandColor } : {},
2779
+ ...resolvedBrandColor ? { "--as-brand": resolvedBrandColor } : {},
2622
2780
  ...brandForeground ? { "--as-visitor-text": brandForeground } : {},
2623
- ...borderColor ? { "--as-border": borderColor } : {},
2781
+ ...resolvedBorderColor ? { "--as-border": resolvedBorderColor } : {},
2624
2782
  ...fontFamily ? { "--as-font-display": fontFamily } : {},
2625
- ...surfaceColor ? { "--as-surface": surfaceColor } : {},
2626
- ...textColor ? { "--as-text": textColor } : {}
2783
+ ...resolvedSurfaceColor ? { "--as-surface": resolvedSurfaceColor } : {},
2784
+ ...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
2785
+ colorScheme: resolvedColorScheme
2627
2786
  };
2628
2787
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2629
2788
  "button",
2630
2789
  {
2631
2790
  type: "button",
2632
2791
  className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
2792
+ "data-color-scheme": resolvedColorScheme,
2633
2793
  style,
2634
2794
  "aria-label": `Open ${visibleLabel}`,
2635
2795
  "aria-hidden": !visible,
@@ -2643,7 +2803,7 @@ function AssistEdgeTab({
2643
2803
  className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
2644
2804
  "aria-hidden": "true",
2645
2805
  children: [
2646
- customIconUrl?.trim() ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }) : visibleLabel.slice(0, 1).toUpperCase(),
2806
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
2647
2807
  showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2648
2808
  "img",
2649
2809
  {
@@ -2658,10 +2818,7 @@ function AssistEdgeTab({
2658
2818
  ]
2659
2819
  }
2660
2820
  ),
2661
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__label", children: [
2662
- "Ask ",
2663
- visibleLabel
2664
- ] })
2821
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
2665
2822
  ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2666
2823
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
2667
2824
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
@@ -2725,9 +2882,9 @@ function AgentWidget({
2725
2882
  }) {
2726
2883
  const isMobile = useIsMobile();
2727
2884
  const placement = normalizeAgentPlacement(placementInput);
2728
- const railSlotRef = (0, import_react7.useRef)(null);
2729
- const [railCollapsed, setRailCollapsed] = (0, import_react7.useState)(defaultCollapsed);
2730
- const [railExpanded, setRailExpanded] = (0, import_react7.useState)(false);
2885
+ const railSlotRef = (0, import_react10.useRef)(null);
2886
+ const [railCollapsed, setRailCollapsed] = (0, import_react10.useState)(defaultCollapsed);
2887
+ const [railExpanded, setRailExpanded] = (0, import_react10.useState)(false);
2731
2888
  const pageShiftActive = shouldApplyPageShift({
2732
2889
  pageShift,
2733
2890
  isMobile,
@@ -2747,7 +2904,7 @@ function AgentWidget({
2747
2904
  runtimeOrigin,
2748
2905
  greeting: branding?.greeting
2749
2906
  });
2750
- const agentName = branding?.agentName ?? "Webless Guide";
2907
+ const agentName = branding?.agentName ?? "";
2751
2908
  const tabLabel = branding?.tabLabel ?? agentName;
2752
2909
  const theme = {
2753
2910
  ...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
@@ -2766,7 +2923,7 @@ function AgentWidget({
2766
2923
  } : {},
2767
2924
  ...branding?.colors?.border ? { border: branding.colors.border } : {}
2768
2925
  };
2769
- (0, import_react7.useEffect)(() => {
2926
+ (0, import_react10.useEffect)(() => {
2770
2927
  if (!registerPanelController) return;
2771
2928
  registerAgentPanelController(customerId, {
2772
2929
  open: () => setRailCollapsed(false),
@@ -2781,7 +2938,7 @@ function AgentWidget({
2781
2938
  if (isMobile) setRailCollapsed(false);
2782
2939
  await submit(message);
2783
2940
  }
2784
- (0, import_react7.useEffect)(() => {
2941
+ (0, import_react10.useEffect)(() => {
2785
2942
  if (railCollapsed) return;
2786
2943
  const handleKeyDown = (event) => {
2787
2944
  if (event.key === "Tab" && (isMobile || railExpanded)) {
@@ -2816,58 +2973,40 @@ function AgentWidget({
2816
2973
  return () => window.removeEventListener("keydown", handleKeyDown);
2817
2974
  }, [isMobile, railCollapsed, railExpanded]);
2818
2975
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
2819
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2976
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2820
2977
  "div",
2821
2978
  {
2822
2979
  className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
2823
- children: [
2824
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2825
- "div",
2826
- {
2827
- ref: railSlotRef,
2828
- className: "webless-agent-root__rail-slot",
2829
- inert: railCollapsed || void 0,
2830
- "aria-hidden": railCollapsed,
2831
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2832
- AgentRail,
2833
- {
2834
- theme,
2835
- brandLabel: agentName,
2836
- brandLogoUrl: branding?.logoUrl,
2837
- composerPlaceholder: branding?.composerPlaceholder ?? "Ask as a visitor\u2026",
2838
- poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
2839
- state,
2840
- mobileFullscreen: isMobile && !railCollapsed,
2841
- expanded: railExpanded,
2842
- onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
2843
- onClose: isMobile ? () => setRailCollapsed(true) : void 0,
2844
- onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
2845
- onSubmit: handleSubmit,
2846
- onReset: reset,
2847
- onRetry: () => void retry(),
2848
- onFollowUpSelect: (label) => void handleSubmit(label),
2849
- onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
2850
- }
2851
- )
2852
- }
2853
- ),
2854
- !railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2855
- "button",
2856
- {
2857
- type: "button",
2858
- className: `webless-agent-root__backdrop${isMobile ? " webless-agent-root__backdrop--mobile" : ""}`,
2859
- tabIndex: -1,
2860
- "aria-label": isMobile ? "Close agent" : "Exit focus view",
2861
- onClick: () => {
2862
- if (isMobile) {
2863
- setRailCollapsed(true);
2864
- } else {
2865
- setRailExpanded(false);
2866
- }
2980
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2981
+ "div",
2982
+ {
2983
+ ref: railSlotRef,
2984
+ className: "webless-agent-root__rail-slot",
2985
+ inert: railCollapsed || void 0,
2986
+ "aria-hidden": railCollapsed,
2987
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2988
+ AgentRail,
2989
+ {
2990
+ theme,
2991
+ brandLabel: agentName,
2992
+ brandLogoUrl: branding?.logoUrl,
2993
+ composerPlaceholder: branding?.composerPlaceholder ?? "Ask a question\u2026",
2994
+ poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
2995
+ state,
2996
+ mobileFullscreen: isMobile && !railCollapsed,
2997
+ expanded: railExpanded,
2998
+ onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
2999
+ onClose: isMobile ? () => setRailCollapsed(true) : void 0,
3000
+ onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
3001
+ onSubmit: handleSubmit,
3002
+ onReset: reset,
3003
+ onRetry: () => void retry(),
3004
+ onFollowUpSelect: (label) => void handleSubmit(label),
3005
+ onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
2867
3006
  }
2868
- }
2869
- ) : null
2870
- ]
3007
+ )
3008
+ }
3009
+ )
2871
3010
  }
2872
3011
  ),
2873
3012
  railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(