@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.
@@ -188,6 +188,9 @@ function runtimeSessionIdKey(visitorSessionId, prefix) {
188
188
  function runtimeStreamIndexKey(visitorSessionId, prefix) {
189
189
  return `${prefix}:eve:${visitorSessionId}:streamIndex`;
190
190
  }
191
+ function runtimeLastMessageKey(visitorSessionId, prefix) {
192
+ return `${prefix}:eve:${visitorSessionId}:lastMessage`;
193
+ }
191
194
  function loadPersistedAgentSession(visitorSessionId, options) {
192
195
  if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
193
196
  const prefix = resolvePrefix(options);
@@ -195,9 +198,11 @@ function loadPersistedAgentSession(visitorSessionId, options) {
195
198
  if (!sessionId) return null;
196
199
  const rawIndex = sessionStorage.getItem(runtimeStreamIndexKey(visitorSessionId, prefix));
197
200
  const streamIndex = rawIndex ? Number.parseInt(rawIndex, 10) : 0;
201
+ const lastMessage = sessionStorage.getItem(runtimeLastMessageKey(visitorSessionId, prefix))?.trim();
198
202
  return {
199
203
  sessionId,
200
- streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0
204
+ streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0,
205
+ ...lastMessage ? { lastMessage } : {}
201
206
  };
202
207
  }
203
208
  function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, options) {
@@ -211,11 +216,19 @@ function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, opt
211
216
  String(Math.max(0, streamIndex))
212
217
  );
213
218
  }
219
+ function savePersistedAgentTurnMessage(visitorSessionId, message, options) {
220
+ if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !message) {
221
+ return;
222
+ }
223
+ const prefix = resolvePrefix(options);
224
+ sessionStorage.setItem(runtimeLastMessageKey(visitorSessionId, prefix), message);
225
+ }
214
226
  function clearPersistedAgentSession(visitorSessionId, options) {
215
227
  if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return;
216
228
  const prefix = resolvePrefix(options);
217
229
  sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
218
230
  sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
231
+ sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
219
232
  }
220
233
 
221
234
  // src/runtime/client.ts
@@ -230,6 +243,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
230
243
  if (event.type === "message.completed") {
231
244
  handlers.onComplete?.();
232
245
  }
246
+ if (event.type === "action.result") {
247
+ const result = event.data.result;
248
+ if (result && typeof result === "object" && "output" in result) {
249
+ handlers.onActionResult?.(result.output);
250
+ }
251
+ }
233
252
  if (event.type !== "message.appended") return rendered;
234
253
  const { messageDelta, messageSoFar } = event.data;
235
254
  let delta = messageDelta;
@@ -243,6 +262,12 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
243
262
  if (delta) handlers.onDelta(delta);
244
263
  return next;
245
264
  }
265
+ function isResumeTurnMessage(received, candidate) {
266
+ if (received === candidate) return true;
267
+ return Boolean(candidate) && received.endsWith(`
268
+
269
+ ${candidate}`);
270
+ }
246
271
  function latestTurnEvents(events) {
247
272
  let startIndex = -1;
248
273
  for (let index = events.length - 1; index >= 0; index -= 1) {
@@ -474,6 +499,11 @@ var AgentSession = class {
474
499
  this.session = session;
475
500
  try {
476
501
  const activeSession = session;
502
+ savePersistedAgentTurnMessage(
503
+ this.visitorSessionId,
504
+ message,
505
+ this.storeOptions
506
+ );
477
507
  response = await withCapabilityRefresh(
478
508
  this.capability,
479
509
  () => activeSession.send(message, { signal })
@@ -489,6 +519,11 @@ var AgentSession = class {
489
519
  }
490
520
  }
491
521
  if (!response) {
522
+ savePersistedAgentTurnMessage(
523
+ this.visitorSessionId,
524
+ message,
525
+ this.storeOptions
526
+ );
492
527
  const created = await withCapabilityRefresh(
493
528
  this.capability,
494
529
  () => client.sessions.create({ message, signal })
@@ -543,7 +578,9 @@ var AgentSession = class {
543
578
  );
544
579
  const turnEvents = latestTurnEvents(snapshot.events);
545
580
  const received = turnEvents[0];
546
- if (received?.type !== "message.received" || received.data.message !== message) {
581
+ const lastSent = persisted.lastMessage;
582
+ const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
583
+ if (received?.type !== "message.received" || !(isResumeTurnMessage(received.data.message, message) || inFlight && lastSent !== void 0 && received.data.message === lastSent)) {
547
584
  return null;
548
585
  }
549
586
  let rendered = renderTurn(turnEvents);
@@ -707,8 +744,248 @@ function formatAgentError(error) {
707
744
  return TRANSIENT_AGENT_ERROR_MESSAGE;
708
745
  }
709
746
 
747
+ // src/react/lib/tool-card.ts
748
+ function bookingOfferIdentityKey(offer) {
749
+ const eventTypes = offer.eventTypes.map(
750
+ (item) => `${item.uri}:${item.duration ?? ""}:${item.locationKind ?? ""}:${item.location ?? ""}`
751
+ ).join("|");
752
+ const slots = offer.slots.map((slot) => `${slot.eventTypeUri ?? ""}:${slot.startTime}`).join("|");
753
+ return `${eventTypes}::${slots}` || "offer";
754
+ }
755
+ var FENCE_PATTERN = /```(?:webless-tool-card|json)\s*([\s\S]*?)```/gi;
756
+ function asRecord(value) {
757
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
758
+ }
759
+ function asString(value) {
760
+ return typeof value === "string" ? value.trim() : "";
761
+ }
762
+ function isEventUri(value) {
763
+ return /^https:\/\/api\.calendly\.com\/scheduled_events\/[^/]+$/i.test(value);
764
+ }
765
+ function isEventTypeUri(value) {
766
+ return /^https:\/\/api\.calendly\.com\/event_types\/[^/]+$/i.test(value);
767
+ }
768
+ function parseToolCard(value) {
769
+ const record = asRecord(value);
770
+ if (!record) return null;
771
+ if (record.booking_offer && asString(record.type) !== "booking_offer") {
772
+ const nested = parseToolCard(record.booking_offer);
773
+ if (nested) return nested;
774
+ }
775
+ const type = asString(record.type);
776
+ if (type === "booking_offer") {
777
+ const eventTypes = Array.isArray(record.eventTypes) ? record.eventTypes.flatMap((item) => {
778
+ const entry = asRecord(item);
779
+ const uri = asString(entry?.uri);
780
+ if (!entry || !isEventTypeUri(uri)) return [];
781
+ const duration = entry.duration;
782
+ const locationKind = asString(entry.locationKind);
783
+ const location = asString(entry.location);
784
+ return [
785
+ {
786
+ name: asString(entry.name) || "Meeting",
787
+ uri,
788
+ ...typeof duration === "number" ? { duration } : {},
789
+ ...locationKind ? { locationKind } : {},
790
+ ...location ? { location } : {}
791
+ }
792
+ ];
793
+ }) : [];
794
+ const slots = Array.isArray(record.slots) ? record.slots.flatMap((item) => {
795
+ const entry = asRecord(item);
796
+ const startTime = asString(entry?.startTime);
797
+ if (!entry || !startTime) return [];
798
+ const eventTypeUri = asString(entry.eventTypeUri);
799
+ return [
800
+ {
801
+ startTime,
802
+ ...isEventTypeUri(eventTypeUri) ? { eventTypeUri } : {}
803
+ }
804
+ ];
805
+ }) : [];
806
+ if (slots.length === 0) return null;
807
+ return { type: "booking_offer", eventTypes, slots };
808
+ }
809
+ if (type === "booking_confirmed") {
810
+ const eventUri = asString(record.eventUri);
811
+ if (!isEventUri(eventUri)) return null;
812
+ const inviteeUri = asString(record.inviteeUri);
813
+ const inviteeEmail = asString(record.inviteeEmail);
814
+ const startTime = asString(record.startTime);
815
+ return {
816
+ type: "booking_confirmed",
817
+ eventUri,
818
+ ...inviteeUri ? { inviteeUri } : {},
819
+ ...inviteeEmail ? { inviteeEmail } : {},
820
+ ...startTime ? { startTime } : {}
821
+ };
822
+ }
823
+ if (type === "booking_canceled") {
824
+ const eventUri = asString(record.eventUri);
825
+ if (!isEventUri(eventUri)) return null;
826
+ return { type: "booking_canceled", eventUri };
827
+ }
828
+ return null;
829
+ }
830
+ function formatBookingOfferFence(offer) {
831
+ return [
832
+ "```webless-tool-card",
833
+ JSON.stringify({
834
+ type: "booking_offer",
835
+ eventTypes: offer.eventTypes,
836
+ slots: offer.slots
837
+ }),
838
+ "```"
839
+ ].join("\n");
840
+ }
841
+ function bookingOfferFromActionOutput(output) {
842
+ const record = asRecord(output);
843
+ const data = asRecord(record?.data) ?? record;
844
+ const card = parseToolCard(data);
845
+ return card?.type === "booking_offer" ? card : null;
846
+ }
847
+ function ensureBookingOfferText(text, offer) {
848
+ if (!offer) return text;
849
+ if (extractToolCards(text).some((card) => card.type === "booking_offer")) {
850
+ return text;
851
+ }
852
+ const visible = stripToolCards(text).trim() || text.trim();
853
+ return `${visible}
854
+
855
+ ${formatBookingOfferFence(offer)}`;
856
+ }
857
+ function hideToolCardFences(text) {
858
+ 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();
859
+ }
860
+ function visitorTimeZone() {
861
+ try {
862
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
863
+ } catch {
864
+ return "UTC";
865
+ }
866
+ }
867
+ function extractToolCards(text) {
868
+ const cards = [];
869
+ for (const match of text.matchAll(FENCE_PATTERN)) {
870
+ try {
871
+ const card = parseToolCard(JSON.parse(match[1] ?? ""));
872
+ if (card) cards.push(card);
873
+ } catch {
874
+ }
875
+ }
876
+ return cards;
877
+ }
878
+ function stripToolCards(text) {
879
+ return text.replace(FENCE_PATTERN, "").replace(/\n{3,}/g, "\n\n").trim();
880
+ }
881
+ function localDateKey(date) {
882
+ if (Number.isNaN(date.getTime())) return "";
883
+ return [
884
+ date.getFullYear(),
885
+ String(date.getMonth() + 1).padStart(2, "0"),
886
+ String(date.getDate()).padStart(2, "0")
887
+ ].join("-");
888
+ }
889
+ function slotDateKey(startTime) {
890
+ return localDateKey(new Date(startTime)) || startTime;
891
+ }
892
+ function bookingSlotsForEventType(slots, eventTypeUri) {
893
+ return slots.filter(
894
+ (slot) => !eventTypeUri || !slot.eventTypeUri || slot.eventTypeUri === eventTypeUri
895
+ );
896
+ }
897
+ function firstAvailableBookingMonth(slots) {
898
+ let earliest;
899
+ for (const slot of slots) {
900
+ const key = slotDateKey(slot.startTime);
901
+ if (!earliest || key < earliest) earliest = key;
902
+ }
903
+ const [year, month] = (earliest ?? slotDateKey((/* @__PURE__ */ new Date()).toISOString())).split("-").map(Number);
904
+ if (!year || !month) {
905
+ const now = /* @__PURE__ */ new Date();
906
+ return { year: now.getFullYear(), month: now.getMonth() };
907
+ }
908
+ return { year, month: month - 1 };
909
+ }
910
+ function formatMonthTitle(year, month) {
911
+ return new Intl.DateTimeFormat(void 0, {
912
+ month: "long",
913
+ year: "numeric"
914
+ }).format(new Date(year, month, 1));
915
+ }
916
+ function formatLongDate(startTime) {
917
+ const date = new Date(startTime);
918
+ if (Number.isNaN(date.getTime())) return startTime;
919
+ return new Intl.DateTimeFormat(void 0, {
920
+ weekday: "long",
921
+ month: "long",
922
+ day: "numeric"
923
+ }).format(date);
924
+ }
925
+ function weekdayLabels() {
926
+ return Array.from(
927
+ { length: 7 },
928
+ (_, index) => new Intl.DateTimeFormat(void 0, { weekday: "short" }).format(
929
+ new Date(2026, 7, 3 + index)
930
+ )
931
+ );
932
+ }
933
+ function formatTimeChip(startTime) {
934
+ const date = new Date(startTime);
935
+ if (Number.isNaN(date.getTime())) return startTime;
936
+ return new Intl.DateTimeFormat(void 0, {
937
+ hour: "numeric",
938
+ minute: "2-digit"
939
+ }).format(date);
940
+ }
941
+ function formatSlotTimeZone(startTime) {
942
+ const date = new Date(startTime);
943
+ if (Number.isNaN(date.getTime())) return "";
944
+ return new Intl.DateTimeFormat(void 0, { timeZoneName: "short" }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value ?? "";
945
+ }
946
+ function formatSlotLabel(startTime) {
947
+ const date = new Date(startTime);
948
+ if (Number.isNaN(date.getTime())) return startTime;
949
+ return new Intl.DateTimeFormat(void 0, {
950
+ weekday: "short",
951
+ month: "short",
952
+ day: "numeric",
953
+ hour: "numeric",
954
+ minute: "2-digit",
955
+ timeZoneName: "short"
956
+ }).format(date);
957
+ }
958
+ function formatBookingRequest(input) {
959
+ return [
960
+ "Book this meeting now with CALENDLY_POST_INVITEE.",
961
+ "Do not open a Calendly URL and do not list other scheduled events.",
962
+ "Do not invent a location kind. Use only the location fields below.",
963
+ `event_type: ${input.eventTypeUri}`,
964
+ `start_time: ${input.startTime}`,
965
+ `invitee.name: ${input.inviteeName}`,
966
+ `invitee.email: ${input.inviteeEmail}`,
967
+ `invitee.timezone: ${input.timezone}`,
968
+ ...input.locationKind ? [
969
+ `location.kind: ${input.locationKind}`,
970
+ ...input.location ? [`location.location: ${input.location}`] : []
971
+ ] : ["Do not send a location field."],
972
+ "After it succeeds, reply with one short confirmation and a webless-tool-card booking_confirmed block using visitor_booking."
973
+ ].join("\n");
974
+ }
975
+ function visitorBookingPrefix(booking) {
976
+ return [
977
+ "This visitor already booked a meeting. Use only this meeting:",
978
+ `- scheduled event URI: ${booking.eventUri}`,
979
+ ...booking.inviteeUri ? [`- invitee URI: ${booking.inviteeUri}`] : [],
980
+ ...booking.inviteeEmail ? [`- invitee email: ${booking.inviteeEmail}`] : [],
981
+ "For details call CALENDLY_GET_EVENT or CALENDLY_GET_EVENT_INVITEE with those URIs.",
982
+ "If you must list events, pass this invitee_email. Never describe any other scheduled event.",
983
+ "start_time values from Calendly are UTC."
984
+ ].join("\n");
985
+ }
986
+
710
987
  // src/react/persisted-conversation.ts
711
- var CONVERSATION_VERSION = 1;
988
+ var CONVERSATION_VERSION = 2;
712
989
  function conversationKey(storageKeyPrefix, visitorSessionId) {
713
990
  return `${storageKeyPrefix}:conversation:${visitorSessionId}`;
714
991
  }
@@ -718,13 +995,39 @@ function parseMessage(value) {
718
995
  if (typeof record.id !== "string" || record.role !== "agent" && record.role !== "visitor" || typeof record.text !== "string" || typeof record.createdAt !== "number" || !Number.isFinite(record.createdAt)) {
719
996
  return null;
720
997
  }
998
+ if (record.role === "visitor") {
999
+ return {
1000
+ id: record.id,
1001
+ role: "visitor",
1002
+ text: record.text,
1003
+ createdAt: record.createdAt,
1004
+ ...typeof record.runtimeText === "string" && record.runtimeText ? { runtimeText: record.runtimeText } : {}
1005
+ };
1006
+ }
721
1007
  return {
722
1008
  id: record.id,
723
- role: record.role,
1009
+ role: "agent",
724
1010
  text: record.text,
725
1011
  createdAt: record.createdAt
726
1012
  };
727
1013
  }
1014
+ function parseToolStep(value) {
1015
+ if (typeof value !== "object" || value === null) return null;
1016
+ const record = value;
1017
+ 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") {
1018
+ return null;
1019
+ }
1020
+ return {
1021
+ id: record.id,
1022
+ kind: record.kind,
1023
+ label: record.label,
1024
+ state: record.state,
1025
+ ...typeof record.detail === "string" && record.detail ? { detail: record.detail } : {}
1026
+ };
1027
+ }
1028
+ function visitorTurnText(message) {
1029
+ return message.role === "visitor" && message.runtimeText ? message.runtimeText : message.text;
1030
+ }
728
1031
  function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
729
1032
  if (typeof sessionStorage === "undefined") return null;
730
1033
  const raw = sessionStorage.getItem(conversationKey(storageKeyPrefix, visitorSessionId));
@@ -733,15 +1036,19 @@ function loadPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
733
1036
  const value = JSON.parse(raw);
734
1037
  if (typeof value !== "object" || value === null) return null;
735
1038
  const record = value;
736
- if (record.version !== CONVERSATION_VERSION || !Array.isArray(record.messages) || typeof record.pending !== "boolean" || typeof record.streamingText !== "string") {
1039
+ 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)) {
737
1040
  return null;
738
1041
  }
739
1042
  const messages = record.messages.map(parseMessage);
740
1043
  if (messages.some((message) => message === null)) return null;
1044
+ const storedToolSteps = record.version === CONVERSATION_VERSION && Array.isArray(record.toolSteps) ? record.toolSteps : [];
1045
+ const toolSteps = storedToolSteps.map(parseToolStep);
1046
+ if (toolSteps.some((step) => step === null)) return null;
741
1047
  return {
742
1048
  messages: messages.filter((message) => message !== null),
743
1049
  pending: record.pending,
744
- streamingText: record.streamingText
1050
+ streamingText: record.streamingText,
1051
+ toolSteps: toolSteps.filter((step) => step !== null)
745
1052
  };
746
1053
  } catch {
747
1054
  return null;
@@ -757,6 +1064,42 @@ function savePersistedAgentConversation(storageKeyPrefix, visitorSessionId, conv
757
1064
  function clearPersistedAgentConversation(storageKeyPrefix, visitorSessionId) {
758
1065
  if (typeof sessionStorage === "undefined") return;
759
1066
  sessionStorage.removeItem(conversationKey(storageKeyPrefix, visitorSessionId));
1067
+ clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId);
1068
+ }
1069
+ function pendingBookingKey(storageKeyPrefix, visitorSessionId) {
1070
+ return `${storageKeyPrefix}:pending-booking:${visitorSessionId}`;
1071
+ }
1072
+ function loadPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
1073
+ if (typeof sessionStorage === "undefined") return null;
1074
+ const raw = sessionStorage.getItem(
1075
+ pendingBookingKey(storageKeyPrefix, visitorSessionId)
1076
+ );
1077
+ if (!raw) return null;
1078
+ try {
1079
+ const value = JSON.parse(raw);
1080
+ if (typeof value !== "object" || value === null) return null;
1081
+ const record = value;
1082
+ if (typeof record.eventUri !== "string" || !record.eventUri) return null;
1083
+ return {
1084
+ eventUri: record.eventUri,
1085
+ ...typeof record.inviteeUri === "string" && record.inviteeUri ? { inviteeUri: record.inviteeUri } : {},
1086
+ ...typeof record.inviteeEmail === "string" && record.inviteeEmail ? { inviteeEmail: record.inviteeEmail } : {},
1087
+ ...typeof record.startTime === "string" && record.startTime ? { startTime: record.startTime } : {}
1088
+ };
1089
+ } catch {
1090
+ return null;
1091
+ }
1092
+ }
1093
+ function savePendingWidgetBooking(storageKeyPrefix, visitorSessionId, booking) {
1094
+ if (typeof sessionStorage === "undefined") return;
1095
+ sessionStorage.setItem(
1096
+ pendingBookingKey(storageKeyPrefix, visitorSessionId),
1097
+ JSON.stringify(booking)
1098
+ );
1099
+ }
1100
+ function clearPendingWidgetBooking(storageKeyPrefix, visitorSessionId) {
1101
+ if (typeof sessionStorage === "undefined") return;
1102
+ sessionStorage.removeItem(pendingBookingKey(storageKeyPrefix, visitorSessionId));
760
1103
  }
761
1104
 
762
1105
  // src/react/hooks/useAgentChat.ts
@@ -776,6 +1119,7 @@ function createInitialState(greeting = DEFAULT_GREETING) {
776
1119
  journey: null,
777
1120
  followUps: [],
778
1121
  streamingText: "",
1122
+ pendingOffer: null,
779
1123
  error: null
780
1124
  };
781
1125
  }
@@ -785,7 +1129,8 @@ function stateFromConversation(conversation, initialState) {
785
1129
  ...initialState,
786
1130
  messages: conversation.messages,
787
1131
  phase: conversation.pending ? conversation.streamingText ? "streaming" : "thinking" : "complete",
788
- streamingText: conversation.streamingText
1132
+ streamingText: conversation.streamingText,
1133
+ toolSteps: conversation.toolSteps
789
1134
  };
790
1135
  }
791
1136
  function upsertToolStep(steps, item) {
@@ -858,6 +1203,9 @@ function useAgentChat({
858
1203
  initialState
859
1204
  )
860
1205
  );
1206
+ const pendingBookingRef = useRef(
1207
+ loadPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId)
1208
+ );
861
1209
  const runRef = useRef(null);
862
1210
  const clientRef = useRef(
863
1211
  createAgentClient({
@@ -890,6 +1238,10 @@ function useAgentChat({
890
1238
  visitorSessionId: visitorId,
891
1239
  storageKeyPrefix: resolvedStorageKeyPrefix
892
1240
  });
1241
+ pendingBookingRef.current = loadPendingWidgetBooking(
1242
+ resolvedStorageKeyPrefix,
1243
+ visitorId
1244
+ );
893
1245
  setState(
894
1246
  stateFromConversation(
895
1247
  loadPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId),
@@ -912,19 +1264,22 @@ function useAgentChat({
912
1264
  savePersistedAgentConversation(resolvedStorageKeyPrefix, visitorId, {
913
1265
  messages: state.messages,
914
1266
  pending: isAgentBusy(state.phase),
915
- streamingText: state.streamingText
1267
+ streamingText: state.streamingText,
1268
+ toolSteps: state.toolSteps
916
1269
  });
917
1270
  }, [
918
1271
  resolvedStorageKeyPrefix,
919
1272
  state.messages,
920
1273
  state.phase,
921
1274
  state.streamingText,
1275
+ state.toolSteps,
922
1276
  visitorId
923
1277
  ]);
924
1278
  const reset = useCallback(() => {
925
1279
  runRef.current?.abort();
926
1280
  runRef.current = null;
927
1281
  clientRef.current.reset();
1282
+ pendingBookingRef.current = null;
928
1283
  clearPersistedAgentConversation(resolvedStorageKeyPrefix, visitorId);
929
1284
  setState(initialState);
930
1285
  }, [initialState, resolvedStorageKeyPrefix, visitorId]);
@@ -936,6 +1291,7 @@ function useAgentChat({
936
1291
  try {
937
1292
  let streamStarted = Boolean(initialText);
938
1293
  let streamed = initialText;
1294
+ const capturedOffers = [];
939
1295
  const handlers = {
940
1296
  onWork: (item) => {
941
1297
  if (!isActiveRun()) return;
@@ -945,6 +1301,12 @@ function useAgentChat({
945
1301
  toolSteps: upsertToolStep(prev.toolSteps, item)
946
1302
  }));
947
1303
  },
1304
+ onActionResult: (output) => {
1305
+ const offer = bookingOfferFromActionOutput(output);
1306
+ if (!offer) return;
1307
+ capturedOffers.push(offer);
1308
+ setState((prev) => ({ ...prev, pendingOffer: offer }));
1309
+ },
948
1310
  onDelta: (delta) => {
949
1311
  if (!isActiveRun()) return;
950
1312
  if (!streamStarted) {
@@ -960,7 +1322,8 @@ function useAgentChat({
960
1322
  setState((prev) => ({
961
1323
  ...prev,
962
1324
  phase: "streaming",
963
- streamingText: streamed
1325
+ streamingText: hideToolCardFences(streamed),
1326
+ pendingOffer: prev.pendingOffer ?? capturedOffers.at(-1) ?? null
964
1327
  }));
965
1328
  },
966
1329
  onComplete: () => {
@@ -981,18 +1344,34 @@ function useAgentChat({
981
1344
  });
982
1345
  }
983
1346
  if (!isActiveRun() || finalText === null) return;
1347
+ const displayText = ensureBookingOfferText(
1348
+ finalText,
1349
+ capturedOffers.at(-1) ?? null
1350
+ );
984
1351
  const agentMessage = {
985
1352
  id: `agent-${Date.now()}`,
986
1353
  role: "agent",
987
- text: finalText,
1354
+ text: displayText,
988
1355
  createdAt: Date.now()
989
1356
  };
1357
+ const parsedCards = extractToolCards(displayText);
1358
+ for (const card of parsedCards) {
1359
+ if (card.type === "booking_confirmed") {
1360
+ pendingBookingRef.current = card;
1361
+ savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, card);
1362
+ }
1363
+ if (card.type === "booking_canceled" && pendingBookingRef.current?.eventUri === card.eventUri) {
1364
+ pendingBookingRef.current = null;
1365
+ clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
1366
+ }
1367
+ }
990
1368
  setState((prev) => ({
991
1369
  ...prev,
992
1370
  phase: "complete",
993
1371
  messages: [...prev.messages, agentMessage],
994
1372
  toolSteps: completeActivePlanning(prev.toolSteps),
995
1373
  streamingText: "",
1374
+ pendingOffer: null,
996
1375
  followUps: [],
997
1376
  journey: null
998
1377
  }));
@@ -1014,26 +1393,54 @@ function useAgentChat({
1014
1393
  } : step
1015
1394
  ),
1016
1395
  streamingText: "",
1396
+ pendingOffer: null,
1017
1397
  error: message
1018
1398
  }));
1019
1399
  runRef.current = null;
1020
1400
  }
1021
1401
  },
1022
- []
1402
+ [resolvedStorageKeyPrefix, visitorId]
1403
+ );
1404
+ const rememberBooking = useCallback(
1405
+ (booking) => {
1406
+ const current = pendingBookingRef.current;
1407
+ if (current?.eventUri === booking.eventUri && current.inviteeEmail === booking.inviteeEmail && current.inviteeUri === booking.inviteeUri) {
1408
+ return;
1409
+ }
1410
+ pendingBookingRef.current = booking;
1411
+ savePendingWidgetBooking(resolvedStorageKeyPrefix, visitorId, booking);
1412
+ },
1413
+ [resolvedStorageKeyPrefix, visitorId]
1414
+ );
1415
+ const forgetBooking = useCallback(
1416
+ (eventUri) => {
1417
+ const current = pendingBookingRef.current;
1418
+ if (!current) return;
1419
+ if (eventUri && current.eventUri !== eventUri) return;
1420
+ pendingBookingRef.current = null;
1421
+ clearPendingWidgetBooking(resolvedStorageKeyPrefix, visitorId);
1422
+ },
1423
+ [resolvedStorageKeyPrefix, visitorId]
1023
1424
  );
1024
1425
  const submit = useCallback(
1025
- async (visitorText) => {
1426
+ async (visitorText, options) => {
1026
1427
  if (runRef.current) {
1027
1428
  runRef.current.abort();
1028
1429
  clientRef.current.cancelActive();
1029
1430
  }
1030
1431
  const controller = new AbortController();
1031
1432
  runRef.current = controller;
1433
+ const booking = pendingBookingRef.current;
1434
+ const outgoing = options?.runtimeText ?? visitorText;
1435
+ const runtimeText = booking ? `${visitorBookingPrefix(booking)}
1436
+
1437
+ ${outgoing}` : outgoing;
1032
1438
  const visitorMessage = {
1033
1439
  id: `visitor-${Date.now()}`,
1034
1440
  role: "visitor",
1035
1441
  text: visitorText,
1036
- createdAt: Date.now()
1442
+ createdAt: Date.now(),
1443
+ ...runtimeText !== visitorText ? { runtimeText } : {}
1037
1444
  };
1038
1445
  setState((prev) => ({
1039
1446
  ...prev,
@@ -1050,9 +1457,10 @@ function useAgentChat({
1050
1457
  journey: null,
1051
1458
  followUps: [],
1052
1459
  streamingText: "",
1460
+ pendingOffer: null,
1053
1461
  error: null
1054
1462
  }));
1055
- await runTurn({ controller, resume: false, visitorText });
1463
+ await runTurn({ controller, resume: false, visitorText: runtimeText });
1056
1464
  },
1057
1465
  [runTurn]
1058
1466
  );
@@ -1079,12 +1487,13 @@ function useAgentChat({
1079
1487
  journey: null,
1080
1488
  followUps: [],
1081
1489
  streamingText: "",
1490
+ pendingOffer: null,
1082
1491
  error: null
1083
1492
  }));
1084
1493
  await runTurn({
1085
1494
  controller,
1086
1495
  resume: false,
1087
- visitorText: visitorMessage.text
1496
+ visitorText: visitorTurnText(visitorMessage)
1088
1497
  });
1089
1498
  }, [runTurn, state.messages]);
1090
1499
  useEffect(() => {
@@ -1101,7 +1510,7 @@ function useAgentChat({
1101
1510
  controller,
1102
1511
  initialText: conversation.streamingText,
1103
1512
  resume: true,
1104
- visitorText: visitorMessage.text
1513
+ visitorText: visitorTurnText(visitorMessage)
1105
1514
  });
1106
1515
  return () => {
1107
1516
  if (runRef.current === controller) {
@@ -1121,6 +1530,8 @@ function useAgentChat({
1121
1530
  reset,
1122
1531
  retry,
1123
1532
  submit,
1533
+ rememberBooking,
1534
+ forgetBooking,
1124
1535
  visitorSessionId: visitorId,
1125
1536
  sessionId: clientRef.current.getActiveSessionId()
1126
1537
  };
@@ -1175,18 +1586,65 @@ var defaultAgentRailTheme = {
1175
1586
  fontBody: '"Mulish", "Avenir Next", "Segoe UI", sans-serif',
1176
1587
  fontDisplay: '"Space Grotesk", sans-serif'
1177
1588
  };
1589
+ var defaultDarkAgentRailTheme = {
1590
+ ...defaultAgentRailTheme,
1591
+ brand: "#a77bff",
1592
+ brandSoft: "#2b2140",
1593
+ brandDeep: "#f5f0ff",
1594
+ surface: "#101218",
1595
+ surfaceMuted: "#1a1e27",
1596
+ text: "#f5f7fb",
1597
+ textMuted: "#b6bfce",
1598
+ textSubtle: "#919cad",
1599
+ border: "rgb(226 232 240 / 0.16)",
1600
+ visitorBubble: "#7c3aed",
1601
+ success: "#55cf91",
1602
+ danger: "#ff8da1"
1603
+ };
1178
1604
 
1179
1605
  // src/react/components/AgentRail/AgentRail.tsx
1180
- import { useEffect as useEffect2, useId, useRef as useRef3 } from "react";
1606
+ import { useEffect as useEffect2, useRef as useRef3, useState as useState6 } from "react";
1607
+
1608
+ // src/react/hooks/useAgentColorScheme.ts
1609
+ import { useSyncExternalStore } from "react";
1610
+ var DARK_MODE_QUERY = "(prefers-color-scheme: dark)";
1611
+ function subscribeToDarkMode(onChange) {
1612
+ if (typeof window === "undefined" || !window.matchMedia) {
1613
+ return () => void 0;
1614
+ }
1615
+ const mediaQuery = window.matchMedia(DARK_MODE_QUERY);
1616
+ if (typeof mediaQuery.addEventListener === "function") {
1617
+ mediaQuery.addEventListener("change", onChange);
1618
+ return () => mediaQuery.removeEventListener("change", onChange);
1619
+ }
1620
+ mediaQuery.addListener(onChange);
1621
+ return () => mediaQuery.removeListener(onChange);
1622
+ }
1623
+ function getPrefersDarkMode() {
1624
+ return typeof window !== "undefined" && Boolean(window.matchMedia?.(DARK_MODE_QUERY).matches);
1625
+ }
1626
+ function useAgentColorScheme(colorScheme = "auto") {
1627
+ const prefersDarkMode = useSyncExternalStore(
1628
+ subscribeToDarkMode,
1629
+ getPrefersDarkMode,
1630
+ () => false
1631
+ );
1632
+ return resolveAgentColorScheme(colorScheme, prefersDarkMode);
1633
+ }
1634
+ function resolveAgentColorScheme(colorScheme = "auto", prefersDarkMode) {
1635
+ return colorScheme === "auto" ? prefersDarkMode ? "dark" : "light" : colorScheme;
1636
+ }
1181
1637
 
1182
1638
  // src/react/components/AgentActivityBubble/AgentActivityBubble.tsx
1639
+ import { useState as useState2 } from "react";
1183
1640
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1184
1641
  function workSummary(steps, failed, brandLabel) {
1185
1642
  const active = [...steps].reverse().find((step) => step.state === "active");
1186
1643
  if (active?.kind === "specialist")
1187
1644
  return `${active.label} is reviewing your question`;
1188
1645
  if (active?.kind === "search") return "Searching this site";
1189
- if (active) return `${brandLabel} is choosing the best way to help`;
1646
+ if (active)
1647
+ return brandLabel ? `${brandLabel} is choosing the best way to help` : "Choosing the best way to help";
1190
1648
  if (failed) return "Couldn\u2019t complete this request";
1191
1649
  const hasError = steps.some((step) => step.state === "error");
1192
1650
  const specialists = steps.filter(
@@ -1197,23 +1655,24 @@ function workSummary(steps, failed, brandLabel) {
1197
1655
  );
1198
1656
  if (hasError) return "Answered with available information";
1199
1657
  if (specialists.length > 1)
1200
- return `Answer prepared with ${specialists.length} specialists`;
1658
+ return `Brought in ${specialists.length} specialists`;
1201
1659
  if (specialists.length === 1)
1202
- return `Answer prepared with ${specialists[0]?.label}`;
1203
- if (searched) return "Answer prepared from this site";
1660
+ return `Brought in ${specialists[0]?.label}`;
1661
+ if (searched) return "Searched this site";
1204
1662
  return "Answer ready";
1205
1663
  }
1206
1664
  function stepLabel(step, brandLabel) {
1207
- return step.kind === "planning" ? brandLabel : step.label;
1665
+ return step.kind === "planning" ? brandLabel || "Supervisor" : step.label;
1208
1666
  }
1209
1667
  function stepDetail(step, steps) {
1210
1668
  if (step.kind !== "planning" || step.state !== "completed") {
1211
1669
  return step.detail;
1212
1670
  }
1213
1671
  const specialists = steps.filter((item) => item.kind === "specialist");
1214
- if (specialists.length === 1) return `Delegated to ${specialists[0]?.label}`;
1672
+ if (specialists.length === 1)
1673
+ return `Routed your question to ${specialists[0]?.label}`;
1215
1674
  if (specialists.length > 1)
1216
- return `Delegated to ${specialists.length} specialists`;
1675
+ return `Routed your question to ${specialists.length} specialists`;
1217
1676
  if (steps.some((item) => item.kind === "search"))
1218
1677
  return "Used built-in Search & Discovery";
1219
1678
  return step.detail;
@@ -1244,13 +1703,17 @@ function PlanningIcon() {
1244
1703
  ) });
1245
1704
  }
1246
1705
  function AgentActivityBubble({
1247
- brandLabel = "Webless Guide",
1706
+ brandLabel = "",
1248
1707
  brandLogoUrl,
1249
1708
  failed = false,
1250
1709
  steps
1251
1710
  }) {
1252
1711
  const active = steps.some((step) => step.state === "active");
1253
- const delegated = steps.some((step) => step.kind === "specialist");
1712
+ const receiptId = steps.map((step) => step.id).join(":");
1713
+ const [expandedReceiptId, setExpandedReceiptId] = useState2(
1714
+ null
1715
+ );
1716
+ const detailsOpen = active || expandedReceiptId === receiptId;
1254
1717
  return /* @__PURE__ */ jsxs("article", { className: "agent-activity-bubble", children: [
1255
1718
  /* @__PURE__ */ jsxs("p", { className: "agent-activity-bubble__status", "aria-live": "polite", children: [
1256
1719
  /* @__PURE__ */ jsx(
@@ -1262,62 +1725,69 @@ function AgentActivityBubble({
1262
1725
  ),
1263
1726
  workSummary(steps, failed, brandLabel)
1264
1727
  ] }),
1265
- /* @__PURE__ */ jsxs(
1266
- "details",
1267
- {
1268
- className: "agent-activity-bubble__details",
1269
- open: active || delegated,
1270
- children: [
1271
- /* @__PURE__ */ jsx("summary", { children: failed ? "What happened" : active ? "Working" : "How this answer was prepared" }),
1272
- /* @__PURE__ */ jsx("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
1273
- const detail = stepDetail(step, steps);
1274
- return /* @__PURE__ */ jsxs(
1275
- "li",
1276
- {
1277
- className: "agent-activity-bubble__step",
1278
- "data-kind": step.kind,
1279
- "data-state": step.state,
1280
- children: [
1281
- /* @__PURE__ */ jsx(
1282
- "span",
1283
- {
1284
- className: "agent-activity-bubble__step-icon",
1285
- "aria-hidden": "true",
1286
- children: step.kind === "planning" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1287
- /* @__PURE__ */ jsx(PlanningIcon, {}),
1288
- brandLogoUrl ? /* @__PURE__ */ jsx(
1289
- "img",
1290
- {
1291
- src: brandLogoUrl,
1292
- alt: "",
1293
- onError: (event) => {
1294
- event.currentTarget.hidden = true;
1295
- }
1296
- }
1297
- ) : null
1298
- ] }) : step.kind === "search" ? /* @__PURE__ */ jsx(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
1299
- }
1300
- ),
1301
- /* @__PURE__ */ jsxs("span", { className: "agent-activity-bubble__step-copy", children: [
1302
- /* @__PURE__ */ jsxs("span", { className: "agent-activity-bubble__step-heading", children: [
1303
- /* @__PURE__ */ jsx("strong", { children: stepLabel(step, brandLabel) }),
1304
- /* @__PURE__ */ jsx("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
1305
- ] }),
1306
- detail ? /* @__PURE__ */ jsx("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1307
- ] })
1308
- ]
1309
- },
1310
- step.id
1728
+ /* @__PURE__ */ jsxs("div", { className: "agent-activity-bubble__details", children: [
1729
+ /* @__PURE__ */ jsx(
1730
+ "button",
1731
+ {
1732
+ type: "button",
1733
+ className: "agent-activity-bubble__summary",
1734
+ "aria-expanded": detailsOpen,
1735
+ onClick: () => {
1736
+ if (active) return;
1737
+ setExpandedReceiptId(
1738
+ (current) => current === receiptId ? null : receiptId
1311
1739
  );
1312
- }) })
1313
- ]
1314
- }
1315
- )
1740
+ },
1741
+ children: "How this answer was made"
1742
+ }
1743
+ ),
1744
+ detailsOpen ? /* @__PURE__ */ jsx("ol", { className: "agent-activity-bubble__steps", children: steps.map((step) => {
1745
+ const detail = stepDetail(step, steps);
1746
+ return /* @__PURE__ */ jsxs(
1747
+ "li",
1748
+ {
1749
+ className: "agent-activity-bubble__step",
1750
+ "data-kind": step.kind,
1751
+ "data-state": step.state,
1752
+ children: [
1753
+ /* @__PURE__ */ jsx(
1754
+ "span",
1755
+ {
1756
+ className: "agent-activity-bubble__step-icon",
1757
+ "aria-hidden": "true",
1758
+ children: step.kind === "planning" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1759
+ /* @__PURE__ */ jsx(PlanningIcon, {}),
1760
+ brandLogoUrl ? /* @__PURE__ */ jsx(
1761
+ "img",
1762
+ {
1763
+ src: brandLogoUrl,
1764
+ alt: "",
1765
+ onError: (event) => {
1766
+ event.currentTarget.hidden = true;
1767
+ }
1768
+ }
1769
+ ) : null
1770
+ ] }) : step.kind === "search" ? /* @__PURE__ */ jsx(SearchIcon, {}) : step.label.slice(0, 1).toUpperCase()
1771
+ }
1772
+ ),
1773
+ /* @__PURE__ */ jsxs("span", { className: "agent-activity-bubble__step-copy", children: [
1774
+ /* @__PURE__ */ jsxs("span", { className: "agent-activity-bubble__step-heading", children: [
1775
+ /* @__PURE__ */ jsx("strong", { children: stepLabel(step, brandLabel) }),
1776
+ step.kind === "planning" && !brandLabel ? null : /* @__PURE__ */ jsx("small", { children: step.kind === "specialist" ? "Specialist" : step.kind === "search" ? "Built-in capability" : "Supervisor" })
1777
+ ] }),
1778
+ detail ? /* @__PURE__ */ jsx("span", { className: "agent-activity-bubble__step-detail", children: detail }) : null
1779
+ ] })
1780
+ ]
1781
+ },
1782
+ step.id
1783
+ );
1784
+ }) }) : null
1785
+ ] })
1316
1786
  ] });
1317
1787
  }
1318
1788
 
1319
1789
  // src/react/components/Composer/Composer.tsx
1320
- import { useRef as useRef2, useState as useState2 } from "react";
1790
+ import { useRef as useRef2, useState as useState3 } from "react";
1321
1791
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1322
1792
  function SendIcon() {
1323
1793
  return /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx2("path", { d: "M8 12V4M8 4l-3 3M8 4l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
@@ -1328,7 +1798,7 @@ function Composer({
1328
1798
  variant = "default",
1329
1799
  onSubmit
1330
1800
  }) {
1331
- const [value, setValue] = useState2("");
1801
+ const [value, setValue] = useState3("");
1332
1802
  const inputRef = useRef2(null);
1333
1803
  function submitCurrent() {
1334
1804
  const trimmed = value.trim();
@@ -1414,34 +1884,338 @@ function FollowUpChips({
1414
1884
  ] });
1415
1885
  }
1416
1886
 
1887
+ // src/react/components/MessageBubble/MessageBubble.tsx
1888
+ import { useState as useState5 } from "react";
1889
+
1890
+ // src/react/components/BookingCard/BookingCard.tsx
1891
+ import { useId, useMemo as useMemo2, useState as useState4 } from "react";
1892
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1893
+ function monthFromKey(key) {
1894
+ const [year, month] = key.split("-").map(Number);
1895
+ if (!year || !month) return { year: (/* @__PURE__ */ new Date()).getFullYear(), month: (/* @__PURE__ */ new Date()).getMonth() };
1896
+ return { year, month: month - 1 };
1897
+ }
1898
+ function dateKeyFromParts(year, month, day) {
1899
+ return [
1900
+ year,
1901
+ String(month + 1).padStart(2, "0"),
1902
+ String(day).padStart(2, "0")
1903
+ ].join("-");
1904
+ }
1905
+ function calendarCells(year, month) {
1906
+ const firstWeekday = (new Date(year, month, 1).getDay() + 6) % 7;
1907
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
1908
+ const cells = [];
1909
+ for (let index = 0; index < firstWeekday; index += 1) cells.push(null);
1910
+ for (let day = 1; day <= daysInMonth; day += 1) {
1911
+ cells.push({ day, key: dateKeyFromParts(year, month, day) });
1912
+ }
1913
+ while (cells.length < 42) cells.push(null);
1914
+ return cells;
1915
+ }
1916
+ function BookingCard({
1917
+ offer,
1918
+ onBook
1919
+ }) {
1920
+ const fieldId = useId();
1921
+ const defaultType = offer.eventTypes[0]?.uri ?? offer.slots[0]?.eventTypeUri ?? "";
1922
+ const [step, setStep] = useState4("date");
1923
+ const [eventTypeUri, setEventTypeUri] = useState4(defaultType);
1924
+ const [selectedDate, setSelectedDate] = useState4("");
1925
+ const [startTime, setStartTime] = useState4("");
1926
+ const [name, setName] = useState4("");
1927
+ const [email, setEmail] = useState4("");
1928
+ const slots = useMemo2(
1929
+ () => bookingSlotsForEventType(offer.slots, eventTypeUri),
1930
+ [eventTypeUri, offer.slots]
1931
+ );
1932
+ const availableByDate = useMemo2(() => {
1933
+ const next = /* @__PURE__ */ new Map();
1934
+ for (const slot of slots) {
1935
+ const key = slotDateKey(slot.startTime);
1936
+ if (!next.has(key)) next.set(key, slot.startTime);
1937
+ }
1938
+ return next;
1939
+ }, [slots]);
1940
+ const [visibleMonth, setVisibleMonth] = useState4(
1941
+ () => firstAvailableBookingMonth(slots)
1942
+ );
1943
+ function selectEventType(nextType) {
1944
+ setEventTypeUri(nextType);
1945
+ setSelectedDate("");
1946
+ setStartTime("");
1947
+ setVisibleMonth(
1948
+ firstAvailableBookingMonth(bookingSlotsForEventType(offer.slots, nextType))
1949
+ );
1950
+ }
1951
+ const daySlots = useMemo2(
1952
+ () => slots.filter((slot) => slotDateKey(slot.startTime) === selectedDate),
1953
+ [selectedDate, slots]
1954
+ );
1955
+ const selectedType = offer.eventTypes.find((item) => item.uri === eventTypeUri);
1956
+ const selectedSample = availableByDate.get(selectedDate) ?? startTime;
1957
+ const timeZone = formatSlotTimeZone(slots[0]?.startTime ?? selectedSample);
1958
+ const weekdays = useMemo2(() => weekdayLabels(), []);
1959
+ const cells = calendarCells(visibleMonth.year, visibleMonth.month);
1960
+ const canPrevMonth = [...availableByDate.keys()].some((key) => {
1961
+ const month = monthFromKey(key);
1962
+ return month.year < visibleMonth.year || month.year === visibleMonth.year && month.month < visibleMonth.month;
1963
+ });
1964
+ const canNextMonth = [...availableByDate.keys()].some((key) => {
1965
+ const month = monthFromKey(key);
1966
+ return month.year > visibleMonth.year || month.year === visibleMonth.year && month.month > visibleMonth.month;
1967
+ });
1968
+ function goToMonth(offset) {
1969
+ setVisibleMonth((current) => {
1970
+ const next = new Date(current.year, current.month + offset, 1);
1971
+ return { year: next.getFullYear(), month: next.getMonth() };
1972
+ });
1973
+ }
1974
+ function selectDate(key) {
1975
+ if (!availableByDate.has(key)) return;
1976
+ setSelectedDate(key);
1977
+ setStartTime("");
1978
+ setStep("time");
1979
+ }
1980
+ function selectTime(value) {
1981
+ setStartTime(value);
1982
+ setStep("details");
1983
+ }
1984
+ function handleSubmit(event) {
1985
+ event.preventDefault();
1986
+ if (!eventTypeUri || !startTime || !name.trim() || !email.trim()) return;
1987
+ onBook?.({
1988
+ displayText: `Book the ${formatSlotLabel(startTime)} demo`,
1989
+ runtimeText: formatBookingRequest({
1990
+ eventTypeUri,
1991
+ inviteeEmail: email.trim(),
1992
+ inviteeName: name.trim(),
1993
+ startTime,
1994
+ timezone: visitorTimeZone(),
1995
+ locationKind: selectedType?.locationKind,
1996
+ location: selectedType?.location
1997
+ })
1998
+ });
1999
+ }
2000
+ return /* @__PURE__ */ jsx4("section", { className: "booking-card", "aria-label": "Book a meeting", children: /* @__PURE__ */ jsxs4("form", { className: "booking-card__form", onSubmit: handleSubmit, children: [
2001
+ step === "date" ? /* @__PURE__ */ jsxs4("div", { className: "booking-card__step", children: [
2002
+ /* @__PURE__ */ jsx4("p", { className: "booking-card__title", children: selectedType?.name || "Pick a date" }),
2003
+ timeZone ? /* @__PURE__ */ jsxs4("p", { className: "booking-card__tz", children: [
2004
+ "Times in ",
2005
+ timeZone
2006
+ ] }) : null,
2007
+ offer.eventTypes.length > 1 ? /* @__PURE__ */ jsxs4("label", { className: "booking-card__field", htmlFor: `${fieldId}-type`, children: [
2008
+ /* @__PURE__ */ jsx4("span", { children: "Meeting" }),
2009
+ /* @__PURE__ */ jsx4(
2010
+ "select",
2011
+ {
2012
+ id: `${fieldId}-type`,
2013
+ value: eventTypeUri,
2014
+ onChange: (event) => selectEventType(event.target.value),
2015
+ children: offer.eventTypes.map((item) => /* @__PURE__ */ jsx4("option", { value: item.uri, children: item.name }, item.uri))
2016
+ }
2017
+ )
2018
+ ] }) : null,
2019
+ /* @__PURE__ */ jsxs4("div", { className: "booking-card__month", children: [
2020
+ /* @__PURE__ */ jsx4(
2021
+ "button",
2022
+ {
2023
+ type: "button",
2024
+ className: "booking-card__nav",
2025
+ "aria-label": "Previous month",
2026
+ disabled: !canPrevMonth,
2027
+ onClick: () => goToMonth(-1),
2028
+ children: "\u2039"
2029
+ }
2030
+ ),
2031
+ /* @__PURE__ */ jsx4("p", { className: "booking-card__month-title", children: formatMonthTitle(visibleMonth.year, visibleMonth.month) }),
2032
+ /* @__PURE__ */ jsx4(
2033
+ "button",
2034
+ {
2035
+ type: "button",
2036
+ className: "booking-card__nav",
2037
+ "aria-label": "Next month",
2038
+ disabled: !canNextMonth,
2039
+ onClick: () => goToMonth(1),
2040
+ children: "\u203A"
2041
+ }
2042
+ )
2043
+ ] }),
2044
+ /* @__PURE__ */ jsx4("div", { className: "booking-card__weekdays", children: weekdays.map((label) => /* @__PURE__ */ jsx4("span", { children: label }, label)) }),
2045
+ /* @__PURE__ */ jsx4("div", { className: "booking-card__calendar", role: "grid", "aria-label": "Available dates", children: cells.map((cell, index) => {
2046
+ if (!cell) {
2047
+ return /* @__PURE__ */ jsx4("span", { className: "booking-card__day" }, `empty-${index}`);
2048
+ }
2049
+ const available = availableByDate.has(cell.key);
2050
+ const selected = cell.key === selectedDate;
2051
+ return /* @__PURE__ */ jsx4(
2052
+ "button",
2053
+ {
2054
+ type: "button",
2055
+ className: [
2056
+ "booking-card__day",
2057
+ available ? "booking-card__day--available" : "",
2058
+ selected ? "booking-card__day--selected" : ""
2059
+ ].filter(Boolean).join(" "),
2060
+ disabled: !available,
2061
+ "aria-pressed": selected,
2062
+ onClick: () => selectDate(cell.key),
2063
+ children: cell.day
2064
+ },
2065
+ cell.key
2066
+ );
2067
+ }) })
2068
+ ] }, "date") : null,
2069
+ step === "time" ? /* @__PURE__ */ jsxs4("div", { className: "booking-card__step", children: [
2070
+ /* @__PURE__ */ jsxs4("div", { className: "booking-card__step-bar", children: [
2071
+ /* @__PURE__ */ jsx4(
2072
+ "button",
2073
+ {
2074
+ type: "button",
2075
+ className: "booking-card__nav",
2076
+ "aria-label": "Back to dates",
2077
+ onClick: () => setStep("date"),
2078
+ children: "\u2039"
2079
+ }
2080
+ ),
2081
+ /* @__PURE__ */ jsxs4("div", { children: [
2082
+ /* @__PURE__ */ jsx4("p", { className: "booking-card__title", children: selectedSample ? formatLongDate(selectedSample) : "Pick a time" }),
2083
+ timeZone ? /* @__PURE__ */ jsxs4("p", { className: "booking-card__tz", children: [
2084
+ "Times in ",
2085
+ timeZone
2086
+ ] }) : null
2087
+ ] })
2088
+ ] }),
2089
+ /* @__PURE__ */ jsx4("div", { className: "booking-card__times", children: daySlots.map((slot) => /* @__PURE__ */ jsx4(
2090
+ "button",
2091
+ {
2092
+ type: "button",
2093
+ className: startTime === slot.startTime ? "booking-card__time booking-card__time--selected" : "booking-card__time",
2094
+ onClick: () => selectTime(slot.startTime),
2095
+ children: formatTimeChip(slot.startTime)
2096
+ },
2097
+ slot.startTime
2098
+ )) })
2099
+ ] }, "time") : null,
2100
+ step === "details" ? /* @__PURE__ */ jsxs4("div", { className: "booking-card__step", children: [
2101
+ /* @__PURE__ */ jsxs4("div", { className: "booking-card__step-bar", children: [
2102
+ /* @__PURE__ */ jsx4(
2103
+ "button",
2104
+ {
2105
+ type: "button",
2106
+ className: "booking-card__nav",
2107
+ "aria-label": "Back to times",
2108
+ onClick: () => setStep("time"),
2109
+ children: "\u2039"
2110
+ }
2111
+ ),
2112
+ /* @__PURE__ */ jsxs4("div", { children: [
2113
+ /* @__PURE__ */ jsx4("p", { className: "booking-card__title", children: "Enter details" }),
2114
+ /* @__PURE__ */ jsx4("p", { className: "booking-card__tz", children: formatSlotLabel(startTime) }),
2115
+ selectedType?.location ? /* @__PURE__ */ jsx4("p", { className: "booking-card__tz", children: selectedType.location }) : null
2116
+ ] })
2117
+ ] }),
2118
+ /* @__PURE__ */ jsxs4("div", { className: "booking-card__identity", children: [
2119
+ /* @__PURE__ */ jsxs4("label", { className: "booking-card__field", htmlFor: `${fieldId}-name`, children: [
2120
+ /* @__PURE__ */ jsx4("span", { children: "Name" }),
2121
+ /* @__PURE__ */ jsx4(
2122
+ "input",
2123
+ {
2124
+ id: `${fieldId}-name`,
2125
+ autoComplete: "name",
2126
+ value: name,
2127
+ onChange: (event) => setName(event.target.value),
2128
+ required: true
2129
+ }
2130
+ )
2131
+ ] }),
2132
+ /* @__PURE__ */ jsxs4("label", { className: "booking-card__field", htmlFor: `${fieldId}-email`, children: [
2133
+ /* @__PURE__ */ jsx4("span", { children: "Email" }),
2134
+ /* @__PURE__ */ jsx4(
2135
+ "input",
2136
+ {
2137
+ id: `${fieldId}-email`,
2138
+ type: "email",
2139
+ autoComplete: "email",
2140
+ value: email,
2141
+ onChange: (event) => setEmail(event.target.value),
2142
+ required: true
2143
+ }
2144
+ )
2145
+ ] })
2146
+ ] }),
2147
+ /* @__PURE__ */ jsx4("button", { type: "submit", className: "booking-card__submit", children: "Book this time" })
2148
+ ] }, "details") : null
2149
+ ] }) });
2150
+ }
2151
+
1417
2152
  // src/react/components/MessageBubble/MessageBubble.tsx
1418
2153
  import { Streamdown } from "streamdown";
1419
2154
  import "streamdown/styles.css";
1420
- import { jsx as jsx4 } from "react/jsx-runtime";
1421
- function MessageBubble({ message }) {
2155
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2156
+ function MessageBubble({
2157
+ message,
2158
+ brandLogoUrl,
2159
+ offer,
2160
+ onBook
2161
+ }) {
2162
+ const resolvedLogoUrl = brandLogoUrl?.trim();
2163
+ const [failedLogoUrl, setFailedLogoUrl] = useState5(null);
2164
+ const showBrandLogo = Boolean(resolvedLogoUrl) && failedLogoUrl !== resolvedLogoUrl;
2165
+ const cards = message.role === "agent" ? extractToolCards(message.text) : [];
2166
+ const extractedOffers = cards.filter(
2167
+ (card) => card.type === "booking_offer"
2168
+ );
2169
+ const offers = offer ? [offer] : extractedOffers;
2170
+ const visibleText = hideToolCardFences(message.text);
2171
+ const isStreaming = message.role === "agent" && Boolean(message.streaming);
2172
+ const displayText = offers.length > 0 ? visibleText || "Pick a date and time that works for you." : visibleText || (isStreaming ? "" : message.text);
1422
2173
  if (message.role === "visitor") {
1423
- return /* @__PURE__ */ jsx4("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ jsx4("p", { className: "message-bubble__text", children: message.text }) });
2174
+ return /* @__PURE__ */ jsx5("article", { className: "message-bubble message-bubble--visitor", children: /* @__PURE__ */ jsx5("p", { className: "message-bubble__text", children: message.text }) });
1424
2175
  }
1425
- return /* @__PURE__ */ jsx4("article", { className: "message-bubble message-bubble--agent", children: /* @__PURE__ */ jsx4("div", { className: "message-bubble__text", children: /* @__PURE__ */ jsx4(
2176
+ const agentText = /* @__PURE__ */ jsx5("div", { className: "message-bubble__text", children: /* @__PURE__ */ jsx5(
1426
2177
  Streamdown,
1427
2178
  {
1428
2179
  animated: true,
1429
2180
  caret: "circle",
1430
2181
  className: "message-bubble__markdown",
1431
2182
  controls: false,
1432
- isAnimating: message.streaming,
2183
+ isAnimating: isStreaming,
1433
2184
  linkSafety: { enabled: false },
1434
- mode: message.streaming ? "streaming" : "static",
2185
+ mode: isStreaming ? "streaming" : "static",
1435
2186
  skipHtml: true,
1436
- children: message.text
2187
+ children: displayText
1437
2188
  }
1438
- ) }) });
2189
+ ) });
2190
+ return /* @__PURE__ */ jsxs5("article", { className: "message-bubble message-bubble--agent", children: [
2191
+ displayText ? showBrandLogo ? /* @__PURE__ */ jsxs5("div", { className: "message-bubble__agent-row", children: [
2192
+ /* @__PURE__ */ jsx5("span", { className: "message-bubble__agent-avatar", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
2193
+ "img",
2194
+ {
2195
+ src: resolvedLogoUrl,
2196
+ alt: "",
2197
+ onError: () => {
2198
+ setFailedLogoUrl(resolvedLogoUrl ?? null);
2199
+ }
2200
+ }
2201
+ ) }),
2202
+ agentText
2203
+ ] }) : agentText : null,
2204
+ offers.map((nextOffer, index) => /* @__PURE__ */ jsx5(
2205
+ BookingCard,
2206
+ {
2207
+ offer: nextOffer,
2208
+ onBook
2209
+ },
2210
+ `${bookingOfferIdentityKey(nextOffer)}-${index}`
2211
+ ))
2212
+ ] });
1439
2213
  }
1440
2214
 
1441
2215
  // src/react/components/AgentRail/AgentRail.tsx
1442
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2216
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1443
2217
  function MinimizeIcon() {
1444
- return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
2218
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1445
2219
  "path",
1446
2220
  {
1447
2221
  d: "M3.5 8h9",
@@ -1452,7 +2226,7 @@ function MinimizeIcon() {
1452
2226
  ) });
1453
2227
  }
1454
2228
  function CloseIcon() {
1455
- return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
2229
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1456
2230
  "path",
1457
2231
  {
1458
2232
  d: "M4 4l8 8M12 4l-8 8",
@@ -1463,7 +2237,7 @@ function CloseIcon() {
1463
2237
  ) });
1464
2238
  }
1465
2239
  function NewChatIcon() {
1466
- return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
2240
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1467
2241
  "path",
1468
2242
  {
1469
2243
  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",
@@ -1475,7 +2249,7 @@ function NewChatIcon() {
1475
2249
  ) });
1476
2250
  }
1477
2251
  function ExpandIcon() {
1478
- return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
2252
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1479
2253
  "path",
1480
2254
  {
1481
2255
  d: "M6 3.5H3.5V6M10 3.5h2.5V6M10 12.5h2.5V10M6 12.5H3.5V10",
@@ -1487,7 +2261,7 @@ function ExpandIcon() {
1487
2261
  ) });
1488
2262
  }
1489
2263
  function RestoreIcon() {
1490
- return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx5(
2264
+ return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
1491
2265
  "path",
1492
2266
  {
1493
2267
  d: "M5.5 5.5H3.5V7.5M10.5 5.5h2V7.5M10.5 10.5h2V8.5M5.5 10.5H3.5V8.5",
@@ -1501,7 +2275,8 @@ function RestoreIcon() {
1501
2275
  function AgentRail({
1502
2276
  state,
1503
2277
  theme,
1504
- brandLabel = "Webless Assist",
2278
+ colorScheme = "auto",
2279
+ brandLabel = "",
1505
2280
  brandLogoUrl,
1506
2281
  poweredByLabel = "Powered by Webless",
1507
2282
  composerPlaceholder = "Ask anything\u2026",
@@ -1513,11 +2288,31 @@ function AgentRail({
1513
2288
  onReset,
1514
2289
  onRetry,
1515
2290
  onSubmit,
1516
- onFollowUpSelect
2291
+ onFollowUpSelect,
2292
+ onBook
1517
2293
  }) {
1518
2294
  const transcriptRef = useRef3(null);
1519
- const welcomeTitleId = useId();
1520
- const resolvedTheme = { ...defaultAgentRailTheme, ...theme };
2295
+ const resolvedBrandLabel = brandLabel.trim();
2296
+ const resolvedBrandLogoUrl = brandLogoUrl?.trim();
2297
+ const [failedLogoUrl, setFailedLogoUrl] = useState6(null);
2298
+ const showBrandLogo = Boolean(resolvedBrandLogoUrl) && failedLogoUrl !== resolvedBrandLogoUrl;
2299
+ const resolvedColorScheme = useAgentColorScheme(colorScheme);
2300
+ const brandedTheme = { ...defaultAgentRailTheme, ...theme };
2301
+ const resolvedTheme = resolvedColorScheme === "dark" ? {
2302
+ ...brandedTheme,
2303
+ brand: theme?.brand ?? defaultDarkAgentRailTheme.brand,
2304
+ brandDeep: defaultDarkAgentRailTheme.brandDeep,
2305
+ brandSoft: `color-mix(in srgb, ${theme?.brand ?? defaultDarkAgentRailTheme.brand} 18%, ${defaultDarkAgentRailTheme.surface})`,
2306
+ border: defaultDarkAgentRailTheme.border,
2307
+ danger: defaultDarkAgentRailTheme.danger,
2308
+ success: defaultDarkAgentRailTheme.success,
2309
+ surface: defaultDarkAgentRailTheme.surface,
2310
+ surfaceMuted: defaultDarkAgentRailTheme.surfaceMuted,
2311
+ text: defaultDarkAgentRailTheme.text,
2312
+ textMuted: defaultDarkAgentRailTheme.textMuted,
2313
+ textSubtle: defaultDarkAgentRailTheme.textSubtle,
2314
+ visitorBubble: theme?.visitorBubble ?? theme?.brand ?? defaultDarkAgentRailTheme.visitorBubble
2315
+ } : brandedTheme;
1521
2316
  const railStyle = {
1522
2317
  "--rail-width": resolvedTheme.railMaxWidth,
1523
2318
  "--as-rail-max-width": resolvedTheme.railMaxWidth,
@@ -1535,7 +2330,8 @@ function AgentRail({
1535
2330
  "--as-success": resolvedTheme.success,
1536
2331
  "--as-danger": resolvedTheme.danger,
1537
2332
  "--as-font-body": resolvedTheme.fontBody,
1538
- "--as-font-display": resolvedTheme.fontDisplay
2333
+ "--as-font-display": resolvedTheme.fontDisplay,
2334
+ colorScheme: resolvedColorScheme
1539
2335
  };
1540
2336
  const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
1541
2337
  const showActivity = state.toolSteps.length > 0;
@@ -1546,7 +2342,7 @@ function AgentRail({
1546
2342
  const greeting = state.messages.find(
1547
2343
  (message) => message.role === "agent" && message.id === "greeting"
1548
2344
  );
1549
- const visibleMessages = hasVisitorMessages2 ? state.messages.filter((message) => message.id !== "greeting") : [];
2345
+ const visibleMessages = hasVisitorMessages2 ? state.messages : [];
1550
2346
  const lastMessage = visibleMessages.at(-1);
1551
2347
  const completedAnswer = showActivity && state.phase === "complete" && lastMessage?.role === "agent" ? lastMessage : null;
1552
2348
  const transcriptMessages = completedAnswer ? visibleMessages.slice(0, -1) : visibleMessages;
@@ -1556,6 +2352,12 @@ function AgentRail({
1556
2352
  role: "agent",
1557
2353
  streaming: true,
1558
2354
  text: state.streamingText
2355
+ } : state.pendingOffer ? {
2356
+ createdAt: 0,
2357
+ id: "pending-booking",
2358
+ role: "agent",
2359
+ streaming: false,
2360
+ text: "Pick a date and time that works for you."
1559
2361
  } : null;
1560
2362
  useEffect2(() => {
1561
2363
  const node = transcriptRef.current;
@@ -1568,10 +2370,11 @@ function AgentRail({
1568
2370
  state.followUps,
1569
2371
  state.journey
1570
2372
  ]);
1571
- return /* @__PURE__ */ jsxs4(
2373
+ return /* @__PURE__ */ jsxs6(
1572
2374
  "aside",
1573
2375
  {
1574
2376
  className: `agent-rail${mobileFullscreen ? " agent-rail--mobile-fullscreen" : ""}${expanded ? " agent-rail--expanded" : ""}`,
2377
+ "data-color-scheme": resolvedColorScheme,
1575
2378
  style: railStyle,
1576
2379
  "aria-label": "Agent conversation",
1577
2380
  "aria-modal": mobileFullscreen || expanded ? true : void 0,
@@ -1579,45 +2382,42 @@ function AgentRail({
1579
2382
  role: mobileFullscreen || expanded ? "dialog" : void 0,
1580
2383
  tabIndex: mobileFullscreen || expanded ? -1 : void 0,
1581
2384
  children: [
1582
- /* @__PURE__ */ jsx5("header", { className: "agent-rail__header", children: /* @__PURE__ */ jsxs4("div", { className: "agent-rail__brand-row", children: [
1583
- onCollapse ? /* @__PURE__ */ jsx5(
2385
+ /* @__PURE__ */ jsx6("header", { className: "agent-rail__header", children: /* @__PURE__ */ jsxs6("div", { className: "agent-rail__brand-row", children: [
2386
+ onCollapse ? /* @__PURE__ */ jsx6(
1584
2387
  "button",
1585
2388
  {
1586
2389
  type: "button",
1587
2390
  className: "agent-rail__collapse",
1588
2391
  "aria-label": "Collapse assist",
1589
2392
  onClick: onCollapse,
1590
- children: /* @__PURE__ */ jsx5(MinimizeIcon, {})
2393
+ children: /* @__PURE__ */ jsx6(MinimizeIcon, {})
1591
2394
  }
1592
- ) : onClose ? /* @__PURE__ */ jsx5(
2395
+ ) : onClose ? /* @__PURE__ */ jsx6(
1593
2396
  "button",
1594
2397
  {
1595
2398
  type: "button",
1596
2399
  className: "agent-rail__close",
1597
2400
  "aria-label": "Close agent",
1598
2401
  onClick: onClose,
1599
- children: /* @__PURE__ */ jsx5(CloseIcon, {})
2402
+ children: /* @__PURE__ */ jsx6(CloseIcon, {})
1600
2403
  }
1601
- ) : /* @__PURE__ */ jsx5("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
1602
- /* @__PURE__ */ jsxs4("span", { className: "agent-rail__identity", children: [
1603
- /* @__PURE__ */ jsxs4("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: [
1604
- brandLabel.slice(0, 1).toUpperCase(),
1605
- brandLogoUrl ? /* @__PURE__ */ jsx5(
1606
- "img",
1607
- {
1608
- className: "agent-rail__brand-logo",
1609
- src: brandLogoUrl,
1610
- alt: "",
1611
- onError: (event) => {
1612
- event.currentTarget.hidden = true;
1613
- }
2404
+ ) : /* @__PURE__ */ jsx6("span", { className: "agent-rail__brand-spacer", "aria-hidden": "true" }),
2405
+ resolvedBrandLabel || showBrandLogo ? /* @__PURE__ */ jsxs6("span", { className: "agent-rail__identity", children: [
2406
+ showBrandLogo ? /* @__PURE__ */ jsx6("span", { className: "agent-rail__brand-mark", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
2407
+ "img",
2408
+ {
2409
+ className: "agent-rail__brand-logo",
2410
+ src: resolvedBrandLogoUrl,
2411
+ alt: "",
2412
+ onError: () => {
2413
+ setFailedLogoUrl(resolvedBrandLogoUrl ?? null);
1614
2414
  }
1615
- ) : null
1616
- ] }),
1617
- /* @__PURE__ */ jsx5("span", { className: "agent-rail__brand-label", children: brandLabel })
1618
- ] }),
1619
- /* @__PURE__ */ jsxs4("span", { className: "agent-rail__actions", children: [
1620
- onReset ? /* @__PURE__ */ jsx5(
2415
+ }
2416
+ ) }) : null,
2417
+ resolvedBrandLabel ? /* @__PURE__ */ jsx6("span", { className: "agent-rail__brand-label", children: resolvedBrandLabel }) : null
2418
+ ] }) : null,
2419
+ /* @__PURE__ */ jsxs6("span", { className: "agent-rail__actions", children: [
2420
+ onReset ? /* @__PURE__ */ jsx6(
1621
2421
  "button",
1622
2422
  {
1623
2423
  type: "button",
@@ -1625,80 +2425,86 @@ function AgentRail({
1625
2425
  "aria-label": "Start a new conversation",
1626
2426
  disabled: !hasVisitorMessages2,
1627
2427
  onClick: onReset,
1628
- children: /* @__PURE__ */ jsx5(NewChatIcon, {})
2428
+ children: /* @__PURE__ */ jsx6(NewChatIcon, {})
1629
2429
  }
1630
2430
  ) : null,
1631
- onExpandToggle ? /* @__PURE__ */ jsx5(
2431
+ onExpandToggle ? /* @__PURE__ */ jsx6(
1632
2432
  "button",
1633
2433
  {
1634
2434
  type: "button",
1635
2435
  className: "agent-rail__expand",
1636
- "aria-label": expanded ? "Exit focus view" : "Open focus view",
2436
+ "aria-label": expanded ? "Exit full screen" : "Open full screen",
1637
2437
  onClick: onExpandToggle,
1638
- children: expanded ? /* @__PURE__ */ jsx5(RestoreIcon, {}) : /* @__PURE__ */ jsx5(ExpandIcon, {})
2438
+ children: expanded ? /* @__PURE__ */ jsx6(RestoreIcon, {}) : /* @__PURE__ */ jsx6(ExpandIcon, {})
1639
2439
  }
1640
2440
  ) : null
1641
2441
  ] })
1642
2442
  ] }) }),
1643
- /* @__PURE__ */ jsx5("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ jsxs4("div", { className: "agent-rail__thread", children: [
1644
- !hasVisitorMessages2 ? /* @__PURE__ */ jsxs4(
1645
- "section",
2443
+ /* @__PURE__ */ jsx6("div", { ref: transcriptRef, className: "agent-rail__transcript", children: /* @__PURE__ */ jsxs6("div", { className: "agent-rail__thread", children: [
2444
+ !hasVisitorMessages2 ? /* @__PURE__ */ jsxs6("section", { className: "agent-rail__welcome", "aria-label": "Welcome", children: [
2445
+ greeting?.role === "agent" ? /* @__PURE__ */ jsx6(
2446
+ MessageBubble,
2447
+ {
2448
+ message: greeting,
2449
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2450
+ onBook
2451
+ }
2452
+ ) : null,
2453
+ showIdleFollowUps ? /* @__PURE__ */ jsx6("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ jsx6(
2454
+ FollowUpChips,
2455
+ {
2456
+ suggestions: state.followUps,
2457
+ disabled: isBusy,
2458
+ label: "Start here",
2459
+ onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
2460
+ }
2461
+ ) }) : null
2462
+ ] }) : null,
2463
+ transcriptMessages.map((message) => /* @__PURE__ */ jsx6(
2464
+ MessageBubble,
1646
2465
  {
1647
- className: "agent-rail__welcome",
1648
- "aria-labelledby": welcomeTitleId,
1649
- children: [
1650
- /* @__PURE__ */ jsxs4("span", { className: "agent-rail__welcome-mark", "aria-hidden": "true", children: [
1651
- brandLabel.slice(0, 1).toUpperCase(),
1652
- brandLogoUrl ? /* @__PURE__ */ jsx5(
1653
- "img",
1654
- {
1655
- className: "agent-rail__welcome-logo",
1656
- src: brandLogoUrl,
1657
- alt: "",
1658
- onError: (event) => {
1659
- event.currentTarget.hidden = true;
1660
- }
1661
- }
1662
- ) : null
1663
- ] }),
1664
- /* @__PURE__ */ jsxs4("div", { className: "agent-rail__welcome-copy", children: [
1665
- /* @__PURE__ */ jsx5("h2", { id: welcomeTitleId, children: "What can I help you find?" }),
1666
- greeting?.role === "agent" ? /* @__PURE__ */ jsx5("p", { children: greeting.text }) : null
1667
- ] }),
1668
- showIdleFollowUps ? /* @__PURE__ */ jsx5("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ jsx5(
1669
- FollowUpChips,
1670
- {
1671
- suggestions: state.followUps,
1672
- disabled: isBusy,
1673
- label: "Start here",
1674
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
1675
- }
1676
- ) }) : null
1677
- ]
1678
- }
1679
- ) : null,
1680
- transcriptMessages.map((message) => /* @__PURE__ */ jsx5(MessageBubble, { message }, message.id)),
1681
- showActivity ? /* @__PURE__ */ jsx5(
2466
+ message,
2467
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2468
+ onBook
2469
+ },
2470
+ message.id
2471
+ )),
2472
+ showActivity ? /* @__PURE__ */ jsx6(
1682
2473
  AgentActivityBubble,
1683
2474
  {
1684
- brandLabel,
1685
- brandLogoUrl,
2475
+ brandLabel: resolvedBrandLabel,
2476
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
1686
2477
  failed: state.phase === "error",
1687
2478
  steps: state.toolSteps
1688
2479
  }
1689
2480
  ) : null,
1690
- streamingMessage ? /* @__PURE__ */ jsx5(MessageBubble, { message: streamingMessage }) : null,
1691
- completedAnswer ? /* @__PURE__ */ jsx5(MessageBubble, { message: completedAnswer }) : null,
1692
- state.error ? /* @__PURE__ */ jsxs4("section", { className: "agent-rail__error", role: "alert", children: [
1693
- /* @__PURE__ */ jsxs4("div", { children: [
1694
- /* @__PURE__ */ jsx5("strong", { children: "Something went wrong" }),
1695
- /* @__PURE__ */ jsx5("p", { children: state.error })
2481
+ completedAnswer ? /* @__PURE__ */ jsx6(
2482
+ MessageBubble,
2483
+ {
2484
+ message: completedAnswer,
2485
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2486
+ onBook
2487
+ }
2488
+ ) : null,
2489
+ streamingMessage ? /* @__PURE__ */ jsx6(
2490
+ MessageBubble,
2491
+ {
2492
+ message: streamingMessage,
2493
+ brandLogoUrl: showBrandLogo ? resolvedBrandLogoUrl : void 0,
2494
+ offer: state.pendingOffer,
2495
+ onBook
2496
+ }
2497
+ ) : null,
2498
+ state.error ? /* @__PURE__ */ jsxs6("section", { className: "agent-rail__error", role: "alert", children: [
2499
+ /* @__PURE__ */ jsxs6("div", { children: [
2500
+ /* @__PURE__ */ jsx6("strong", { children: "Something went wrong" }),
2501
+ /* @__PURE__ */ jsx6("p", { children: state.error })
1696
2502
  ] }),
1697
- onRetry ? /* @__PURE__ */ jsx5("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
2503
+ onRetry ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: onRetry, children: "Try again" }) : null
1698
2504
  ] }) : null
1699
2505
  ] }) }),
1700
- /* @__PURE__ */ jsxs4("div", { className: "agent-rail__composer-wrap", children: [
1701
- /* @__PURE__ */ jsx5(
2506
+ /* @__PURE__ */ jsxs6("div", { className: "agent-rail__composer-wrap", children: [
2507
+ /* @__PURE__ */ jsx6(
1702
2508
  Composer,
1703
2509
  {
1704
2510
  variant: expanded || mobileFullscreen ? "dock" : "default",
@@ -1707,10 +2513,9 @@ function AgentRail({
1707
2513
  onSubmit
1708
2514
  }
1709
2515
  ),
1710
- /* @__PURE__ */ jsx5("div", { className: "agent-rail__footer", children: /* @__PURE__ */ jsxs4("p", { children: [
1711
- /* @__PURE__ */ jsx5("span", { children: "AI can make mistakes." }),
1712
- /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", children: " \xB7 " }),
1713
- /* @__PURE__ */ jsx5("span", { children: poweredByLabel })
2516
+ /* @__PURE__ */ jsx6("div", { className: "agent-rail__footer", children: /* @__PURE__ */ jsxs6("p", { children: [
2517
+ /* @__PURE__ */ jsx6("span", { children: "AI can make mistakes. Check important info." }),
2518
+ /* @__PURE__ */ jsx6("span", { children: poweredByLabel })
1714
2519
  ] }) })
1715
2520
  ] })
1716
2521
  ]
@@ -1719,9 +2524,9 @@ function AgentRail({
1719
2524
  }
1720
2525
 
1721
2526
  // src/react/components/AssistEdgeTab/AssistEdgeTab.tsx
1722
- import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2527
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1723
2528
  function SparklesIcon() {
1724
- return /* @__PURE__ */ jsxs5(
2529
+ return /* @__PURE__ */ jsxs7(
1725
2530
  "svg",
1726
2531
  {
1727
2532
  className: "assist-edge-tab__sparkles",
@@ -1729,21 +2534,21 @@ function SparklesIcon() {
1729
2534
  fill: "none",
1730
2535
  "aria-hidden": "true",
1731
2536
  children: [
1732
- /* @__PURE__ */ jsx6(
2537
+ /* @__PURE__ */ jsx7(
1733
2538
  "path",
1734
2539
  {
1735
2540
  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",
1736
2541
  fill: "currentColor"
1737
2542
  }
1738
2543
  ),
1739
- /* @__PURE__ */ jsx6(
2544
+ /* @__PURE__ */ jsx7(
1740
2545
  "path",
1741
2546
  {
1742
2547
  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",
1743
2548
  fill: "currentColor"
1744
2549
  }
1745
2550
  ),
1746
- /* @__PURE__ */ jsx6(
2551
+ /* @__PURE__ */ jsx7(
1747
2552
  "path",
1748
2553
  {
1749
2554
  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",
@@ -1754,8 +2559,23 @@ function SparklesIcon() {
1754
2559
  }
1755
2560
  );
1756
2561
  }
2562
+ function TabMarkIcon({ customIconUrl }) {
2563
+ const url = customIconUrl?.trim();
2564
+ if (url) {
2565
+ return /* @__PURE__ */ jsx7(
2566
+ "img",
2567
+ {
2568
+ alt: "",
2569
+ "aria-hidden": true,
2570
+ className: "assist-edge-tab__custom-icon",
2571
+ src: url
2572
+ }
2573
+ );
2574
+ }
2575
+ return /* @__PURE__ */ jsx7(SparklesIcon, {});
2576
+ }
1757
2577
  function ChevronLeftIcon() {
1758
- return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
2578
+ return /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx7(
1759
2579
  "path",
1760
2580
  {
1761
2581
  d: "M10 4L6 8l4 4",
@@ -1767,7 +2587,7 @@ function ChevronLeftIcon() {
1767
2587
  ) });
1768
2588
  }
1769
2589
  function ChevronDownIcon() {
1770
- return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx6(
2590
+ return /* @__PURE__ */ jsx7("svg", { viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx7(
1771
2591
  "path",
1772
2592
  {
1773
2593
  d: "M4 6l4 4 4-4",
@@ -1779,12 +2599,12 @@ function ChevronDownIcon() {
1779
2599
  ) });
1780
2600
  }
1781
2601
  function DragDots() {
1782
- return /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ jsx6("i", {}, index)) });
2602
+ return /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__dots", "aria-hidden": "true", children: Array.from({ length: 12 }, (_, index) => /* @__PURE__ */ jsx7("i", {}, index)) });
1783
2603
  }
1784
2604
  var VARIANT_COPY = {
1785
- outline: { label: "Assist", aria: "Open Assist" },
2605
+ outline: { label: "Ask anything", aria: "Ask anything" },
1786
2606
  ask: { label: "Ask anything", aria: "Ask anything" },
1787
- fill: { label: "Assist", aria: "Open Assist" }
2607
+ fill: { label: "Ask anything", aria: "Ask anything" }
1788
2608
  };
1789
2609
  function AssistEdgeTab({
1790
2610
  variant,
@@ -1793,6 +2613,7 @@ function AssistEdgeTab({
1793
2613
  inset,
1794
2614
  visible,
1795
2615
  label,
2616
+ customIconUrl,
1796
2617
  logoUrl,
1797
2618
  brandColor,
1798
2619
  brandForeground,
@@ -1801,40 +2622,49 @@ function AssistEdgeTab({
1801
2622
  mobile = false,
1802
2623
  surfaceColor,
1803
2624
  textColor,
2625
+ colorScheme = "auto",
1804
2626
  onOpen
1805
2627
  }) {
2628
+ const resolvedColorScheme = useAgentColorScheme(colorScheme);
1806
2629
  const copy = VARIANT_COPY[variant];
1807
2630
  const visibleLabel = label?.trim() || copy.label;
2631
+ const showLogo = Boolean(logoUrl?.trim()) && !customIconUrl?.trim();
2632
+ const resolvedBrandColor = brandColor ?? (resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.brand : void 0);
2633
+ const resolvedBorderColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.border : borderColor;
2634
+ const resolvedSurfaceColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.surface : surfaceColor;
2635
+ const resolvedTextColor = resolvedColorScheme === "dark" ? defaultDarkAgentRailTheme.text : textColor;
1808
2636
  const style = {
1809
2637
  "--tab-along": `${along}%`,
1810
2638
  "--tab-inset": `${inset}px`,
1811
- ...brandColor ? { "--as-brand": brandColor } : {},
2639
+ ...resolvedBrandColor ? { "--as-brand": resolvedBrandColor } : {},
1812
2640
  ...brandForeground ? { "--as-visitor-text": brandForeground } : {},
1813
- ...borderColor ? { "--as-border": borderColor } : {},
2641
+ ...resolvedBorderColor ? { "--as-border": resolvedBorderColor } : {},
1814
2642
  ...fontFamily ? { "--as-font-display": fontFamily } : {},
1815
- ...surfaceColor ? { "--as-surface": surfaceColor } : {},
1816
- ...textColor ? { "--as-text": textColor } : {}
2643
+ ...resolvedSurfaceColor ? { "--as-surface": resolvedSurfaceColor } : {},
2644
+ ...resolvedTextColor ? { "--as-text": resolvedTextColor } : {},
2645
+ colorScheme: resolvedColorScheme
1817
2646
  };
1818
- return /* @__PURE__ */ jsxs5(
2647
+ return /* @__PURE__ */ jsxs7(
1819
2648
  "button",
1820
2649
  {
1821
2650
  type: "button",
1822
2651
  className: `assist-edge-tab assist-edge-tab--${variant} assist-edge-tab--${side}${mobile ? " assist-edge-tab--mobile" : ""}${visible ? " is-visible" : ""}`,
2652
+ "data-color-scheme": resolvedColorScheme,
1823
2653
  style,
1824
2654
  "aria-label": `Open ${visibleLabel}`,
1825
2655
  "aria-hidden": !visible,
1826
2656
  tabIndex: visible ? 0 : -1,
1827
2657
  onClick: onOpen,
1828
2658
  children: [
1829
- mobile ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1830
- /* @__PURE__ */ jsxs5(
2659
+ mobile ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
2660
+ /* @__PURE__ */ jsxs7(
1831
2661
  "span",
1832
2662
  {
1833
2663
  className: "assist-edge-tab__mark assist-edge-tab__mark--mobile",
1834
2664
  "aria-hidden": "true",
1835
2665
  children: [
1836
- visibleLabel.slice(0, 1).toUpperCase(),
1837
- logoUrl ? /* @__PURE__ */ jsx6(
2666
+ /* @__PURE__ */ jsx7(TabMarkIcon, { customIconUrl }),
2667
+ showLogo ? /* @__PURE__ */ jsx7(
1838
2668
  "img",
1839
2669
  {
1840
2670
  className: "assist-edge-tab__logo",
@@ -1848,14 +2678,11 @@ function AssistEdgeTab({
1848
2678
  ]
1849
2679
  }
1850
2680
  ),
1851
- /* @__PURE__ */ jsxs5("span", { className: "assist-edge-tab__label", children: [
1852
- "Ask ",
1853
- visibleLabel
1854
- ] })
1855
- ] }) : variant === "outline" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1856
- /* @__PURE__ */ jsxs5("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1857
- /* @__PURE__ */ jsx6(SparklesIcon, {}),
1858
- logoUrl ? /* @__PURE__ */ jsx6(
2681
+ /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__label", children: visibleLabel })
2682
+ ] }) : variant === "outline" ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
2683
+ /* @__PURE__ */ jsxs7("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
2684
+ /* @__PURE__ */ jsx7(TabMarkIcon, { customIconUrl }),
2685
+ showLogo ? /* @__PURE__ */ jsx7(
1859
2686
  "img",
1860
2687
  {
1861
2688
  className: "assist-edge-tab__logo",
@@ -1867,18 +2694,18 @@ function AssistEdgeTab({
1867
2694
  }
1868
2695
  ) : null
1869
2696
  ] }),
1870
- /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1871
- /* @__PURE__ */ jsx6(ChevronDownIcon, {})
2697
+ /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2698
+ /* @__PURE__ */ jsx7(ChevronDownIcon, {})
1872
2699
  ] }) : null,
1873
- variant === "ask" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1874
- /* @__PURE__ */ jsx6(ChevronLeftIcon, {}),
1875
- /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1876
- /* @__PURE__ */ jsx6(DragDots, {})
2700
+ variant === "ask" ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
2701
+ /* @__PURE__ */ jsx7(ChevronLeftIcon, {}),
2702
+ /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2703
+ /* @__PURE__ */ jsx7(DragDots, {})
1877
2704
  ] }) : null,
1878
- variant === "fill" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1879
- /* @__PURE__ */ jsxs5("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
1880
- /* @__PURE__ */ jsx6(SparklesIcon, {}),
1881
- logoUrl ? /* @__PURE__ */ jsx6(
2705
+ variant === "fill" ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
2706
+ /* @__PURE__ */ jsxs7("span", { className: "assist-edge-tab__mark", "aria-hidden": "true", children: [
2707
+ /* @__PURE__ */ jsx7(TabMarkIcon, { customIconUrl }),
2708
+ showLogo ? /* @__PURE__ */ jsx7(
1882
2709
  "img",
1883
2710
  {
1884
2711
  className: "assist-edge-tab__logo",
@@ -1890,8 +2717,8 @@ function AssistEdgeTab({
1890
2717
  }
1891
2718
  ) : null
1892
2719
  ] }),
1893
- /* @__PURE__ */ jsx6("span", { className: "assist-edge-tab__label", children: visibleLabel }),
1894
- /* @__PURE__ */ jsx6(ChevronLeftIcon, {})
2720
+ /* @__PURE__ */ jsx7("span", { className: "assist-edge-tab__label", children: visibleLabel }),
2721
+ /* @__PURE__ */ jsx7(ChevronLeftIcon, {})
1895
2722
  ] }) : null
1896
2723
  ]
1897
2724
  }
@@ -1899,7 +2726,7 @@ function AssistEdgeTab({
1899
2726
  }
1900
2727
 
1901
2728
  // src/react/components/AgentWidget/AgentWidget.tsx
1902
- import { useEffect as useEffect5, useRef as useRef4, useState as useState4 } from "react";
2729
+ import { useEffect as useEffect5, useRef as useRef4, useState as useState8 } from "react";
1903
2730
 
1904
2731
  // src/react/page-shift.ts
1905
2732
  import { useEffect as useEffect3 } from "react";
@@ -1982,9 +2809,9 @@ function usePageShift(input) {
1982
2809
  }
1983
2810
 
1984
2811
  // src/react/hooks/useIsMobile.ts
1985
- import { useEffect as useEffect4, useState as useState3 } from "react";
2812
+ import { useEffect as useEffect4, useState as useState7 } from "react";
1986
2813
  function useIsMobile(breakpoint = 767) {
1987
- const [isMobile, setIsMobile] = useState3(
2814
+ const [isMobile, setIsMobile] = useState7(
1988
2815
  () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
1989
2816
  );
1990
2817
  useEffect4(() => {
@@ -2013,7 +2840,7 @@ function closeAgentPanel(customerId) {
2013
2840
  }
2014
2841
 
2015
2842
  // src/react/components/AgentWidget/AgentWidget.tsx
2016
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2843
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2017
2844
  function AgentWidget({
2018
2845
  indexId,
2019
2846
  customerId,
@@ -2030,8 +2857,8 @@ function AgentWidget({
2030
2857
  const isMobile = useIsMobile();
2031
2858
  const placement = normalizeAgentPlacement(placementInput);
2032
2859
  const railSlotRef = useRef4(null);
2033
- const [railCollapsed, setRailCollapsed] = useState4(defaultCollapsed);
2034
- const [railExpanded, setRailExpanded] = useState4(false);
2860
+ const [railCollapsed, setRailCollapsed] = useState8(defaultCollapsed);
2861
+ const [railExpanded, setRailExpanded] = useState8(false);
2035
2862
  const pageShiftActive = shouldApplyPageShift({
2036
2863
  pageShift,
2037
2864
  isMobile,
@@ -2051,7 +2878,8 @@ function AgentWidget({
2051
2878
  runtimeOrigin,
2052
2879
  greeting: branding?.greeting
2053
2880
  });
2054
- const agentName = branding?.agentName ?? "Webless Guide";
2881
+ const agentName = branding?.agentName ?? "";
2882
+ const tabLabel = branding?.tabLabel ?? agentName;
2055
2883
  const theme = {
2056
2884
  ...branding?.fontFamily ? { fontBody: branding.fontFamily, fontDisplay: branding.fontFamily } : {},
2057
2885
  ...branding?.colors?.primary ? {
@@ -2069,7 +2897,6 @@ function AgentWidget({
2069
2897
  } : {},
2070
2898
  ...branding?.colors?.border ? { border: branding.colors.border } : {}
2071
2899
  };
2072
- const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);
2073
2900
  useEffect5(() => {
2074
2901
  if (!registerPanelController) return;
2075
2902
  registerAgentPanelController(customerId, {
@@ -2119,64 +2946,44 @@ function AgentWidget({
2119
2946
  window.addEventListener("keydown", handleKeyDown);
2120
2947
  return () => window.removeEventListener("keydown", handleKeyDown);
2121
2948
  }, [isMobile, railCollapsed, railExpanded]);
2122
- return /* @__PURE__ */ jsxs6("div", { className: "webless-agent-root", children: [
2123
- /* @__PURE__ */ jsxs6(
2949
+ return /* @__PURE__ */ jsxs8("div", { className: "webless-agent-root", children: [
2950
+ /* @__PURE__ */ jsx8(
2124
2951
  "div",
2125
2952
  {
2126
2953
  className: `webless-agent-root__shell${railCollapsed ? " webless-agent-root__shell--collapsed" : ""}${railExpanded ? " webless-agent-root__shell--expanded" : ""}`,
2127
- children: [
2128
- /* @__PURE__ */ jsx7(
2129
- "div",
2130
- {
2131
- ref: railSlotRef,
2132
- className: "webless-agent-root__rail-slot",
2133
- inert: railCollapsed || void 0,
2134
- "aria-hidden": railCollapsed,
2135
- children: /* @__PURE__ */ jsx7(
2136
- AgentRail,
2137
- {
2138
- theme,
2139
- brandLabel: agentName,
2140
- brandLogoUrl: branding?.logoUrl,
2141
- composerPlaceholder: branding?.composerPlaceholder ?? `Ask ${agentName}\u2026`,
2142
- poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
2143
- state: idle ? {
2144
- ...state,
2145
- followUps: createIdleSuggestions()
2146
- } : state,
2147
- mobileFullscreen: isMobile && !railCollapsed,
2148
- expanded: railExpanded,
2149
- onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
2150
- onClose: isMobile ? () => setRailCollapsed(true) : void 0,
2151
- onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
2152
- onSubmit: handleSubmit,
2153
- onReset: reset,
2154
- onRetry: () => void retry(),
2155
- onFollowUpSelect: (label) => void handleSubmit(label)
2156
- }
2157
- )
2158
- }
2159
- ),
2160
- !railCollapsed && isMobile || !isMobile && railExpanded && !railCollapsed ? /* @__PURE__ */ jsx7(
2161
- "button",
2162
- {
2163
- type: "button",
2164
- className: `webless-agent-root__backdrop${isMobile ? " webless-agent-root__backdrop--mobile" : ""}`,
2165
- tabIndex: -1,
2166
- "aria-label": isMobile ? "Close agent" : "Exit focus view",
2167
- onClick: () => {
2168
- if (isMobile) {
2169
- setRailCollapsed(true);
2170
- } else {
2171
- setRailExpanded(false);
2172
- }
2954
+ children: /* @__PURE__ */ jsx8(
2955
+ "div",
2956
+ {
2957
+ ref: railSlotRef,
2958
+ className: "webless-agent-root__rail-slot",
2959
+ inert: railCollapsed || void 0,
2960
+ "aria-hidden": railCollapsed,
2961
+ children: /* @__PURE__ */ jsx8(
2962
+ AgentRail,
2963
+ {
2964
+ theme,
2965
+ brandLabel: agentName,
2966
+ brandLogoUrl: branding?.logoUrl,
2967
+ composerPlaceholder: branding?.composerPlaceholder ?? "Ask a question\u2026",
2968
+ poweredByLabel: branding?.poweredByLabel ?? "Powered by Webless",
2969
+ state,
2970
+ mobileFullscreen: isMobile && !railCollapsed,
2971
+ expanded: railExpanded,
2972
+ onCollapse: !isMobile ? () => setRailCollapsed(true) : void 0,
2973
+ onClose: isMobile ? () => setRailCollapsed(true) : void 0,
2974
+ onExpandToggle: !isMobile ? () => setRailExpanded((current) => !current) : void 0,
2975
+ onSubmit: handleSubmit,
2976
+ onReset: reset,
2977
+ onRetry: () => void retry(),
2978
+ onFollowUpSelect: (label) => void handleSubmit(label),
2979
+ onBook: (input) => void submit(input.displayText, { runtimeText: input.runtimeText })
2173
2980
  }
2174
- }
2175
- ) : null
2176
- ]
2981
+ )
2982
+ }
2983
+ )
2177
2984
  }
2178
2985
  ),
2179
- railCollapsed ? /* @__PURE__ */ jsx7(
2986
+ railCollapsed ? /* @__PURE__ */ jsx8(
2180
2987
  AssistEdgeTab,
2181
2988
  {
2182
2989
  variant: placement.variant,
@@ -2184,7 +2991,8 @@ function AgentWidget({
2184
2991
  along: placement.along,
2185
2992
  inset: placement.inset,
2186
2993
  visible: true,
2187
- label: agentName,
2994
+ label: tabLabel,
2995
+ customIconUrl: branding?.tabIconUrl,
2188
2996
  logoUrl: branding?.logoUrl,
2189
2997
  brandColor: branding?.colors?.primary,
2190
2998
  brandForeground: branding?.colors?.primaryForeground,
@@ -2211,8 +3019,9 @@ export {
2211
3019
  openAgentPanel,
2212
3020
  closeAgentPanel,
2213
3021
  defaultAgentRailTheme,
3022
+ defaultDarkAgentRailTheme,
2214
3023
  AgentRail,
2215
3024
  AssistEdgeTab,
2216
3025
  AgentWidget
2217
3026
  };
2218
- //# sourceMappingURL=chunk-SVWXFDV3.js.map
3027
+ //# sourceMappingURL=chunk-Y7Z3ZIAQ.js.map