@eshal-bot/chat-widget 0.1.67 → 0.1.69

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.
@@ -8010,20 +8010,21 @@
8010
8010
  * Sends a streaming chat request using fetch
8011
8011
  * @param {object} request - Chat request object
8012
8012
  * @param {string} apiBaseUrl - Base URL for the API
8013
+ * @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
8013
8014
  */
8014
- const sendStreamingRequest = async (request, apiBaseUrl, organizationId, secretKey) => {
8015
+ const sendStreamingRequest = async (request, apiBaseUrl, getAuthToken) => {
8015
8016
  const headers = {
8016
8017
  'Content-Type': 'application/json',
8017
8018
  Accept: 'text/event-stream'
8018
8019
  };
8019
8020
 
8020
- // Mode A — secret key (direct embed): key replaces orgId in the header
8021
- // Mode B — proxy (@eshal-bot/server): no header from widget; proxy injects x-eshal-sig + x-eshal-org
8022
- // Mode C — legacy: orgId sent directly
8023
- if (secretKey) {
8024
- headers['x-eshal-key'] = secretKey;
8025
- } else if (organizationId) {
8026
- headers['x-eshal-org'] = organizationId;
8021
+ // In-app JWT path: call getAuthToken() and set Authorization header
8022
+ // Mode B proxy: no auth header from widget; proxy injects x-eshal-sig + x-eshal-org server-side
8023
+ if (typeof getAuthToken === 'function') {
8024
+ const token = await getAuthToken();
8025
+ if (token) {
8026
+ headers['Authorization'] = "Bearer ".concat(token);
8027
+ }
8027
8028
  }
8028
8029
  const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
8029
8030
  method: 'POST',
@@ -8792,15 +8793,12 @@
8792
8793
  */
8793
8794
  const getWebSocketUrl = apiBaseUrl => {
8794
8795
  if (!apiBaseUrl) {
8795
- // No base URL yet (config still loading) — safe public default.
8796
8796
  return 'wss://dev.eshal.ai/voice/ws';
8797
8797
  }
8798
8798
  try {
8799
8799
  const url = new URL(apiBaseUrl);
8800
- // http→ws, https→wss; point at the frontend voice-proxy path.
8801
8800
  url.protocol = url.protocol.replace(/^http/, 'ws');
8802
8801
  url.pathname = '/voice/ws';
8803
- // Clean URL without a trailing slash.
8804
8802
  return url.toString().replace(/\/$/, '');
8805
8803
  } catch (error) {
8806
8804
  console.error("Invalid API base URL provided for WebSocket:", error);
@@ -9146,7 +9144,7 @@
9146
9144
  apiBaseUrl,
9147
9145
  apiKey,
9148
9146
  organizationId,
9149
- secretKey,
9147
+ getAuthToken,
9150
9148
  autoOpen,
9151
9149
  openDelay,
9152
9150
  darkMode,
@@ -9314,8 +9312,6 @@
9314
9312
  const visitorProfileRef = reactExports.useRef(null);
9315
9313
  const websocketUrl = reactExports.useMemo(() => {
9316
9314
  if (!organizationId) return null;
9317
- // Voice WS base is always derived from apiBaseUrl (the frontend origin
9318
- // the widget is embedded with) → wss://{host}/voice/ws. No override.
9319
9315
  const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl);
9320
9316
  return "".concat(resolvedWsBaseUrl, "/").concat(organizationId, "/").concat(bidiSessionId);
9321
9317
  }, [apiBaseUrl, organizationId, bidiSessionId]);
@@ -9783,7 +9779,7 @@
9783
9779
  if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
9784
9780
  // For onboarding fields, just consume the stream without processing.
9785
9781
  // Single attempt — no retry, no user-visible partial state to clean up.
9786
- const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId, secretKey);
9782
+ const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
9787
9783
  const reader = stream.getReader();
9788
9784
  const decoder = new TextDecoder();
9789
9785
  while (true) {
@@ -9802,7 +9798,7 @@
9802
9798
  let lastErr;
9803
9799
  for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
9804
9800
  try {
9805
- const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId, secretKey);
9801
+ const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
9806
9802
  await processStream(stream, setMessages);
9807
9803
  updateActivity(organizationId);
9808
9804
  return {
@@ -9830,7 +9826,7 @@
9830
9826
 
9831
9827
  // Non-chat, non-onboarding apiType (shouldn't currently happen):
9832
9828
  // fire and forget, single attempt.
9833
- await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
9829
+ await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
9834
9830
  updateActivity(organizationId);
9835
9831
  return {
9836
9832
  success: true
@@ -9866,7 +9862,7 @@
9866
9862
  // UX-M09: release the concurrency guard so the next send can proceed.
9867
9863
  isSendingRef.current = false;
9868
9864
  }
9869
- }, [apiBaseUrl, apiKey, getNextMessageId, organizationId, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
9865
+ }, [apiBaseUrl, apiKey, getNextMessageId, getAuthToken, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
9870
9866
  const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
9871
9867
  const questions = getOnboardingQuestions(onboardingQuestions);
9872
9868
  if (questions.length === 0) return;
@@ -94384,7 +94380,7 @@
94384
94380
  let {
94385
94381
  sources = [],
94386
94382
  apiBaseUrl,
94387
- organizationId,
94383
+ getAuthToken,
94388
94384
  isDark,
94389
94385
  textColor,
94390
94386
  fontFamily,
@@ -94410,7 +94406,7 @@
94410
94406
  // corpora that have none of the above.
94411
94407
  const _downloadKey = s => s.url || s.source_id || s.source_name || "";
94412
94408
  const handleFileDownload = async source => {
94413
- if (!apiBaseUrl || !organizationId) return;
94409
+ if (!apiBaseUrl) return;
94414
94410
  // SharePoint proxy URLs are GET endpoints that stream the file
94415
94411
  // directly via Microsoft Graph using the stored OAuth credential.
94416
94412
  // Bypass the legacy POST /file-download lookup (which expects an
@@ -94436,11 +94432,14 @@
94436
94432
  // set, or future deployments that set it) pass through
94437
94433
  // unchanged.
94438
94434
  const spUrl = source.url.startsWith("http") ? source.url : "".concat(apiBaseUrl.replace(/\/$/, "")).concat(source.url);
94435
+ const spHeaders = {};
94436
+ if (typeof getAuthToken === 'function') {
94437
+ const token = await getAuthToken();
94438
+ if (token) spHeaders['Authorization'] = "Bearer ".concat(token);
94439
+ }
94439
94440
  res = await fetch(spUrl, {
94440
94441
  method: "GET",
94441
- headers: {
94442
- "x-eshal-org": organizationId
94443
- },
94442
+ headers: spHeaders,
94444
94443
  credentials: "include"
94445
94444
  });
94446
94445
  } else {
@@ -94456,15 +94455,18 @@
94456
94455
  // - source.source_name (the user's original filename)
94457
94456
  // Sending both lets the server skip the Milvus resolution
94458
94457
  // AND advertise the readable name in Content-Disposition.
94458
+ const dlHeaders = {
94459
+ "Content-Type": "application/json"
94460
+ };
94461
+ if (typeof getAuthToken === 'function') {
94462
+ const token = await getAuthToken();
94463
+ if (token) dlHeaders['Authorization'] = "Bearer ".concat(token);
94464
+ }
94459
94465
  res = await fetch(url, {
94460
94466
  method: "POST",
94461
- headers: {
94462
- "Content-Type": "application/json",
94463
- "x-eshal-org": organizationId
94464
- },
94467
+ headers: dlHeaders,
94465
94468
  credentials: "include",
94466
94469
  body: JSON.stringify({
94467
- org_id: organizationId,
94468
94470
  source_id: source.source_id,
94469
94471
  file_name: source.url || "",
94470
94472
  source_name: source.source_name || ""
@@ -94649,7 +94651,7 @@
94649
94651
  conciergeName,
94650
94652
  companyName,
94651
94653
  apiBaseUrl,
94652
- organizationId,
94654
+ getAuthToken,
94653
94655
  onPromptSuggestionClick,
94654
94656
  allowFeedback = true,
94655
94657
  provideCitations = true,
@@ -94821,7 +94823,7 @@
94821
94823
  }), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
94822
94824
  sources: message.sources,
94823
94825
  apiBaseUrl: apiBaseUrl,
94824
- organizationId: organizationId,
94826
+ getAuthToken: getAuthToken,
94825
94827
  isDark: isDark,
94826
94828
  textColor: messageTextColor,
94827
94829
  fontFamily: messageFontFamily,
@@ -94847,7 +94849,7 @@
94847
94849
  }), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
94848
94850
  sources: message.sources,
94849
94851
  apiBaseUrl: apiBaseUrl,
94850
- organizationId: organizationId,
94852
+ getAuthToken: getAuthToken,
94851
94853
  isDark: isDark,
94852
94854
  textColor: messageTextColor,
94853
94855
  fontFamily: messageFontFamily,
@@ -95027,7 +95029,7 @@
95027
95029
  conciergeName,
95028
95030
  companyName,
95029
95031
  apiBaseUrl,
95030
- organizationId,
95032
+ getAuthToken,
95031
95033
  onPromptSuggestionClick,
95032
95034
  allowFeedback,
95033
95035
  provideCitations,
@@ -95071,7 +95073,7 @@
95071
95073
  conciergeName: conciergeName,
95072
95074
  companyName: companyName,
95073
95075
  apiBaseUrl: apiBaseUrl,
95074
- organizationId: organizationId,
95076
+ getAuthToken: getAuthToken,
95075
95077
  onPromptSuggestionClick: onPromptSuggestionClick,
95076
95078
  allowFeedback: allowFeedback,
95077
95079
  provideCitations: provideCitations,
@@ -95916,7 +95918,7 @@
95916
95918
  conciergeName,
95917
95919
  companyName,
95918
95920
  apiBaseUrl,
95919
- organizationId,
95921
+ getAuthToken,
95920
95922
  onPromptSuggestionClick,
95921
95923
  conversationResetKey,
95922
95924
  allowFeedback,
@@ -96098,7 +96100,7 @@
96098
96100
  conciergeName: conciergeName,
96099
96101
  companyName: companyName,
96100
96102
  apiBaseUrl: apiBaseUrl,
96101
- organizationId: organizationId,
96103
+ getAuthToken: getAuthToken,
96102
96104
  allowFeedback: allowFeedback,
96103
96105
  provideCitations: provideCitations,
96104
96106
  onFeedback: onFeedback,
@@ -96130,7 +96132,7 @@
96130
96132
  conciergeName: conciergeName,
96131
96133
  companyName: companyName,
96132
96134
  apiBaseUrl: apiBaseUrl,
96133
- organizationId: organizationId,
96135
+ getAuthToken: getAuthToken,
96134
96136
  headerTextColor: headerTextColor
96135
96137
  }, "skeleton-waiting");
96136
96138
  }
@@ -99645,8 +99647,7 @@
99645
99647
  showPoweredBy = true,
99646
99648
  apiBaseUrl,
99647
99649
  apiKey,
99648
- organizationId,
99649
- secretKey,
99650
+ getAuthToken,
99650
99651
  conversationId,
99651
99652
  conciergeId,
99652
99653
  onboardingQuestions = [],
@@ -99794,7 +99795,7 @@
99794
99795
  setCsatSubmitted(submitted);
99795
99796
  if (submitted) setShowCsat(false);
99796
99797
  } catch (_unused4) {}
99797
- }, [conversationId, organizationId]);
99798
+ }, [conversationId]);
99798
99799
 
99799
99800
  // Auto-scroll hook
99800
99801
  useAutoScroll(messagesContainerRef, [messages, isLoading], {
@@ -99932,14 +99933,15 @@
99932
99933
  const handleCsatRating = async (rating, format) => {
99933
99934
  setCsatSubmitted(true); // optimistic UI
99934
99935
  setShowCsat(false);
99935
- markCsatSubmitted(organizationId); // persist so page refresh doesn't re-show CSAT
99936
+ markCsatSubmitted(undefined); // persist so page refresh doesn't re-show CSAT
99936
99937
 
99937
99938
  try {
99938
99939
  const headers = {
99939
99940
  "Content-Type": "application/json"
99940
99941
  };
99941
- if (organizationId) {
99942
- headers["x-eshal-org"] = organizationId;
99942
+ if (typeof getAuthToken === 'function') {
99943
+ const token = await getAuthToken();
99944
+ if (token) headers['Authorization'] = "Bearer ".concat(token);
99943
99945
  }
99944
99946
  const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
99945
99947
  method: "POST",
@@ -99949,7 +99951,6 @@
99949
99951
  conversationId,
99950
99952
  rating,
99951
99953
  ratingFormat: format,
99952
- organizationId,
99953
99954
  conciergeId
99954
99955
  })
99955
99956
  });
@@ -99973,8 +99974,9 @@
99973
99974
  const headers = {
99974
99975
  "Content-Type": "application/json"
99975
99976
  };
99976
- if (organizationId) {
99977
- headers["x-eshal-org"] = organizationId;
99977
+ if (typeof getAuthToken === 'function') {
99978
+ const token = await getAuthToken();
99979
+ if (token) headers['Authorization'] = "Bearer ".concat(token);
99978
99980
  }
99979
99981
  const ratingFormat = normalizeCsatFormat(csatFormat);
99980
99982
  const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
@@ -99985,7 +99987,6 @@
99985
99987
  conversationId,
99986
99988
  rating: String(resolveRating),
99987
99989
  ratingFormat,
99988
- organizationId,
99989
99990
  conciergeId,
99990
99991
  feedback: resolveFeedbackText || undefined
99991
99992
  })
@@ -100019,7 +100020,7 @@
100019
100020
  } finally {
100020
100021
  setIsSubmittingResolve(false);
100021
100022
  }
100022
- }, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, organizationId, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
100023
+ }, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, getAuthToken, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
100023
100024
 
100024
100025
  // Handle X button: first click shows form, second click closes
100025
100026
  const handleCloseClick = reactExports.useCallback(() => {
@@ -100357,7 +100358,7 @@
100357
100358
  conciergeName: conciergeName,
100358
100359
  companyName: companyName,
100359
100360
  apiBaseUrl: apiBaseUrl,
100360
- organizationId: organizationId,
100361
+ getAuthToken: getAuthToken,
100361
100362
  conversationResetKey: conversationId,
100362
100363
  allowFeedback: allowFeedback,
100363
100364
  provideCitations: provideCitations,
@@ -100442,8 +100443,7 @@
100442
100443
 
100443
100444
  /**
100444
100445
  * @param {string|undefined|null} apiBaseUrl
100445
- * @param {string|undefined|null} orgId
100446
- * @param {string|undefined|null} secretKey — Mode A/B: orgId resolved server-side
100446
+ * @param {Function|undefined|null} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined/null (Mode B proxy)
100447
100447
  * @returns {{
100448
100448
  * categories: Array<{id:string,label:string,count:number,tags:string[]}>,
100449
100449
  * loading: boolean,
@@ -100451,17 +100451,15 @@
100451
100451
  * hasFilters: boolean,
100452
100452
  * }}
100453
100453
  */
100454
- function useFilterCatalog(apiBaseUrl, orgId, secretKey) {
100455
- // Mode A: secretKey present, orgId null → key-based endpoint
100456
- // Mode C: orgId present (legacy) → orgId-in-path endpoint
100457
- const hasAuth = !!(apiBaseUrl && (orgId || secretKey));
100454
+ function useFilterCatalog(apiBaseUrl, getAuthToken) {
100455
+ const hasAuth = !!apiBaseUrl;
100458
100456
  const [state, setState] = reactExports.useState({
100459
100457
  categories: [],
100460
100458
  loading: hasAuth,
100461
100459
  error: null
100462
100460
  });
100463
100461
  reactExports.useEffect(() => {
100464
- if (!apiBaseUrl || !orgId && !secretKey) {
100462
+ if (!apiBaseUrl) {
100465
100463
  setState({
100466
100464
  categories: [],
100467
100465
  loading: false,
@@ -100469,7 +100467,7 @@
100469
100467
  });
100470
100468
  return;
100471
100469
  }
100472
- const cacheKey = secretKey ? "".concat(apiBaseUrl, "|__key__") : "".concat(apiBaseUrl, "|").concat(orgId);
100470
+ const cacheKey = getAuthToken ? "".concat(apiBaseUrl, "|__session__") : "".concat(apiBaseUrl, "|__proxy__");
100473
100471
  const cached = _PROCESS_CACHE.get(cacheKey);
100474
100472
  if (cached) {
100475
100473
  setState({
@@ -100485,45 +100483,54 @@
100485
100483
  error: null
100486
100484
  }));
100487
100485
  const base = apiBaseUrl.replace(/\/$/, "");
100488
- const url = secretKey ? "".concat(base, "/api/v1/widget/filters") : "".concat(base, "/api/v1/widget/filters/").concat(encodeURIComponent(orgId));
100489
- const headers = {
100490
- Accept: "application/json"
100491
- };
100492
- if (secretKey) headers["x-eshal-key"] = secretKey;
100493
- fetch(url, {
100494
- method: "GET",
100495
- headers
100496
- }).then(async res => {
100497
- if (!res.ok) {
100498
- throw new Error("filter catalog HTTP ".concat(res.status));
100486
+ const url = "".concat(base, "/api/v1/widget/filters");
100487
+ (async () => {
100488
+ try {
100489
+ const headers = {
100490
+ Accept: "application/json"
100491
+ };
100492
+ // In-app JWT path: set Authorization header
100493
+ // Mode B proxy: no auth header; proxy handles org resolution
100494
+ if (typeof getAuthToken === 'function') {
100495
+ const token = await getAuthToken();
100496
+ if (token) {
100497
+ headers['Authorization'] = "Bearer ".concat(token);
100498
+ }
100499
+ }
100500
+ const res = await fetch(url, {
100501
+ method: "GET",
100502
+ headers
100503
+ });
100504
+ if (!res.ok) {
100505
+ throw new Error("filter catalog HTTP ".concat(res.status));
100506
+ }
100507
+ const body = await res.json();
100508
+ const categories = Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
100509
+ if (cancelled) return;
100510
+ _PROCESS_CACHE.set(cacheKey, categories);
100511
+ setState({
100512
+ categories,
100513
+ loading: false,
100514
+ error: null
100515
+ });
100516
+ } catch (err) {
100517
+ if (cancelled) return;
100518
+ // Fail-soft: log and present empty so the UI hides itself.
100519
+ // We do NOT bubble the error to the caller because chat
100520
+ // should keep working even if the catalog is unreachable.
100521
+ // eslint-disable-next-line no-console
100522
+ console.warn("[useFilterCatalog] catalog fetch failed:", err);
100523
+ setState({
100524
+ categories: [],
100525
+ loading: false,
100526
+ error: err
100527
+ });
100499
100528
  }
100500
- const body = await res.json();
100501
- return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
100502
- }).then(categories => {
100503
- if (cancelled) return;
100504
- _PROCESS_CACHE.set(cacheKey, categories);
100505
- setState({
100506
- categories,
100507
- loading: false,
100508
- error: null
100509
- });
100510
- }).catch(err => {
100511
- if (cancelled) return;
100512
- // Fail-soft: log and present empty so the UI hides itself.
100513
- // We do NOT bubble the error to the caller because chat
100514
- // should keep working even if the catalog is unreachable.
100515
- // eslint-disable-next-line no-console
100516
- console.warn("[useFilterCatalog] catalog fetch failed:", err);
100517
- setState({
100518
- categories: [],
100519
- loading: false,
100520
- error: err
100521
- });
100522
- });
100529
+ })();
100523
100530
  return () => {
100524
100531
  cancelled = true;
100525
100532
  };
100526
- }, [apiBaseUrl, orgId, secretKey]);
100533
+ }, [apiBaseUrl, getAuthToken]);
100527
100534
  const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
100528
100535
  return _objectSpread2(_objectSpread2({}, state), {}, {
100529
100536
  hasFilters
@@ -100602,24 +100609,28 @@
100602
100609
  /**
100603
100610
  * Fetches agent configuration from the deploy-agent endpoint
100604
100611
  * @param {string} apiBaseUrl - Base URL for the API
100605
- * @param {string} orgId - Organization ID
100612
+ * @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
100606
100613
  * @returns {Promise<object>} Agent configuration object
100607
100614
  */
100608
- const fetchAgentConfig = async (apiBaseUrl, orgId, secretKey) => {
100615
+ const fetchAgentConfig = async (apiBaseUrl, getAuthToken) => {
100609
100616
  if (!apiBaseUrl) {
100610
100617
  throw new Error("apiBaseUrl is required");
100611
100618
  }
100612
- // Mode A: secretKey in header, no orgId in URL
100613
- // Mode B (proxy): no secretKey, no orgId — proxy intercepts and signs the request
100614
- // Mode C legacy: orgId in URL path
100615
-
100616
100619
  try {
100617
100620
  const base = apiBaseUrl.replace(/\/$/, '');
100618
- const url = orgId ? "".concat(base, "/api/v1/deploy-agent/").concat(orgId) : "".concat(base, "/api/v1/deploy-agent");
100621
+ const url = "".concat(base, "/api/v1/deploy-agent");
100619
100622
  const headers = {
100620
100623
  "Content-Type": "application/json"
100621
100624
  };
100622
- if (secretKey) headers['x-eshal-key'] = secretKey;
100625
+
100626
+ // In-app JWT path: set Authorization header
100627
+ // Mode B proxy: no auth header; proxy handles org resolution
100628
+ if (typeof getAuthToken === 'function') {
100629
+ const token = await getAuthToken();
100630
+ if (token) {
100631
+ headers['Authorization'] = "Bearer ".concat(token);
100632
+ }
100633
+ }
100623
100634
  const response = await fetch(url, {
100624
100635
  method: "GET",
100625
100636
  headers,
@@ -100639,11 +100650,10 @@
100639
100650
  const ChatWidget = _ref => {
100640
100651
  var _agentConfig$concierg;
100641
100652
  let {
100642
- // Auth — at least one of secretKey (Mode A/B) or orgId (Mode C legacy) is needed.
100643
- secretKey,
100644
- // sk_live_... from ChatWidget.init({ secretKey, apiBaseUrl })
100645
- orgId,
100646
- // legacy — kept for backward compatibility
100653
+ /**
100654
+ * @param {Function|undefined} getAuthToken - async fn returning a Keycloak Bearer token (in-app JWT path)
100655
+ */
100656
+ getAuthToken,
100647
100657
  apiBaseUrl,
100648
100658
  // Optional override props (will override fetched config if provided)
100649
100659
  darkMode,
@@ -100699,13 +100709,9 @@
100699
100709
  setIsLoadingConfig(false);
100700
100710
  return;
100701
100711
  }
100702
- // Mode B proxy: neither secretKey nor orgId in browser — proxy handles auth
100703
-
100704
100712
  try {
100705
100713
  setIsLoadingConfig(true);
100706
- // Pass secretKey for Mode A/B — fetchAgentConfig will send it as a
100707
- // header and omit the orgId path segment. Pass orgId for Mode C legacy.
100708
- const config = await fetchAgentConfig(apiBaseUrl, orgId, secretKey);
100714
+ const config = await fetchAgentConfig(apiBaseUrl, getAuthToken);
100709
100715
  setAgentConfig(config);
100710
100716
  setConfigError(null);
100711
100717
  } catch (error) {
@@ -100716,11 +100722,11 @@
100716
100722
  }
100717
100723
  };
100718
100724
  loadAgentConfig();
100719
- }, [orgId, secretKey, apiBaseUrl]);
100725
+ }, [apiBaseUrl, getAuthToken]);
100720
100726
 
100721
100727
  // Map API response to widget props (use props as overrides if provided)
100722
100728
  const widgetConfig = reactExports.useMemo(() => {
100723
- 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, _typography$headerTex, _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;
100729
+ 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, _typography$headerTex, _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, _agentConfig$organiza;
100724
100730
  if (!agentConfig) {
100725
100731
  return null;
100726
100732
  }
@@ -100779,7 +100785,6 @@
100779
100785
  fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
100780
100786
  fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
100781
100787
  defaultLanguage: locale,
100782
- organizationId: orgId,
100783
100788
  headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
100784
100789
  headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
100785
100790
  headerTextColor: (_typography$headerTex = typography.headerTextColor) !== null && _typography$headerTex !== void 0 ? _typography$headerTex : "#FFFFFF",
@@ -100816,9 +100821,12 @@
100816
100821
  // every message and tell the backend to skip citation generation.
100817
100822
  // Defaults to true so agents that haven't set the field keep showing
100818
100823
  // citations (matches the testing-lab / chatbot-preview default).
100819
- provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
100824
+ provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true,
100825
+ // organizationId from the backend response — used internally for
100826
+ // session isolation (localStorage keys) and WS paths. Not a user prop.
100827
+ organizationId: (_agentConfig$organiza = agentConfig.organizationId) !== null && _agentConfig$organiza !== void 0 ? _agentConfig$organiza : null
100820
100828
  };
100821
- }, [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
100829
+ }, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage, headerTextBold, headerTextItalic, enableVoiceInteraction, disclaimerEnabled, disclaimerText, disclaimerPosition, disclaimerLinkLabel, disclaimerLinkUrl, showPoweredBy, onboardingQuestions, onboardingEnabled, collectionPrompt, allowedDomains, agentMessageBubbleFontFamily, agentMessageBubbleFontSize, userMessageBubbleFontFamily, userMessageBubbleFontSize
100822
100830
  // CSAT dependencies
100823
100831
  // (kept minimal because these come from agentConfig)
100824
100832
  ]);
@@ -100837,7 +100845,7 @@
100837
100845
  quickQuestions: [],
100838
100846
  apiBaseUrl: apiBaseUrl || "",
100839
100847
  apiKey: apiKey || "",
100840
- organizationId: orgId || "",
100848
+ getAuthToken,
100841
100849
  autoOpen: false,
100842
100850
  openDelay: 0,
100843
100851
  darkMode: false,
@@ -100845,7 +100853,7 @@
100845
100853
  onboardingQuestions: [],
100846
100854
  onboardingEnabled: false,
100847
100855
  collectionPrompt: undefined
100848
- }), [apiBaseUrl, apiKey, orgId]);
100856
+ }), [apiBaseUrl, apiKey, getAuthToken]);
100849
100857
 
100850
100858
  // ALWAYS call hooks before any early returns (Rules of Hooks)
100851
100859
  const {
@@ -100887,14 +100895,9 @@
100887
100895
  welcomeMessage: widgetConfig.welcomeMessage,
100888
100896
  quickQuestions: widgetConfig.quickQuestions,
100889
100897
  apiBaseUrl,
100890
- // Voice WS endpoint is derived entirely from apiBaseUrl inside
100891
- // useChatState (→ wss://{host}/voice/ws, the frontend voice proxy).
100892
- // Nothing is forwarded/overridable here, so a stale backend or embed
100893
- // wsBaseUrl can't point voice at the old (now non-public)
100894
- // knowledge-api LoadBalancer.
100895
100898
  apiKey,
100896
100899
  organizationId: widgetConfig.organizationId,
100897
- secretKey,
100900
+ getAuthToken,
100898
100901
  autoOpen,
100899
100902
  openDelay,
100900
100903
  darkMode: widgetConfig.darkMode,
@@ -100921,25 +100924,33 @@
100921
100924
  // navigation (but not a page refresh — feedback is not written to
100922
100925
  // localStorage so it resets on reload as intended).
100923
100926
  updateMessageFeedback(messageContent, feedback);
100924
- if (!orgId && !secretKey || !conversationId || !apiBaseUrl) return;
100927
+ if (!conversationId || !apiBaseUrl) return;
100925
100928
  const base = apiBaseUrl.replace(/\/$/, "");
100926
- const feedbackHeaders = {
100927
- "Content-Type": "application/json"
100928
- };
100929
- if (secretKey) feedbackHeaders['x-eshal-key'] = secretKey;else if (orgId) feedbackHeaders['x-eshal-org'] = orgId;
100930
- fetch("".concat(base, "/api/v1/support/feedback"), {
100931
- method: "POST",
100932
- headers: feedbackHeaders,
100933
- credentials: "include",
100934
- body: JSON.stringify({
100935
- messageContent,
100936
- conversationId,
100937
- organizationId: orgId,
100938
- feedback,
100939
- feedbackText
100940
- })
100941
- }).catch(err => console.error("Feedback submission error:", err));
100942
- }, [orgId, conversationId, apiBaseUrl, updateMessageFeedback]);
100929
+ (async () => {
100930
+ try {
100931
+ const feedbackHeaders = {
100932
+ "Content-Type": "application/json"
100933
+ };
100934
+ if (typeof getAuthToken === 'function') {
100935
+ const token = await getAuthToken();
100936
+ if (token) feedbackHeaders['Authorization'] = "Bearer ".concat(token);
100937
+ }
100938
+ await fetch("".concat(base, "/api/v1/support/feedback"), {
100939
+ method: "POST",
100940
+ headers: feedbackHeaders,
100941
+ credentials: "include",
100942
+ body: JSON.stringify({
100943
+ messageContent,
100944
+ conversationId,
100945
+ feedback,
100946
+ feedbackText
100947
+ })
100948
+ });
100949
+ } catch (err) {
100950
+ console.error("Feedback submission error:", err);
100951
+ }
100952
+ })();
100953
+ }, [conversationId, apiBaseUrl, getAuthToken, updateMessageFeedback]);
100943
100954
 
100944
100955
  // Refresh chat handler — reset conversation
100945
100956
  const handleRefreshChat = reactExports.useCallback(() => {
@@ -100955,7 +100966,7 @@
100955
100966
  const {
100956
100967
  categories: filterCategories,
100957
100968
  hasFilters: showFilterStep
100958
- } = useFilterCatalog(apiBaseUrl, orgId, secretKey);
100969
+ } = useFilterCatalog(apiBaseUrl, getAuthToken);
100959
100970
 
100960
100971
  // Copy chat handler — copy all messages to clipboard
100961
100972
  const handleCopyChat = reactExports.useCallback(() => {
@@ -101060,8 +101071,7 @@
101060
101071
  showPoweredBy: widgetConfig.showPoweredBy,
101061
101072
  apiBaseUrl: apiBaseUrl,
101062
101073
  apiKey: apiKey,
101063
- organizationId: widgetConfig.organizationId,
101064
- secretKey: secretKey,
101074
+ getAuthToken: getAuthToken,
101065
101075
  conversationId: conversationId,
101066
101076
  conciergeId: (_agentConfig$concierg = agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.conciergeId) !== null && _agentConfig$concierg !== void 0 ? _agentConfig$concierg : agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.id,
101067
101077
  onboardingQuestions: widgetConfig.onboardingQuestions,
@@ -101209,15 +101219,10 @@
101209
101219
  }
101210
101220
 
101211
101221
  // Render component
101212
- // Mode A/B: secretKey present — orgId derived server-side from the key
101213
- // Mode C legacy: orgId in config — backward compatible
101214
- // Both: apiBaseUrl required
101215
- // Mode B proxy: apiBaseUrl alone is enough — no secretKey or orgId in browser
101222
+ // Mode B proxy: apiBaseUrl alone is enough — no key or orgId in browser
101223
+ // In-app JWT: getAuthToken fn passed via config
101216
101224
  const hasNewAuth = config.apiBaseUrl;
101217
101225
  const widgetProps = hasNewAuth ? _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
101218
- // Auth props — secretKey replaces orgId in Mode A/B
101219
- secretKey: config.secretKey || null,
101220
- orgId: config.orgId || null,
101221
101226
  apiBaseUrl: config.apiBaseUrl
101222
101227
  }, options.wsBaseUrl !== undefined && {
101223
101228
  wsBaseUrl: config.wsBaseUrl