@eshal-bot/chat-widget 0.1.69 → 0.1.71

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,21 +8010,16 @@
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)
8014
8013
  */
8015
- const sendStreamingRequest = async (request, apiBaseUrl, getAuthToken) => {
8014
+ const sendStreamingRequest = async (request, apiBaseUrl, organizationId) => {
8016
8015
  const headers = {
8017
8016
  'Content-Type': 'application/json',
8018
8017
  Accept: 'text/event-stream'
8019
8018
  };
8020
8019
 
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
- }
8020
+ // Add organization ID header if provided
8021
+ if (organizationId) {
8022
+ headers['x-eshal-org'] = organizationId;
8028
8023
  }
8029
8024
  const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
8030
8025
  method: 'POST',
@@ -8348,33 +8343,20 @@
8348
8343
  // Handle text_chunk events
8349
8344
  if (parsed.type === 'text_chunk') {
8350
8345
  var _parsed$choices;
8351
- // ALWAYS create the skeleton on the first text_chunk, even when it
8352
- // carries no content (a role-only "skeleton" chunk — delta.role
8353
- // without delta.content — is the backend's "an assistant turn is
8354
- // starting" signal used by the input/source-clarification short-
8355
- // circuits in chat_utils.py). Without this, a clarification turn
8356
- // that emits [text_chunk role-only] -> [complete content+
8357
- // finalContent] never created the assistant bubble (this branch
8358
- // skipped on empty content; the COMPLETE branch's updateFinalMessage
8359
- // requires lastMessage.role === 'assistant' to render — see
8360
- // updateFinalMessage line ~189) → the follow-up question was
8361
- // silently dropped, even though the wire carried everything
8362
- // correctly. Mirrors handleStreamEvent (line ~257) and packages/
8363
- // frontend ChatService.ts (line ~213) which both create the
8364
- // skeleton unconditionally on the first text_chunk.
8365
- if (!hasReceivedFirstChunk.current) {
8366
- hasReceivedFirstChunk.current = true;
8367
- setMessages(prev => {
8368
- const lastMessage = prev[prev.length - 1];
8369
- if (!lastMessage || lastMessage.role !== 'assistant' || !lastMessage.isProcessing) {
8370
- const skeletonMessage = createSkeletonMessage();
8371
- return [...(prev || []), skeletonMessage];
8372
- }
8373
- return prev;
8374
- });
8375
- }
8376
8346
  const content = ((_parsed$choices = parsed.choices) === null || _parsed$choices === void 0 || (_parsed$choices = _parsed$choices[0]) === null || _parsed$choices === void 0 || (_parsed$choices = _parsed$choices.delta) === null || _parsed$choices === void 0 ? void 0 : _parsed$choices.content) || parsed.content;
8377
8347
  if (content) {
8348
+ // Create skeleton on first chunk if not already created
8349
+ if (!hasReceivedFirstChunk.current) {
8350
+ hasReceivedFirstChunk.current = true;
8351
+ setMessages(prev => {
8352
+ const lastMessage = prev[prev.length - 1];
8353
+ if (!lastMessage || lastMessage.role !== 'assistant' || !lastMessage.isProcessing) {
8354
+ const skeletonMessage = createSkeletonMessage();
8355
+ return [...(prev || []), skeletonMessage];
8356
+ }
8357
+ return prev;
8358
+ });
8359
+ }
8378
8360
  accumulatedText.current += content;
8379
8361
  updateMessage(accumulatedText.current);
8380
8362
  }
@@ -8793,12 +8775,15 @@
8793
8775
  */
8794
8776
  const getWebSocketUrl = apiBaseUrl => {
8795
8777
  if (!apiBaseUrl) {
8778
+ // No base URL yet (config still loading) — safe public default.
8796
8779
  return 'wss://dev.eshal.ai/voice/ws';
8797
8780
  }
8798
8781
  try {
8799
8782
  const url = new URL(apiBaseUrl);
8783
+ // http→ws, https→wss; point at the frontend voice-proxy path.
8800
8784
  url.protocol = url.protocol.replace(/^http/, 'ws');
8801
8785
  url.pathname = '/voice/ws';
8786
+ // Clean URL without a trailing slash.
8802
8787
  return url.toString().replace(/\/$/, '');
8803
8788
  } catch (error) {
8804
8789
  console.error("Invalid API base URL provided for WebSocket:", error);
@@ -9144,7 +9129,6 @@
9144
9129
  apiBaseUrl,
9145
9130
  apiKey,
9146
9131
  organizationId,
9147
- getAuthToken,
9148
9132
  autoOpen,
9149
9133
  openDelay,
9150
9134
  darkMode,
@@ -9312,6 +9296,8 @@
9312
9296
  const visitorProfileRef = reactExports.useRef(null);
9313
9297
  const websocketUrl = reactExports.useMemo(() => {
9314
9298
  if (!organizationId) return null;
9299
+ // Voice WS base is always derived from apiBaseUrl (the frontend origin
9300
+ // the widget is embedded with) → wss://{host}/voice/ws. No override.
9315
9301
  const resolvedWsBaseUrl = getWebSocketUrl(apiBaseUrl);
9316
9302
  return "".concat(resolvedWsBaseUrl, "/").concat(organizationId, "/").concat(bidiSessionId);
9317
9303
  }, [apiBaseUrl, organizationId, bidiSessionId]);
@@ -9779,7 +9765,7 @@
9779
9765
  if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
9780
9766
  // For onboarding fields, just consume the stream without processing.
9781
9767
  // Single attempt — no retry, no user-visible partial state to clean up.
9782
- const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
9768
+ const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
9783
9769
  const reader = stream.getReader();
9784
9770
  const decoder = new TextDecoder();
9785
9771
  while (true) {
@@ -9798,7 +9784,7 @@
9798
9784
  let lastErr;
9799
9785
  for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
9800
9786
  try {
9801
- const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
9787
+ const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
9802
9788
  await processStream(stream, setMessages);
9803
9789
  updateActivity(organizationId);
9804
9790
  return {
@@ -9826,7 +9812,7 @@
9826
9812
 
9827
9813
  // Non-chat, non-onboarding apiType (shouldn't currently happen):
9828
9814
  // fire and forget, single attempt.
9829
- await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
9815
+ await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
9830
9816
  updateActivity(organizationId);
9831
9817
  return {
9832
9818
  success: true
@@ -9862,7 +9848,7 @@
9862
9848
  // UX-M09: release the concurrency guard so the next send can proceed.
9863
9849
  isSendingRef.current = false;
9864
9850
  }
9865
- }, [apiBaseUrl, apiKey, getNextMessageId, getAuthToken, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
9851
+ }, [apiBaseUrl, apiKey, getNextMessageId, organizationId, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
9866
9852
  const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
9867
9853
  const questions = getOnboardingQuestions(onboardingQuestions);
9868
9854
  if (questions.length === 0) return;
@@ -91763,31 +91749,6 @@
91763
91749
  return String(n);
91764
91750
  }
91765
91751
 
91766
- // ── Currency-aware value formatting ──────────────────────────────────────
91767
- // A2UI chart data is normalized to `{ label, value }` by A2uiBlock's
91768
- // numericData(), which drops the original Y-column key. A2uiBlock now threads
91769
- // that key through as `props.yKey` so these renderers can decide whether the
91770
- // axis / tooltip values are money. Heuristic mirrors the frontend's
91771
- // chartShared.tsx `isCurrencyKey` — keep the keyword list in lockstep.
91772
- // `(^|[_\W])` (not `\b`) so "average_salary" / "total_expenses" still match —
91773
- // JS `\b` doesn't fire next to `_`.
91774
- const CURRENCY_KEY_RE$1 = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
91775
- function isCurrencyKey$1(key) {
91776
- return typeof key === 'string' && CURRENCY_KEY_RE$1.test(key);
91777
- }
91778
-
91779
- // Compact currency for axis ticks → "$120.0k". Full currency for tooltips →
91780
- // "$87,600" (comma-grouped, up to 2 decimals, no forced trailing zeros so a
91781
- // whole-dollar salary reads "$81,200" not "$81,200.00").
91782
- function formatCurrencyCompact(v) {
91783
- return "$".concat(formatCompactNumber(v));
91784
- }
91785
- function formatCurrencyFull(v) {
91786
- return "$".concat(Number(v).toLocaleString('en-US', {
91787
- maximumFractionDigits: 2
91788
- }));
91789
- }
91790
-
91791
91752
  /**
91792
91753
  * Recharts bar chart — mirrors agent-sdk's eshalRenderers.BarChart.
91793
91754
  *
@@ -91800,11 +91761,6 @@
91800
91761
  isDark = false
91801
91762
  } = _ref;
91802
91763
  const data = Array.isArray(props.data) ? props.data : [];
91803
- // Whether the Y values are money — drives the `$` on axis ticks + tooltip.
91804
- const currency = isCurrencyKey$1(props.yKey);
91805
- // Human series name for the tooltip ("Average Salary"), so it no longer
91806
- // reads the generic dataKey "value".
91807
- const seriesName = props.valueLabel || undefined;
91808
91764
  const borderColor = isDark ? '#374151' : '#e2e8f0';
91809
91765
  const bgColor = isDark ? '#1f2937' : '#ffffff';
91810
91766
  const labelColor = isDark ? '#9ca3af' : '#64748b';
@@ -91879,9 +91835,6 @@
91879
91835
  fontSize: 11,
91880
91836
  fill: labelColor
91881
91837
  },
91882
- angle: 0,
91883
- textAnchor: "middle",
91884
- interval: 0,
91885
91838
  label: props.xAxisLabel ? {
91886
91839
  value: props.xAxisLabel,
91887
91840
  position: 'insideBottom',
@@ -91902,9 +91855,9 @@
91902
91855
  fontSize: 11,
91903
91856
  fill: labelColor
91904
91857
  } : undefined,
91905
- tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
91858
+ tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
91906
91859
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
91907
- formatter: v => currency ? formatCurrencyFull(v) : props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
91860
+ formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
91908
91861
  contentStyle: {
91909
91862
  background: tooltipBg,
91910
91863
  border: "1px solid ".concat(borderColor),
@@ -91914,7 +91867,6 @@
91914
91867
  }
91915
91868
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Bar, {
91916
91869
  dataKey: "value",
91917
- name: seriesName,
91918
91870
  fill: "#3b82f6",
91919
91871
  radius: [2, 2, 0, 0]
91920
91872
  })]
@@ -91998,7 +91950,6 @@
91998
91950
  donut = false
91999
91951
  } = _ref3;
92000
91952
  const data = Array.isArray(props.data) ? props.data : [];
92001
- const currency = isCurrencyKey$1(props.yKey);
92002
91953
  const borderColor = isDark ? '#374151' : '#e2e8f0';
92003
91954
  const bgColor = isDark ? '#1f2937' : '#ffffff';
92004
91955
  const titleColor = isDark ? '#f3f4f6' : '#0f172a';
@@ -92049,7 +92000,7 @@
92049
92000
  fill: PIE_COLORS[i % PIE_COLORS.length]
92050
92001
  }, i))
92051
92002
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
92052
- formatter: v => currency ? formatCurrencyFull(v) : Number(v).toLocaleString(),
92003
+ formatter: v => Number(v).toLocaleString(),
92053
92004
  contentStyle: {
92054
92005
  background: tooltipBg,
92055
92006
  border: "1px solid ".concat(borderColor),
@@ -92083,8 +92034,6 @@
92083
92034
  area = false
92084
92035
  } = _ref4;
92085
92036
  const data = Array.isArray(props.data) ? props.data : [];
92086
- const currency = isCurrencyKey$1(props.yKey);
92087
- const seriesName = props.valueLabel || undefined;
92088
92037
  const borderColor = isDark ? '#374151' : '#e2e8f0';
92089
92038
  const bgColor = isDark ? '#1f2937' : '#ffffff';
92090
92039
  const labelColor = isDark ? '#9ca3af' : '#64748b';
@@ -92160,9 +92109,6 @@
92160
92109
  fontSize: 11,
92161
92110
  fill: labelColor
92162
92111
  },
92163
- angle: 0,
92164
- textAnchor: "middle",
92165
- interval: 0,
92166
92112
  label: props.xAxisLabel ? {
92167
92113
  value: props.xAxisLabel,
92168
92114
  position: 'insideBottom',
@@ -92176,9 +92122,9 @@
92176
92122
  fontSize: 11,
92177
92123
  fill: labelColor
92178
92124
  },
92179
- tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
92125
+ tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
92180
92126
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
92181
- formatter: v => currency ? formatCurrencyFull(v) : props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
92127
+ formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
92182
92128
  contentStyle: {
92183
92129
  background: tooltipBg,
92184
92130
  border: "1px solid ".concat(borderColor),
@@ -92189,7 +92135,6 @@
92189
92135
  }), area ? /*#__PURE__*/jsxRuntimeExports.jsx(Area, {
92190
92136
  type: "monotone",
92191
92137
  dataKey: "value",
92192
- name: seriesName,
92193
92138
  stroke: "#3b82f6",
92194
92139
  fill: "#3b82f6",
92195
92140
  fillOpacity: 0.15,
@@ -92201,7 +92146,6 @@
92201
92146
  }) : /*#__PURE__*/jsxRuntimeExports.jsx(Line, {
92202
92147
  type: "monotone",
92203
92148
  dataKey: "value",
92204
- name: seriesName,
92205
92149
  stroke: "#3b82f6",
92206
92150
  strokeWidth: 2,
92207
92151
  dot: {
@@ -92399,14 +92343,7 @@
92399
92343
  data: rows,
92400
92344
  unit: spec.unit,
92401
92345
  xAxisLabel: spec.xAxisLabel,
92402
- yAxisLabel: spec.yAxisLabel,
92403
- // numericData() collapses the rows to { label, value }, dropping the
92404
- // original Y-column key. Pass it through so the renderer can detect a
92405
- // currency column (→ "$" on the Y-axis + tooltip) and label the tooltip
92406
- // series with the human column name ("Average Salary") instead of the
92407
- // generic dataKey "value".
92408
- yKey: y,
92409
- valueLabel: prettifyLabel(y)
92346
+ yAxisLabel: spec.yAxisLabel
92410
92347
  };
92411
92348
  if (t === 'pie') return /*#__PURE__*/jsxRuntimeExports.jsx(PieChartRenderer, {
92412
92349
  props: chartProps,
@@ -92733,12 +92670,10 @@
92733
92670
 
92734
92671
  // ISO date / datetime prefix — matches the typical SQL output the
92735
92672
  // agent's tools produce when GROUP BY-ing by month
92736
- // (`DATE_TRUNC('month', col)` → `YYYY-MM-DD`, or `to_char(..,'YYYY-MM')`
92737
- // → `YYYY-MM`). Used by formatAxisTick below so a chart of monthly
92738
- // expenses shows "Jan '26" / "Feb '26" horizontally instead of
92739
- // "2026 01 01" or "2026 01" slanted (EV-1187 follow-up + 2026-06-24
92740
- // day-less variant fix).
92741
- const ISO_DATE_RE = /^(\d{4})-(\d{2})(?:-\d{2})?(?:[T\s]|$)/;
92673
+ // (`DATE_TRUNC('month', col)` → `YYYY-MM-DD`). Used by formatAxisTick
92674
+ // below so a chart of monthly expenses shows "Jan '26" / "Feb '26"
92675
+ // horizontally instead of "2026 01 01" slanted (EV-1187 follow-up).
92676
+ const ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})(?:T|$)/;
92742
92677
  function formatIsoDateAsMonthTick(value) {
92743
92678
  if (typeof value !== 'string') return null;
92744
92679
  const m = value.match(ISO_DATE_RE);
@@ -92781,14 +92716,6 @@
92781
92716
  if (!SNAKE_CASE_CATEGORY_RE.test(value)) return value;
92782
92717
  return prettifyLabel(value);
92783
92718
  }
92784
-
92785
- // Numeric string under a money column, e.g. "81,200" / "105444" / "-1,250.50".
92786
- // Markdown tables (the common concierge path) arrive as text cells, so a
92787
- // salary value is the STRING "81,200" — not a number — and skips the
92788
- // USD_FORMATTER branch below. We prefix "$" while keeping the source's own
92789
- // grouping/decimals so it reads "$81,200" (not "$81,200.00"). A leading "$"
92790
- // already present makes the regex miss, so we never double-prefix.
92791
- const _NUMERIC_STRING_RE = /^-?\d[\d,]*(\.\d+)?$/;
92792
92719
  function formatCell(v, columnKey) {
92793
92720
  if (v == null) return '';
92794
92721
  if (typeof v === 'number') {
@@ -92798,11 +92725,6 @@
92798
92725
  return Number.isInteger(v) ? String(v) : v.toFixed(2);
92799
92726
  }
92800
92727
  if (typeof v === 'object') return JSON.stringify(v);
92801
- // Numeric string under a money-named column → prefix "$" (Bug: concierge
92802
- // salary table showed "81,200" with no currency symbol).
92803
- if (isCurrencyKey(columnKey) && _NUMERIC_STRING_RE.test(String(v).trim())) {
92804
- return "$".concat(String(v).trim());
92805
- }
92806
92728
  // String cells: prettify snake_case category labels (e.g.
92807
92729
  // `office_supplies` → "Office Supplies") so the table reads
92808
92730
  // consistently with the chart's X-axis ticks on the same data.
@@ -94380,7 +94302,7 @@
94380
94302
  let {
94381
94303
  sources = [],
94382
94304
  apiBaseUrl,
94383
- getAuthToken,
94305
+ organizationId,
94384
94306
  isDark,
94385
94307
  textColor,
94386
94308
  fontFamily,
@@ -94406,7 +94328,7 @@
94406
94328
  // corpora that have none of the above.
94407
94329
  const _downloadKey = s => s.url || s.source_id || s.source_name || "";
94408
94330
  const handleFileDownload = async source => {
94409
- if (!apiBaseUrl) return;
94331
+ if (!apiBaseUrl || !organizationId) return;
94410
94332
  // SharePoint proxy URLs are GET endpoints that stream the file
94411
94333
  // directly via Microsoft Graph using the stored OAuth credential.
94412
94334
  // Bypass the legacy POST /file-download lookup (which expects an
@@ -94432,14 +94354,11 @@
94432
94354
  // set, or future deployments that set it) pass through
94433
94355
  // unchanged.
94434
94356
  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
- }
94440
94357
  res = await fetch(spUrl, {
94441
94358
  method: "GET",
94442
- headers: spHeaders,
94359
+ headers: {
94360
+ "x-eshal-org": organizationId
94361
+ },
94443
94362
  credentials: "include"
94444
94363
  });
94445
94364
  } else {
@@ -94455,18 +94374,15 @@
94455
94374
  // - source.source_name (the user's original filename)
94456
94375
  // Sending both lets the server skip the Milvus resolution
94457
94376
  // 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
- }
94465
94377
  res = await fetch(url, {
94466
94378
  method: "POST",
94467
- headers: dlHeaders,
94379
+ headers: {
94380
+ "Content-Type": "application/json",
94381
+ "x-eshal-org": organizationId
94382
+ },
94468
94383
  credentials: "include",
94469
94384
  body: JSON.stringify({
94385
+ org_id: organizationId,
94470
94386
  source_id: source.source_id,
94471
94387
  file_name: source.url || "",
94472
94388
  source_name: source.source_name || ""
@@ -94651,7 +94567,7 @@
94651
94567
  conciergeName,
94652
94568
  companyName,
94653
94569
  apiBaseUrl,
94654
- getAuthToken,
94570
+ organizationId,
94655
94571
  onPromptSuggestionClick,
94656
94572
  allowFeedback = true,
94657
94573
  provideCitations = true,
@@ -94823,7 +94739,7 @@
94823
94739
  }), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
94824
94740
  sources: message.sources,
94825
94741
  apiBaseUrl: apiBaseUrl,
94826
- getAuthToken: getAuthToken,
94742
+ organizationId: organizationId,
94827
94743
  isDark: isDark,
94828
94744
  textColor: messageTextColor,
94829
94745
  fontFamily: messageFontFamily,
@@ -94849,7 +94765,7 @@
94849
94765
  }), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
94850
94766
  sources: message.sources,
94851
94767
  apiBaseUrl: apiBaseUrl,
94852
- getAuthToken: getAuthToken,
94768
+ organizationId: organizationId,
94853
94769
  isDark: isDark,
94854
94770
  textColor: messageTextColor,
94855
94771
  fontFamily: messageFontFamily,
@@ -95029,7 +94945,7 @@
95029
94945
  conciergeName,
95030
94946
  companyName,
95031
94947
  apiBaseUrl,
95032
- getAuthToken,
94948
+ organizationId,
95033
94949
  onPromptSuggestionClick,
95034
94950
  allowFeedback,
95035
94951
  provideCitations,
@@ -95073,7 +94989,7 @@
95073
94989
  conciergeName: conciergeName,
95074
94990
  companyName: companyName,
95075
94991
  apiBaseUrl: apiBaseUrl,
95076
- getAuthToken: getAuthToken,
94992
+ organizationId: organizationId,
95077
94993
  onPromptSuggestionClick: onPromptSuggestionClick,
95078
94994
  allowFeedback: allowFeedback,
95079
94995
  provideCitations: provideCitations,
@@ -95918,7 +95834,7 @@
95918
95834
  conciergeName,
95919
95835
  companyName,
95920
95836
  apiBaseUrl,
95921
- getAuthToken,
95837
+ organizationId,
95922
95838
  onPromptSuggestionClick,
95923
95839
  conversationResetKey,
95924
95840
  allowFeedback,
@@ -96100,7 +96016,7 @@
96100
96016
  conciergeName: conciergeName,
96101
96017
  companyName: companyName,
96102
96018
  apiBaseUrl: apiBaseUrl,
96103
- getAuthToken: getAuthToken,
96019
+ organizationId: organizationId,
96104
96020
  allowFeedback: allowFeedback,
96105
96021
  provideCitations: provideCitations,
96106
96022
  onFeedback: onFeedback,
@@ -96132,7 +96048,7 @@
96132
96048
  conciergeName: conciergeName,
96133
96049
  companyName: companyName,
96134
96050
  apiBaseUrl: apiBaseUrl,
96135
- getAuthToken: getAuthToken,
96051
+ organizationId: organizationId,
96136
96052
  headerTextColor: headerTextColor
96137
96053
  }, "skeleton-waiting");
96138
96054
  }
@@ -96247,18 +96163,7 @@
96247
96163
  copyChat: "Copy Chat",
96248
96164
  startChat: "Start Chat",
96249
96165
  startingVoiceSession: "Starting voice session...",
96250
- allowMicAccess: "Allow microphone access",
96251
- // Onboarding-form validation messages. These are the English fallback for
96252
- // every locale (translate() falls back to EN[key]); without them the form
96253
- // rendered the raw key strings ("emailInvalid", etc.). `fieldRequiredSuffix`
96254
- // is appended after a field label: `${label} ${fieldRequiredSuffix}`.
96255
- emailRequired: "Please enter your email address",
96256
- nameRequired: "Please enter your name",
96257
- phoneRequired: "Please enter your phone number",
96258
- fieldRequiredSuffix: "is required",
96259
- emailInvalid: "Please enter a valid email address",
96260
- nameInvalid: "Please enter a valid name",
96261
- phoneInvalid: "Please enter a valid phone number"
96166
+ allowMicAccess: "Allow microphone access"
96262
96167
  };
96263
96168
  const DICTIONARIES = {
96264
96169
  en: EN,
@@ -99647,7 +99552,7 @@
99647
99552
  showPoweredBy = true,
99648
99553
  apiBaseUrl,
99649
99554
  apiKey,
99650
- getAuthToken,
99555
+ organizationId,
99651
99556
  conversationId,
99652
99557
  conciergeId,
99653
99558
  onboardingQuestions = [],
@@ -99795,7 +99700,7 @@
99795
99700
  setCsatSubmitted(submitted);
99796
99701
  if (submitted) setShowCsat(false);
99797
99702
  } catch (_unused4) {}
99798
- }, [conversationId]);
99703
+ }, [conversationId, organizationId]);
99799
99704
 
99800
99705
  // Auto-scroll hook
99801
99706
  useAutoScroll(messagesContainerRef, [messages, isLoading], {
@@ -99933,15 +99838,14 @@
99933
99838
  const handleCsatRating = async (rating, format) => {
99934
99839
  setCsatSubmitted(true); // optimistic UI
99935
99840
  setShowCsat(false);
99936
- markCsatSubmitted(undefined); // persist so page refresh doesn't re-show CSAT
99841
+ markCsatSubmitted(organizationId); // persist so page refresh doesn't re-show CSAT
99937
99842
 
99938
99843
  try {
99939
99844
  const headers = {
99940
99845
  "Content-Type": "application/json"
99941
99846
  };
99942
- if (typeof getAuthToken === 'function') {
99943
- const token = await getAuthToken();
99944
- if (token) headers['Authorization'] = "Bearer ".concat(token);
99847
+ if (organizationId) {
99848
+ headers["x-eshal-org"] = organizationId;
99945
99849
  }
99946
99850
  const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
99947
99851
  method: "POST",
@@ -99951,6 +99855,7 @@
99951
99855
  conversationId,
99952
99856
  rating,
99953
99857
  ratingFormat: format,
99858
+ organizationId,
99954
99859
  conciergeId
99955
99860
  })
99956
99861
  });
@@ -99974,9 +99879,8 @@
99974
99879
  const headers = {
99975
99880
  "Content-Type": "application/json"
99976
99881
  };
99977
- if (typeof getAuthToken === 'function') {
99978
- const token = await getAuthToken();
99979
- if (token) headers['Authorization'] = "Bearer ".concat(token);
99882
+ if (organizationId) {
99883
+ headers["x-eshal-org"] = organizationId;
99980
99884
  }
99981
99885
  const ratingFormat = normalizeCsatFormat(csatFormat);
99982
99886
  const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
@@ -99987,6 +99891,7 @@
99987
99891
  conversationId,
99988
99892
  rating: String(resolveRating),
99989
99893
  ratingFormat,
99894
+ organizationId,
99990
99895
  conciergeId,
99991
99896
  feedback: resolveFeedbackText || undefined
99992
99897
  })
@@ -100020,7 +99925,7 @@
100020
99925
  } finally {
100021
99926
  setIsSubmittingResolve(false);
100022
99927
  }
100023
- }, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, getAuthToken, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
99928
+ }, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, organizationId, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
100024
99929
 
100025
99930
  // Handle X button: first click shows form, second click closes
100026
99931
  const handleCloseClick = reactExports.useCallback(() => {
@@ -100358,7 +100263,7 @@
100358
100263
  conciergeName: conciergeName,
100359
100264
  companyName: companyName,
100360
100265
  apiBaseUrl: apiBaseUrl,
100361
- getAuthToken: getAuthToken,
100266
+ organizationId: organizationId,
100362
100267
  conversationResetKey: conversationId,
100363
100268
  allowFeedback: allowFeedback,
100364
100269
  provideCitations: provideCitations,
@@ -100443,7 +100348,7 @@
100443
100348
 
100444
100349
  /**
100445
100350
  * @param {string|undefined|null} apiBaseUrl
100446
- * @param {Function|undefined|null} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined/null (Mode B proxy)
100351
+ * @param {string|undefined|null} orgId
100447
100352
  * @returns {{
100448
100353
  * categories: Array<{id:string,label:string,count:number,tags:string[]}>,
100449
100354
  * loading: boolean,
@@ -100451,15 +100356,14 @@
100451
100356
  * hasFilters: boolean,
100452
100357
  * }}
100453
100358
  */
100454
- function useFilterCatalog(apiBaseUrl, getAuthToken) {
100455
- const hasAuth = !!apiBaseUrl;
100359
+ function useFilterCatalog(apiBaseUrl, orgId) {
100456
100360
  const [state, setState] = reactExports.useState({
100457
100361
  categories: [],
100458
- loading: hasAuth,
100362
+ loading: !!(apiBaseUrl && orgId),
100459
100363
  error: null
100460
100364
  });
100461
100365
  reactExports.useEffect(() => {
100462
- if (!apiBaseUrl) {
100366
+ if (!apiBaseUrl || !orgId) {
100463
100367
  setState({
100464
100368
  categories: [],
100465
100369
  loading: false,
@@ -100467,8 +100371,8 @@
100467
100371
  });
100468
100372
  return;
100469
100373
  }
100470
- const cacheKey = getAuthToken ? "".concat(apiBaseUrl, "|__session__") : "".concat(apiBaseUrl, "|__proxy__");
100471
- const cached = _PROCESS_CACHE.get(cacheKey);
100374
+ const key = "".concat(apiBaseUrl, "|").concat(orgId);
100375
+ const cached = _PROCESS_CACHE.get(key);
100472
100376
  if (cached) {
100473
100377
  setState({
100474
100378
  categories: cached,
@@ -100482,55 +100386,43 @@
100482
100386
  loading: true,
100483
100387
  error: null
100484
100388
  }));
100485
- const base = apiBaseUrl.replace(/\/$/, "");
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
- });
100389
+ const url = "".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/widget/filters/").concat(encodeURIComponent(orgId));
100390
+ fetch(url, {
100391
+ method: "GET",
100392
+ headers: {
100393
+ Accept: "application/json"
100528
100394
  }
100529
- })();
100395
+ }).then(async res => {
100396
+ if (!res.ok) {
100397
+ throw new Error("filter catalog HTTP ".concat(res.status));
100398
+ }
100399
+ const body = await res.json();
100400
+ return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
100401
+ }).then(categories => {
100402
+ if (cancelled) return;
100403
+ _PROCESS_CACHE.set(key, categories);
100404
+ setState({
100405
+ categories,
100406
+ loading: false,
100407
+ error: null
100408
+ });
100409
+ }).catch(err => {
100410
+ if (cancelled) return;
100411
+ // Fail-soft: log and present empty so the UI hides itself.
100412
+ // We do NOT bubble the error to the caller because chat
100413
+ // should keep working even if the catalog is unreachable.
100414
+ // eslint-disable-next-line no-console
100415
+ console.warn("[useFilterCatalog] catalog fetch failed:", err);
100416
+ setState({
100417
+ categories: [],
100418
+ loading: false,
100419
+ error: err
100420
+ });
100421
+ });
100530
100422
  return () => {
100531
100423
  cancelled = true;
100532
100424
  };
100533
- }, [apiBaseUrl, getAuthToken]);
100425
+ }, [apiBaseUrl, orgId]);
100534
100426
  const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
100535
100427
  return _objectSpread2(_objectSpread2({}, state), {}, {
100536
100428
  hasFilters
@@ -100609,31 +100501,20 @@
100609
100501
  /**
100610
100502
  * Fetches agent configuration from the deploy-agent endpoint
100611
100503
  * @param {string} apiBaseUrl - Base URL for the API
100612
- * @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
100504
+ * @param {string} orgId - Organization ID
100613
100505
  * @returns {Promise<object>} Agent configuration object
100614
100506
  */
100615
- const fetchAgentConfig = async (apiBaseUrl, getAuthToken) => {
100616
- if (!apiBaseUrl) {
100617
- throw new Error("apiBaseUrl is required");
100507
+ const fetchAgentConfig = async (apiBaseUrl, orgId) => {
100508
+ if (!apiBaseUrl || !orgId) {
100509
+ throw new Error("apiBaseUrl and orgId are required");
100618
100510
  }
100619
100511
  try {
100620
- const base = apiBaseUrl.replace(/\/$/, '');
100621
- const url = "".concat(base, "/api/v1/deploy-agent");
100622
- const headers = {
100623
- "Content-Type": "application/json"
100624
- };
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
- }
100512
+ const url = "".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/deploy-agent/").concat(orgId);
100634
100513
  const response = await fetch(url, {
100635
100514
  method: "GET",
100636
- headers,
100515
+ headers: {
100516
+ "Content-Type": "application/json"
100517
+ },
100637
100518
  credentials: "include"
100638
100519
  });
100639
100520
  if (!response.ok) {
@@ -100650,10 +100531,8 @@
100650
100531
  const ChatWidget = _ref => {
100651
100532
  var _agentConfig$concierg;
100652
100533
  let {
100653
- /**
100654
- * @param {Function|undefined} getAuthToken - async fn returning a Keycloak Bearer token (in-app JWT path)
100655
- */
100656
- getAuthToken,
100534
+ // Required props for dynamic config
100535
+ orgId,
100657
100536
  apiBaseUrl,
100658
100537
  // Optional override props (will override fetched config if provided)
100659
100538
  darkMode,
@@ -100704,14 +100583,14 @@
100704
100583
  // Fetch agent configuration on mount
100705
100584
  reactExports.useEffect(() => {
100706
100585
  const loadAgentConfig = async () => {
100707
- if (!apiBaseUrl) {
100708
- setConfigError("apiBaseUrl is required");
100586
+ if (!orgId || !apiBaseUrl) {
100587
+ setConfigError("orgId and apiBaseUrl are required");
100709
100588
  setIsLoadingConfig(false);
100710
100589
  return;
100711
100590
  }
100712
100591
  try {
100713
100592
  setIsLoadingConfig(true);
100714
- const config = await fetchAgentConfig(apiBaseUrl, getAuthToken);
100593
+ const config = await fetchAgentConfig(apiBaseUrl, orgId);
100715
100594
  setAgentConfig(config);
100716
100595
  setConfigError(null);
100717
100596
  } catch (error) {
@@ -100722,11 +100601,11 @@
100722
100601
  }
100723
100602
  };
100724
100603
  loadAgentConfig();
100725
- }, [apiBaseUrl, getAuthToken]);
100604
+ }, [orgId, apiBaseUrl]);
100726
100605
 
100727
100606
  // Map API response to widget props (use props as overrides if provided)
100728
100607
  const widgetConfig = reactExports.useMemo(() => {
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;
100608
+ 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;
100730
100609
  if (!agentConfig) {
100731
100610
  return null;
100732
100611
  }
@@ -100785,6 +100664,7 @@
100785
100664
  fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
100786
100665
  fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
100787
100666
  defaultLanguage: locale,
100667
+ organizationId: orgId,
100788
100668
  headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
100789
100669
  headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
100790
100670
  headerTextColor: (_typography$headerTex = typography.headerTextColor) !== null && _typography$headerTex !== void 0 ? _typography$headerTex : "#FFFFFF",
@@ -100821,12 +100701,9 @@
100821
100701
  // every message and tell the backend to skip citation generation.
100822
100702
  // Defaults to true so agents that haven't set the field keep showing
100823
100703
  // citations (matches the testing-lab / chatbot-preview default).
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
100704
+ provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
100828
100705
  };
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
100706
+ }, [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
100830
100707
  // CSAT dependencies
100831
100708
  // (kept minimal because these come from agentConfig)
100832
100709
  ]);
@@ -100845,7 +100722,7 @@
100845
100722
  quickQuestions: [],
100846
100723
  apiBaseUrl: apiBaseUrl || "",
100847
100724
  apiKey: apiKey || "",
100848
- getAuthToken,
100725
+ organizationId: orgId || "",
100849
100726
  autoOpen: false,
100850
100727
  openDelay: 0,
100851
100728
  darkMode: false,
@@ -100853,7 +100730,7 @@
100853
100730
  onboardingQuestions: [],
100854
100731
  onboardingEnabled: false,
100855
100732
  collectionPrompt: undefined
100856
- }), [apiBaseUrl, apiKey, getAuthToken]);
100733
+ }), [apiBaseUrl, apiKey, orgId]);
100857
100734
 
100858
100735
  // ALWAYS call hooks before any early returns (Rules of Hooks)
100859
100736
  const {
@@ -100895,9 +100772,13 @@
100895
100772
  welcomeMessage: widgetConfig.welcomeMessage,
100896
100773
  quickQuestions: widgetConfig.quickQuestions,
100897
100774
  apiBaseUrl,
100775
+ // Voice WS endpoint is derived entirely from apiBaseUrl inside
100776
+ // useChatState (→ wss://{host}/voice/ws, the frontend voice proxy).
100777
+ // Nothing is forwarded/overridable here, so a stale backend or embed
100778
+ // wsBaseUrl can't point voice at the old (now non-public)
100779
+ // knowledge-api LoadBalancer.
100898
100780
  apiKey,
100899
100781
  organizationId: widgetConfig.organizationId,
100900
- getAuthToken,
100901
100782
  autoOpen,
100902
100783
  openDelay,
100903
100784
  darkMode: widgetConfig.darkMode,
@@ -100924,33 +100805,24 @@
100924
100805
  // navigation (but not a page refresh — feedback is not written to
100925
100806
  // localStorage so it resets on reload as intended).
100926
100807
  updateMessageFeedback(messageContent, feedback);
100927
- if (!conversationId || !apiBaseUrl) return;
100808
+ if (!orgId || !conversationId || !apiBaseUrl) return;
100928
100809
  const base = apiBaseUrl.replace(/\/$/, "");
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]);
100810
+ fetch("".concat(base, "/api/v1/support/feedback"), {
100811
+ method: "POST",
100812
+ headers: {
100813
+ "Content-Type": "application/json",
100814
+ "x-eshal-org": orgId
100815
+ },
100816
+ credentials: "include",
100817
+ body: JSON.stringify({
100818
+ messageContent,
100819
+ conversationId,
100820
+ organizationId: orgId,
100821
+ feedback,
100822
+ feedbackText
100823
+ })
100824
+ }).catch(err => console.error("Feedback submission error:", err));
100825
+ }, [orgId, conversationId, apiBaseUrl, updateMessageFeedback]);
100954
100826
 
100955
100827
  // Refresh chat handler — reset conversation
100956
100828
  const handleRefreshChat = reactExports.useCallback(() => {
@@ -100966,7 +100838,7 @@
100966
100838
  const {
100967
100839
  categories: filterCategories,
100968
100840
  hasFilters: showFilterStep
100969
- } = useFilterCatalog(apiBaseUrl, getAuthToken);
100841
+ } = useFilterCatalog(apiBaseUrl, orgId);
100970
100842
 
100971
100843
  // Copy chat handler — copy all messages to clipboard
100972
100844
  const handleCopyChat = reactExports.useCallback(() => {
@@ -101071,7 +100943,7 @@
101071
100943
  showPoweredBy: widgetConfig.showPoweredBy,
101072
100944
  apiBaseUrl: apiBaseUrl,
101073
100945
  apiKey: apiKey,
101074
- getAuthToken: getAuthToken,
100946
+ organizationId: widgetConfig.organizationId,
101075
100947
  conversationId: conversationId,
101076
100948
  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,
101077
100949
  onboardingQuestions: widgetConfig.onboardingQuestions,
@@ -101219,10 +101091,11 @@
101219
101091
  }
101220
101092
 
101221
101093
  // Render component
101222
- // Mode B proxy: apiBaseUrl alone is enough — no key or orgId in browser
101223
- // In-app JWT: getAuthToken fn passed via config
101224
- const hasNewAuth = config.apiBaseUrl;
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({
101094
+ // If orgId and apiBaseUrl are provided, use dynamic config
101095
+ // Otherwise, use the old static config approach for backward compatibility
101096
+ const widgetProps = config.orgId && config.apiBaseUrl ? _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({
101097
+ // Required props
101098
+ orgId: config.orgId,
101226
101099
  apiBaseUrl: config.apiBaseUrl
101227
101100
  }, options.wsBaseUrl !== undefined && {
101228
101101
  wsBaseUrl: config.wsBaseUrl