@eshal-bot/chat-widget 0.1.41 → 0.1.43

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.
@@ -8582,31 +8582,34 @@
8582
8582
  };
8583
8583
 
8584
8584
  /**
8585
- * Constructs the WebSocket URL.
8586
- * @param {string} apiBaseUrl - The base URL of the API (used when wsBaseUrl is not provided).
8587
- * @param {string} [wsBaseUrl] - Explicit WebSocket base URL (e.g. wss://knowledge-api-prod.eshal.ai/ws).
8588
- * When provided, it is returned directly without any transformation.
8589
- * @returns {string} The WebSocket base URL.
8585
+ * Constructs the voice WebSocket base URL — ALWAYS derived from the API
8586
+ * base URL the widget is embedded with, as `wss://{host}/voice/ws`.
8587
+ *
8588
+ * `/voice/ws/{org}/{session}` is terminated by the frontend custom server
8589
+ * (packages/frontend/server.js), which proxies it in-cluster to
8590
+ * knowledge-api on `/ws`. The widget always talks to the same origin it
8591
+ * loaded its config from, so the WS endpoint is fully determined by
8592
+ * `apiBaseUrl`. There is intentionally NO override parameter: voice can
8593
+ * never be pointed at the (no-longer-public) knowledge-api LoadBalancer by
8594
+ * a stale embed option or backend config value.
8595
+ *
8596
+ * @param {string} apiBaseUrl - Frontend base URL, e.g. https://demo.eshal.ai
8597
+ * @returns {string} Voice WS base, e.g. wss://demo.eshal.ai/voice/ws
8590
8598
  */
8591
- const getWebSocketUrl = (apiBaseUrl, wsBaseUrl) => {
8592
- if (wsBaseUrl) {
8593
- return wsBaseUrl.replace(/\/$/, '');
8594
- }
8599
+ const getWebSocketUrl = apiBaseUrl => {
8595
8600
  if (!apiBaseUrl) {
8596
- // Default fallback if no API base URL is provided
8601
+ // No base URL yet (config still loading) — safe public default.
8597
8602
  return 'wss://dev.eshal.ai/voice/ws';
8598
8603
  }
8599
8604
  try {
8600
8605
  const url = new URL(apiBaseUrl);
8601
- // Replace http/https with ws/wss
8606
+ // http→ws, https→wss; point at the frontend voice-proxy path.
8602
8607
  url.protocol = url.protocol.replace(/^http/, 'ws');
8603
- // Append the WebSocket specific path
8604
8608
  url.pathname = '/voice/ws';
8605
- // Ensure it returns a clean URL without extra slashes
8609
+ // Clean URL without a trailing slash.
8606
8610
  return url.toString().replace(/\/$/, '');
8607
8611
  } catch (error) {
8608
8612
  console.error("Invalid API base URL provided for WebSocket:", error);
8609
- // Fallback to default if URL parsing fails
8610
8613
  return 'wss://dev.eshal.ai/voice/ws';
8611
8614
  }
8612
8615
  };
@@ -8926,13 +8929,17 @@
8926
8929
  welcomeMessage,
8927
8930
  quickQuestions = [],
8928
8931
  apiBaseUrl,
8929
- wsBaseUrl,
8930
8932
  apiKey,
8931
8933
  organizationId,
8932
8934
  autoOpen,
8933
8935
  openDelay,
8934
8936
  darkMode,
8935
8937
  enableVoiceInteraction = false,
8938
+ // behavior.provideCitations — forwarded as enable_citation on the chat
8939
+ // request so the backend skips citation generation when disabled. The
8940
+ // Sources render is also gated in MessageBubble (defence-in-depth, same
8941
+ // as the dashboard testing lab). Defaults to true.
8942
+ provideCitations = true,
8936
8943
  onboardingQuestions = [],
8937
8944
  onboardingEnabled = false,
8938
8945
  collectionPrompt,
@@ -9077,8 +9084,9 @@
9077
9084
  const visitorProfileRef = reactExports.useRef(null);
9078
9085
  const websocketUrl = reactExports.useMemo(() => {
9079
9086
  if (!organizationId) return null;
9080
- // Construct WebSocket URL from the API base URL
9081
- const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl, wsBaseUrl);
9087
+ // Voice WS base is always derived from apiBaseUrl (the frontend origin
9088
+ // the widget is embedded with) → wss://{host}/voice/ws. No override.
9089
+ const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl);
9082
9090
  return "".concat(resolvedWsBaseUrl, "/").concat(organizationId, "/").concat(bidiSessionId);
9083
9091
  }, [apiBaseUrl, organizationId, bidiSessionId]);
9084
9092
  const getNextMessageId = reactExports.useCallback(() => {
@@ -9455,6 +9463,11 @@
9455
9463
  message: trimmed,
9456
9464
  type: apiType,
9457
9465
  conversationId: bidiSessionId,
9466
+ // Citation toggle (behavior.provideCitations). Mirrors the testing
9467
+ // lab's useChat.ts so knowledge-api skips citation generation when
9468
+ // the admin disabled them. The Sources panel is also gated client-
9469
+ // side in MessageBubble (defence-in-depth). Defaults to on.
9470
+ enable_citation: provideCitations !== null && provideCitations !== void 0 ? provideCitations : true,
9458
9471
  // Identify this as a widget call so the knowledge-api side
9459
9472
  // (chat_utils.EXTERNAL_CHAT_CHANNELS in services/
9460
9473
  // knowledge-api) can enforce the visibility ACL — private
@@ -9559,7 +9572,7 @@
9559
9572
  setIsLoading(false);
9560
9573
  }
9561
9574
  }
9562
- }, [apiBaseUrl, apiKey, getNextMessageId, organizationId, messages, bidiSessionId, routingChoice, widgetFilter]);
9575
+ }, [apiBaseUrl, apiKey, getNextMessageId, organizationId, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
9563
9576
  const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
9564
9577
  const questions = getOnboardingQuestions(onboardingQuestions);
9565
9578
  if (questions.length === 0) return;
@@ -31823,7 +31836,7 @@
31823
31836
  if (typeof document === 'undefined') {
31824
31837
  return null;
31825
31838
  }
31826
- if ('currentScript' in document && 1 < 2 /* hack to trip TS' flow analysis */) {
31839
+ if (document.currentScript && document.currentScript.tagName === 'SCRIPT' && 1 < 2 /* hack to trip TS' flow analysis */) {
31827
31840
  return /** @type {any} */document.currentScript;
31828
31841
  }
31829
31842
 
@@ -54629,6 +54642,7 @@
54629
54642
  organizationId,
54630
54643
  onPromptSuggestionClick,
54631
54644
  allowFeedback = true,
54645
+ provideCitations = true,
54632
54646
  onFeedback,
54633
54647
  headerTextColor
54634
54648
  } = _ref17;
@@ -54737,7 +54751,7 @@
54737
54751
  }), message.decisionResponse && /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
54738
54752
  className: "mt-2 text-xs ".concat(isDark ? "text-gray-400" : "text-gray-500"),
54739
54753
  children: ["Selected: ", message.decisionResponse.selectedOption]
54740
- }), ((_message$sources = message.sources) === null || _message$sources === void 0 ? void 0 : _message$sources.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54754
+ }), provideCitations !== false && ((_message$sources = message.sources) === null || _message$sources === void 0 ? void 0 : _message$sources.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54741
54755
  children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
54742
54756
  className: "mt-3",
54743
54757
  style: {
@@ -54762,7 +54776,7 @@
54762
54776
  textColor: messageTextColor,
54763
54777
  fontFamily: messageFontFamily,
54764
54778
  fontSize: messageFontSize
54765
- }), ((_message$sources2 = message.sources) === null || _message$sources2 === void 0 ? void 0 : _message$sources2.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54779
+ }), provideCitations !== false && ((_message$sources2 = message.sources) === null || _message$sources2 === void 0 ? void 0 : _message$sources2.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54766
54780
  children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
54767
54781
  className: "mt-3",
54768
54782
  style: {
@@ -54905,6 +54919,7 @@
54905
54919
  organizationId,
54906
54920
  onPromptSuggestionClick,
54907
54921
  allowFeedback,
54922
+ provideCitations,
54908
54923
  onFeedback,
54909
54924
  headerTextColor
54910
54925
  } = _ref19;
@@ -54948,6 +54963,7 @@
54948
54963
  organizationId: organizationId,
54949
54964
  onPromptSuggestionClick: onPromptSuggestionClick,
54950
54965
  allowFeedback: allowFeedback,
54966
+ provideCitations: provideCitations,
54951
54967
  onFeedback: onFeedback,
54952
54968
  headerTextColor: headerTextColor
54953
54969
  });
@@ -55803,6 +55819,7 @@
55803
55819
  onPromptSuggestionClick,
55804
55820
  conversationResetKey,
55805
55821
  allowFeedback,
55822
+ provideCitations,
55806
55823
  onFeedback,
55807
55824
  headerTextColor,
55808
55825
  // Inline filter slots — rendered in the initial-state region
@@ -55958,6 +55975,7 @@
55958
55975
  apiBaseUrl: apiBaseUrl,
55959
55976
  organizationId: organizationId,
55960
55977
  allowFeedback: allowFeedback,
55978
+ provideCitations: provideCitations,
55961
55979
  onFeedback: onFeedback,
55962
55980
  headerTextColor: headerTextColor
55963
55981
  }, message.id)), (() => {
@@ -59579,6 +59597,7 @@
59579
59597
  onResetConversation,
59580
59598
  autoPromptEnabled = true,
59581
59599
  allowFeedback = true,
59600
+ provideCitations = true,
59582
59601
  onFeedback,
59583
59602
  onRefreshChat,
59584
59603
  onCopyChat,
@@ -60117,6 +60136,7 @@
60117
60136
  organizationId: organizationId,
60118
60137
  conversationResetKey: conversationId,
60119
60138
  allowFeedback: allowFeedback,
60139
+ provideCitations: provideCitations,
60120
60140
  onFeedback: onFeedback,
60121
60141
  headerTextColor: headerTextColor,
60122
60142
  beforePromptsSlot: showInlineFilter ? /*#__PURE__*/jsxRuntimeExports.jsx(CategoryFilterInline, {
@@ -60383,7 +60403,6 @@
60383
60403
  // Required props for dynamic config
60384
60404
  orgId,
60385
60405
  apiBaseUrl,
60386
- wsBaseUrl,
60387
60406
  // Optional override props (will override fetched config if provided)
60388
60407
  darkMode,
60389
60408
  primaryColor,
@@ -60455,7 +60474,7 @@
60455
60474
 
60456
60475
  // Map API response to widget props (use props as overrides if provided)
60457
60476
  const widgetConfig = reactExports.useMemo(() => {
60458
- var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba;
60477
+ var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita;
60459
60478
  if (!agentConfig) {
60460
60479
  return null;
60461
60480
  }
@@ -60546,7 +60565,12 @@
60546
60565
  csatFollowUpEnabled: (_csat$followUpEnabled = csat.followUpEnabled) !== null && _csat$followUpEnabled !== void 0 ? _csat$followUpEnabled : false,
60547
60566
  csatFollowUpPrompt: (_csat$followUpPrompt = csat.followUpPrompt) !== null && _csat$followUpPrompt !== void 0 ? _csat$followUpPrompt : null,
60548
60567
  autoPromptEnabled: (_behavior$autoPromptE = behavior.autoPromptEnabled) !== null && _behavior$autoPromptE !== void 0 ? _behavior$autoPromptE : true,
60549
- allowFeedback: (_behavior$allowFeedba = behavior.allowFeedback) !== null && _behavior$allowFeedba !== void 0 ? _behavior$allowFeedba : true
60568
+ allowFeedback: (_behavior$allowFeedba = behavior.allowFeedback) !== null && _behavior$allowFeedba !== void 0 ? _behavior$allowFeedba : true,
60569
+ // behavior.provideCitations — when false, hide the Sources panel on
60570
+ // every message and tell the backend to skip citation generation.
60571
+ // Defaults to true so agents that haven't set the field keep showing
60572
+ // citations (matches the testing-lab / chatbot-preview default).
60573
+ provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
60550
60574
  };
60551
60575
  }, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage, orgId, headerTextBold, headerTextItalic, enableVoiceInteraction, disclaimerEnabled, disclaimerText, disclaimerPosition, disclaimerLinkLabel, disclaimerLinkUrl, showPoweredBy, onboardingQuestions, onboardingEnabled, collectionPrompt, allowedDomains, agentMessageBubbleFontFamily, agentMessageBubbleFontSize, userMessageBubbleFontFamily, userMessageBubbleFontSize
60552
60576
  // CSAT dependencies
@@ -60566,7 +60590,6 @@
60566
60590
  welcomeMessage: "Hi! How can we help?",
60567
60591
  quickQuestions: [],
60568
60592
  apiBaseUrl: apiBaseUrl || "",
60569
- wsBaseUrl: agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.wsBaseUrl,
60570
60593
  apiKey: apiKey || "",
60571
60594
  organizationId: orgId || "",
60572
60595
  autoOpen: false,
@@ -60576,7 +60599,7 @@
60576
60599
  onboardingQuestions: [],
60577
60600
  onboardingEnabled: false,
60578
60601
  collectionPrompt: undefined
60579
- }), [apiBaseUrl, wsBaseUrl, apiKey, orgId]);
60602
+ }), [apiBaseUrl, apiKey, orgId]);
60580
60603
 
60581
60604
  // ALWAYS call hooks before any early returns (Rules of Hooks)
60582
60605
  const {
@@ -60617,7 +60640,11 @@
60617
60640
  welcomeMessage: widgetConfig.welcomeMessage,
60618
60641
  quickQuestions: widgetConfig.quickQuestions,
60619
60642
  apiBaseUrl,
60620
- wsBaseUrl: agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.wsBaseUrl,
60643
+ // Voice WS endpoint is derived entirely from apiBaseUrl inside
60644
+ // useChatState (→ wss://{host}/voice/ws, the frontend voice proxy).
60645
+ // Nothing is forwarded/overridable here, so a stale backend or embed
60646
+ // wsBaseUrl can't point voice at the old (now non-public)
60647
+ // knowledge-api LoadBalancer.
60621
60648
  apiKey,
60622
60649
  organizationId: widgetConfig.organizationId,
60623
60650
  autoOpen,
@@ -60627,6 +60654,9 @@
60627
60654
  onboardingQuestions: widgetConfig.onboardingQuestions,
60628
60655
  onboardingEnabled: widgetConfig.onboardingEnabled,
60629
60656
  collectionPrompt: widgetConfig.collectionPrompt,
60657
+ // Gates the chat request's enable_citation flag (text path). Voice
60658
+ // already reads provideCitations from the DB in voice_chat.py.
60659
+ provideCitations: widgetConfig.provideCitations,
60630
60660
  inactivityTimeoutValue: widgetConfig.inactivityTimeoutValue,
60631
60661
  inactivityTimeoutUnit: widgetConfig.inactivityTimeoutUnit,
60632
60662
  // Sliding-window cap for the localStorage transcript. Comes from
@@ -60793,6 +60823,7 @@
60793
60823
  onResetConversation: resetConversation,
60794
60824
  autoPromptEnabled: widgetConfig.autoPromptEnabled,
60795
60825
  allowFeedback: widgetConfig.allowFeedback,
60826
+ provideCitations: widgetConfig.provideCitations,
60796
60827
  onFeedback: handleMessageFeedback,
60797
60828
  onRefreshChat: handleRefreshChat,
60798
60829
  onCopyChat: handleCopyChat