@webless/agent 0.5.0 → 0.6.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
@@ -26,6 +26,7 @@ __export(react_exports, {
26
26
  DEFAULT_AGENT_PLACEMENT: () => DEFAULT_AGENT_PLACEMENT,
27
27
  createIdleSuggestions: () => createIdleSuggestions,
28
28
  defaultAgentRailTheme: () => defaultAgentRailTheme,
29
+ defaultDarkAgentRailTheme: () => defaultDarkAgentRailTheme,
29
30
  hasVisitorMessages: () => hasVisitorMessages,
30
31
  isAgentBusy: () => isAgentBusy,
31
32
  normalizeAgentPlacement: () => normalizeAgentPlacement,
@@ -34,7 +35,7 @@ __export(react_exports, {
34
35
  module.exports = __toCommonJS(react_exports);
35
36
 
36
37
  // src/react/components/AgentWidget/AgentWidget.tsx
37
- var import_react7 = require("react");
38
+ var import_react10 = require("react");
38
39
 
39
40
  // src/react/page-shift.ts
40
41
  var import_react = require("react");
@@ -1100,7 +1101,7 @@ function visitorBookingPrefix(booking) {
1100
1101
  }
1101
1102
 
1102
1103
  // src/react/persisted-conversation.ts
1103
- var CONVERSATION_VERSION = 1;
1104
+ var CONVERSATION_VERSION = 2;
1104
1105
  function conversationKey(storageKeyPrefix, visitorSessionId) {
1105
1106
  return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
1106
1107
  }
@@ -1126,6 +1127,20 @@ function parseMessage(value) {
1126
1127
  createdAt: record.createdAt
1127
1128
  };
1128
1129
  }
1130
+ function parseToolStep(value) {
1131
+ if (typeof value !== "object" || value === null) return null;
1132
+ const record = value;
1133
+ 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") {
1134
+ return null;
1135
+ }
1136
+ return {
1137
+ id: record.id,
1138
+ kind: record.kind,
1139
+ label: record.label,
1140
+ state: record.state,
1141
+ ...typeof record.detail === "string" && record.detail ? { detail: record.detail } : {}
1142
+ };
1143
+ }
1129
1144
  function visitorTurnText(message) {
1130
1145
  return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
1131
1146
  }
@@ -1137,15 +1152,19 @@ function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
1137
1152
  const value = JSON.parse(raw);
1138
1153
  if (typeof value !== "object" || value === null) return null;
1139
1154
  const record = value;
1140
- if (record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string") {
1155
+ 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)) {
1141
1156
  return null;
1142
1157
  }
1143
1158
  const messages = record.messages.map(parseMessage);
1144
1159
  if (messages.some((message) => message === null)) return null;
1160
+ const storedToolSteps = record.version === CONVERSATION_VERSION && Array.isArray(record.toolSteps) ? record.toolSteps : [];
1161
+ const toolSteps = storedToolSteps.map(parseToolStep);
1162
+ if (toolSteps.some((step) => step === null)) return null;
1145
1163
  return {
1146
1164
  messages: messages.filter((message) => message !== null),
1147
1165
  pending: record.pending,
1148
- streamingText: record.streamingText
1166
+ streamingText: record.streamingText,
1167
+ toolSteps: toolSteps.filter((step) => step !== null)
1149
1168
  };
1150
1169
  } catch {
1151
1170
  return null;
@@ -1226,7 +1245,8 @@ function stateFromConversation(conversation, initialState) {
1226
1245
  ...initialState,
1227
1246
  messages: conversation.messages,
1228
1247
  phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
1229
- streamingText: conversation.streamingText
1248
+ streamingText: conversation.streamingText,
1249
+ toolSteps: conversation.toolSteps
1230
1250
  };
1231
1251
  }
1232
1252
  function upsertToolStep(steps, item) {
@@ -1360,13 +1380,15 @@ function useAgentChat({
1360
1380
  savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
1361
1381
  messages: state.messages,
1362
1382
  pending: isAgentBusy(state.phase),
1363
- streamingText: state.streamingText
1383
+ streamingText: state.streamingText,
1384
+ toolSteps: state.toolSteps
1364
1385
  });
1365
1386
  }, [
1366
1387
  resolvedStorageKeyPrefix,
1367
1388
  state.messages,
1368
1389
  state.phase,
1369
1390
  state.streamingText,
1391
+ state.toolSteps,
1370
1392
  visitorId
1371
1393
  ]);
1372
1394
  const reset = (0, import_react2.useCallback)(() => {
@@ -1687,7 +1709,7 @@ function unregisterAgentPanelController(customerId) {
1687
1709
  }
1688
1710
 
1689
1711
  // src/react/components/AgentRail/AgentRail.tsx
1690
- var import_react6 = require("react");
1712
+ var import_react9 = require("react");
1691
1713
 
1692
1714
  // src/react/types/conversation.ts
1693
1715
  var defaultAgentRailTheme = {
@@ -1708,15 +1730,62 @@ var defaultAgentRailTheme = {
1708
1730
  fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
1709
1731
  fontDisplay: '"Space Grotesk", sans-serif'
1710
1732
  };
1733
+ var defaultDarkAgentRailTheme = {
1734
+ ...defaultAgentRailTheme,
1735
+ brand: "#a77bff",
1736
+ brandSoft: "#2b2140",
1737
+ brandDeep: "#f5f0ff",
1738
+ surface: "#101218",
1739
+ surfaceMuted: "#1a1e27",
1740
+ text: "#f5f7fb",
1741
+ textMuted: "#b6bfce",
1742
+ textSubtle: "#919cad",
1743
+ border: "rgb(226 232 240 / 0.16)",
1744
+ visitorBubble: "#7c3aed",
1745
+ success: "#55cf91",
1746
+ danger: "#ff8da1"
1747
+ };
1748
+
1749
+ // src/react/hooks/useAgentColorScheme.ts
1750
+ var import_react4 = require("react");
1751
+ var DARK_MODE_QUERY = "(prefers-color-scheme: dark)";
1752
+ function subscribeToDarkMode(onChange) {
1753
+ if (typeof window === "undefined" || !window.matchMedia) {
1754
+ return () => void 0;
1755
+ }
1756
+ const mediaQuery = window.matchMedia(DARK_MODE_QUERY);
1757
+ if (typeof mediaQuery.addEventListener === "function") {
1758
+ mediaQuery.addEventListener("change", onChange);
1759
+ return () => mediaQuery.removeEventListener("change", onChange);
1760
+ }
1761
+ mediaQuery.addListener(onChange);
1762
+ return () => mediaQuery.removeListener(onChange);
1763
+ }
1764
+ function getPrefersDarkMode() {
1765
+ return typeof window !== "undefined" && Boolean(window.matchMedia?.(DARK_MODE_QUERY).matches);
1766
+ }
1767
+ function useAgentColorScheme(colorScheme = "auto") {
1768
+ const prefersDarkMode = (0, import_react4.useSyncExternalStore)(
1769
+ subscribeToDarkMode,
1770
+ getPrefersDarkMode,
1771
+ () => false
1772
+ );
1773
+ return resolveAgentColorScheme(colorScheme, prefersDarkMode);
1774
+ }
1775
+ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
1776
+ return colorScheme === "auto" ? prefersDarkMode ? "dark" : "light" : colorScheme;
1777
+ }
1711
1778
 
1712
1779
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1780
+ var import_react5 = require("react");
1713
1781
  var import_jsx_runtime = require("react/jsx-runtime");
1714
1782
  function workSummary(steps, failed, brandLabel) {
1715
1783
  const active = [...steps].reverse().find((step) => step.state === "active");
1716
1784
  if (active?.kind === "specialist")
1717
1785
  return `${active.label} is reviewing your question`;
1718
1786
  if (active?.kind === "search") return "Searching this site";
1719
- if (active) return `${brandLabel} is choosing the best way to help`;
1787
+ if (active)
1788
+ return brandLabel ? `${brandLabel} is choosing the best way to help` : "Choosing the best way to help";
1720
1789
  if (failed) return "Couldn\u2019t complete this request";
1721
1790
  const hasError = steps.some((step) => step.state === "error");
1722
1791
  const specialists = steps.filter(
@@ -1727,22 +1796,24 @@ function workSummary(steps, failed, brandLabel) {
1727
1796
  );
1728
1797
  if (hasError) return "Answered with available information";
1729
1798
  if (specialists.length > 1)
1730
- return `Consulted ${specialists.length} specialists`;
1731
- if (specialists.length === 1) return `Consulted ${specialists[0]?.label}`;
1799
+ return `Brought in ${specialists.length} specialists`;
1800
+ if (specialists.length === 1)
1801
+ return `Brought in ${specialists[0]?.label}`;
1732
1802
  if (searched) return "Searched this site";
1733
1803
  return "Answer ready";
1734
1804
  }
1735
1805
  function stepLabel(step, brandLabel) {
1736
- return step.kind === "planning" ? brandLabel : step.label;
1806
+ return step.kind === "planning" ? brandLabel || "Supervisor" : step.label;
1737
1807
  }
1738
1808
  function stepDetail(step, steps) {
1739
1809
  if (step.kind !== "planning" || step.state !== "completed") {
1740
1810
  return step.detail;
1741
1811
  }
1742
1812
  const specialists = steps.filter((item) => item.kind === "specialist");
1743
- if (specialists.length === 1) return `Delegated to ${specialists[0]?.label}`;
1813
+ if (specialists.length === 1)
1814
+ return `Routed your question to ${specialists[0]?.label}`;
1744
1815
  if (specialists.length > 1)
1745
- return `Delegated to ${specialists.length} specialists`;
1816
+ return `Routed your question to ${specialists.length} specialists`;
1746
1817
  if (steps.some((item) => item.kind === "search"))
1747
1818
  return "Used built-in Search & Discovery";
1748
1819
  return step.detail;
@@ -1773,12 +1844,17 @@ function PlanningIcon() {
1773
1844
  ) });
1774
1845
  }
1775
1846
  function AgentActivityBubble({
1776
- brandLabel = "Webless Guide",
1847
+ brandLabel = "",
1777
1848
  brandLogoUrl,
1778
1849
  failed = false,
1779
1850
  steps
1780
1851
  }) {
1781
1852
  const active = steps.some((step) => step.state === "active");
1853
+ const receiptId = steps.map((step) => step.id).join(":");
1854
+ const [expandedReceiptId, setExpandedReceiptId] = (0, import_react5.useState)(
1855
+ null
1856
+ );
1857
+ const detailsOpen = active || expandedReceiptId === receiptId;
1782
1858
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1783
1859
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1784
1860
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1790,9 +1866,23 @@ function AgentActivityBubble({
1790
1866
  ),
1791
1867
  workSummary(steps, failed, brandLabel)
1792
1868
  ] }),
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) => {
1869
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "agent-activity-bubble__details", children: [
1870
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1871
+ "button",
1872
+ {
1873
+ type: "button",
1874
+ className: "agent-activity-bubble__summary",
1875
+ "aria-expanded": detailsOpen,
1876
+ onClick: () => {
1877
+ if (active) return;
1878
+ setExpandedReceiptId(
1879
+ (current) => current === receiptId ? null : receiptId
1880
+ );
1881
+ },
1882
+ children: "How this answer was made"
1883
+ }
1884
+ ),
1885
+ detailsOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
1796
1886
  const detail = stepDetail(step, steps);
1797
1887
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1798
1888
  "li",
@@ -1824,7 +1914,7 @@ function AgentActivityBubble({
1824
1914
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1825
1915
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-heading", children: [
1826
1916
  /* @__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" })
1917
+ step.kind === "planning" && !brandLabel ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
1828
1918
  ] }),
1829
1919
  detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1830
1920
  ] })
@@ -1832,13 +1922,13 @@ function AgentActivityBubble({
1832
1922
  },
1833
1923
  step.id
1834
1924
  );
1835
- }) })
1925
+ }) }) : null
1836
1926
  ] })
1837
1927
  ] });
1838
1928
  }
1839
1929
 
1840
1930
  // src/react/components/Composer/Composer.tsx
1841
- var import_react4 = require("react");
1931
+ var import_react6 = require("react");
1842
1932
  var import_jsx_runtime2 = require("react/jsx-runtime");
1843
1933
  function SendIcon() {
1844
1934
  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 +1939,8 @@ function Composer({
1849
1939
  variant = "default",
1850
1940
  onSubmit
1851
1941
  }) {
1852
- const [value, setValue] = (0, import_react4.useState)("");
1853
- const inputRef = (0, import_react4.useRef)(null);
1942
+ const [value, setValue] = (0, import_react6.useState)("");
1943
+ const inputRef = (0, import_react6.useRef)(null);
1854
1944
  function submitCurrent() {
1855
1945
  const trimmed = value.trim();
1856
1946
  if (!trimmed || disabled) return;
@@ -1935,8 +2025,11 @@ function FollowUpChips({
1935
2025
  ] });
1936
2026
  }
1937
2027
 
2028
+ // src/react/components/MessageBubble/MessageBubble.tsx
2029
+ var import_react8 = require("react");
2030
+
1938
2031
  // src/react/components/BookingCard/BookingCard.tsx
1939
- var import_react5 = require("react");
2032
+ var import_react7 = require("react");
1940
2033
  var import_jsx_runtime4 = require("react/jsx-runtime");
1941
2034
  function monthFromKey(key) {
1942
2035
  const [year, month] = key.split("-").map(Number);
@@ -1965,19 +2058,19 @@ function BookingCard({
1965
2058
  offer,
1966
2059
  onBook
1967
2060
  }) {
1968
- const fieldId = (0, import_react5.useId)();
2061
+ const fieldId = (0, import_react7.useId)();
1969
2062
  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)(
2063
+ const [step, setStep] = (0, import_react7.useState)("date");
2064
+ const [eventTypeUri, setEventTypeUri] = (0, import_react7.useState)(defaultType);
2065
+ const [selectedDate, setSelectedDate] = (0, import_react7.useState)("");
2066
+ const [startTime, setStartTime] = (0, import_react7.useState)("");
2067
+ const [name, setName] = (0, import_react7.useState)("");
2068
+ const [email, setEmail] = (0, import_react7.useState)("");
2069
+ const slots = (0, import_react7.useMemo)(
1977
2070
  () => bookingSlotsForEventType(offer.slots, eventTypeUri),
1978
2071
  [eventTypeUri, offer.slots]
1979
2072
  );
1980
- const availableByDate = (0, import_react5.useMemo)(() => {
2073
+ const availableByDate = (0, import_react7.useMemo)(() => {
1981
2074
  const next = /* @__PURE__ */ new Map();
1982
2075
  for (const slot of slots) {
1983
2076
  const key = slotDateKey(slot.startTime);
@@ -1985,7 +2078,7 @@ function BookingCard({
1985
2078
  }
1986
2079
  return next;
1987
2080
  }, [slots]);
1988
- const [visibleMonth, setVisibleMonth] = (0, import_react5.useState)(
2081
+ const [visibleMonth, setVisibleMonth] = (0, import_react7.useState)(
1989
2082
  () => firstAvailableBookingMonth(slots)
1990
2083
  );
1991
2084
  function selectEventType(nextType) {
@@ -1996,14 +2089,14 @@ function BookingCard({
1996
2089
  firstAvailableBookingMonth(bookingSlotsForEventType(offer.slots, nextType))
1997
2090
  );
1998
2091
  }
1999
- const daySlots = (0, import_react5.useMemo)(
2092
+ const daySlots = (0, import_react7.useMemo)(
2000
2093
  () => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
2001
2094
  [selectedDate, slots]
2002
2095
  );
2003
2096
  const selectedType = offer.eventTypes.find((item) => item.uri === eventTypeUri);
2004
2097
  const selectedSample = availableByDate.get(selectedDate) ?? startTime;
2005
2098
  const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
2006
- const weekdays = (0, import_react5.useMemo)(() => weekdayLabels(), []);
2099
+ const weekdays = (0, import_react7.useMemo)(() => weekdayLabels(), []);
2007
2100
  const cells = calendarCells(visibleMonth.year, visibleMonth.month);
2008
2101
  const canPrevMonth = [...availableByDate.keys()].some((key) => {
2009
2102
  const month = monthFromKey(key);
@@ -2203,9 +2296,13 @@ var import_styles = require("streamdown/styles.css");
2203
2296
  var import_jsx_runtime5 = require("react/jsx-runtime");
2204
2297
  function MessageBubble({
2205
2298
  message,
2299
+ brandLogoUrl,
2206
2300
  offer,
2207
2301
  onBook
2208
2302
  }) {
2303
+ const resolvedLogoUrl = brandLogoUrl?.trim();
2304
+ const [failedLogoUrl, setFailedLogoUrl] = (0, import_react8.useState)(null);
2305
+ const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
2209
2306
  const cards = message.role === "agent" ? extractToolCards(message.text) : [];
2210
2307
  const extractedOffers = cards.filter(
2211
2308
  (card) => card.type === "booking_offer"
@@ -2217,21 +2314,34 @@ function MessageBubble({
2217
2314
  if (message.role === "visitor") {
2218
2315
  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
2316
  }
2317
+ const agentText = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2318
+ import_streamdown.Streamdown,
2319
+ {
2320
+ animated: true,
2321
+ caret: "circle",
2322
+ className: "message-bubble__markdown",
2323
+ controls: false,
2324
+ isAnimating: isStreaming,
2325
+ linkSafety: { enabled: false },
2326
+ mode: isStreaming ? "streaming" : "static",
2327
+ skipHtml: true,
2328
+ children: displayText
2329
+ }
2330
+ ) });
2220
2331
  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,
2332
+ displayText ? showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "message-bubble__agent-row", children: [
2333
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2334
+ "img",
2335
+ {
2336
+ src: resolvedLogoUrl,
2337
+ alt: "",
2338
+ onError: () => {
2339
+ setFailedLogoUrl(resolvedLogoUrl ?? null);
2340
+ }
2341
+ }
2342
+ ) }),
2343
+ agentText
2344
+ ] }) : agentText : null,
2235
2345
  offers.map((nextOffer, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2236
2346
  BookingCard,
2237
2347
  {
@@ -2306,7 +2416,8 @@ function RestoreIcon() {
2306
2416
  function AgentRail({
2307
2417
  state,
2308
2418
  theme,
2309
- brandLabel = "Webless Assist",
2419
+ colorScheme = "auto",
2420
+ brandLabel = "",
2310
2421
  brandLogoUrl,
2311
2422
  poweredByLabel = "Powered by Webless",
2312
2423
  composerPlaceholder = "Ask anything\u2026",
@@ -2321,8 +2432,28 @@ function AgentRail({
2321
2432
  onFollowUpSelect,
2322
2433
  onBook
2323
2434
  }) {
2324
- const transcriptRef = (0, import_react6.useRef)(null);
2325
- const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
2435
+ const transcriptRef = (0, import_react9.useRef)(null);
2436
+ const resolvedBrandLabel = brandLabel.trim();
2437
+ const resolvedBrandLogoUrl = brandLogoUrl?.trim();
2438
+ const [failedLogoUrl, setFailedLogoUrl] = (0, import_react9.useState)(null);
2439
+ const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
2440
+ const resolvedColorScheme = useAgentColorScheme(colorScheme);
2441
+ const brandedTheme = { ...defaultAgentRailTheme, ...theme };
2442
+ const resolvedTheme = resolvedColorScheme === "dark" ? {
2443
+ ...brandedTheme,
2444
+ brand: theme?.brand ?? defaultDarkAgentRailTheme.brand,
2445
+ brandDeep: defaultDarkAgentRailTheme.brandDeep,
2446
+ brandSoft: `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
2447
+ border: defaultDarkAgentRailTheme.border,
2448
+ danger: defaultDarkAgentRailTheme.danger,
2449
+ success: defaultDarkAgentRailTheme.success,
2450
+ surface: defaultDarkAgentRailTheme.surface,
2451
+ surfaceMuted: defaultDarkAgentRailTheme.surfaceMuted,
2452
+ text: defaultDarkAgentRailTheme.text,
2453
+ textMuted: defaultDarkAgentRailTheme.textMuted,
2454
+ textSubtle: defaultDarkAgentRailTheme.textSubtle,
2455
+ visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
2456
+ } : brandedTheme;
2326
2457
  const railStyle = {
2327
2458
  "--rail-width": resolvedTheme.railMaxWidth,
2328
2459
  "--as-rail-max-width": resolvedTheme.railMaxWidth,
@@ -2340,7 +2471,8 @@ function AgentRail({
2340
2471
  "--as-success": resolvedTheme.success,
2341
2472
  "--as-danger": resolvedTheme.danger,
2342
2473
  "--as-font-body": resolvedTheme.fontBody,
2343
- "--as-font-display": resolvedTheme.fontDisplay
2474
+ "--as-font-display": resolvedTheme.fontDisplay,
2475
+ colorScheme: resolvedColorScheme
2344
2476
  };
2345
2477
  const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
2346
2478
  const showActivity = state.toolSteps.length > 0;
@@ -2351,7 +2483,7 @@ function AgentRail({
2351
2483
  const greeting = state.messages.find(
2352
2484
  (message) => message.role === "agent" && message.id === "greeting"
2353
2485
  );
2354
- const visibleMessages = hasVisitorMessages2 ? state.messages.filter((message) => message.id !== "greeting") : [];
2486
+ const visibleMessages = hasVisitorMessages2 ? state.messages : [];
2355
2487
  const lastMessage = visibleMessages.at(-1);
2356
2488
  const completedAnswer = showActivity && state.phase === "complete" && lastMessage?.role === "agent" ? lastMessage : null;
2357
2489
  const transcriptMessages = completedAnswer ? visibleMessages.slice(0, -1) : visibleMessages;
@@ -2368,7 +2500,7 @@ function AgentRail({
2368
2500
  streaming: false,
2369
2501
  text: "Pick a date and time that works for you."
2370
2502
  } : null;
2371
- (0, import_react6.useEffect)(() => {
2503
+ (0, import_react9.useEffect)(() => {
2372
2504
  const node = transcriptRef.current;
2373
2505
  if (!node) return;
2374
2506
  node.scrollTop = node.scrollHeight;
@@ -2383,6 +2515,7 @@ function AgentRail({
2383
2515
  "aside",
2384
2516
  {
2385
2517
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
2518
+ "data-color-scheme": resolvedColorScheme,
2386
2519
  style: railStyle,
2387
2520
  "aria-label": "Agent conversation",
2388
2521
  "aria-modal": mobileFullscreen || expanded ? true : void 0,
@@ -2410,23 +2543,20 @@ function AgentRail({
2410
2543
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon, {})
2411
2544
  }
2412
2545
  ) : /* @__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
- }
2546
+ resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__identity", children: [
2547
+ showBrandLogo ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2548
+ "img",
2549
+ {
2550
+ className: "agent-rail__brand-logo",
2551
+ src: resolvedBrandLogoUrl,
2552
+ alt: "",
2553
+ onError: () => {
2554
+ setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
2425
2555
  }
2426
- ) : null
2427
- ] }),
2428
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
2429
- ] }),
2556
+ }
2557
+ ) }) : null,
2558
+ resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
2559
+ ] }) : null,
2430
2560
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__actions", children: [
2431
2561
  onReset ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2432
2562
  "button",
@@ -2444,7 +2574,7 @@ function AgentRail({
2444
2574
  {
2445
2575
  type: "button",
2446
2576
  className: "agent-rail__expand",
2447
- "aria-label": expanded ? "Exit focus view" : "Open focus view",
2577
+ "aria-label": expanded ? "Exit full screen" : "Open full screen",
2448
2578
  onClick: onExpandToggle,
2449
2579
  children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
2450
2580
  }
@@ -2453,7 +2583,14 @@ function AgentRail({
2453
2583
  ] }) }),
2454
2584
  /* @__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
2585
  !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,
2586
+ greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2587
+ MessageBubble,
2588
+ {
2589
+ message: greeting,
2590
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2591
+ onBook
2592
+ }
2593
+ ) : null,
2457
2594
  showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2458
2595
  FollowUpChips,
2459
2596
  {
@@ -2464,21 +2601,37 @@ function AgentRail({
2464
2601
  }
2465
2602
  ) }) : null
2466
2603
  ] }) : 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,
2604
+ transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2605
+ MessageBubble,
2606
+ {
2607
+ message,
2608
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2609
+ onBook
2610
+ },
2611
+ message.id
2612
+ )),
2469
2613
  showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2470
2614
  AgentActivityBubble,
2471
2615
  {
2472
- brandLabel,
2473
- brandLogoUrl,
2616
+ brandLabel: resolvedBrandLabel,
2617
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2474
2618
  failed: state.phase === "error",
2475
2619
  steps: state.toolSteps
2476
2620
  }
2477
2621
  ) : null,
2622
+ completedAnswer ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2623
+ MessageBubble,
2624
+ {
2625
+ message: completedAnswer,
2626
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2627
+ onBook
2628
+ }
2629
+ ) : null,
2478
2630
  streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2479
2631
  MessageBubble,
2480
2632
  {
2481
2633
  message: streamingMessage,
2634
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2482
2635
  offer: state.pendingOffer,
2483
2636
  onBook
2484
2637
  }
@@ -2590,9 +2743,9 @@ function DragDots() {
2590
2743
  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
2744
  }
2592
2745
  var VARIANT_COPY = {
2593
- outline: { label: "Assist", aria: "Open Assist" },
2746
+ outline: { label: "Ask anything", aria: "Ask anything" },
2594
2747
  ask: { label: "Ask anything", aria: "Ask anything" },
2595
- fill: { label: "Assist", aria: "Open Assist" }
2748
+ fill: { label: "Ask anything", aria: "Ask anything" }
2596
2749
  };
2597
2750
  function AssistEdgeTab({
2598
2751
  variant,
@@ -2610,26 +2763,34 @@ function AssistEdgeTab({
2610
2763
  mobile = false,
2611
2764
  surfaceColor,
2612
2765
  textColor,
2766
+ colorScheme = "auto",
2613
2767
  onOpen
2614
2768
  }) {
2769
+ const resolvedColorScheme = useAgentColorScheme(colorScheme);
2615
2770
  const copy = VARIANT_COPY[variant];
2616
2771
  const visibleLabel = label?.trim() || copy.label;
2617
2772
  const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
2773
+ const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
2774
+ const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
2775
+ const resolvedSurfaceColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.surface : surfaceColor;
2776
+ const resolvedTextColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.text : textColor;
2618
2777
  const style = {
2619
2778
  "--tab-along": `${along}%`,
2620
2779
  "--tab-inset": `${inset}px`,
2621
- ...brandColor ? { "--as-brand": brandColor } : {},
2780
+ ...resolvedBrandColor ? { "--as-brand": resolvedBrandColor } : {},
2622
2781
  ...brandForeground ? { "--as-visitor-text": brandForeground } : {},
2623
- ...borderColor ? { "--as-border": borderColor } : {},
2782
+ ...resolvedBorderColor ? { "--as-border": resolvedBorderColor } : {},
2624
2783
  ...fontFamily ? { "--as-font-display": fontFamily } : {},
2625
- ...surfaceColor ? { "--as-surface": surfaceColor } : {},
2626
- ...textColor ? { "--as-text": textColor } : {}
2784
+ ...resolvedSurfaceColor ? { "--as-surface": resolvedSurfaceColor } : {},
2785
+ ...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
2786
+ colorScheme: resolvedColorScheme
2627
2787
  };
2628
2788
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2629
2789
  "button",
2630
2790
  {
2631
2791
  type: "button",
2632
2792
  className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
2793
+ "data-color-scheme": resolvedColorScheme,
2633
2794
  style,
2634
2795
  "aria-label": `Open ${visibleLabel}`,
2635
2796
  "aria-hidden": !visible,
@@ -2643,7 +2804,7 @@ function AssistEdgeTab({
2643
2804
  className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
2644
2805
  "aria-hidden": "true",
2645
2806
  children: [
2646
- customIconUrl?.trim() ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }) : visibleLabel.slice(0, 1).toUpperCase(),
2807
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
2647
2808
  showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2648
2809
  "img",
2649
2810
  {
@@ -2658,10 +2819,7 @@ function AssistEdgeTab({
2658
2819
  ]
2659
2820
  }
2660
2821
  ),
2661
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__label", children: [
2662
- "Ask ",
2663
- visibleLabel
2664
- ] })
2822
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
2665
2823
  ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2666
2824
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
2667
2825
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
@@ -2725,9 +2883,9 @@ function AgentWidget({
2725
2883
  }) {
2726
2884
  const isMobile = useIsMobile();
2727
2885
  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);
2886
+ const railSlotRef = (0, import_react10.useRef)(null);
2887
+ const [railCollapsed, setRailCollapsed] = (0, import_react10.useState)(defaultCollapsed);
2888
+ const [railExpanded, setRailExpanded] = (0, import_react10.useState)(false);
2731
2889
  const pageShiftActive = shouldApplyPageShift({
2732
2890
  pageShift,
2733
2891
  isMobile,
@@ -2747,7 +2905,7 @@ function AgentWidget({
2747
2905
  runtimeOrigin,
2748
2906
  greeting: branding?.greeting
2749
2907
  });
2750
- const agentName = branding?.agentName ?? "Webless Guide";
2908
+ const agentName = branding?.agentName ?? "";
2751
2909
  const tabLabel = branding?.tabLabel ?? agentName;
2752
2910
  const theme = {
2753
2911
  ...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
@@ -2766,7 +2924,7 @@ function AgentWidget({
2766
2924
  } : {},
2767
2925
  ...branding?.colors?.border ? { border: branding.colors.border } : {}
2768
2926
  };
2769
- (0, import_react7.useEffect)(() => {
2927
+ (0, import_react10.useEffect)(() => {
2770
2928
  if (!registerPanelController) return;
2771
2929
  registerAgentPanelController(customerId, {
2772
2930
  open: () => setRailCollapsed(false),
@@ -2781,7 +2939,7 @@ function AgentWidget({
2781
2939
  if (isMobile) setRailCollapsed(false);
2782
2940
  await submit(message);
2783
2941
  }
2784
- (0, import_react7.useEffect)(() => {
2942
+ (0, import_react10.useEffect)(() => {
2785
2943
  if (railCollapsed) return;
2786
2944
  const handleKeyDown = (event) => {
2787
2945
  if (event.key === "Tab" && (isMobile || railExpanded)) {
@@ -2816,58 +2974,40 @@ function AgentWidget({
2816
2974
  return () => window.removeEventListener("keydown", handleKeyDown);
2817
2975
  }, [isMobile, railCollapsed, railExpanded]);
2818
2976
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
2819
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2977
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2820
2978
  "div",
2821
2979
  {
2822
2980
  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
- }
2981
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2982
+ "div",
2983
+ {
2984
+ ref: railSlotRef,
2985
+ className: "webless-agent-root__rail-slot",
2986
+ inert: railCollapsed || void 0,
2987
+ "aria-hidden": railCollapsed,
2988
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2989
+ AgentRail,
2990
+ {
2991
+ theme,
2992
+ brandLabel: agentName,
2993
+ brandLogoUrl: branding?.logoUrl,
2994
+ composerPlaceholder: branding?.composerPlaceholder ?? "Ask a question\u2026",
2995
+ poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
2996
+ state,
2997
+ mobileFullscreen: isMobile && !railCollapsed,
2998
+ expanded: railExpanded,
2999
+ onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
3000
+ onClose: isMobile ? () => setRailCollapsed(true) : void 0,
3001
+ onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
3002
+ onSubmit: handleSubmit,
3003
+ onReset: reset,
3004
+ onRetry: () => void retry(),
3005
+ onFollowUpSelect: (label) => void handleSubmit(label),
3006
+ onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
2867
3007
  }
2868
- }
2869
- ) : null
2870
- ]
3008
+ )
3009
+ }
3010
+ )
2871
3011
  }
2872
3012
  ),
2873
3013
  railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
@@ -2901,6 +3041,7 @@ function AgentWidget({
2901
3041
  DEFAULT_AGENT_PLACEMENT,
2902
3042
  createIdleSuggestions,
2903
3043
  defaultAgentRailTheme,
3044
+ defaultDarkAgentRailTheme,
2904
3045
  hasVisitorMessages,
2905
3046
  isAgentBusy,
2906
3047
  normalizeAgentPlacement,