@webless/agent 0.4.1 → 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_react6 = require("react");
38
+ var import_react10 = require("react");
38
39
 
39
40
  // src/react/page-shift.ts
40
41
  var import_react = require("react");
@@ -303,6 +304,9 @@ function runtimeSessionIdKey(visitorSessionId, prefix) {
303
304
  function runtimeStreamIndexKey(visitorSessionId, prefix) {
304
305
  return `${prefix}:eve:${visitorSessionId}:streamIndex`;
305
306
  }
307
+ function runtimeLastMessageKey(visitorSessionId, prefix) {
308
+ return `${prefix}:eve:${visitorSessionId}:lastMessage`;
309
+ }
306
310
  function loadPersistedAgentSession(visitorSessionId, options) {
307
311
  if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
308
312
  const prefix = resolvePrefix(options);
@@ -310,9 +314,11 @@ function loadPersistedAgentSession(visitorSessionId, options) {
310
314
  if (!sessionId) return null;
311
315
  const rawIndex = sessionStorage.getItem(runtimeStreamIndexKey(visitorSessionId, prefix));
312
316
  const streamIndex = rawIndex ? Number.parseInt(rawIndex, 10) : 0;
317
+ const lastMessage = sessionStorage.getItem(runtimeLastMessageKey(visitorSessionId, prefix))?.trim();
313
318
  return {
314
319
  sessionId,
315
- streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0
320
+ streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0,
321
+ ...lastMessage ? { lastMessage } : {}
316
322
  };
317
323
  }
318
324
  function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, options) {
@@ -326,11 +332,19 @@ function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, opt
326
332
  String(Math.max(0, streamIndex))
327
333
  );
328
334
  }
335
+ function savePersistedAgentTurnMessage(visitorSessionId, message, options) {
336
+ if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !message) {
337
+ return;
338
+ }
339
+ const prefix = resolvePrefix(options);
340
+ sessionStorage.setItem(runtimeLastMessageKey(visitorSessionId, prefix), message);
341
+ }
329
342
  function clearPersistedAgentSession(visitorSessionId, options) {
330
343
  if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return;
331
344
  const prefix = resolvePrefix(options);
332
345
  sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
333
346
  sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
347
+ sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
334
348
  }
335
349
 
336
350
  // src/runtime/client.ts
@@ -345,6 +359,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
345
359
  if (event.type === "message.completed") {
346
360
  handlers.onComplete?.();
347
361
  }
362
+ if (event.type === "action.result") {
363
+ const result = event.data.result;
364
+ if (result && typeof result === "object" && "output" in result) {
365
+ handlers.onActionResult?.(result.output);
366
+ }
367
+ }
348
368
  if (event.type !== "message.appended") return rendered;
349
369
  const { messageDelta, messageSoFar } = event.data;
350
370
  let delta = messageDelta;
@@ -358,6 +378,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
358
378
  if (delta) handlers.onDelta(delta);
359
379
  return next;
360
380
  }
381
+ function isResumeTurnMessage(received, candidate) {
382
+ if (received === candidate) return true;
383
+ return Boolean(candidate) && received.endsWith(`
384
+
385
+ ${candidate}`);
386
+ }
361
387
  function latestTurnEvents(events) {
362
388
  let startIndex = -1;
363
389
  for (let index = events.length - 1; index >= 0; index -= 1) {
@@ -589,6 +615,11 @@ var AgentSession = class {
589
615
  this.session = session;
590
616
  try {
591
617
  const activeSession = session;
618
+ savePersistedAgentTurnMessage(
619
+ this.visitorSessionId,
620
+ message,
621
+ this.storeOptions
622
+ );
592
623
  response = await withCapabilityRefresh(
593
624
  this.capability,
594
625
  () => activeSession.send(message, { signal })
@@ -604,6 +635,11 @@ var AgentSession = class {
604
635
  }
605
636
  }
606
637
  if (!response) {
638
+ savePersistedAgentTurnMessage(
639
+ this.visitorSessionId,
640
+ message,
641
+ this.storeOptions
642
+ );
607
643
  const created = await withCapabilityRefresh(
608
644
  this.capability,
609
645
  () => client.sessions.create({ message, signal })
@@ -658,7 +694,9 @@ var AgentSession = class {
658
694
  );
659
695
  const turnEvents = latestTurnEvents(snapshot.events);
660
696
  const received = turnEvents[0];
661
- if (received?.type !== "message.received" || received.data.message !== message) {
697
+ const lastSent = persisted.lastMessage;
698
+ const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
699
+ if (received?.type !== "message.received" || !(isResumeTurnMessage(received.data.message, message) || inFlight && lastSent !== void 0 && received.data.message === lastSent)) {
662
700
  return null;
663
701
  }
664
702
  let rendered = renderTurn(turnEvents);
@@ -822,8 +860,248 @@ function formatAgentError(error) {
822
860
  return TRANSIENT_AGENT_ERROR_MESSAGE;
823
861
  }
824
862
 
863
+ // src/react/lib/tool-card.ts
864
+ function bookingOfferIdentityKey(offer) {
865
+ const eventTypes = offer.eventTypes.map(
866
+ (item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
867
+ ).join("|");
868
+ const slots = offer.slots.map((slot) => `${slot.eventTypeUri ?? ""}:${slot.startTime}`).join("|");
869
+ return `${eventTypes}::${slots}` || "offer";
870
+ }
871
+ var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
872
+ function asRecord(value) {
873
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
874
+ }
875
+ function asString(value) {
876
+ return typeof value === "string" ? value.trim() : "";
877
+ }
878
+ function isEventUri(value) {
879
+ return /^https:\/\/api\.calendly\.com\/scheduled_events\/[^/]+$/i.test(value);
880
+ }
881
+ function isEventTypeUri(value) {
882
+ return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
883
+ }
884
+ function parseToolCard(value) {
885
+ const record = asRecord(value);
886
+ if (!record) return null;
887
+ if (record.booking_offer && asString(record.type) !== "booking_offer") {
888
+ const nested = parseToolCard(record.booking_offer);
889
+ if (nested) return nested;
890
+ }
891
+ const type = asString(record.type);
892
+ if (type === "booking_offer") {
893
+ const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
894
+ const entry = asRecord(item);
895
+ const uri = asString(entry?.uri);
896
+ if (!entry || !isEventTypeUri(uri)) return [];
897
+ const duration = entry.duration;
898
+ const locationKind = asString(entry.locationKind);
899
+ const location = asString(entry.location);
900
+ return [
901
+ {
902
+ name: asString(entry.name) || "Meeting",
903
+ uri,
904
+ ...typeof duration === "number" ? { duration } : {},
905
+ ...locationKind ? { locationKind } : {},
906
+ ...location ? { location } : {}
907
+ }
908
+ ];
909
+ }) : [];
910
+ const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
911
+ const entry = asRecord(item);
912
+ const startTime = asString(entry?.startTime);
913
+ if (!entry || !startTime) return [];
914
+ const eventTypeUri = asString(entry.eventTypeUri);
915
+ return [
916
+ {
917
+ startTime,
918
+ ...isEventTypeUri(eventTypeUri) ? { eventTypeUri } : {}
919
+ }
920
+ ];
921
+ }) : [];
922
+ if (slots.length === 0) return null;
923
+ return { type: "booking_offer", eventTypes, slots };
924
+ }
925
+ if (type === "booking_confirmed") {
926
+ const eventUri = asString(record.eventUri);
927
+ if (!isEventUri(eventUri)) return null;
928
+ const inviteeUri = asString(record.inviteeUri);
929
+ const inviteeEmail = asString(record.inviteeEmail);
930
+ const startTime = asString(record.startTime);
931
+ return {
932
+ type: "booking_confirmed",
933
+ eventUri,
934
+ ...inviteeUri ? { inviteeUri } : {},
935
+ ...inviteeEmail ? { inviteeEmail } : {},
936
+ ...startTime ? { startTime } : {}
937
+ };
938
+ }
939
+ if (type === "booking_canceled") {
940
+ const eventUri = asString(record.eventUri);
941
+ if (!isEventUri(eventUri)) return null;
942
+ return { type: "booking_canceled", eventUri };
943
+ }
944
+ return null;
945
+ }
946
+ function formatBookingOfferFence(offer) {
947
+ return [
948
+ "```webless-tool-card",
949
+ JSON.stringify({
950
+ type: "booking_offer",
951
+ eventTypes: offer.eventTypes,
952
+ slots: offer.slots
953
+ }),
954
+ "```"
955
+ ].join("\n");
956
+ }
957
+ function bookingOfferFromActionOutput(output) {
958
+ const record = asRecord(output);
959
+ const data = asRecord(record?.data) ?? record;
960
+ const card = parseToolCard(data);
961
+ return card?.type === "booking_offer" ? card : null;
962
+ }
963
+ function ensureBookingOfferText(text, offer) {
964
+ if (!offer) return text;
965
+ if (extractToolCards(text).some((card) => card.type === "booking_offer")) {
966
+ return text;
967
+ }
968
+ const visible = stripToolCards(text).trim() || text.trim();
969
+ return `${visible}
970
+
971
+ ${formatBookingOfferFence(offer)}`;
972
+ }
973
+ function hideToolCardFences(text) {
974
+ return text.replace(/```(?:webless-tool-card|json)\s*[\s\S]*?```/gi, "").replace(/```(?:webless-tool-card|json)[\s\S]*$/i, "").replace(/\n{3,}/g, "\n\n").trim();
975
+ }
976
+ function visitorTimeZone() {
977
+ try {
978
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
979
+ } catch {
980
+ return "UTC";
981
+ }
982
+ }
983
+ function extractToolCards(text) {
984
+ const cards = [];
985
+ for (const match of text.matchAll(FENCE_PATTERN)) {
986
+ try {
987
+ const card = parseToolCard(JSON.parse(match[1] ?? ""));
988
+ if (card) cards.push(card);
989
+ } catch {
990
+ }
991
+ }
992
+ return cards;
993
+ }
994
+ function stripToolCards(text) {
995
+ return text.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
996
+ }
997
+ function localDateKey(date) {
998
+ if (Number.isNaN(date.getTime())) return "";
999
+ return [
1000
+ date.getFullYear(),
1001
+ String(date.getMonth() + 1).padStart(2, "0"),
1002
+ String(date.getDate()).padStart(2, "0")
1003
+ ].join("-");
1004
+ }
1005
+ function slotDateKey(startTime) {
1006
+ return localDateKey(new Date(startTime)) || startTime;
1007
+ }
1008
+ function bookingSlotsForEventType(slots, eventTypeUri) {
1009
+ return slots.filter(
1010
+ (slot) => !eventTypeUri || !slot.eventTypeUri || slot.eventTypeUri === eventTypeUri
1011
+ );
1012
+ }
1013
+ function firstAvailableBookingMonth(slots) {
1014
+ let earliest;
1015
+ for (const slot of slots) {
1016
+ const key = slotDateKey(slot.startTime);
1017
+ if (!earliest || key < earliest) earliest = key;
1018
+ }
1019
+ const [year, month] = (earliest ?? slotDateKey((/* @__PURE__ */ new Date()).toISOString())).split("-").map(Number);
1020
+ if (!year || !month) {
1021
+ const now = /* @__PURE__ */ new Date();
1022
+ return { year: now.getFullYear(), month: now.getMonth() };
1023
+ }
1024
+ return { year, month: month - 1 };
1025
+ }
1026
+ function formatMonthTitle(year, month) {
1027
+ return new Intl.DateTimeFormat(void 0, {
1028
+ month: "long",
1029
+ year: "numeric"
1030
+ }).format(new Date(year, month, 1));
1031
+ }
1032
+ function formatLongDate(startTime) {
1033
+ const date = new Date(startTime);
1034
+ if (Number.isNaN(date.getTime())) return startTime;
1035
+ return new Intl.DateTimeFormat(void 0, {
1036
+ weekday: "long",
1037
+ month: "long",
1038
+ day: "numeric"
1039
+ }).format(date);
1040
+ }
1041
+ function weekdayLabels() {
1042
+ return Array.from(
1043
+ { length: 7 },
1044
+ (_, index) => new Intl.DateTimeFormat(void 0, { weekday: "short" }).format(
1045
+ new Date(2026, 7, 3 + index)
1046
+ )
1047
+ );
1048
+ }
1049
+ function formatTimeChip(startTime) {
1050
+ const date = new Date(startTime);
1051
+ if (Number.isNaN(date.getTime())) return startTime;
1052
+ return new Intl.DateTimeFormat(void 0, {
1053
+ hour: "numeric",
1054
+ minute: "2-digit"
1055
+ }).format(date);
1056
+ }
1057
+ function formatSlotTimeZone(startTime) {
1058
+ const date = new Date(startTime);
1059
+ if (Number.isNaN(date.getTime())) return "";
1060
+ return new Intl.DateTimeFormat(void 0, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
1061
+ }
1062
+ function formatSlotLabel(startTime) {
1063
+ const date = new Date(startTime);
1064
+ if (Number.isNaN(date.getTime())) return startTime;
1065
+ return new Intl.DateTimeFormat(void 0, {
1066
+ weekday: "short",
1067
+ month: "short",
1068
+ day: "numeric",
1069
+ hour: "numeric",
1070
+ minute: "2-digit",
1071
+ timeZoneName: "short"
1072
+ }).format(date);
1073
+ }
1074
+ function formatBookingRequest(input) {
1075
+ return [
1076
+ "Book this meeting now with CALENDLY_POST_INVITEE.",
1077
+ "Do not open a Calendly URL and do not list other scheduled events.",
1078
+ "Do not invent a location kind. Use only the location fields below.",
1079
+ `event_type: ${input.eventTypeUri}`,
1080
+ `start_time: ${input.startTime}`,
1081
+ `invitee.name: ${input.inviteeName}`,
1082
+ `invitee.email: ${input.inviteeEmail}`,
1083
+ `invitee.timezone: ${input.timezone}`,
1084
+ ...input.locationKind ? [
1085
+ `location.kind: ${input.locationKind}`,
1086
+ ...input.location ? [`location.location: ${input.location}`] : []
1087
+ ] : ["Do not send a location field."],
1088
+ "After it succeeds, reply with one short confirmation and a webless-tool-card booking_confirmed block using visitor_booking."
1089
+ ].join("\n");
1090
+ }
1091
+ function visitorBookingPrefix(booking) {
1092
+ return [
1093
+ "This visitor already booked a meeting. Use only this meeting:",
1094
+ `- scheduled event URI: ${booking.eventUri}`,
1095
+ ...booking.inviteeUri ? [`- invitee URI: ${booking.inviteeUri}`] : [],
1096
+ ...booking.inviteeEmail ? [`- invitee email: ${booking.inviteeEmail}`] : [],
1097
+ "For details call CALENDLY_GET_EVENT or CALENDLY_GET_EVENT_INVITEE with those URIs.",
1098
+ "If you must list events, pass this invitee_email. Never describe any other scheduled event.",
1099
+ "start_time values from Calendly are UTC."
1100
+ ].join("\n");
1101
+ }
1102
+
825
1103
  // src/react/persisted-conversation.ts
826
- var CONVERSATION_VERSION = 1;
1104
+ var CONVERSATION_VERSION = 2;
827
1105
  function conversationKey(storageKeyPrefix, visitorSessionId) {
828
1106
  return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
829
1107
  }
@@ -833,13 +1111,39 @@ function parseMessage(value) {
833
1111
  if (typeof record.id !== "string" || record.role !== "agent" && record.role !== "visitor" || typeof record.text !== "string" || typeof record.createdAt !== "number" || !Number.isFinite(record.createdAt)) {
834
1112
  return null;
835
1113
  }
1114
+ if (record.role === "visitor") {
1115
+ return {
1116
+ id: record.id,
1117
+ role: "visitor",
1118
+ text: record.text,
1119
+ createdAt: record.createdAt,
1120
+ ...typeof record.runtimeText === "string" && record.runtimeText ? { runtimeText: record.runtimeText } : {}
1121
+ };
1122
+ }
836
1123
  return {
837
1124
  id: record.id,
838
- role: record.role,
1125
+ role: "agent",
839
1126
  text: record.text,
840
1127
  createdAt: record.createdAt
841
1128
  };
842
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
+ }
1144
+ function visitorTurnText(message) {
1145
+ return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
1146
+ }
843
1147
  function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
844
1148
  if (typeof sessionStorage === "undefined") return null;
845
1149
  const raw = sessionStorage.getItem(conversationKey(storageKeyPrefix, visitorSessionId));
@@ -848,15 +1152,19 @@ function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
848
1152
  const value = JSON.parse(raw);
849
1153
  if (typeof value !== "object" || value === null) return null;
850
1154
  const record = value;
851
- 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)) {
852
1156
  return null;
853
1157
  }
854
1158
  const messages = record.messages.map(parseMessage);
855
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;
856
1163
  return {
857
1164
  messages: messages.filter((message) => message !== null),
858
1165
  pending: record.pending,
859
- streamingText: record.streamingText
1166
+ streamingText: record.streamingText,
1167
+ toolSteps: toolSteps.filter((step) => step !== null)
860
1168
  };
861
1169
  } catch {
862
1170
  return null;
@@ -872,6 +1180,42 @@ function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conv
872
1180
  function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
873
1181
  if (typeof sessionStorage === "undefined") return;
874
1182
  sessionStorage.removeItem(conversationKey(storageKeyPrefix, visitorSessionId));
1183
+ clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId);
1184
+ }
1185
+ function pendingBookingKey(storageKeyPrefix, visitorSessionId) {
1186
+ return `${storageKeyPrefix}:pending-booking:${visitorSessionId}`;
1187
+ }
1188
+ function loadPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
1189
+ if (typeof sessionStorage === "undefined") return null;
1190
+ const raw = sessionStorage.getItem(
1191
+ pendingBookingKey(storageKeyPrefix, visitorSessionId)
1192
+ );
1193
+ if (!raw) return null;
1194
+ try {
1195
+ const value = JSON.parse(raw);
1196
+ if (typeof value !== "object" || value === null) return null;
1197
+ const record = value;
1198
+ if (typeof record.eventUri !== "string" || !record.eventUri) return null;
1199
+ return {
1200
+ eventUri: record.eventUri,
1201
+ ...typeof record.inviteeUri === "string" && record.inviteeUri ? { inviteeUri: record.inviteeUri } : {},
1202
+ ...typeof record.inviteeEmail === "string" && record.inviteeEmail ? { inviteeEmail: record.inviteeEmail } : {},
1203
+ ...typeof record.startTime === "string" && record.startTime ? { startTime: record.startTime } : {}
1204
+ };
1205
+ } catch {
1206
+ return null;
1207
+ }
1208
+ }
1209
+ function savePendingWidgetBooking(storageKeyPrefix, visitorSessionId, booking) {
1210
+ if (typeof sessionStorage === "undefined") return;
1211
+ sessionStorage.setItem(
1212
+ pendingBookingKey(storageKeyPrefix, visitorSessionId),
1213
+ JSON.stringify(booking)
1214
+ );
1215
+ }
1216
+ function clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
1217
+ if (typeof sessionStorage === "undefined") return;
1218
+ sessionStorage.removeItem(pendingBookingKey(storageKeyPrefix, visitorSessionId));
875
1219
  }
876
1220
 
877
1221
  // src/react/hooks/useAgentChat.ts
@@ -891,6 +1235,7 @@ function createInitialState(greeting = DEFAULT_GREETING) {
891
1235
  journey: null,
892
1236
  followUps: [],
893
1237
  streamingText: "",
1238
+ pendingOffer: null,
894
1239
  error: null
895
1240
  };
896
1241
  }
@@ -900,7 +1245,8 @@ function stateFromConversation(conversation, initialState) {
900
1245
  ...initialState,
901
1246
  messages: conversation.messages,
902
1247
  phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
903
- streamingText: conversation.streamingText
1248
+ streamingText: conversation.streamingText,
1249
+ toolSteps: conversation.toolSteps
904
1250
  };
905
1251
  }
906
1252
  function upsertToolStep(steps, item) {
@@ -973,6 +1319,9 @@ function useAgentChat({
973
1319
  initialState
974
1320
  )
975
1321
  );
1322
+ const pendingBookingRef = (0, import_react2.useRef)(
1323
+ loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
1324
+ );
976
1325
  const runRef = (0, import_react2.useRef)(null);
977
1326
  const clientRef = (0, import_react2.useRef)(
978
1327
  createAgentClient({
@@ -1005,6 +1354,10 @@ function useAgentChat({
1005
1354
  visitorSessionId: visitorId,
1006
1355
  storageKeyPrefix: resolvedStorageKeyPrefix
1007
1356
  });
1357
+ pendingBookingRef.current = loadPendingWidgetBooking(
1358
+ resolvedStorageKeyPrefix,
1359
+ visitorId
1360
+ );
1008
1361
  setState(
1009
1362
  stateFromConversation(
1010
1363
  loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
@@ -1027,19 +1380,22 @@ function useAgentChat({
1027
1380
  savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
1028
1381
  messages: state.messages,
1029
1382
  pending: isAgentBusy(state.phase),
1030
- streamingText: state.streamingText
1383
+ streamingText: state.streamingText,
1384
+ toolSteps: state.toolSteps
1031
1385
  });
1032
1386
  }, [
1033
1387
  resolvedStorageKeyPrefix,
1034
1388
  state.messages,
1035
1389
  state.phase,
1036
1390
  state.streamingText,
1391
+ state.toolSteps,
1037
1392
  visitorId
1038
1393
  ]);
1039
1394
  const reset = (0, import_react2.useCallback)(() => {
1040
1395
  runRef.current?.abort();
1041
1396
  runRef.current = null;
1042
1397
  clientRef.current.reset();
1398
+ pendingBookingRef.current = null;
1043
1399
  clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
1044
1400
  setState(initialState);
1045
1401
  }, [initialState, resolvedStorageKeyPrefix, visitorId]);
@@ -1051,6 +1407,7 @@ function useAgentChat({
1051
1407
  try {
1052
1408
  let streamStarted = Boolean(initialText);
1053
1409
  let streamed = initialText;
1410
+ const capturedOffers = [];
1054
1411
  const handlers = {
1055
1412
  onWork: (item) => {
1056
1413
  if (!isActiveRun()) return;
@@ -1060,6 +1417,12 @@ function useAgentChat({
1060
1417
  toolSteps: upsertToolStep(prev.toolSteps, item)
1061
1418
  }));
1062
1419
  },
1420
+ onActionResult: (output) => {
1421
+ const offer = bookingOfferFromActionOutput(output);
1422
+ if (!offer) return;
1423
+ capturedOffers.push(offer);
1424
+ setState((prev) => ({ ...prev, pendingOffer: offer }));
1425
+ },
1063
1426
  onDelta: (delta) => {
1064
1427
  if (!isActiveRun()) return;
1065
1428
  if (!streamStarted) {
@@ -1075,7 +1438,8 @@ function useAgentChat({
1075
1438
  setState((prev) => ({
1076
1439
  ...prev,
1077
1440
  phase: "streaming",
1078
- streamingText: streamed
1441
+ streamingText: hideToolCardFences(streamed),
1442
+ pendingOffer: prev.pendingOffer ?? capturedOffers.at(-1) ?? null
1079
1443
  }));
1080
1444
  },
1081
1445
  onComplete: () => {
@@ -1096,18 +1460,34 @@ function useAgentChat({
1096
1460
  });
1097
1461
  }
1098
1462
  if (!isActiveRun() || finalText === null) return;
1463
+ const displayText = ensureBookingOfferText(
1464
+ finalText,
1465
+ capturedOffers.at(-1) ?? null
1466
+ );
1099
1467
  const agentMessage = {
1100
1468
  id: `agent-${Date.now()}`,
1101
1469
  role: "agent",
1102
- text: finalText,
1470
+ text: displayText,
1103
1471
  createdAt: Date.now()
1104
1472
  };
1473
+ const parsedCards = extractToolCards(displayText);
1474
+ for (const card of parsedCards) {
1475
+ if (card.type === "booking_confirmed") {
1476
+ pendingBookingRef.current = card;
1477
+ savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, card);
1478
+ }
1479
+ if (card.type === "booking_canceled" && pendingBookingRef.current?.eventUri === card.eventUri) {
1480
+ pendingBookingRef.current = null;
1481
+ clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
1482
+ }
1483
+ }
1105
1484
  setState((prev) => ({
1106
1485
  ...prev,
1107
1486
  phase: "complete",
1108
1487
  messages: [...prev.messages, agentMessage],
1109
1488
  toolSteps: completeActivePlanning(prev.toolSteps),
1110
1489
  streamingText: "",
1490
+ pendingOffer: null,
1111
1491
  followUps: [],
1112
1492
  journey: null
1113
1493
  }));
@@ -1129,26 +1509,54 @@ function useAgentChat({
1129
1509
  } : step
1130
1510
  ),
1131
1511
  streamingText: "",
1512
+ pendingOffer: null,
1132
1513
  error: message
1133
1514
  }));
1134
1515
  runRef.current = null;
1135
1516
  }
1136
1517
  },
1137
- []
1518
+ [resolvedStorageKeyPrefix, visitorId]
1519
+ );
1520
+ const rememberBooking = (0, import_react2.useCallback)(
1521
+ (booking) => {
1522
+ const current = pendingBookingRef.current;
1523
+ if (current?.eventUri === booking.eventUri && current.inviteeEmail === booking.inviteeEmail && current.inviteeUri === booking.inviteeUri) {
1524
+ return;
1525
+ }
1526
+ pendingBookingRef.current = booking;
1527
+ savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, booking);
1528
+ },
1529
+ [resolvedStorageKeyPrefix, visitorId]
1530
+ );
1531
+ const forgetBooking = (0, import_react2.useCallback)(
1532
+ (eventUri) => {
1533
+ const current = pendingBookingRef.current;
1534
+ if (!current) return;
1535
+ if (eventUri && current.eventUri !== eventUri) return;
1536
+ pendingBookingRef.current = null;
1537
+ clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
1538
+ },
1539
+ [resolvedStorageKeyPrefix, visitorId]
1138
1540
  );
1139
1541
  const submit = (0, import_react2.useCallback)(
1140
- async (visitorText) => {
1542
+ async (visitorText, options) => {
1141
1543
  if (runRef.current) {
1142
1544
  runRef.current.abort();
1143
1545
  clientRef.current.cancelActive();
1144
1546
  }
1145
1547
  const controller = new AbortController();
1146
1548
  runRef.current = controller;
1549
+ const booking = pendingBookingRef.current;
1550
+ const outgoing = options?.runtimeText ?? visitorText;
1551
+ const runtimeText = booking ? `${visitorBookingPrefix(booking)}
1552
+
1553
+ ${outgoing}` : outgoing;
1147
1554
  const visitorMessage = {
1148
1555
  id: `visitor-${Date.now()}`,
1149
1556
  role: "visitor",
1150
1557
  text: visitorText,
1151
- createdAt: Date.now()
1558
+ createdAt: Date.now(),
1559
+ ...runtimeText !== visitorText ? { runtimeText } : {}
1152
1560
  };
1153
1561
  setState((prev) => ({
1154
1562
  ...prev,
@@ -1165,9 +1573,10 @@ function useAgentChat({
1165
1573
  journey: null,
1166
1574
  followUps: [],
1167
1575
  streamingText: "",
1576
+ pendingOffer: null,
1168
1577
  error: null
1169
1578
  }));
1170
- await runTurn({ controller, resume: false, visitorText });
1579
+ await runTurn({ controller, resume: false, visitorText: runtimeText });
1171
1580
  },
1172
1581
  [runTurn]
1173
1582
  );
@@ -1194,12 +1603,13 @@ function useAgentChat({
1194
1603
  journey: null,
1195
1604
  followUps: [],
1196
1605
  streamingText: "",
1606
+ pendingOffer: null,
1197
1607
  error: null
1198
1608
  }));
1199
1609
  await runTurn({
1200
1610
  controller,
1201
1611
  resume: false,
1202
- visitorText: visitorMessage.text
1612
+ visitorText: visitorTurnText(visitorMessage)
1203
1613
  });
1204
1614
  }, [runTurn, state.messages]);
1205
1615
  (0, import_react2.useEffect)(() => {
@@ -1216,7 +1626,7 @@ function useAgentChat({
1216
1626
  controller,
1217
1627
  initialText: conversation.streamingText,
1218
1628
  resume: true,
1219
- visitorText: visitorMessage.text
1629
+ visitorText: visitorTurnText(visitorMessage)
1220
1630
  });
1221
1631
  return () => {
1222
1632
  if (runRef.current === controller) {
@@ -1236,6 +1646,8 @@ function useAgentChat({
1236
1646
  reset,
1237
1647
  retry,
1238
1648
  submit,
1649
+ rememberBooking,
1650
+ forgetBooking,
1239
1651
  visitorSessionId: visitorId,
1240
1652
  sessionId: clientRef.current.getActiveSessionId()
1241
1653
  };
@@ -1297,7 +1709,7 @@ function unregisterAgentPanelController(customerId) {
1297
1709
  }
1298
1710
 
1299
1711
  // src/react/components/AgentRail/AgentRail.tsx
1300
- var import_react5 = require("react");
1712
+ var import_react9 = require("react");
1301
1713
 
1302
1714
  // src/react/types/conversation.ts
1303
1715
  var defaultAgentRailTheme = {
@@ -1318,15 +1730,62 @@ var defaultAgentRailTheme = {
1318
1730
  fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
1319
1731
  fontDisplay: '"Space Grotesk", sans-serif'
1320
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
+ }
1321
1778
 
1322
1779
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1780
+ var import_react5 = require("react");
1323
1781
  var import_jsx_runtime = require("react/jsx-runtime");
1324
1782
  function workSummary(steps, failed, brandLabel) {
1325
1783
  const active = [...steps].reverse().find((step) => step.state === "active");
1326
1784
  if (active?.kind === "specialist")
1327
1785
  return `${active.label} is reviewing your question`;
1328
1786
  if (active?.kind === "search") return "Searching this site";
1329
- 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";
1330
1789
  if (failed) return "Couldn\u2019t complete this request";
1331
1790
  const hasError = steps.some((step) => step.state === "error");
1332
1791
  const specialists = steps.filter(
@@ -1337,23 +1796,24 @@ function workSummary(steps, failed, brandLabel) {
1337
1796
  );
1338
1797
  if (hasError) return "Answered with available information";
1339
1798
  if (specialists.length > 1)
1340
- return `Answer prepared with ${specialists.length} specialists`;
1799
+ return `Brought in ${specialists.length} specialists`;
1341
1800
  if (specialists.length === 1)
1342
- return `Answer prepared with ${specialists[0]?.label}`;
1343
- if (searched) return "Answer prepared from this site";
1801
+ return `Brought in ${specialists[0]?.label}`;
1802
+ if (searched) return "Searched this site";
1344
1803
  return "Answer ready";
1345
1804
  }
1346
1805
  function stepLabel(step, brandLabel) {
1347
- return step.kind === "planning" ? brandLabel : step.label;
1806
+ return step.kind === "planning" ? brandLabel || "Supervisor" : step.label;
1348
1807
  }
1349
1808
  function stepDetail(step, steps) {
1350
1809
  if (step.kind !== "planning" || step.state !== "completed") {
1351
1810
  return step.detail;
1352
1811
  }
1353
1812
  const specialists = steps.filter((item) => item.kind === "specialist");
1354
- if (specialists.length === 1) return `Delegated to ${specialists[0]?.label}`;
1813
+ if (specialists.length === 1)
1814
+ return `Routed your question to ${specialists[0]?.label}`;
1355
1815
  if (specialists.length > 1)
1356
- return `Delegated to ${specialists.length} specialists`;
1816
+ return `Routed your question to ${specialists.length} specialists`;
1357
1817
  if (steps.some((item) => item.kind === "search"))
1358
1818
  return "Used built-in Search & Discovery";
1359
1819
  return step.detail;
@@ -1384,13 +1844,17 @@ function PlanningIcon() {
1384
1844
  ) });
1385
1845
  }
1386
1846
  function AgentActivityBubble({
1387
- brandLabel = "Webless Guide",
1847
+ brandLabel = "",
1388
1848
  brandLogoUrl,
1389
1849
  failed = false,
1390
1850
  steps
1391
1851
  }) {
1392
1852
  const active = steps.some((step) => step.state === "active");
1393
- const delegated = steps.some((step) => step.kind === "specialist");
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;
1394
1858
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "agent-activity-bubble", children: [
1395
1859
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1396
1860
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1402,62 +1866,69 @@ function AgentActivityBubble({
1402
1866
  ),
1403
1867
  workSummary(steps, failed, brandLabel)
1404
1868
  ] }),
1405
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1406
- "details",
1407
- {
1408
- className: "agent-activity-bubble__details",
1409
- open: active || delegated,
1410
- children: [
1411
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { children: failed ? "What happened" : active ? "Working" : "How this answer was prepared" }),
1412
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
1413
- const detail = stepDetail(step, steps);
1414
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1415
- "li",
1416
- {
1417
- className: "agent-activity-bubble__step",
1418
- "data-kind": step.kind,
1419
- "data-state": step.state,
1420
- children: [
1421
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1422
- "span",
1423
- {
1424
- className: "agent-activity-bubble__step-icon",
1425
- "aria-hidden": "true",
1426
- children: step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1427
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanningIcon, {}),
1428
- brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1429
- "img",
1430
- {
1431
- src: brandLogoUrl,
1432
- alt: "",
1433
- onError: (event) => {
1434
- event.currentTarget.hidden = true;
1435
- }
1436
- }
1437
- ) : null
1438
- ] }) : step.kind === "search" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
1439
- }
1440
- ),
1441
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1442
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-heading", children: [
1443
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }),
1444
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
1445
- ] }),
1446
- detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1447
- ] })
1448
- ]
1449
- },
1450
- step.id
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
1451
1880
  );
1452
- }) })
1453
- ]
1454
- }
1455
- )
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) => {
1886
+ const detail = stepDetail(step, steps);
1887
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1888
+ "li",
1889
+ {
1890
+ className: "agent-activity-bubble__step",
1891
+ "data-kind": step.kind,
1892
+ "data-state": step.state,
1893
+ children: [
1894
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1895
+ "span",
1896
+ {
1897
+ className: "agent-activity-bubble__step-icon",
1898
+ "aria-hidden": "true",
1899
+ children: step.kind === "planning" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1900
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanningIcon, {}),
1901
+ brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1902
+ "img",
1903
+ {
1904
+ src: brandLogoUrl,
1905
+ alt: "",
1906
+ onError: (event) => {
1907
+ event.currentTarget.hidden = true;
1908
+ }
1909
+ }
1910
+ ) : null
1911
+ ] }) : step.kind === "search" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
1912
+ }
1913
+ ),
1914
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-copy", children: [
1915
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "agent-activity-bubble__step-heading", children: [
1916
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: stepLabel(step, brandLabel) }),
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" })
1918
+ ] }),
1919
+ detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1920
+ ] })
1921
+ ]
1922
+ },
1923
+ step.id
1924
+ );
1925
+ }) }) : null
1926
+ ] })
1456
1927
  ] });
1457
1928
  }
1458
1929
 
1459
1930
  // src/react/components/Composer/Composer.tsx
1460
- var import_react4 = require("react");
1931
+ var import_react6 = require("react");
1461
1932
  var import_jsx_runtime2 = require("react/jsx-runtime");
1462
1933
  function SendIcon() {
1463
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" }) });
@@ -1468,8 +1939,8 @@ function Composer({
1468
1939
  variant = "default",
1469
1940
  onSubmit
1470
1941
  }) {
1471
- const [value, setValue] = (0, import_react4.useState)("");
1472
- const inputRef = (0, import_react4.useRef)(null);
1942
+ const [value, setValue] = (0, import_react6.useState)("");
1943
+ const inputRef = (0, import_react6.useRef)(null);
1473
1944
  function submitCurrent() {
1474
1945
  const trimmed = value.trim();
1475
1946
  if (!trimmed || disabled) return;
@@ -1554,34 +2025,338 @@ function FollowUpChips({
1554
2025
  ] });
1555
2026
  }
1556
2027
 
2028
+ // src/react/components/MessageBubble/MessageBubble.tsx
2029
+ var import_react8 = require("react");
2030
+
2031
+ // src/react/components/BookingCard/BookingCard.tsx
2032
+ var import_react7 = require("react");
2033
+ var import_jsx_runtime4 = require("react/jsx-runtime");
2034
+ function monthFromKey(key) {
2035
+ const [year, month] = key.split("-").map(Number);
2036
+ if (!year || !month) return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
2037
+ return { year, month: month - 1 };
2038
+ }
2039
+ function dateKeyFromParts(year, month, day) {
2040
+ return [
2041
+ year,
2042
+ String(month + 1).padStart(2, "0"),
2043
+ String(day).padStart(2, "0")
2044
+ ].join("-");
2045
+ }
2046
+ function calendarCells(year, month) {
2047
+ const firstWeekday = (new Date(year, month, 1).getDay() + 6) % 7;
2048
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
2049
+ const cells = [];
2050
+ for (let index = 0; index < firstWeekday; index += 1) cells.push(null);
2051
+ for (let day = 1; day <= daysInMonth; day += 1) {
2052
+ cells.push({ day, key: dateKeyFromParts(year, month, day) });
2053
+ }
2054
+ while (cells.length < 42) cells.push(null);
2055
+ return cells;
2056
+ }
2057
+ function BookingCard({
2058
+ offer,
2059
+ onBook
2060
+ }) {
2061
+ const fieldId = (0, import_react7.useId)();
2062
+ const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
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)(
2070
+ () => bookingSlotsForEventType(offer.slots, eventTypeUri),
2071
+ [eventTypeUri, offer.slots]
2072
+ );
2073
+ const availableByDate = (0, import_react7.useMemo)(() => {
2074
+ const next = /* @__PURE__ */ new Map();
2075
+ for (const slot of slots) {
2076
+ const key = slotDateKey(slot.startTime);
2077
+ if (!next.has(key)) next.set(key, slot.startTime);
2078
+ }
2079
+ return next;
2080
+ }, [slots]);
2081
+ const [visibleMonth, setVisibleMonth] = (0, import_react7.useState)(
2082
+ () => firstAvailableBookingMonth(slots)
2083
+ );
2084
+ function selectEventType(nextType) {
2085
+ setEventTypeUri(nextType);
2086
+ setSelectedDate("");
2087
+ setStartTime("");
2088
+ setVisibleMonth(
2089
+ firstAvailableBookingMonth(bookingSlotsForEventType(offer.slots, nextType))
2090
+ );
2091
+ }
2092
+ const daySlots = (0, import_react7.useMemo)(
2093
+ () => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
2094
+ [selectedDate, slots]
2095
+ );
2096
+ const selectedType = offer.eventTypes.find((item) => item.uri === eventTypeUri);
2097
+ const selectedSample = availableByDate.get(selectedDate) ?? startTime;
2098
+ const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
2099
+ const weekdays = (0, import_react7.useMemo)(() => weekdayLabels(), []);
2100
+ const cells = calendarCells(visibleMonth.year, visibleMonth.month);
2101
+ const canPrevMonth = [...availableByDate.keys()].some((key) => {
2102
+ const month = monthFromKey(key);
2103
+ return month.year < visibleMonth.year || month.year === visibleMonth.year && month.month < visibleMonth.month;
2104
+ });
2105
+ const canNextMonth = [...availableByDate.keys()].some((key) => {
2106
+ const month = monthFromKey(key);
2107
+ return month.year > visibleMonth.year || month.year === visibleMonth.year && month.month > visibleMonth.month;
2108
+ });
2109
+ function goToMonth(offset) {
2110
+ setVisibleMonth((current) => {
2111
+ const next = new Date(current.year, current.month + offset, 1);
2112
+ return { year: next.getFullYear(), month: next.getMonth() };
2113
+ });
2114
+ }
2115
+ function selectDate(key) {
2116
+ if (!availableByDate.has(key)) return;
2117
+ setSelectedDate(key);
2118
+ setStartTime("");
2119
+ setStep("time");
2120
+ }
2121
+ function selectTime(value) {
2122
+ setStartTime(value);
2123
+ setStep("details");
2124
+ }
2125
+ function handleSubmit(event) {
2126
+ event.preventDefault();
2127
+ if (!eventTypeUri || !startTime || !name.trim() || !email.trim()) return;
2128
+ onBook?.({
2129
+ displayText: `Book the ${formatSlotLabel(startTime)} demo`,
2130
+ runtimeText: formatBookingRequest({
2131
+ eventTypeUri,
2132
+ inviteeEmail: email.trim(),
2133
+ inviteeName: name.trim(),
2134
+ startTime,
2135
+ timezone: visitorTimeZone(),
2136
+ locationKind: selectedType?.locationKind,
2137
+ location: selectedType?.location
2138
+ })
2139
+ });
2140
+ }
2141
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { className: "booking-card", "aria-label": "Book a meeting", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { className: "booking-card__form", onSubmit: handleSubmit, children: [
2142
+ step === "date" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
2143
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
2144
+ timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
2145
+ "Times in ",
2146
+ timeZone
2147
+ ] }) : null,
2148
+ offer.eventTypes.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "booking-card__field", htmlFor: `${fieldId}-type`, children: [
2149
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Meeting" }),
2150
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2151
+ "select",
2152
+ {
2153
+ id: `${fieldId}-type`,
2154
+ value: eventTypeUri,
2155
+ onChange: (event) => selectEventType(event.target.value),
2156
+ children: offer.eventTypes.map((item) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: item.uri, children: item.name }, item.uri))
2157
+ }
2158
+ )
2159
+ ] }) : null,
2160
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__month", children: [
2161
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2162
+ "button",
2163
+ {
2164
+ type: "button",
2165
+ className: "booking-card__nav",
2166
+ "aria-label": "Previous month",
2167
+ disabled: !canPrevMonth,
2168
+ onClick: () => goToMonth(-1),
2169
+ children: "\u2039"
2170
+ }
2171
+ ),
2172
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__month-title", children: formatMonthTitle(visibleMonth.year, visibleMonth.month) }),
2173
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2174
+ "button",
2175
+ {
2176
+ type: "button",
2177
+ className: "booking-card__nav",
2178
+ "aria-label": "Next month",
2179
+ disabled: !canNextMonth,
2180
+ onClick: () => goToMonth(1),
2181
+ children: "\u203A"
2182
+ }
2183
+ )
2184
+ ] }),
2185
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "booking-card__weekdays", children: weekdays.map((label) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: label }, label)) }),
2186
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "booking-card__calendar", role: "grid", "aria-label": "Available dates", children: cells.map((cell, index) => {
2187
+ if (!cell) {
2188
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "booking-card__day" }, `empty-${index}`);
2189
+ }
2190
+ const available = availableByDate.has(cell.key);
2191
+ const selected = cell.key === selectedDate;
2192
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2193
+ "button",
2194
+ {
2195
+ type: "button",
2196
+ className: [
2197
+ "booking-card__day",
2198
+ available ? "booking-card__day--available" : "",
2199
+ selected ? "booking-card__day--selected" : ""
2200
+ ].filter(Boolean).join(" "),
2201
+ disabled: !available,
2202
+ "aria-pressed": selected,
2203
+ onClick: () => selectDate(cell.key),
2204
+ children: cell.day
2205
+ },
2206
+ cell.key
2207
+ );
2208
+ }) })
2209
+ ] }, "date") : null,
2210
+ step === "time" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
2211
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
2212
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2213
+ "button",
2214
+ {
2215
+ type: "button",
2216
+ className: "booking-card__nav",
2217
+ "aria-label": "Back to dates",
2218
+ onClick: () => setStep("date"),
2219
+ children: "\u2039"
2220
+ }
2221
+ ),
2222
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
2223
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: selectedSample ? formatLongDate(selectedSample) : "Pick a time" }),
2224
+ timeZone ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "booking-card__tz", children: [
2225
+ "Times in ",
2226
+ timeZone
2227
+ ] }) : null
2228
+ ] })
2229
+ ] }),
2230
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "booking-card__times", children: daySlots.map((slot) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2231
+ "button",
2232
+ {
2233
+ type: "button",
2234
+ className: startTime === slot.startTime ? "booking-card__time booking-card__time--selected" : "booking-card__time",
2235
+ onClick: () => selectTime(slot.startTime),
2236
+ children: formatTimeChip(slot.startTime)
2237
+ },
2238
+ slot.startTime
2239
+ )) })
2240
+ ] }, "time") : null,
2241
+ step === "details" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step", children: [
2242
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__step-bar", children: [
2243
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2244
+ "button",
2245
+ {
2246
+ type: "button",
2247
+ className: "booking-card__nav",
2248
+ "aria-label": "Back to times",
2249
+ onClick: () => setStep("time"),
2250
+ children: "\u2039"
2251
+ }
2252
+ ),
2253
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
2254
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__title", children: "Enter details" }),
2255
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__tz", children: formatSlotLabel(startTime) }),
2256
+ selectedType?.location ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "booking-card__tz", children: selectedType.location }) : null
2257
+ ] })
2258
+ ] }),
2259
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "booking-card__identity", children: [
2260
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "booking-card__field", htmlFor: `${fieldId}-name`, children: [
2261
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Name" }),
2262
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2263
+ "input",
2264
+ {
2265
+ id: `${fieldId}-name`,
2266
+ autoComplete: "name",
2267
+ value: name,
2268
+ onChange: (event) => setName(event.target.value),
2269
+ required: true
2270
+ }
2271
+ )
2272
+ ] }),
2273
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "booking-card__field", htmlFor: `${fieldId}-email`, children: [
2274
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "Email" }),
2275
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2276
+ "input",
2277
+ {
2278
+ id: `${fieldId}-email`,
2279
+ type: "email",
2280
+ autoComplete: "email",
2281
+ value: email,
2282
+ onChange: (event) => setEmail(event.target.value),
2283
+ required: true
2284
+ }
2285
+ )
2286
+ ] })
2287
+ ] }),
2288
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "submit", className: "booking-card__submit", children: "Book this time" })
2289
+ ] }, "details") : null
2290
+ ] }) });
2291
+ }
2292
+
1557
2293
  // src/react/components/MessageBubble/MessageBubble.tsx
1558
2294
  var import_streamdown = require("streamdown");
1559
2295
  var import_styles = require("streamdown/styles.css");
1560
- var import_jsx_runtime4 = require("react/jsx-runtime");
1561
- function MessageBubble({ message }) {
2296
+ var import_jsx_runtime5 = require("react/jsx-runtime");
2297
+ function MessageBubble({
2298
+ message,
2299
+ brandLogoUrl,
2300
+ offer,
2301
+ onBook
2302
+ }) {
2303
+ const resolvedLogoUrl = brandLogoUrl?.trim();
2304
+ const [failedLogoUrl, setFailedLogoUrl] = (0, import_react8.useState)(null);
2305
+ const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
2306
+ const cards = message.role === "agent" ? extractToolCards(message.text) : [];
2307
+ const extractedOffers = cards.filter(
2308
+ (card) => card.type === "booking_offer"
2309
+ );
2310
+ const offers = offer ? [offer] : extractedOffers;
2311
+ const visibleText = hideToolCardFences(message.text);
2312
+ const isStreaming = message.role === "agent" && Boolean(message.streaming);
2313
+ const displayText = offers.length > 0 ? visibleText || "Pick a date and time that works for you." : visibleText || (isStreaming ? "" : message.text);
1562
2314
  if (message.role === "visitor") {
1563
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "message-bubble__text", children: message.text }) });
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 }) });
1564
2316
  }
1565
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2317
+ const agentText = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "message-bubble__text", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1566
2318
  import_streamdown.Streamdown,
1567
2319
  {
1568
2320
  animated: true,
1569
2321
  caret: "circle",
1570
2322
  className: "message-bubble__markdown",
1571
2323
  controls: false,
1572
- isAnimating: message.streaming,
2324
+ isAnimating: isStreaming,
1573
2325
  linkSafety: { enabled: false },
1574
- mode: message.streaming ? "streaming" : "static",
2326
+ mode: isStreaming ? "streaming" : "static",
1575
2327
  skipHtml: true,
1576
- children: message.text
2328
+ children: displayText
1577
2329
  }
1578
- ) }) });
2330
+ ) });
2331
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("article", { className: "message-bubble message-bubble--agent", children: [
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,
2345
+ offers.map((nextOffer, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2346
+ BookingCard,
2347
+ {
2348
+ offer: nextOffer,
2349
+ onBook
2350
+ },
2351
+ `${bookingOfferIdentityKey(nextOffer)}-${index}`
2352
+ ))
2353
+ ] });
1579
2354
  }
1580
2355
 
1581
2356
  // src/react/components/AgentRail/AgentRail.tsx
1582
- var import_jsx_runtime5 = require("react/jsx-runtime");
2357
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1583
2358
  function MinimizeIcon() {
1584
- 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)(
2359
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1585
2360
  "path",
1586
2361
  {
1587
2362
  d: "M3.5 8h9",
@@ -1592,7 +2367,7 @@ function MinimizeIcon() {
1592
2367
  ) });
1593
2368
  }
1594
2369
  function CloseIcon() {
1595
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2370
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1596
2371
  "path",
1597
2372
  {
1598
2373
  d: "M4 4l8 8M12 4l-8 8",
@@ -1603,7 +2378,7 @@ function CloseIcon() {
1603
2378
  ) });
1604
2379
  }
1605
2380
  function NewChatIcon() {
1606
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2381
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1607
2382
  "path",
1608
2383
  {
1609
2384
  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",
@@ -1615,7 +2390,7 @@ function NewChatIcon() {
1615
2390
  ) });
1616
2391
  }
1617
2392
  function ExpandIcon() {
1618
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2393
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1619
2394
  "path",
1620
2395
  {
1621
2396
  d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
@@ -1627,7 +2402,7 @@ function ExpandIcon() {
1627
2402
  ) });
1628
2403
  }
1629
2404
  function RestoreIcon() {
1630
- 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)(
2405
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1631
2406
  "path",
1632
2407
  {
1633
2408
  d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
@@ -1641,7 +2416,8 @@ function RestoreIcon() {
1641
2416
  function AgentRail({
1642
2417
  state,
1643
2418
  theme,
1644
- brandLabel = "Webless Assist",
2419
+ colorScheme = "auto",
2420
+ brandLabel = "",
1645
2421
  brandLogoUrl,
1646
2422
  poweredByLabel = "Powered by Webless",
1647
2423
  composerPlaceholder = "Ask anything\u2026",
@@ -1653,11 +2429,31 @@ function AgentRail({
1653
2429
  onReset,
1654
2430
  onRetry,
1655
2431
  onSubmit,
1656
- onFollowUpSelect
2432
+ onFollowUpSelect,
2433
+ onBook
1657
2434
  }) {
1658
- const transcriptRef = (0, import_react5.useRef)(null);
1659
- const welcomeTitleId = (0, import_react5.useId)();
1660
- 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;
1661
2457
  const railStyle = {
1662
2458
  "--rail-width": resolvedTheme.railMaxWidth,
1663
2459
  "--as-rail-max-width": resolvedTheme.railMaxWidth,
@@ -1675,7 +2471,8 @@ function AgentRail({
1675
2471
  "--as-success": resolvedTheme.success,
1676
2472
  "--as-danger": resolvedTheme.danger,
1677
2473
  "--as-font-body": resolvedTheme.fontBody,
1678
- "--as-font-display": resolvedTheme.fontDisplay
2474
+ "--as-font-display": resolvedTheme.fontDisplay,
2475
+ colorScheme: resolvedColorScheme
1679
2476
  };
1680
2477
  const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
1681
2478
  const showActivity = state.toolSteps.length > 0;
@@ -1686,7 +2483,7 @@ function AgentRail({
1686
2483
  const greeting = state.messages.find(
1687
2484
  (message) => message.role === "agent" && message.id === "greeting"
1688
2485
  );
1689
- const visibleMessages = hasVisitorMessages2 ? state.messages.filter((message) => message.id !== "greeting") : [];
2486
+ const visibleMessages = hasVisitorMessages2 ? state.messages : [];
1690
2487
  const lastMessage = visibleMessages.at(-1);
1691
2488
  const completedAnswer = showActivity && state.phase === "complete" && lastMessage?.role === "agent" ? lastMessage : null;
1692
2489
  const transcriptMessages = completedAnswer ? visibleMessages.slice(0, -1) : visibleMessages;
@@ -1696,8 +2493,14 @@ function AgentRail({
1696
2493
  role: "agent",
1697
2494
  streaming: true,
1698
2495
  text: state.streamingText
2496
+ } : state.pendingOffer ? {
2497
+ createdAt: 0,
2498
+ id: "pending-booking",
2499
+ role: "agent",
2500
+ streaming: false,
2501
+ text: "Pick a date and time that works for you."
1699
2502
  } : null;
1700
- (0, import_react5.useEffect)(() => {
2503
+ (0, import_react9.useEffect)(() => {
1701
2504
  const node = transcriptRef.current;
1702
2505
  if (!node) return;
1703
2506
  node.scrollTop = node.scrollHeight;
@@ -1708,10 +2511,11 @@ function AgentRail({
1708
2511
  state.followUps,
1709
2512
  state.journey
1710
2513
  ]);
1711
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2514
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1712
2515
  "aside",
1713
2516
  {
1714
2517
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
2518
+ "data-color-scheme": resolvedColorScheme,
1715
2519
  style: railStyle,
1716
2520
  "aria-label": "Agent conversation",
1717
2521
  "aria-modal": mobileFullscreen || expanded ? true : void 0,
@@ -1719,45 +2523,42 @@ function AgentRail({
1719
2523
  role: mobileFullscreen || expanded ? "dialog" : void 0,
1720
2524
  tabIndex: mobileFullscreen || expanded ? -1 : void 0,
1721
2525
  children: [
1722
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__brand-row", children: [
1723
- onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2526
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("header", { className: "agent-rail__header", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__brand-row", children: [
2527
+ onCollapse ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1724
2528
  "button",
1725
2529
  {
1726
2530
  type: "button",
1727
2531
  className: "agent-rail__collapse",
1728
2532
  "aria-label": "Collapse assist",
1729
2533
  onClick: onCollapse,
1730
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MinimizeIcon, {})
2534
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinimizeIcon, {})
1731
2535
  }
1732
- ) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2536
+ ) : onClose ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1733
2537
  "button",
1734
2538
  {
1735
2539
  type: "button",
1736
2540
  className: "agent-rail__close",
1737
2541
  "aria-label": "Close agent",
1738
2542
  onClick: onClose,
1739
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CloseIcon, {})
2543
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon, {})
1740
2544
  }
1741
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1742
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__identity", children: [
1743
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: [
1744
- brandLabel.slice(0, 1).toUpperCase(),
1745
- brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1746
- "img",
1747
- {
1748
- className: "agent-rail__brand-logo",
1749
- src: brandLogoUrl,
1750
- alt: "",
1751
- onError: (event) => {
1752
- event.currentTarget.hidden = true;
1753
- }
2545
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
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);
1754
2555
  }
1755
- ) : null
1756
- ] }),
1757
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "agent-rail__brand-label", children: brandLabel })
1758
- ] }),
1759
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__actions", children: [
1760
- onReset ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2556
+ }
2557
+ ) }) : null,
2558
+ resolvedBrandLabel ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
2559
+ ] }) : null,
2560
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "agent-rail__actions", children: [
2561
+ onReset ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1761
2562
  "button",
1762
2563
  {
1763
2564
  type: "button",
@@ -1765,80 +2566,86 @@ function AgentRail({
1765
2566
  "aria-label": "Start a new conversation",
1766
2567
  disabled: !hasVisitorMessages2,
1767
2568
  onClick: onReset,
1768
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NewChatIcon, {})
2569
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NewChatIcon, {})
1769
2570
  }
1770
2571
  ) : null,
1771
- onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2572
+ onExpandToggle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1772
2573
  "button",
1773
2574
  {
1774
2575
  type: "button",
1775
2576
  className: "agent-rail__expand",
1776
- "aria-label": expanded ? "Exit focus view" : "Open focus view",
2577
+ "aria-label": expanded ? "Exit full screen" : "Open full screen",
1777
2578
  onClick: onExpandToggle,
1778
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ExpandIcon, {})
2579
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RestoreIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExpandIcon, {})
1779
2580
  }
1780
2581
  ) : null
1781
2582
  ] })
1782
2583
  ] }) }),
1783
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__thread", children: [
1784
- !hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1785
- "section",
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: [
2585
+ !hasVisitorMessages2 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
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,
2594
+ showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2595
+ FollowUpChips,
2596
+ {
2597
+ suggestions: state.followUps,
2598
+ disabled: isBusy,
2599
+ label: "Start here",
2600
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
2601
+ }
2602
+ ) }) : null
2603
+ ] }) : null,
2604
+ transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2605
+ MessageBubble,
1786
2606
  {
1787
- className: "agent-rail__welcome",
1788
- "aria-labelledby": welcomeTitleId,
1789
- children: [
1790
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "agent-rail__welcome-mark", "aria-hidden": "true", children: [
1791
- brandLabel.slice(0, 1).toUpperCase(),
1792
- brandLogoUrl ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1793
- "img",
1794
- {
1795
- className: "agent-rail__welcome-logo",
1796
- src: brandLogoUrl,
1797
- alt: "",
1798
- onError: (event) => {
1799
- event.currentTarget.hidden = true;
1800
- }
1801
- }
1802
- ) : null
1803
- ] }),
1804
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__welcome-copy", children: [
1805
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { id: welcomeTitleId, children: "What can I help you find?" }),
1806
- greeting?.role === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: greeting.text }) : null
1807
- ] }),
1808
- showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1809
- FollowUpChips,
1810
- {
1811
- suggestions: state.followUps,
1812
- disabled: isBusy,
1813
- label: "Start here",
1814
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1815
- }
1816
- ) }) : null
1817
- ]
1818
- }
1819
- ) : null,
1820
- transcriptMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message }, message.id)),
1821
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2607
+ message,
2608
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2609
+ onBook
2610
+ },
2611
+ message.id
2612
+ )),
2613
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1822
2614
  AgentActivityBubble,
1823
2615
  {
1824
- brandLabel,
1825
- brandLogoUrl,
2616
+ brandLabel: resolvedBrandLabel,
2617
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
1826
2618
  failed: state.phase === "error",
1827
2619
  steps: state.toolSteps
1828
2620
  }
1829
2621
  ) : null,
1830
- streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: streamingMessage }) : null,
1831
- completedAnswer ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MessageBubble, { message: completedAnswer }) : null,
1832
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
1833
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
1834
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "Something went wrong" }),
1835
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: state.error })
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,
2630
+ streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2631
+ MessageBubble,
2632
+ {
2633
+ message: streamingMessage,
2634
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2635
+ offer: state.pendingOffer,
2636
+ onBook
2637
+ }
2638
+ ) : null,
2639
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "agent-rail__error", role: "alert", children: [
2640
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
2641
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "Something went wrong" }),
2642
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: state.error })
1836
2643
  ] }),
1837
- onRetry ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
2644
+ onRetry ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
1838
2645
  ] }) : null
1839
2646
  ] }) }),
1840
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
1841
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2647
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__composer-wrap", children: [
2648
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1842
2649
  Composer,
1843
2650
  {
1844
2651
  variant: expanded || mobileFullscreen ? "dock" : "default",
@@ -1847,10 +2654,9 @@ function AgentRail({
1847
2654
  onSubmit
1848
2655
  }
1849
2656
  ),
1850
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
1851
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "AI can make mistakes." }),
1852
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", children: " \xB7 " }),
1853
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: poweredByLabel })
2657
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__footer", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
2658
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "AI can make mistakes. Check important info." }),
2659
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: poweredByLabel })
1854
2660
  ] }) })
1855
2661
  ] })
1856
2662
  ]
@@ -1859,9 +2665,9 @@ function AgentRail({
1859
2665
  }
1860
2666
 
1861
2667
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
1862
- var import_jsx_runtime6 = require("react/jsx-runtime");
2668
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1863
2669
  function SparklesIcon() {
1864
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2670
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1865
2671
  "svg",
1866
2672
  {
1867
2673
  className: "assist-edge-tab__sparkles",
@@ -1869,21 +2675,21 @@ function SparklesIcon() {
1869
2675
  fill: "none",
1870
2676
  "aria-hidden": "true",
1871
2677
  children: [
1872
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2678
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1873
2679
  "path",
1874
2680
  {
1875
2681
  d: "M8 1.2l.95 2.7 2.85.05-2.25 1.75.8 2.75L8 6.7 5.65 8.45l.8-2.75L4.2 3.95l2.85-.05L8 1.2z",
1876
2682
  fill: "currentColor"
1877
2683
  }
1878
2684
  ),
1879
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2685
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1880
2686
  "path",
1881
2687
  {
1882
2688
  d: "M14.2 6.4l.55 1.55 1.65.03-1.3 1 .46 1.58-1.36-1-1.36 1 .46-1.58-1.3-1 1.65-.03.55-1.55z",
1883
2689
  fill: "currentColor"
1884
2690
  }
1885
2691
  ),
1886
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2692
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1887
2693
  "path",
1888
2694
  {
1889
2695
  d: "M3.1 9.1l.4 1.15 1.22.02-.96.74.34 1.17-1-.74-1 .74.34-1.17-.96-.74 1.22-.02.4-1.15z",
@@ -1894,8 +2700,23 @@ function SparklesIcon() {
1894
2700
  }
1895
2701
  );
1896
2702
  }
2703
+ function TabMarkIcon({ customIconUrl }) {
2704
+ const url = customIconUrl?.trim();
2705
+ if (url) {
2706
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2707
+ "img",
2708
+ {
2709
+ alt: "",
2710
+ "aria-hidden": true,
2711
+ className: "assist-edge-tab__custom-icon",
2712
+ src: url
2713
+ }
2714
+ );
2715
+ }
2716
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SparklesIcon, {});
2717
+ }
1897
2718
  function ChevronLeftIcon() {
1898
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2719
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1899
2720
  "path",
1900
2721
  {
1901
2722
  d: "M10 4L6 8l4 4",
@@ -1907,7 +2728,7 @@ function ChevronLeftIcon() {
1907
2728
  ) });
1908
2729
  }
1909
2730
  function ChevronDownIcon() {
1910
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2731
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1911
2732
  "path",
1912
2733
  {
1913
2734
  d: "M4 6l4 4 4-4",
@@ -1919,12 +2740,12 @@ function ChevronDownIcon() {
1919
2740
  ) });
1920
2741
  }
1921
2742
  function DragDots() {
1922
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("i", {}, index)) });
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)) });
1923
2744
  }
1924
2745
  var VARIANT_COPY = {
1925
- outline: { label: "Assist", aria: "Open Assist" },
2746
+ outline: { label: "Ask anything", aria: "Ask anything" },
1926
2747
  ask: { label: "Ask anything", aria: "Ask anything" },
1927
- fill: { label: "Assist", aria: "Open Assist" }
2748
+ fill: { label: "Ask anything", aria: "Ask anything" }
1928
2749
  };
1929
2750
  function AssistEdgeTab({
1930
2751
  variant,
@@ -1933,6 +2754,7 @@ function AssistEdgeTab({
1933
2754
  inset,
1934
2755
  visible,
1935
2756
  label,
2757
+ customIconUrl,
1936
2758
  logoUrl,
1937
2759
  brandColor,
1938
2760
  brandForeground,
@@ -1941,40 +2763,49 @@ function AssistEdgeTab({
1941
2763
  mobile = false,
1942
2764
  surfaceColor,
1943
2765
  textColor,
2766
+ colorScheme = "auto",
1944
2767
  onOpen
1945
2768
  }) {
2769
+ const resolvedColorScheme = useAgentColorScheme(colorScheme);
1946
2770
  const copy = VARIANT_COPY[variant];
1947
2771
  const visibleLabel = label?.trim() || copy.label;
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;
1948
2777
  const style = {
1949
2778
  "--tab-along": `${along}%`,
1950
2779
  "--tab-inset": `${inset}px`,
1951
- ...brandColor ? { "--as-brand": brandColor } : {},
2780
+ ...resolvedBrandColor ? { "--as-brand": resolvedBrandColor } : {},
1952
2781
  ...brandForeground ? { "--as-visitor-text": brandForeground } : {},
1953
- ...borderColor ? { "--as-border": borderColor } : {},
2782
+ ...resolvedBorderColor ? { "--as-border": resolvedBorderColor } : {},
1954
2783
  ...fontFamily ? { "--as-font-display": fontFamily } : {},
1955
- ...surfaceColor ? { "--as-surface": surfaceColor } : {},
1956
- ...textColor ? { "--as-text": textColor } : {}
2784
+ ...resolvedSurfaceColor ? { "--as-surface": resolvedSurfaceColor } : {},
2785
+ ...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
2786
+ colorScheme: resolvedColorScheme
1957
2787
  };
1958
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2788
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1959
2789
  "button",
1960
2790
  {
1961
2791
  type: "button",
1962
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,
1963
2794
  style,
1964
2795
  "aria-label": `Open ${visibleLabel}`,
1965
2796
  "aria-hidden": !visible,
1966
2797
  tabIndex: visible ? 0 : -1,
1967
2798
  onClick: onOpen,
1968
2799
  children: [
1969
- mobile ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1970
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2800
+ mobile ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2801
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1971
2802
  "span",
1972
2803
  {
1973
2804
  className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
1974
2805
  "aria-hidden": "true",
1975
2806
  children: [
1976
- visibleLabel.slice(0, 1).toUpperCase(),
1977
- logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2807
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
2808
+ showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1978
2809
  "img",
1979
2810
  {
1980
2811
  className: "assist-edge-tab__logo",
@@ -1988,14 +2819,11 @@ function AssistEdgeTab({
1988
2819
  ]
1989
2820
  }
1990
2821
  ),
1991
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__label", children: [
1992
- "Ask ",
1993
- visibleLabel
1994
- ] })
1995
- ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1996
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1997
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
1998
- logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2822
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel })
2823
+ ] }) : variant === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2824
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
2825
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
2826
+ showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1999
2827
  "img",
2000
2828
  {
2001
2829
  className: "assist-edge-tab__logo",
@@ -2007,18 +2835,18 @@ function AssistEdgeTab({
2007
2835
  }
2008
2836
  ) : null
2009
2837
  ] }),
2010
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2011
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronDownIcon, {})
2838
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2839
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronDownIcon, {})
2012
2840
  ] }) : null,
2013
- variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2014
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronLeftIcon, {}),
2015
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2016
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DragDots, {})
2841
+ variant === "ask" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2842
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {}),
2843
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2844
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DragDots, {})
2017
2845
  ] }) : null,
2018
- variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2019
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
2020
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparklesIcon, {}),
2021
- logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2846
+ variant === "fill" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2847
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
2848
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabMarkIcon, { customIconUrl }),
2849
+ showLogo ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2022
2850
  "img",
2023
2851
  {
2024
2852
  className: "assist-edge-tab__logo",
@@ -2030,8 +2858,8 @@ function AssistEdgeTab({
2030
2858
  }
2031
2859
  ) : null
2032
2860
  ] }),
2033
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2034
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronLeftIcon, {})
2861
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2862
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronLeftIcon, {})
2035
2863
  ] }) : null
2036
2864
  ]
2037
2865
  }
@@ -2039,7 +2867,7 @@ function AssistEdgeTab({
2039
2867
  }
2040
2868
 
2041
2869
  // src/react/components/AgentWidget/AgentWidget.tsx
2042
- var import_jsx_runtime7 = require("react/jsx-runtime");
2870
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2043
2871
  function AgentWidget({
2044
2872
  indexId,
2045
2873
  customerId,
@@ -2055,9 +2883,9 @@ function AgentWidget({
2055
2883
  }) {
2056
2884
  const isMobile = useIsMobile();
2057
2885
  const placement = normalizeAgentPlacement(placementInput);
2058
- const railSlotRef = (0, import_react6.useRef)(null);
2059
- const [railCollapsed, setRailCollapsed] = (0, import_react6.useState)(defaultCollapsed);
2060
- const [railExpanded, setRailExpanded] = (0, import_react6.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);
2061
2889
  const pageShiftActive = shouldApplyPageShift({
2062
2890
  pageShift,
2063
2891
  isMobile,
@@ -2077,7 +2905,8 @@ function AgentWidget({
2077
2905
  runtimeOrigin,
2078
2906
  greeting: branding?.greeting
2079
2907
  });
2080
- const agentName = branding?.agentName ?? "Webless Guide";
2908
+ const agentName = branding?.agentName ?? "";
2909
+ const tabLabel = branding?.tabLabel ?? agentName;
2081
2910
  const theme = {
2082
2911
  ...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
2083
2912
  ...branding?.colors?.primary ? {
@@ -2095,8 +2924,7 @@ function AgentWidget({
2095
2924
  } : {},
2096
2925
  ...branding?.colors?.border ? { border: branding.colors.border } : {}
2097
2926
  };
2098
- const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);
2099
- (0, import_react6.useEffect)(() => {
2927
+ (0, import_react10.useEffect)(() => {
2100
2928
  if (!registerPanelController) return;
2101
2929
  registerAgentPanelController(customerId, {
2102
2930
  open: () => setRailCollapsed(false),
@@ -2111,7 +2939,7 @@ function AgentWidget({
2111
2939
  if (isMobile) setRailCollapsed(false);
2112
2940
  await submit(message);
2113
2941
  }
2114
- (0, import_react6.useEffect)(() => {
2942
+ (0, import_react10.useEffect)(() => {
2115
2943
  if (railCollapsed) return;
2116
2944
  const handleKeyDown = (event) => {
2117
2945
  if (event.key === "Tab" && (isMobile || railExpanded)) {
@@ -2145,64 +2973,44 @@ function AgentWidget({
2145
2973
  window.addEventListener("keydown", handleKeyDown);
2146
2974
  return () => window.removeEventListener("keydown", handleKeyDown);
2147
2975
  }, [isMobile, railCollapsed, railExpanded]);
2148
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "webless-agent-root", children: [
2149
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2976
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "webless-agent-root", children: [
2977
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2150
2978
  "div",
2151
2979
  {
2152
2980
  className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
2153
- children: [
2154
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2155
- "div",
2156
- {
2157
- ref: railSlotRef,
2158
- className: "webless-agent-root__rail-slot",
2159
- inert: railCollapsed || void 0,
2160
- "aria-hidden": railCollapsed,
2161
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2162
- AgentRail,
2163
- {
2164
- theme,
2165
- brandLabel: agentName,
2166
- brandLogoUrl: branding?.logoUrl,
2167
- composerPlaceholder: branding?.composerPlaceholder ?? `Ask ${agentName}\u2026`,
2168
- poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
2169
- state: idle ? {
2170
- ...state,
2171
- followUps: createIdleSuggestions()
2172
- } : state,
2173
- mobileFullscreen: isMobile && !railCollapsed,
2174
- expanded: railExpanded,
2175
- onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
2176
- onClose: isMobile ? () => setRailCollapsed(true) : void 0,
2177
- onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
2178
- onSubmit: handleSubmit,
2179
- onReset: reset,
2180
- onRetry: () => void retry(),
2181
- onFollowUpSelect: (label) => void handleSubmit(label)
2182
- }
2183
- )
2184
- }
2185
- ),
2186
- !railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2187
- "button",
2188
- {
2189
- type: "button",
2190
- className: `webless-agent-root__backdrop${isMobile ? " webless-agent-root__backdrop--mobile" : ""}`,
2191
- tabIndex: -1,
2192
- "aria-label": isMobile ? "Close agent" : "Exit focus view",
2193
- onClick: () => {
2194
- if (isMobile) {
2195
- setRailCollapsed(true);
2196
- } else {
2197
- setRailExpanded(false);
2198
- }
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 })
2199
3007
  }
2200
- }
2201
- ) : null
2202
- ]
3008
+ )
3009
+ }
3010
+ )
2203
3011
  }
2204
3012
  ),
2205
- railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3013
+ railCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2206
3014
  AssistEdgeTab,
2207
3015
  {
2208
3016
  variant: placement.variant,
@@ -2210,7 +3018,8 @@ function AgentWidget({
2210
3018
  along: placement.along,
2211
3019
  inset: placement.inset,
2212
3020
  visible: true,
2213
- label: agentName,
3021
+ label: tabLabel,
3022
+ customIconUrl: branding?.tabIconUrl,
2214
3023
  logoUrl: branding?.logoUrl,
2215
3024
  brandColor: branding?.colors?.primary,
2216
3025
  brandForeground: branding?.colors?.primaryForeground,
@@ -2232,6 +3041,7 @@ function AgentWidget({
2232
3041
  DEFAULT_AGENT_PLACEMENT,
2233
3042
  createIdleSuggestions,
2234
3043
  defaultAgentRailTheme,
3044
+ defaultDarkAgentRailTheme,
2235
3045
  hasVisitorMessages,
2236
3046
  isAgentBusy,
2237
3047
  normalizeAgentPlacement,