@eshal-bot/chat-widget 0.1.39 → 0.1.41

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.
@@ -8664,94 +8664,102 @@
8664
8664
  return (_ONBOARDING_FIELD_ORD = ONBOARDING_FIELD_ORDER[key]) !== null && _ONBOARDING_FIELD_ORD !== void 0 ? _ONBOARDING_FIELD_ORD : 99;
8665
8665
  };
8666
8666
 
8667
- const createSession = async config => {
8668
- // Initialize chat session if needed
8669
- if (config.sessionUrl) {
8670
- try {
8671
- const response = await fetch(config.sessionUrl, {
8672
- method: "POST",
8673
- headers: _objectSpread2({
8674
- "Content-Type": "application/json"
8675
- }, config.apiKey && {
8676
- Authorization: "Bearer ".concat(config.apiKey)
8677
- }),
8678
- body: JSON.stringify({
8679
- userId: config.userId,
8680
- userName: config.userName,
8681
- userEmail: config.userEmail
8682
- }),
8683
- credentials: "include"
8684
- });
8685
- const data = await response.json();
8686
- return data.sessionId;
8687
- } catch (error) {
8688
- console.error("Session creation error:", error);
8689
- return null;
8690
- }
8691
- }
8692
- return null;
8693
- };
8667
+ const SESSION_KEY$1 = 'eshal_chat_session';
8668
+ const SESSION_MSG_KEY = 'eshal_chat_session_messages';
8669
+
8670
+ // Defaults used when the deploy-agent payload hasn't resolved yet (first paint
8671
+ // race) or doesn't include `messageLimits` (older backend). The authoritative
8672
+ // values come from the backend via `WIDGET_CONFIG.MESSAGE_LIMITS`.
8673
+ const DEFAULT_MESSAGE_CAP = 200;
8674
+ const DEFAULT_MESSAGE_TRIM_BATCH = 50;
8694
8675
 
8695
8676
  /**
8696
- * Fetches conversation history for a given org and conversation
8697
- * @param {string} apiBaseUrl - Base URL for the API
8698
- * @param {string} orgId - Organization ID
8699
- * @param {string} conversationId - Conversation ID
8700
- * @returns {Promise<Array>} Array of messages
8701
- */
8702
- const fetchConversationHistory = async (apiBaseUrl, orgId, conversationId) => {
8703
- if (!apiBaseUrl || !orgId || !conversationId) {
8704
- throw new Error("apiBaseUrl, orgId, and conversationId are required");
8705
- }
8706
- const url = "".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/conversations/").concat(orgId, "/").concat(conversationId);
8707
- const response = await fetch(url, {
8708
- method: "GET",
8709
- headers: {
8710
- "Content-Type": "application/json"
8711
- },
8712
- credentials: "include"
8677
+ * Sliding-window trim. Once the transcript exceeds `cap`, slice off the
8678
+ * oldest `trimBatch` messages when both surfaces are at steady state the
8679
+ * loop runs at most once; the `while` is defensive against bursts (e.g. a
8680
+ * voice turn that lands several transcription chunks in one tick) that
8681
+ * push length far past the cap in a single render.
8682
+ */
8683
+ function applyMessageCap(messages) {
8684
+ let cap = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_MESSAGE_CAP;
8685
+ let trimBatch = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_MESSAGE_TRIM_BATCH;
8686
+ if (!Array.isArray(messages)) return [];
8687
+ if (typeof cap !== 'number' || cap <= 0) cap = DEFAULT_MESSAGE_CAP;
8688
+ if (typeof trimBatch !== 'number' || trimBatch <= 0 || trimBatch >= cap) {
8689
+ trimBatch = Math.min(DEFAULT_MESSAGE_TRIM_BATCH, cap - 1);
8690
+ }
8691
+ let m = messages;
8692
+ while (m.length > cap) m = m.slice(trimBatch);
8693
+ return m;
8694
+ }
8695
+
8696
+ // Strip transient fields (in-flight streaming flags, Date objects) so the
8697
+ // persisted shape is stable across React re-mounts. Keep the bits the UI
8698
+ // needs to render a faithful replay: role + content + timestamp + per-message
8699
+ // metadata (sources, feedback, prompt suggestions, content type).
8700
+ //
8701
+ // The welcome message is INTENTIONALLY excluded. It's client-side display
8702
+ // state generated from `widgetConfig.welcomeMessage` on every mount; the
8703
+ // hydrate path always prepends a fresh welcome above the restored history.
8704
+ // Persisting it would cause a duplicate-welcome render on reload because the
8705
+ // stored copy lacks the `isWelcome` flag (sanitize would strip it) and gets
8706
+ // rendered as a regular assistant message alongside the freshly-added one.
8707
+ function sanitizeMessagesForStorage(messages) {
8708
+ if (!Array.isArray(messages)) return [];
8709
+ return messages.filter(m => m && !m.isWelcome && (m.content || Array.isArray(m.sources) && m.sources.length > 0)).map(m => {
8710
+ var _m$type;
8711
+ return _objectSpread2(_objectSpread2(_objectSpread2({
8712
+ id: m.id,
8713
+ role: m.role,
8714
+ content: m.content,
8715
+ timestamp: m.timestamp instanceof Date ? m.timestamp.toISOString() : typeof m.timestamp === 'string' ? m.timestamp : new Date().toISOString(),
8716
+ type: (_m$type = m.type) !== null && _m$type !== void 0 ? _m$type : null
8717
+ }, Array.isArray(m.sources) && m.sources.length > 0 ? {
8718
+ sources: m.sources
8719
+ } : {}), m.feedback !== undefined ? {
8720
+ feedback: m.feedback
8721
+ } : {}), Array.isArray(m.prompts) && m.prompts.length > 0 ? {
8722
+ prompts: m.prompts
8723
+ } : {});
8713
8724
  });
8714
- if (response.status === 404) {
8715
- return []; // No history for this conversation yet
8716
- }
8717
- if (!response.ok) {
8718
- throw new Error("HTTP error! status: ".concat(response.status));
8725
+ }
8726
+ const saveSessionMessages = (orgId, conversationId, messages, limits) => {
8727
+ if (!orgId || !conversationId) return;
8728
+ try {
8729
+ const cap = limits === null || limits === void 0 ? void 0 : limits.cap;
8730
+ const trimBatch = limits === null || limits === void 0 ? void 0 : limits.trimBatch;
8731
+ const trimmed = applyMessageCap(messages, cap, trimBatch);
8732
+ const sanitized = sanitizeMessagesForStorage(trimmed);
8733
+ localStorage.setItem("".concat(SESSION_MSG_KEY, "_").concat(orgId, "_").concat(conversationId), JSON.stringify(sanitized));
8734
+ } catch (_unused) {
8735
+ // Quota / private-mode / serialization failure — drop silently. The
8736
+ // user-visible chat is unaffected; only the post-reload replay is lost.
8719
8737
  }
8720
- const data = await response.json();
8721
- return data.messages || [];
8722
8738
  };
8723
-
8724
- /**
8725
- * Fetches agent configuration from the deploy-agent endpoint
8726
- * @param {string} apiBaseUrl - Base URL for the API
8727
- * @param {string} orgId - Organization ID
8728
- * @returns {Promise<object>} Agent configuration object
8729
- */
8730
- const fetchAgentConfig = async (apiBaseUrl, orgId) => {
8731
- if (!apiBaseUrl || !orgId) {
8732
- throw new Error("apiBaseUrl and orgId are required");
8739
+ const getSessionMessages = (orgId, conversationId) => {
8740
+ if (!orgId || !conversationId) return [];
8741
+ try {
8742
+ const raw = localStorage.getItem("".concat(SESSION_MSG_KEY, "_").concat(orgId, "_").concat(conversationId));
8743
+ if (!raw) return [];
8744
+ const parsed = JSON.parse(raw);
8745
+ if (!Array.isArray(parsed)) return [];
8746
+ // Restore Date objects so MessageList timestamp formatting works without
8747
+ // every downstream consumer doing the conversion itself.
8748
+ return parsed.map(m => _objectSpread2(_objectSpread2({}, m), {}, {
8749
+ timestamp: m.timestamp ? new Date(m.timestamp) : new Date()
8750
+ }));
8751
+ } catch (_unused2) {
8752
+ return [];
8733
8753
  }
8754
+ };
8755
+ const clearSessionMessages = (orgId, conversationId) => {
8756
+ if (!orgId || !conversationId) return;
8734
8757
  try {
8735
- const url = "".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/deploy-agent/").concat(orgId);
8736
- const response = await fetch(url, {
8737
- method: "GET",
8738
- headers: {
8739
- "Content-Type": "application/json"
8740
- },
8741
- credentials: "include"
8742
- });
8743
- if (!response.ok) {
8744
- throw new Error("HTTP error! status: ".concat(response.status));
8745
- }
8746
- const data = await response.json();
8747
- return data;
8748
- } catch (error) {
8749
- console.error("Failed to fetch agent configuration:", error);
8750
- throw error;
8758
+ localStorage.removeItem("".concat(SESSION_MSG_KEY, "_").concat(orgId, "_").concat(conversationId));
8759
+ } catch (_unused3) {
8760
+ // ignore
8751
8761
  }
8752
8762
  };
8753
-
8754
- const SESSION_KEY$1 = 'eshal_chat_session';
8755
8763
  const getTimeoutMs = (value, unit) => {
8756
8764
  if (value === undefined || value === null || !unit) return null;
8757
8765
  const multipliers = {
@@ -8772,7 +8780,7 @@
8772
8780
  const raw = localStorage.getItem("".concat(SESSION_KEY$1, "_").concat(orgId));
8773
8781
  if (!raw) return null;
8774
8782
  return JSON.parse(raw);
8775
- } catch (_unused) {
8783
+ } catch (_unused4) {
8776
8784
  return null;
8777
8785
  }
8778
8786
  };
@@ -8822,7 +8830,7 @@
8822
8830
  createdAt: now,
8823
8831
  firstInteractionAt: null
8824
8832
  }, extra)));
8825
- } catch (_unused2) {}
8833
+ } catch (_unused5) {}
8826
8834
  };
8827
8835
  const updateActivity = orgId => {
8828
8836
  try {
@@ -8836,7 +8844,7 @@
8836
8844
  session.firstInteractionAt = now;
8837
8845
  }
8838
8846
  localStorage.setItem("".concat(SESSION_KEY$1, "_").concat(orgId), JSON.stringify(session));
8839
- } catch (_unused3) {}
8847
+ } catch (_unused6) {}
8840
8848
  };
8841
8849
  const markOnboardingCompleted = orgId => {
8842
8850
  try {
@@ -8844,7 +8852,7 @@
8844
8852
  if (!session) return;
8845
8853
  session.onboardingCompleted = true;
8846
8854
  localStorage.setItem("".concat(SESSION_KEY$1, "_").concat(orgId), JSON.stringify(session));
8847
- } catch (_unused4) {}
8855
+ } catch (_unused7) {}
8848
8856
  };
8849
8857
  const markCsatSubmitted = orgId => {
8850
8858
  try {
@@ -8852,12 +8860,21 @@
8852
8860
  if (!session) return;
8853
8861
  session.csatSubmitted = true;
8854
8862
  localStorage.setItem("".concat(SESSION_KEY$1, "_").concat(orgId), JSON.stringify(session));
8855
- } catch (_unused5) {}
8863
+ } catch (_unused8) {}
8856
8864
  };
8857
8865
  const clearSession$2 = orgId => {
8858
8866
  try {
8867
+ // Read the session BEFORE removing it so we know which messages bucket to
8868
+ // clear. Inactivity sweepers / explicit resets call this path; leaving the
8869
+ // messages bucket behind would resurrect the prior transcript when a new
8870
+ // session happens to land on the same conversationId (extremely rare, but
8871
+ // free to defend against).
8872
+ const existing = getSession(orgId);
8859
8873
  localStorage.removeItem("".concat(SESSION_KEY$1, "_").concat(orgId));
8860
- } catch (_unused6) {}
8874
+ if (existing !== null && existing !== void 0 && existing.conversationId) {
8875
+ clearSessionMessages(orgId, existing.conversationId);
8876
+ }
8877
+ } catch (_unused9) {}
8861
8878
  };
8862
8879
  const savePromptSuggestions = (orgId, prompts) => {
8863
8880
  try {
@@ -8867,14 +8884,14 @@
8867
8884
  return;
8868
8885
  }
8869
8886
  localStorage.setItem("".concat(SESSION_KEY$1, "_prompts_").concat(orgId), JSON.stringify(prompts));
8870
- } catch (_unused7) {}
8887
+ } catch (_unused0) {}
8871
8888
  };
8872
8889
  const getPromptSuggestions = orgId => {
8873
8890
  try {
8874
8891
  const raw = localStorage.getItem("".concat(SESSION_KEY$1, "_prompts_").concat(orgId));
8875
8892
  if (!raw) return [];
8876
8893
  return JSON.parse(raw) || [];
8877
- } catch (_unused8) {
8894
+ } catch (_unused1) {
8878
8895
  return [];
8879
8896
  }
8880
8897
  };
@@ -8920,8 +8937,17 @@
8920
8937
  onboardingEnabled = false,
8921
8938
  collectionPrompt,
8922
8939
  inactivityTimeoutValue,
8923
- inactivityTimeoutUnit
8940
+ inactivityTimeoutUnit,
8941
+ // Sliding-window limits for the persisted transcript. Sourced from the
8942
+ // backend's `messageLimits` field on the deploy-agent payload so per-env
8943
+ // configuration ships through one channel. Falls back to module defaults
8944
+ // during the brief mount window before the deploy-agent fetch resolves.
8945
+ messageLimits
8924
8946
  } = _ref2;
8947
+ const resolvedMessageLimits = reactExports.useMemo(() => ({
8948
+ cap: typeof (messageLimits === null || messageLimits === void 0 ? void 0 : messageLimits.cap) === 'number' && messageLimits.cap > 0 ? messageLimits.cap : DEFAULT_MESSAGE_CAP,
8949
+ trimBatch: typeof (messageLimits === null || messageLimits === void 0 ? void 0 : messageLimits.trimBatch) === 'number' && messageLimits.trimBatch > 0 ? messageLimits.trimBatch : DEFAULT_MESSAGE_TRIM_BATCH
8950
+ }), [messageLimits === null || messageLimits === void 0 ? void 0 : messageLimits.cap, messageLimits === null || messageLimits === void 0 ? void 0 : messageLimits.trimBatch]);
8925
8951
  const [isOpen, setIsOpen] = reactExports.useState(autoOpen);
8926
8952
  const [isMinimized, setIsMinimized] = reactExports.useState(false);
8927
8953
  const [isDark, setIsDark] = reactExports.useState(darkMode);
@@ -9140,91 +9166,62 @@
9140
9166
  return;
9141
9167
  }
9142
9168
 
9143
- // Restore session — fetch conversation history once
9144
- if (!historyLoadedRef.current && apiBaseUrl) {
9169
+ // Restore session — hydrate from localStorage only. We no longer hit
9170
+ // `GET /api/v1/conversations/:orgId/:conversationId` because orgs with
9171
+ // the Eshal Inbox conversation route turned OFF never get rows written
9172
+ // (`isInboxRouteEnabled` gates persistence) and the empty backend reply
9173
+ // was wiping the on-screen transcript on every reload. localStorage is
9174
+ // now the only source of session replay; inbox-on orgs still see their
9175
+ // full transcript because every settled message is mirrored locally.
9176
+ if (!historyLoadedRef.current) {
9145
9177
  historyLoadedRef.current = true;
9146
- setIsConversationLoading(true);
9147
- fetchConversationHistory(apiBaseUrl, organizationId, bidiSessionId).then(msgs => {
9148
- // Filter out onboarding field messages (name/email/phone) so they don't appear as chat bubbles
9149
- const ONBOARDING_TYPES = ['userName', 'email', 'phone', 'fullname', 'mobileno', 'name'];
9150
- const chatMsgs = msgs.filter(msg => {
9151
- const msgType = msg.type || msg.messageType;
9152
- if (msgType && ONBOARDING_TYPES.includes(msgType)) return false;
9153
- return true;
9154
- });
9155
- if (chatMsgs.length > 0) {
9156
- historyHasMessagesRef.current = true;
9157
- const historyMessages = chatMsgs.map((msg, index) => {
9158
- var _msg$Sources;
9159
- // Normalize sources — backend may store them as "Sources" (capital) with
9160
- // "sourceid" (no underscore). Map to the shape MessageSources expects.
9161
- const rawSources = (_msg$Sources = msg.Sources) !== null && _msg$Sources !== void 0 ? _msg$Sources : msg.sources;
9162
- const sources = Array.isArray(rawSources) ? rawSources.map(s => {
9163
- var _ref3, _s$source_type, _s$source_id, _ref4, _s$source_name;
9164
- return {
9165
- source_type: (_ref3 = (_s$source_type = s.source_type) !== null && _s$source_type !== void 0 ? _s$source_type : s.sourceType) !== null && _ref3 !== void 0 ? _ref3 : 'website',
9166
- source_id: (_s$source_id = s.source_id) !== null && _s$source_id !== void 0 ? _s$source_id : s.sourceid,
9167
- source_name: (_ref4 = (_s$source_name = s.source_name) !== null && _s$source_name !== void 0 ? _s$source_name : s.sourceName) !== null && _ref4 !== void 0 ? _ref4 : '',
9168
- url: s.url
9169
- };
9170
- }).filter(s => s.source_id || s.url) : [];
9171
- return _objectSpread2(_objectSpread2(_objectSpread2({}, createMessage({
9172
- id: msg.id || "history-".concat(index, "-").concat(Date.now()),
9173
- role: msg.role || (msg.sender === 'user' ? 'user' : 'assistant'),
9174
- content: msg.message || msg.content || ''
9175
- })), {}, {
9176
- timestamp: msg.time ? new Date(msg.time) : new Date()
9177
- }, Array.isArray(msg.prompts) && msg.prompts.length > 0 ? {
9178
- prompts: msg.prompts
9179
- } : {}), sources.length > 0 ? {
9180
- sources
9181
- } : {});
9178
+ const stored = getSessionMessages(organizationId, bidiSessionId);
9179
+ if (stored.length > 0) {
9180
+ historyHasMessagesRef.current = true;
9181
+ // Prepend the welcome message so reloads still lead with it,
9182
+ // matching the pre-localStorage replay behaviour. The welcome
9183
+ // message is client-only, never written to the messages bucket,
9184
+ // so it needs to be reattached on every hydrate.
9185
+ if (welcomeMessage) {
9186
+ var _stored$;
9187
+ const firstTs = (_stored$ = stored[0]) === null || _stored$ === void 0 ? void 0 : _stored$.timestamp;
9188
+ const firstTsMs = firstTs instanceof Date ? firstTs.getTime() : firstTs ? new Date(firstTs).getTime() : Date.now();
9189
+ const welcomeMsg = _objectSpread2(_objectSpread2({}, createMessage({
9190
+ id: 'welcome-restored',
9191
+ role: 'assistant',
9192
+ content: welcomeMessage
9193
+ })), {}, {
9194
+ isWelcome: true,
9195
+ timestamp: new Date(firstTsMs - 1)
9182
9196
  });
9197
+ setMessages([welcomeMsg, ...stored]);
9198
+ } else {
9199
+ setMessages(stored);
9200
+ }
9183
9201
 
9184
- // If the API didn't return prompts on any message, restore from localStorage
9185
- const hasAnyPrompts = historyMessages.some(m => m.role === 'assistant' && Array.isArray(m.prompts) && m.prompts.length > 0);
9186
- if (!hasAnyPrompts) {
9187
- const savedPrompts = getPromptSuggestions(organizationId);
9188
- if (savedPrompts.length > 0) {
9189
- // Attach saved prompts to the last assistant message
9190
- for (let i = historyMessages.length - 1; i >= 0; i -= 1) {
9191
- if (historyMessages[i].role === 'assistant') {
9192
- historyMessages[i] = _objectSpread2(_objectSpread2({}, historyMessages[i]), {}, {
9202
+ // If no message in the restored transcript carries prompt
9203
+ // suggestions, fall back to the legacy per-org bucket. Preserves
9204
+ // the "prompts survive refresh" UX even when the persistence layer
9205
+ // hasn't seen an assistant turn yet.
9206
+ const hasAnyPrompts = stored.some(m => m.role === 'assistant' && Array.isArray(m.prompts) && m.prompts.length > 0);
9207
+ if (!hasAnyPrompts) {
9208
+ const savedPrompts = getPromptSuggestions(organizationId);
9209
+ if (savedPrompts.length > 0) {
9210
+ setMessages(prev => {
9211
+ const next = [...prev];
9212
+ for (let i = next.length - 1; i >= 0; i -= 1) {
9213
+ if (next[i].role === 'assistant') {
9214
+ next[i] = _objectSpread2(_objectSpread2({}, next[i]), {}, {
9193
9215
  prompts: savedPrompts
9194
9216
  });
9195
9217
  break;
9196
9218
  }
9197
9219
  }
9198
- }
9199
- }
9200
-
9201
- // Always prepend the welcome message so it shows at the top after refresh,
9202
- // matching chatbot-preview behaviour (welcome message is client-side only
9203
- // and is not stored in server history).
9204
- // Use a timestamp before the first history message so it sorts first
9205
- // (activeMessages is sorted by timestamp before being passed to the UI).
9206
- if (welcomeMessage) {
9207
- var _historyMessages$;
9208
- const firstTs = (_historyMessages$ = historyMessages[0]) === null || _historyMessages$ === void 0 ? void 0 : _historyMessages$.timestamp;
9209
- const firstTsMs = firstTs instanceof Date ? firstTs.getTime() : firstTs ? new Date(firstTs).getTime() : Date.now();
9210
- const welcomeMsg = _objectSpread2(_objectSpread2({}, createMessage({
9211
- id: 'welcome-restored',
9212
- role: 'assistant',
9213
- content: welcomeMessage
9214
- })), {}, {
9215
- isWelcome: true,
9216
- timestamp: new Date(firstTsMs - 1)
9220
+ return next;
9217
9221
  });
9218
- setMessages([welcomeMsg, ...historyMessages]);
9219
- } else {
9220
- setMessages(historyMessages);
9221
9222
  }
9222
9223
  }
9223
- }).catch(err => {
9224
- console.error('[Session] History fetch failed:', err);
9225
- }).finally(() => {
9226
- setIsConversationLoading(false);
9227
- });
9224
+ }
9228
9225
  }
9229
9226
  } else {
9230
9227
  // Session is expired, invalid, or non-existent
@@ -9236,9 +9233,27 @@
9236
9233
  if (existing && bidiSessionId === existing.conversationId) {
9237
9234
  const newId = "widget-session-".concat(Math.random().toString(36).slice(2, 9));
9238
9235
  console.warn("[Session] Rotating expired ID ".concat(bidiSessionId, " -> ").concat(newId));
9236
+ // Drop the expired conversation's localStorage transcript before
9237
+ // rotating — otherwise the bucket sticks around forever (the key
9238
+ // includes the conversationId, so it'd never get hit again but
9239
+ // would still occupy origin storage on the customer's site).
9240
+ clearSessionMessages(organizationId, bidiSessionId);
9239
9241
  setBidiSessionId(newId);
9240
9242
  // Note: saveSession will be called on the next run with the newId
9241
9243
  } else {
9244
+ // Cold-start cleanup: the `useState` initializer above generates a
9245
+ // FRESH `bidiSessionId` when it can't reuse the stored session (timeout
9246
+ // expired before reload, settings snapshot mismatched, or the deploy-
9247
+ // agent payload was still loading at first paint so it couldn't run
9248
+ // `isSessionValid`). In that case `existing.conversationId` points to
9249
+ // a now-stale conversation whose messages bucket would otherwise be
9250
+ // orphaned forever — `clearSession` runs in `resetConversation` (which
9251
+ // we never hit here) and the rotate-id branch above only fires when
9252
+ // the IDs match. Mirror the frontend `useSessionManager` hydrate
9253
+ // effect: clean up the stale bucket before we save the new session.
9254
+ if (existing !== null && existing !== void 0 && existing.conversationId && existing.conversationId !== bidiSessionId) {
9255
+ clearSessionMessages(organizationId, existing.conversationId);
9256
+ }
9242
9257
  // We have a fresh ID (either from initializer or rotation), persist it if not already there
9243
9258
  if (!existing || existing.conversationId !== bidiSessionId) {
9244
9259
  saveSession(organizationId, bidiSessionId, {
@@ -9264,6 +9279,29 @@
9264
9279
  }
9265
9280
  }, [messages, organizationId]);
9266
9281
 
9282
+ // ── Sliding-window trim: drop the oldest TRIM_BATCH messages when state
9283
+ // exceeds CAP. Runs only when no message is mid-stream so we never slice
9284
+ // through a partial assistant token. Mutates React state (matches the
9285
+ // intended UX — old scrollback drops in front of the user once the cap is
9286
+ // hit, keeping what's on screen identical to what's in localStorage).
9287
+ reactExports.useEffect(() => {
9288
+ if (isConversationLoading || isLoading) return;
9289
+ if (messages.length <= resolvedMessageLimits.cap) return;
9290
+ setMessages(prev => applyMessageCap(prev, resolvedMessageLimits.cap, resolvedMessageLimits.trimBatch));
9291
+ }, [messages.length, isConversationLoading, isLoading, resolvedMessageLimits.cap, resolvedMessageLimits.trimBatch]);
9292
+
9293
+ // ── Persist a settled snapshot of the transcript. We write only when
9294
+ // streaming/voice traffic has quiesced so a partial assistant message
9295
+ // never lands in localStorage — on reload the next snapshot will reflect
9296
+ // the completed turn. Skipped while history is hydrating (initial mount
9297
+ // race) so the first restore isn't immediately overwritten by an empty
9298
+ // pre-hydrate state.
9299
+ reactExports.useEffect(() => {
9300
+ if (!organizationId || !bidiSessionId) return;
9301
+ if (isConversationLoading || isLoading) return;
9302
+ saveSessionMessages(organizationId, bidiSessionId, messages, resolvedMessageLimits);
9303
+ }, [messages, organizationId, bidiSessionId, isConversationLoading, isLoading, resolvedMessageLimits]);
9304
+
9267
9305
  // Show the onboarding form on first load; bypass it if already completed (persisted in session)
9268
9306
  reactExports.useEffect(() => {
9269
9307
  if (!onboardingEnabled || onboardingCompleted) return;
@@ -10252,11 +10290,11 @@
10252
10290
  const rawSources = (_event$sources = event.sources) !== null && _event$sources !== void 0 ? _event$sources : event.Sources;
10253
10291
  if (Array.isArray(rawSources) && rawSources.length > 0) {
10254
10292
  const normalizedSources = rawSources.map(s => {
10255
- var _ref5, _s$source_type2, _ref6, _s$source_id2, _ref7, _s$source_name2;
10293
+ var _ref3, _s$source_type, _ref4, _s$source_id, _ref5, _s$source_name;
10256
10294
  return {
10257
- source_type: (_ref5 = (_s$source_type2 = s.source_type) !== null && _s$source_type2 !== void 0 ? _s$source_type2 : s.sourceType) !== null && _ref5 !== void 0 ? _ref5 : 'file',
10258
- source_id: (_ref6 = (_s$source_id2 = s.source_id) !== null && _s$source_id2 !== void 0 ? _s$source_id2 : s.sourceid) !== null && _ref6 !== void 0 ? _ref6 : s.sourceId,
10259
- source_name: (_ref7 = (_s$source_name2 = s.source_name) !== null && _s$source_name2 !== void 0 ? _s$source_name2 : s.sourceName) !== null && _ref7 !== void 0 ? _ref7 : '',
10295
+ source_type: (_ref3 = (_s$source_type = s.source_type) !== null && _s$source_type !== void 0 ? _s$source_type : s.sourceType) !== null && _ref3 !== void 0 ? _ref3 : 'file',
10296
+ source_id: (_ref4 = (_s$source_id = s.source_id) !== null && _s$source_id !== void 0 ? _s$source_id : s.sourceid) !== null && _ref4 !== void 0 ? _ref4 : s.sourceId,
10297
+ source_name: (_ref5 = (_s$source_name = s.source_name) !== null && _s$source_name !== void 0 ? _s$source_name : s.sourceName) !== null && _ref5 !== void 0 ? _ref5 : '',
10260
10298
  url: s.url
10261
10299
  };
10262
10300
  }).filter(s => s.source_id || s.url);
@@ -10563,6 +10601,10 @@
10563
10601
  setMessages([]);
10564
10602
  setBidiMessages([]);
10565
10603
 
10604
+ // Drop the localStorage transcript for the conversation we're abandoning
10605
+ // BEFORE we rotate to a fresh sessionId, otherwise the old bucket leaks.
10606
+ clearSessionMessages(organizationId, bidiSessionId);
10607
+
10566
10608
  // Reset onboarding
10567
10609
  setOnboardingActive(false);
10568
10610
  setOnboardingCompleted(false);
@@ -10601,7 +10643,7 @@
10601
10643
  setOnboardingActive(true);
10602
10644
  }
10603
10645
  }
10604
- }, [organizationId, welcomeMessage, onboardingEnabled, onboardingQuestions]);
10646
+ }, [organizationId, bidiSessionId, welcomeMessage, onboardingEnabled, onboardingQuestions, inactivityTimeoutValue, inactivityTimeoutUnit]);
10605
10647
 
10606
10648
  // Keep a stable ref to resetConversation so the timer effect does not need to
10607
10649
  // list it as a dependency (avoids spurious re-runs — and spurious updateActivity
@@ -56065,6 +56107,12 @@
56065
56107
  const DICTIONARIES = {
56066
56108
  en: EN,
56067
56109
  ar: {
56110
+ // Topic-picker + quick-questions surface strings. {category} is
56111
+ // substituted verbatim at the call site and rendered through
56112
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56113
+ chooseTopicLabel: "اختر موضوعًا للبدء:",
56114
+ categoryTopicsHeading: "يرجى طرح سؤالك حول **{category}** أدناه.",
56115
+ quickQuestionsHeadingLabel: "أو اطرح سؤالًا سريعًا:",
56068
56116
  typeYourMessage: "اكتب رسالتك هنا...",
56069
56117
  agentIsLoading: "جارٍ تحميل الوكيل...",
56070
56118
  conversationResolved: "تم إنهاء المحادثة",
@@ -56097,6 +56145,12 @@
56097
56145
  allowMicAccess: "السماح بالوصول إلى الميكروفون"
56098
56146
  },
56099
56147
  es: {
56148
+ // Topic-picker + quick-questions surface strings. {category} is
56149
+ // substituted verbatim at the call site and rendered through
56150
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56151
+ chooseTopicLabel: "Elige un tema para empezar:",
56152
+ categoryTopicsHeading: "Por favor, haz tu pregunta sobre **{category}** a continuación.",
56153
+ quickQuestionsHeadingLabel: "O haz una pregunta rápida:",
56100
56154
  typeYourMessage: "Escribe tu mensaje...",
56101
56155
  agentIsLoading: "El agente está cargando...",
56102
56156
  conversationResolved: "Conversación resuelta",
@@ -56129,6 +56183,12 @@
56129
56183
  allowMicAccess: "Permitir acceso al micrófono"
56130
56184
  },
56131
56185
  be: {
56186
+ // Topic-picker + quick-questions surface strings. {category} is
56187
+ // substituted verbatim at the call site and rendered through
56188
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56189
+ chooseTopicLabel: "Выберыце тэму, каб пачаць:",
56190
+ categoryTopicsHeading: "Калі ласка, задайце сваё пытанне пра **{category}** ніжэй.",
56191
+ quickQuestionsHeadingLabel: "Або задайце хуткае пытанне:",
56132
56192
  typeYourMessage: "Увядзіце паведамленне...",
56133
56193
  agentIsLoading: "Агент загружаецца...",
56134
56194
  conversationResolved: "Размова завершана",
@@ -56161,6 +56221,12 @@
56161
56221
  allowMicAccess: "Дазволіць доступ да мікрафона"
56162
56222
  },
56163
56223
  bs: {
56224
+ // Topic-picker + quick-questions surface strings. {category} is
56225
+ // substituted verbatim at the call site and rendered through
56226
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56227
+ chooseTopicLabel: "Odaberite temu za početak:",
56228
+ categoryTopicsHeading: "Molimo postavite svoje pitanje o **{category}** ispod.",
56229
+ quickQuestionsHeadingLabel: "Ili postavite brzo pitanje:",
56164
56230
  typeYourMessage: "Upišite vašu poruku...",
56165
56231
  agentIsLoading: "Agent se učitava...",
56166
56232
  conversationResolved: "Razgovor je završen",
@@ -56193,6 +56259,12 @@
56193
56259
  allowMicAccess: "Dozvoli pristup mikrofonu"
56194
56260
  },
56195
56261
  bg: {
56262
+ // Topic-picker + quick-questions surface strings. {category} is
56263
+ // substituted verbatim at the call site and rendered through
56264
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56265
+ chooseTopicLabel: "Изберете тема, за да започнете:",
56266
+ categoryTopicsHeading: "Моля, задайте въпроса си за **{category}** по-долу.",
56267
+ quickQuestionsHeadingLabel: "Или задайте бърз въпрос:",
56196
56268
  typeYourMessage: "Въведете съобщението си...",
56197
56269
  agentIsLoading: "Агентът се зарежда...",
56198
56270
  conversationResolved: "Разговорът е приключен",
@@ -56225,6 +56297,12 @@
56225
56297
  allowMicAccess: "Позволете достъп до микрофона"
56226
56298
  },
56227
56299
  my: {
56300
+ // Topic-picker + quick-questions surface strings. {category} is
56301
+ // substituted verbatim at the call site and rendered through
56302
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56303
+ chooseTopicLabel: "စတင်ရန် ခေါင်းစဉ်တစ်ခု ရွေးချယ်ပါ:",
56304
+ categoryTopicsHeading: "ကျေးဇူးပြု၍ သင်၏ **{category}** မေးခွန်းကို အောက်တွင် မေးပါ။",
56305
+ quickQuestionsHeadingLabel: "သို့မဟုတ် မြန်ဆန်သော မေးခွန်းတစ်ခု မေးပါ:",
56228
56306
  typeYourMessage: "သင့်မက်ဆေ့ချ်ကို ရိုက်ထည့်ပါ...",
56229
56307
  agentIsLoading: "အေးဂျင့်ကို ဖွင့်နေသည်...",
56230
56308
  conversationResolved: "စကားဝိုင်း ပြီးစီးပြီ",
@@ -56257,6 +56335,12 @@
56257
56335
  allowMicAccess: "မိုက်ခရိုဖုန်း ဝင်ရောက်ခွင့်ပြုပါ"
56258
56336
  },
56259
56337
  ca: {
56338
+ // Topic-picker + quick-questions surface strings. {category} is
56339
+ // substituted verbatim at the call site and rendered through
56340
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56341
+ chooseTopicLabel: "Tria un tema per començar:",
56342
+ categoryTopicsHeading: "Si us plau, fes la teva pregunta sobre **{category}** a continuació.",
56343
+ quickQuestionsHeadingLabel: "O fes una pregunta ràpida:",
56260
56344
  typeYourMessage: "Escriu el teu missatge...",
56261
56345
  agentIsLoading: "L'agent està carregant...",
56262
56346
  conversationResolved: "Conversa resolta",
@@ -56289,6 +56373,12 @@
56289
56373
  allowMicAccess: "Permetre accés al micròfon"
56290
56374
  },
56291
56375
  zh: {
56376
+ // Topic-picker + quick-questions surface strings. {category} is
56377
+ // substituted verbatim at the call site and rendered through
56378
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56379
+ chooseTopicLabel: "选择一个主题开始:",
56380
+ categoryTopicsHeading: "请在下方提出您关于 **{category}** 的问题。",
56381
+ quickQuestionsHeadingLabel: "或者提一个快速问题:",
56292
56382
  typeYourMessage: "输入您的消息...",
56293
56383
  agentIsLoading: "客服正在加载...",
56294
56384
  conversationResolved: "对话已结束",
@@ -56321,6 +56411,12 @@
56321
56411
  allowMicAccess: "允许访问麦克风"
56322
56412
  },
56323
56413
  hr: {
56414
+ // Topic-picker + quick-questions surface strings. {category} is
56415
+ // substituted verbatim at the call site and rendered through
56416
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56417
+ chooseTopicLabel: "Odaberite temu za početak:",
56418
+ categoryTopicsHeading: "Molimo postavite svoje pitanje o **{category}** u nastavku.",
56419
+ quickQuestionsHeadingLabel: "Ili postavite brzo pitanje:",
56324
56420
  typeYourMessage: "Upišite svoju poruku...",
56325
56421
  agentIsLoading: "Agent se učitava...",
56326
56422
  conversationResolved: "Razgovor je završen",
@@ -56353,6 +56449,12 @@
56353
56449
  allowMicAccess: "Dopustite pristup mikrofonu"
56354
56450
  },
56355
56451
  cs: {
56452
+ // Topic-picker + quick-questions surface strings. {category} is
56453
+ // substituted verbatim at the call site and rendered through
56454
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56455
+ chooseTopicLabel: "Vyberte téma a začněte:",
56456
+ categoryTopicsHeading: "Prosím, položte svou otázku ohledně **{category}** níže.",
56457
+ quickQuestionsHeadingLabel: "Nebo položte rychlou otázku:",
56356
56458
  typeYourMessage: "Napište zprávu...",
56357
56459
  agentIsLoading: "Agent se načítá...",
56358
56460
  conversationResolved: "Konverzace ukončena",
@@ -56385,6 +56487,12 @@
56385
56487
  allowMicAccess: "Povolit přístup k mikrofonu"
56386
56488
  },
56387
56489
  da: {
56490
+ // Topic-picker + quick-questions surface strings. {category} is
56491
+ // substituted verbatim at the call site and rendered through
56492
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56493
+ chooseTopicLabel: "Vælg et emne for at komme i gang:",
56494
+ categoryTopicsHeading: "Stil venligst dit spørgsmål om **{category}** nedenfor.",
56495
+ quickQuestionsHeadingLabel: "Eller stil et hurtigt spørgsmål:",
56388
56496
  typeYourMessage: "Skriv din besked...",
56389
56497
  agentIsLoading: "Agenten indlæses...",
56390
56498
  conversationResolved: "Samtale afsluttet",
@@ -56417,6 +56525,12 @@
56417
56525
  allowMicAccess: "Tillad mikrofonadgang"
56418
56526
  },
56419
56527
  nl: {
56528
+ // Topic-picker + quick-questions surface strings. {category} is
56529
+ // substituted verbatim at the call site and rendered through
56530
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56531
+ chooseTopicLabel: "Kies een onderwerp om te beginnen:",
56532
+ categoryTopicsHeading: "Stel hieronder uw vraag over **{category}**.",
56533
+ quickQuestionsHeadingLabel: "Of stel een snelle vraag:",
56420
56534
  typeYourMessage: "Typ je bericht...",
56421
56535
  agentIsLoading: "Agent wordt geladen...",
56422
56536
  conversationResolved: "Gesprek beëindigd",
@@ -56449,6 +56563,12 @@
56449
56563
  allowMicAccess: "Toegang tot microfoon toestaan"
56450
56564
  },
56451
56565
  et: {
56566
+ // Topic-picker + quick-questions surface strings. {category} is
56567
+ // substituted verbatim at the call site and rendered through
56568
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56569
+ chooseTopicLabel: "Vali alustamiseks teema:",
56570
+ categoryTopicsHeading: "Palun esita oma **{category}** küsimus allpool.",
56571
+ quickQuestionsHeadingLabel: "Või esita kiire küsimus:",
56452
56572
  typeYourMessage: "Sisesta oma sõnum...",
56453
56573
  agentIsLoading: "Agent laeb...",
56454
56574
  conversationResolved: "Vestlus lõpetatud",
@@ -56481,6 +56601,12 @@
56481
56601
  allowMicAccess: "Luba mikrofoni juurdepääs"
56482
56602
  },
56483
56603
  fi: {
56604
+ // Topic-picker + quick-questions surface strings. {category} is
56605
+ // substituted verbatim at the call site and rendered through
56606
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56607
+ chooseTopicLabel: "Aloita valitsemalla aihe:",
56608
+ categoryTopicsHeading: "Esitä **{category}**-kysymyksesi alla.",
56609
+ quickQuestionsHeadingLabel: "Tai esitä nopea kysymys:",
56484
56610
  typeYourMessage: "Kirjoita viestisi...",
56485
56611
  agentIsLoading: "Agenttia ladataan...",
56486
56612
  conversationResolved: "Keskustelu päättynyt",
@@ -56513,6 +56639,12 @@
56513
56639
  allowMicAccess: "Salli mikrofonin käyttö"
56514
56640
  },
56515
56641
  fr: {
56642
+ // Topic-picker + quick-questions surface strings. {category} is
56643
+ // substituted verbatim at the call site and rendered through
56644
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56645
+ chooseTopicLabel: "Choisissez un sujet pour commencer :",
56646
+ categoryTopicsHeading: "Veuillez poser votre question sur **{category}** ci-dessous.",
56647
+ quickQuestionsHeadingLabel: "Ou posez une question rapide :",
56516
56648
  typeYourMessage: "Tapez votre message...",
56517
56649
  agentIsLoading: "L'agent est en cours de chargement...",
56518
56650
  conversationResolved: "Conversation terminée",
@@ -56545,6 +56677,12 @@
56545
56677
  allowMicAccess: "Autoriser l'accès au microphone"
56546
56678
  },
56547
56679
  de: {
56680
+ // Topic-picker + quick-questions surface strings. {category} is
56681
+ // substituted verbatim at the call site and rendered through
56682
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56683
+ chooseTopicLabel: "Wählen Sie ein Thema, um zu beginnen:",
56684
+ categoryTopicsHeading: "Bitte stellen Sie Ihre Frage zu **{category}** unten.",
56685
+ quickQuestionsHeadingLabel: "Oder stellen Sie eine schnelle Frage:",
56548
56686
  typeYourMessage: "Geben Sie Ihre Nachricht ein...",
56549
56687
  agentIsLoading: "Agent wird geladen...",
56550
56688
  conversationResolved: "Gespräch beendet",
@@ -56577,6 +56715,12 @@
56577
56715
  allowMicAccess: "Mikrofonzugriff erlauben"
56578
56716
  },
56579
56717
  el: {
56718
+ // Topic-picker + quick-questions surface strings. {category} is
56719
+ // substituted verbatim at the call site and rendered through
56720
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56721
+ chooseTopicLabel: "Επιλέξτε ένα θέμα για να ξεκινήσετε:",
56722
+ categoryTopicsHeading: "Παρακαλώ κάντε την ερώτησή σας σχετικά με το **{category}** παρακάτω.",
56723
+ quickQuestionsHeadingLabel: "Ή κάντε μια γρήγορη ερώτηση:",
56580
56724
  typeYourMessage: "Πληκτρολογήστε το μήνυμά σας...",
56581
56725
  agentIsLoading: "Ο πράκτορας φορτώνει...",
56582
56726
  conversationResolved: "Η συζήτηση ολοκληρώθηκε",
@@ -56609,6 +56753,12 @@
56609
56753
  allowMicAccess: "Επιτρέψτε πρόσβαση στο μικρόφωνο"
56610
56754
  },
56611
56755
  hi: {
56756
+ // Topic-picker + quick-questions surface strings. {category} is
56757
+ // substituted verbatim at the call site and rendered through
56758
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56759
+ chooseTopicLabel: "शुरू करने के लिए एक विषय चुनें:",
56760
+ categoryTopicsHeading: "कृपया अपना **{category}** प्रश्न नीचे पूछें।",
56761
+ quickQuestionsHeadingLabel: "या एक त्वरित प्रश्न पूछें:",
56612
56762
  typeYourMessage: "अपना संदेश लिखें...",
56613
56763
  agentIsLoading: "एजेंट लोड हो रहा है...",
56614
56764
  conversationResolved: "बातचीत समाप्त हुई",
@@ -56643,6 +56793,12 @@
56643
56793
  allowMicAccess: "माइक्रोफ़ोन एक्सेस की अनुमति दें"
56644
56794
  },
56645
56795
  mr: {
56796
+ // Topic-picker + quick-questions surface strings. {category} is
56797
+ // substituted verbatim at the call site and rendered through
56798
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56799
+ chooseTopicLabel: "सुरू करण्यासाठी एक विषय निवडा:",
56800
+ categoryTopicsHeading: "कृपया तुमचा **{category}** प्रश्न खाली विचारा.",
56801
+ quickQuestionsHeadingLabel: "किंवा एक झटपट प्रश्न विचारा:",
56646
56802
  typeYourMessage: "तुमचा संदेश टाइप करा...",
56647
56803
  agentIsLoading: "एजंट लोड होत आहे...",
56648
56804
  conversationResolved: "संभाषण समाप्त",
@@ -56677,6 +56833,12 @@
56677
56833
  allowMicAccess: "मायक्रोफोन प्रवेशास परवानगी द्या"
56678
56834
  },
56679
56835
  hu: {
56836
+ // Topic-picker + quick-questions surface strings. {category} is
56837
+ // substituted verbatim at the call site and rendered through
56838
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56839
+ chooseTopicLabel: "Válassz egy témát a kezdéshez:",
56840
+ categoryTopicsHeading: "Kérjük, tedd fel **{category}** kérdésedet alább.",
56841
+ quickQuestionsHeadingLabel: "Vagy tegyél fel egy gyors kérdést:",
56680
56842
  typeYourMessage: "Írja be az üzenetét...",
56681
56843
  agentIsLoading: "Az ügynök betöltődik...",
56682
56844
  conversationResolved: "Beszélgetés befejezve",
@@ -56709,6 +56871,12 @@
56709
56871
  allowMicAccess: "Mikrofon hozzáférés engedélyezése"
56710
56872
  },
56711
56873
  is: {
56874
+ // Topic-picker + quick-questions surface strings. {category} is
56875
+ // substituted verbatim at the call site and rendered through
56876
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56877
+ chooseTopicLabel: "Veldu efni til að byrja:",
56878
+ categoryTopicsHeading: "Vinsamlegast spurðu spurningu þinnar um **{category}** hér að neðan.",
56879
+ quickQuestionsHeadingLabel: "Eða spurðu fljótlegrar spurningar:",
56712
56880
  typeYourMessage: "Sláðu inn skilaboð...",
56713
56881
  agentIsLoading: "Fulltrúi hleðst...",
56714
56882
  conversationResolved: "Samtali lokið",
@@ -56741,6 +56909,12 @@
56741
56909
  allowMicAccess: "Leyfa aðgang að hljóðnema"
56742
56910
  },
56743
56911
  id: {
56912
+ // Topic-picker + quick-questions surface strings. {category} is
56913
+ // substituted verbatim at the call site and rendered through
56914
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56915
+ chooseTopicLabel: "Pilih topik untuk memulai:",
56916
+ categoryTopicsHeading: "Silakan ajukan pertanyaan **{category}** Anda di bawah.",
56917
+ quickQuestionsHeadingLabel: "Atau ajukan pertanyaan cepat:",
56744
56918
  typeYourMessage: "Ketik pesan Anda...",
56745
56919
  agentIsLoading: "Agen sedang dimuat...",
56746
56920
  conversationResolved: "Percakapan selesai",
@@ -56773,6 +56947,12 @@
56773
56947
  allowMicAccess: "Izinkan akses mikrofon"
56774
56948
  },
56775
56949
  it: {
56950
+ // Topic-picker + quick-questions surface strings. {category} is
56951
+ // substituted verbatim at the call site and rendered through
56952
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56953
+ chooseTopicLabel: "Scegli un argomento per iniziare:",
56954
+ categoryTopicsHeading: "Per favore, poni la tua domanda su **{category}** qui sotto.",
56955
+ quickQuestionsHeadingLabel: "Oppure fai una domanda rapida:",
56776
56956
  typeYourMessage: "Scrivi il tuo messaggio...",
56777
56957
  agentIsLoading: "L'agente sta caricando...",
56778
56958
  conversationResolved: "Conversazione conclusa",
@@ -56805,6 +56985,12 @@
56805
56985
  allowMicAccess: "Consenti l'accesso al microfono"
56806
56986
  },
56807
56987
  ja: {
56988
+ // Topic-picker + quick-questions surface strings. {category} is
56989
+ // substituted verbatim at the call site and rendered through
56990
+ // ReactMarkdown — keep the **{category}** bold wrapper.
56991
+ chooseTopicLabel: "始めるにはトピックを選択してください:",
56992
+ categoryTopicsHeading: "**{category}** に関するご質問を以下にお書きください。",
56993
+ quickQuestionsHeadingLabel: "または、簡単な質問をどうぞ:",
56808
56994
  typeYourMessage: "メッセージを入力してください...",
56809
56995
  agentIsLoading: "エージェントを読み込んでいます...",
56810
56996
  conversationResolved: "会話が終了しました",
@@ -56837,6 +57023,12 @@
56837
57023
  allowMicAccess: "マイクへのアクセスを許可"
56838
57024
  },
56839
57025
  ko: {
57026
+ // Topic-picker + quick-questions surface strings. {category} is
57027
+ // substituted verbatim at the call site and rendered through
57028
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57029
+ chooseTopicLabel: "시작하려면 주제를 선택하세요:",
57030
+ categoryTopicsHeading: "**{category}** 에 대한 질문을 아래에 작성해 주세요.",
57031
+ quickQuestionsHeadingLabel: "또는 간단한 질문을 해보세요:",
56840
57032
  typeYourMessage: "메시지를 입력하세요...",
56841
57033
  agentIsLoading: "상담원을 불러오는 중...",
56842
57034
  conversationResolved: "대화가 종료되었습니다",
@@ -56869,6 +57061,12 @@
56869
57061
  allowMicAccess: "마이크 액세스 허용"
56870
57062
  },
56871
57063
  lv: {
57064
+ // Topic-picker + quick-questions surface strings. {category} is
57065
+ // substituted verbatim at the call site and rendered through
57066
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57067
+ chooseTopicLabel: "Izvēlieties tēmu, lai sāktu:",
57068
+ categoryTopicsHeading: "Lūdzu, uzdodiet savu jautājumu par **{category}** zemāk.",
57069
+ quickQuestionsHeadingLabel: "Vai uzdodiet ātru jautājumu:",
56872
57070
  typeYourMessage: "Ierakstiet savu ziņu...",
56873
57071
  agentIsLoading: "Aģents tiek ielādēts...",
56874
57072
  conversationResolved: "Saruna pabeigta",
@@ -56901,6 +57099,12 @@
56901
57099
  allowMicAccess: "Atļaut piekļuvi mikrofonam"
56902
57100
  },
56903
57101
  lt: {
57102
+ // Topic-picker + quick-questions surface strings. {category} is
57103
+ // substituted verbatim at the call site and rendered through
57104
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57105
+ chooseTopicLabel: "Pasirinkite temą, kad pradėtumėte:",
57106
+ categoryTopicsHeading: "Prašome užduoti savo klausimą apie **{category}** žemiau.",
57107
+ quickQuestionsHeadingLabel: "Arba užduokite greitą klausimą:",
56904
57108
  typeYourMessage: "Įveskite pranešimą...",
56905
57109
  agentIsLoading: "Agentas kraunamas...",
56906
57110
  conversationResolved: "Pokalbis baigtas",
@@ -56933,6 +57137,12 @@
56933
57137
  allowMicAccess: "Leisti prieigą prie mikrofono"
56934
57138
  },
56935
57139
  mk: {
57140
+ // Topic-picker + quick-questions surface strings. {category} is
57141
+ // substituted verbatim at the call site and rendered through
57142
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57143
+ chooseTopicLabel: "Изберете тема за да започнете:",
57144
+ categoryTopicsHeading: "Ве молиме поставете го вашето прашање за **{category}** подолу.",
57145
+ quickQuestionsHeadingLabel: "Или поставете брзо прашање:",
56936
57146
  typeYourMessage: "Напишете ја вашата порака...",
56937
57147
  agentIsLoading: "Агентот се вчитува...",
56938
57148
  conversationResolved: "Разговорот е завршен",
@@ -56965,6 +57175,12 @@
56965
57175
  allowMicAccess: "Дозволи пристап до микрофонот"
56966
57176
  },
56967
57177
  ms: {
57178
+ // Topic-picker + quick-questions surface strings. {category} is
57179
+ // substituted verbatim at the call site and rendered through
57180
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57181
+ chooseTopicLabel: "Pilih topik untuk memulakan:",
57182
+ categoryTopicsHeading: "Sila ajukan soalan **{category}** anda di bawah.",
57183
+ quickQuestionsHeadingLabel: "Atau ajukan soalan ringkas:",
56968
57184
  typeYourMessage: "Taip mesej anda...",
56969
57185
  agentIsLoading: "Ejen sedang dimuatkan...",
56970
57186
  conversationResolved: "Perbualan selesai",
@@ -56997,6 +57213,12 @@
56997
57213
  allowMicAccess: "Benarkan akses mikrofon"
56998
57214
  },
56999
57215
  no: {
57216
+ // Topic-picker + quick-questions surface strings. {category} is
57217
+ // substituted verbatim at the call site and rendered through
57218
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57219
+ chooseTopicLabel: "Velg et emne for å komme i gang:",
57220
+ categoryTopicsHeading: "Vennligst still spørsmålet ditt om **{category}** nedenfor.",
57221
+ quickQuestionsHeadingLabel: "Eller still et raskt spørsmål:",
57000
57222
  typeYourMessage: "Skriv meldingen din...",
57001
57223
  agentIsLoading: "Agenten laster...",
57002
57224
  conversationResolved: "Samtale avsluttet",
@@ -57029,6 +57251,12 @@
57029
57251
  allowMicAccess: "Tillat mikrofontilgang"
57030
57252
  },
57031
57253
  fa: {
57254
+ // Topic-picker + quick-questions surface strings. {category} is
57255
+ // substituted verbatim at the call site and rendered through
57256
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57257
+ chooseTopicLabel: "برای شروع، یک موضوع را انتخاب کنید:",
57258
+ categoryTopicsHeading: "لطفاً سؤال خود درباره **{category}** را در زیر مطرح کنید.",
57259
+ quickQuestionsHeadingLabel: "یا یک سؤال سریع بپرسید:",
57032
57260
  typeYourMessage: "پیام خود را تایپ کنید...",
57033
57261
  agentIsLoading: "در حال بارگذاری نماینده...",
57034
57262
  conversationResolved: "مکالمه به پایان رسید",
@@ -57061,6 +57289,12 @@
57061
57289
  allowMicAccess: "اجازه دسترسی به میکروفون"
57062
57290
  },
57063
57291
  pl: {
57292
+ // Topic-picker + quick-questions surface strings. {category} is
57293
+ // substituted verbatim at the call site and rendered through
57294
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57295
+ chooseTopicLabel: "Wybierz temat, aby rozpocząć:",
57296
+ categoryTopicsHeading: "Proszę zadać pytanie dotyczące **{category}** poniżej.",
57297
+ quickQuestionsHeadingLabel: "Lub zadaj szybkie pytanie:",
57064
57298
  typeYourMessage: "Wpisz wiadomość...",
57065
57299
  agentIsLoading: "Agent się ładuje...",
57066
57300
  conversationResolved: "Rozmowa zakończona",
@@ -57093,6 +57327,12 @@
57093
57327
  allowMicAccess: "Zezwól na dostęp do mikrofonu"
57094
57328
  },
57095
57329
  pt: {
57330
+ // Topic-picker + quick-questions surface strings. {category} is
57331
+ // substituted verbatim at the call site and rendered through
57332
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57333
+ chooseTopicLabel: "Escolha um tópico para começar:",
57334
+ categoryTopicsHeading: "Por favor, faça sua pergunta sobre **{category}** abaixo.",
57335
+ quickQuestionsHeadingLabel: "Ou faça uma pergunta rápida:",
57096
57336
  typeYourMessage: "Digite sua mensagem...",
57097
57337
  agentIsLoading: "O agente está carregando...",
57098
57338
  conversationResolved: "Conversa encerrada",
@@ -57125,6 +57365,12 @@
57125
57365
  allowMicAccess: "Permitir acesso ao microfone"
57126
57366
  },
57127
57367
  ro: {
57368
+ // Topic-picker + quick-questions surface strings. {category} is
57369
+ // substituted verbatim at the call site and rendered through
57370
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57371
+ chooseTopicLabel: "Alegeți un subiect pentru a începe:",
57372
+ categoryTopicsHeading: "Vă rugăm să adresați întrebarea dvs. despre **{category}** mai jos.",
57373
+ quickQuestionsHeadingLabel: "Sau puneți o întrebare rapidă:",
57128
57374
  typeYourMessage: "Scrieți mesajul dvs...",
57129
57375
  agentIsLoading: "Agentul se încarcă...",
57130
57376
  conversationResolved: "Conversație încheiată",
@@ -57157,6 +57403,12 @@
57157
57403
  allowMicAccess: "Permiteți accesul la microfon"
57158
57404
  },
57159
57405
  ru: {
57406
+ // Topic-picker + quick-questions surface strings. {category} is
57407
+ // substituted verbatim at the call site and rendered through
57408
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57409
+ chooseTopicLabel: "Выберите тему, чтобы начать:",
57410
+ categoryTopicsHeading: "Пожалуйста, задайте свой вопрос о **{category}** ниже.",
57411
+ quickQuestionsHeadingLabel: "Или задайте быстрый вопрос:",
57160
57412
  typeYourMessage: "Введите ваше сообщение...",
57161
57413
  agentIsLoading: "Агент загружается...",
57162
57414
  conversationResolved: "Разговор завершен",
@@ -57189,6 +57441,12 @@
57189
57441
  allowMicAccess: "Разрешить доступ к микрофону"
57190
57442
  },
57191
57443
  sr: {
57444
+ // Topic-picker + quick-questions surface strings. {category} is
57445
+ // substituted verbatim at the call site and rendered through
57446
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57447
+ chooseTopicLabel: "Изаберите тему да бисте започели:",
57448
+ categoryTopicsHeading: "Молимо поставите своје питање о **{category}** испод.",
57449
+ quickQuestionsHeadingLabel: "Или поставите брзо питање:",
57192
57450
  typeYourMessage: "Укуцајте вашу поруку...",
57193
57451
  agentIsLoading: "Агент се учитава...",
57194
57452
  conversationResolved: "Разговор је завршен",
@@ -57221,6 +57479,12 @@
57221
57479
  allowMicAccess: "Дозволи приступ микрофону"
57222
57480
  },
57223
57481
  sk: {
57482
+ // Topic-picker + quick-questions surface strings. {category} is
57483
+ // substituted verbatim at the call site and rendered through
57484
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57485
+ chooseTopicLabel: "Vyberte tému, aby ste začali:",
57486
+ categoryTopicsHeading: "Prosím, položte svoju otázku o **{category}** nižšie.",
57487
+ quickQuestionsHeadingLabel: "Alebo položte rýchlu otázku:",
57224
57488
  typeYourMessage: "Napíšte svoju správu...",
57225
57489
  agentIsLoading: "Agent sa načítava...",
57226
57490
  conversationResolved: "Konverzácia ukončená",
@@ -57253,6 +57517,12 @@
57253
57517
  allowMicAccess: "Povoliť prístup k mikrofónu"
57254
57518
  },
57255
57519
  sl: {
57520
+ // Topic-picker + quick-questions surface strings. {category} is
57521
+ // substituted verbatim at the call site and rendered through
57522
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57523
+ chooseTopicLabel: "Izberite temo za začetek:",
57524
+ categoryTopicsHeading: "Prosimo, postavite vprašanje o **{category}** spodaj.",
57525
+ quickQuestionsHeadingLabel: "Ali postavite hitro vprašanje:",
57256
57526
  typeYourMessage: "Vnesite svoje sporočilo...",
57257
57527
  agentIsLoading: "Agent se nalaga...",
57258
57528
  conversationResolved: "Pogovor je končan",
@@ -57285,6 +57555,12 @@
57285
57555
  allowMicAccess: "Dovoli dostop do mikrofona"
57286
57556
  },
57287
57557
  sv: {
57558
+ // Topic-picker + quick-questions surface strings. {category} is
57559
+ // substituted verbatim at the call site and rendered through
57560
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57561
+ chooseTopicLabel: "Välj ett ämne för att komma igång:",
57562
+ categoryTopicsHeading: "Ställ gärna din **{category}**-fråga nedan.",
57563
+ quickQuestionsHeadingLabel: "Eller ställ en snabb fråga:",
57288
57564
  typeYourMessage: "Skriv ditt meddelande...",
57289
57565
  agentIsLoading: "Agenten laddas...",
57290
57566
  conversationResolved: "Konversation avslutad",
@@ -57317,6 +57593,12 @@
57317
57593
  allowMicAccess: "Tillåt mikrofonåtkomst"
57318
57594
  },
57319
57595
  th: {
57596
+ // Topic-picker + quick-questions surface strings. {category} is
57597
+ // substituted verbatim at the call site and rendered through
57598
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57599
+ chooseTopicLabel: "เลือกหัวข้อเพื่อเริ่มต้น:",
57600
+ categoryTopicsHeading: "โปรดถามคำถามเกี่ยวกับ **{category}** ด้านล่าง",
57601
+ quickQuestionsHeadingLabel: "หรือถามคำถามด่วน:",
57320
57602
  typeYourMessage: "พิมพ์ข้อความของคุณ...",
57321
57603
  agentIsLoading: "กำลังโหลดตัวแทน...",
57322
57604
  conversationResolved: "การสนทนาสิ้นสุดลง",
@@ -57349,6 +57631,12 @@
57349
57631
  allowMicAccess: "อนุญาตการเข้าถึงไมโครโฟน"
57350
57632
  },
57351
57633
  tr: {
57634
+ // Topic-picker + quick-questions surface strings. {category} is
57635
+ // substituted verbatim at the call site and rendered through
57636
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57637
+ chooseTopicLabel: "Başlamak için bir konu seçin:",
57638
+ categoryTopicsHeading: "Lütfen **{category}** sorunuzu aşağıya yazın.",
57639
+ quickQuestionsHeadingLabel: "Veya hızlı bir soru sorun:",
57352
57640
  typeYourMessage: "Mesajınızı yazın...",
57353
57641
  agentIsLoading: "Temsilci yükleniyor...",
57354
57642
  conversationResolved: "Görüşme sonlandırıldı",
@@ -57381,6 +57669,12 @@
57381
57669
  allowMicAccess: "Mikrofon erişimine izin ver"
57382
57670
  },
57383
57671
  uk: {
57672
+ // Topic-picker + quick-questions surface strings. {category} is
57673
+ // substituted verbatim at the call site and rendered through
57674
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57675
+ chooseTopicLabel: "Виберіть тему, щоб почати:",
57676
+ categoryTopicsHeading: "Будь ласка, поставте своє запитання щодо **{category}** нижче.",
57677
+ quickQuestionsHeadingLabel: "Або поставте швидке запитання:",
57384
57678
  typeYourMessage: "Введіть ваше повідомлення...",
57385
57679
  agentIsLoading: "Агент завантажується...",
57386
57680
  conversationResolved: "Розмову завершено",
@@ -57413,6 +57707,12 @@
57413
57707
  allowMicAccess: "Дозволити доступ до мікрофона"
57414
57708
  },
57415
57709
  ur: {
57710
+ // Topic-picker + quick-questions surface strings. {category} is
57711
+ // substituted verbatim at the call site and rendered through
57712
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57713
+ chooseTopicLabel: "شروع کرنے کے لیے ایک موضوع منتخب کریں:",
57714
+ categoryTopicsHeading: "براہ کرم اپنا **{category}** سوال نیچے پوچھیں۔",
57715
+ quickQuestionsHeadingLabel: "یا کوئی فوری سوال پوچھیں:",
57416
57716
  typeYourMessage: "اپنا پیغام ٹائپ کریں...",
57417
57717
  agentIsLoading: "ایجنٹ لوڈ ہو رہا ہے...",
57418
57718
  conversationResolved: "گفتگو مکمل",
@@ -57445,6 +57745,12 @@
57445
57745
  allowMicAccess: "مائیکروفون تک رسائی کی اجازت دیں"
57446
57746
  },
57447
57747
  vi: {
57748
+ // Topic-picker + quick-questions surface strings. {category} is
57749
+ // substituted verbatim at the call site and rendered through
57750
+ // ReactMarkdown — keep the **{category}** bold wrapper.
57751
+ chooseTopicLabel: "Chọn một chủ đề để bắt đầu:",
57752
+ categoryTopicsHeading: "Vui lòng đặt câu hỏi của bạn về **{category}** bên dưới.",
57753
+ quickQuestionsHeadingLabel: "Hoặc đặt một câu hỏi nhanh:",
57448
57754
  typeYourMessage: "Nhập tin nhắn của bạn...",
57449
57755
  agentIsLoading: "Đang tải trợ lý...",
57450
57756
  conversationResolved: "Cuộc trò chuyện đã kết thúc",
@@ -60013,6 +60319,64 @@
60013
60319
  });
60014
60320
  };
60015
60321
 
60322
+ const createSession = async config => {
60323
+ // Initialize chat session if needed
60324
+ if (config.sessionUrl) {
60325
+ try {
60326
+ const response = await fetch(config.sessionUrl, {
60327
+ method: "POST",
60328
+ headers: _objectSpread2({
60329
+ "Content-Type": "application/json"
60330
+ }, config.apiKey && {
60331
+ Authorization: "Bearer ".concat(config.apiKey)
60332
+ }),
60333
+ body: JSON.stringify({
60334
+ userId: config.userId,
60335
+ userName: config.userName,
60336
+ userEmail: config.userEmail
60337
+ }),
60338
+ credentials: "include"
60339
+ });
60340
+ const data = await response.json();
60341
+ return data.sessionId;
60342
+ } catch (error) {
60343
+ console.error("Session creation error:", error);
60344
+ return null;
60345
+ }
60346
+ }
60347
+ return null;
60348
+ };
60349
+
60350
+ /**
60351
+ * Fetches agent configuration from the deploy-agent endpoint
60352
+ * @param {string} apiBaseUrl - Base URL for the API
60353
+ * @param {string} orgId - Organization ID
60354
+ * @returns {Promise<object>} Agent configuration object
60355
+ */
60356
+ const fetchAgentConfig = async (apiBaseUrl, orgId) => {
60357
+ if (!apiBaseUrl || !orgId) {
60358
+ throw new Error("apiBaseUrl and orgId are required");
60359
+ }
60360
+ try {
60361
+ const url = "".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/deploy-agent/").concat(orgId);
60362
+ const response = await fetch(url, {
60363
+ method: "GET",
60364
+ headers: {
60365
+ "Content-Type": "application/json"
60366
+ },
60367
+ credentials: "include"
60368
+ });
60369
+ if (!response.ok) {
60370
+ throw new Error("HTTP error! status: ".concat(response.status));
60371
+ }
60372
+ const data = await response.json();
60373
+ return data;
60374
+ } catch (error) {
60375
+ console.error("Failed to fetch agent configuration:", error);
60376
+ throw error;
60377
+ }
60378
+ };
60379
+
60016
60380
  const ChatWidget = _ref => {
60017
60381
  var _agentConfig$concierg;
60018
60382
  let {
@@ -60091,7 +60455,7 @@
60091
60455
 
60092
60456
  // Map API response to widget props (use props as overrides if provided)
60093
60457
  const widgetConfig = reactExports.useMemo(() => {
60094
- 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, _ref3, _messages$bundles$loc, _messages$bundles, _messages$bundles2, _ref4, _ref5, _ref6, _ref7, _branding$chatbotAvat, _branding$chatbotAvat2, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _ref19, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _ref27, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba;
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;
60095
60459
  if (!agentConfig) {
60096
60460
  return null;
60097
60461
  }
@@ -60111,55 +60475,69 @@
60111
60475
  const security = (_cfg$security = cfg.security) !== null && _cfg$security !== void 0 ? _cfg$security : {};
60112
60476
  const layout = (_cfg$layout = cfg.layout) !== null && _cfg$layout !== void 0 ? _cfg$layout : {};
60113
60477
  const locale = (_ref2 = defaultLanguage !== null && defaultLanguage !== void 0 ? defaultLanguage : messages.defaultLocale) !== null && _ref2 !== void 0 ? _ref2 : "en";
60114
- const bundle = (_ref3 = (_messages$bundles$loc = (_messages$bundles = messages.bundles) === null || _messages$bundles === void 0 ? void 0 : _messages$bundles[locale]) !== null && _messages$bundles$loc !== void 0 ? _messages$bundles$loc : (_messages$bundles2 = messages.bundles) === null || _messages$bundles2 === void 0 ? void 0 : _messages$bundles2["en"]) !== null && _ref3 !== void 0 ? _ref3 : {};
60478
+ // Per-field English fallback for the localized bundle. We can't use
60479
+ // `bundles[locale] ?? bundles.en` because `??` only catches
60480
+ // null/undefined — and the Languages config UI doesn't currently
60481
+ // expose per-language welcome / quickQuestions text, so picking
60482
+ // Hindi (etc.) as default leaves `bundles.hi = { welcome: "",
60483
+ // quickQuestions: [] }`. The empty string would shadow English and
60484
+ // useChatState (`if (!welcomeMessage) return []`) silently skips
60485
+ // the greeting bubble. Treat empty string / empty array as
60486
+ // "missing" and fall back to the English bundle for that field.
60487
+ const localeBundle = (_messages$bundles$loc = (_messages$bundles = messages.bundles) === null || _messages$bundles === void 0 ? void 0 : _messages$bundles[locale]) !== null && _messages$bundles$loc !== void 0 ? _messages$bundles$loc : {};
60488
+ const enBundle = (_messages$bundles$en = (_messages$bundles2 = messages.bundles) === null || _messages$bundles2 === void 0 ? void 0 : _messages$bundles2["en"]) !== null && _messages$bundles$en !== void 0 ? _messages$bundles$en : {};
60489
+ const bundle = {
60490
+ welcome: localeBundle.welcome || enBundle.welcome,
60491
+ quickQuestions: Array.isArray(localeBundle.quickQuestions) && localeBundle.quickQuestions.length > 0 ? localeBundle.quickQuestions : enBundle.quickQuestions
60492
+ };
60115
60493
 
60116
60494
  // Enum values from the DB are UPPER_SNAKE_CASE; widget uses lower-kebab-case
60117
60495
  const toLauncherPos = val => val ? val.toLowerCase().replace(/_/g, "-") : "bottom-right";
60118
60496
  const toDisclaimerPos = val => val ? val.toLowerCase() : "top";
60119
- const finalAllowedDomains = (_ref4 = allowedDomains !== null && allowedDomains !== void 0 ? allowedDomains : security.allowedDomains) !== null && _ref4 !== void 0 ? _ref4 : [];
60497
+ const finalAllowedDomains = (_ref3 = allowedDomains !== null && allowedDomains !== void 0 ? allowedDomains : security.allowedDomains) !== null && _ref3 !== void 0 ? _ref3 : [];
60120
60498
  return {
60121
60499
  darkMode: darkMode !== null && darkMode !== void 0 ? darkMode : false,
60122
- primaryColor: (_ref5 = primaryColor !== null && primaryColor !== void 0 ? primaryColor : branding.brandColor) !== null && _ref5 !== void 0 ? _ref5 : "#2563eb",
60123
- companyName: (_ref6 = companyName !== null && companyName !== void 0 ? companyName : identity.companyName) !== null && _ref6 !== void 0 ? _ref6 : "Support Team",
60500
+ primaryColor: (_ref4 = primaryColor !== null && primaryColor !== void 0 ? primaryColor : branding.brandColor) !== null && _ref4 !== void 0 ? _ref4 : "#2563eb",
60501
+ companyName: (_ref5 = companyName !== null && companyName !== void 0 ? companyName : identity.companyName) !== null && _ref5 !== void 0 ? _ref5 : "Support Team",
60124
60502
  conciergeName: conciergeName !== null && conciergeName !== void 0 ? conciergeName : identity.conciergeName,
60125
- companyLogo: (_ref7 = companyLogo !== null && companyLogo !== void 0 ? companyLogo : branding.avatar) !== null && _ref7 !== void 0 ? _ref7 : null,
60503
+ companyLogo: (_ref6 = companyLogo !== null && companyLogo !== void 0 ? companyLogo : branding.avatar) !== null && _ref6 !== void 0 ? _ref6 : null,
60126
60504
  chatbotAvatarEnabled: (_branding$chatbotAvat = branding.chatbotAvatarEnabled) !== null && _branding$chatbotAvat !== void 0 ? _branding$chatbotAvat : false,
60127
60505
  chatbotAvatar: (_branding$chatbotAvat2 = branding.chatbotAvatar) !== null && _branding$chatbotAvat2 !== void 0 ? _branding$chatbotAvat2 : null,
60128
- welcomeMessage: (_ref8 = welcomeMessage !== null && welcomeMessage !== void 0 ? welcomeMessage : bundle.welcome) !== null && _ref8 !== void 0 ? _ref8 : "Hi! How can we help?",
60129
- quickQuestions: (_ref9 = quickQuestions !== null && quickQuestions !== void 0 ? quickQuestions : bundle.quickQuestions) !== null && _ref9 !== void 0 ? _ref9 : [],
60506
+ welcomeMessage: (_ref7 = welcomeMessage !== null && welcomeMessage !== void 0 ? welcomeMessage : bundle.welcome) !== null && _ref7 !== void 0 ? _ref7 : "Hi! How can we help?",
60507
+ quickQuestions: (_ref8 = quickQuestions !== null && quickQuestions !== void 0 ? quickQuestions : bundle.quickQuestions) !== null && _ref8 !== void 0 ? _ref8 : [],
60130
60508
  quickQuestionsEnabled: quickQuestionsEnabled !== null && quickQuestionsEnabled !== void 0 ? quickQuestionsEnabled : messages.quickQuestionsEnabled,
60131
60509
  textColor: textColor !== null && textColor !== void 0 ? textColor : "#000000",
60132
60510
  agentMessageBubbleColor: agentMessageBubbleColor !== null && agentMessageBubbleColor !== void 0 ? agentMessageBubbleColor : typography.agentMessageBubbleColor,
60133
60511
  userMessageBoxColor: userMessageBoxColor !== null && userMessageBoxColor !== void 0 ? userMessageBoxColor : typography.userMessageBoxColor,
60134
- assistantTextColor: (_ref0 = assistantTextColor !== null && assistantTextColor !== void 0 ? assistantTextColor : typography.assistantTextColor) !== null && _ref0 !== void 0 ? _ref0 : "#000000",
60135
- userTextColor: (_ref1 = userTextColor !== null && userTextColor !== void 0 ? userTextColor : typography.userTextColor) !== null && _ref1 !== void 0 ? _ref1 : "#000000",
60136
- fontFamily: (_ref10 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref10 !== void 0 ? _ref10 : "Inter",
60137
- fontSize: (_ref11 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref11 !== void 0 ? _ref11 : "14px",
60512
+ assistantTextColor: (_ref9 = assistantTextColor !== null && assistantTextColor !== void 0 ? assistantTextColor : typography.assistantTextColor) !== null && _ref9 !== void 0 ? _ref9 : "#000000",
60513
+ userTextColor: (_ref0 = userTextColor !== null && userTextColor !== void 0 ? userTextColor : typography.userTextColor) !== null && _ref0 !== void 0 ? _ref0 : "#000000",
60514
+ fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
60515
+ fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
60138
60516
  defaultLanguage: locale,
60139
60517
  organizationId: orgId,
60140
- headerTextBold: (_ref12 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref12 !== void 0 ? _ref12 : false,
60141
- headerTextItalic: (_ref13 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref13 !== void 0 ? _ref13 : false,
60518
+ headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
60519
+ headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
60142
60520
  headerTextColor: typography.headerTextColor,
60143
60521
  headerFontFamily: typography.headerFontFamily,
60144
60522
  headerFontSize: typography.headerFontSize,
60145
- enableVoiceInteraction: (_ref14 = enableVoiceInteraction !== null && enableVoiceInteraction !== void 0 ? enableVoiceInteraction : voice.enableVoiceInteraction) !== null && _ref14 !== void 0 ? _ref14 : false,
60146
- disclaimerEnabled: (_ref15 = disclaimerEnabled !== null && disclaimerEnabled !== void 0 ? disclaimerEnabled : disclaimer.enabled) !== null && _ref15 !== void 0 ? _ref15 : false,
60523
+ enableVoiceInteraction: (_ref13 = enableVoiceInteraction !== null && enableVoiceInteraction !== void 0 ? enableVoiceInteraction : voice.enableVoiceInteraction) !== null && _ref13 !== void 0 ? _ref13 : false,
60524
+ disclaimerEnabled: (_ref14 = disclaimerEnabled !== null && disclaimerEnabled !== void 0 ? disclaimerEnabled : disclaimer.enabled) !== null && _ref14 !== void 0 ? _ref14 : false,
60147
60525
  disclaimerText: disclaimerText !== null && disclaimerText !== void 0 ? disclaimerText : disclaimer.text,
60148
60526
  disclaimerPosition: disclaimerPosition !== null && disclaimerPosition !== void 0 ? disclaimerPosition : toDisclaimerPos(disclaimer.position),
60149
60527
  disclaimerLinkUrl: normalizeExternalUrl(disclaimerLinkUrl !== null && disclaimerLinkUrl !== void 0 ? disclaimerLinkUrl : disclaimer.linkUrl),
60150
60528
  disclaimerLinkLabel: disclaimerLinkLabel !== null && disclaimerLinkLabel !== void 0 ? disclaimerLinkLabel : disclaimer.linkLabel,
60151
- showPoweredBy: (_ref16 = showPoweredBy !== null && showPoweredBy !== void 0 ? showPoweredBy : branding.showPoweredBy) !== null && _ref16 !== void 0 ? _ref16 : true,
60152
- onboardingQuestions: (_ref17 = onboardingQuestions !== null && onboardingQuestions !== void 0 ? onboardingQuestions : onboarding.questions) !== null && _ref17 !== void 0 ? _ref17 : [],
60153
- collectionPrompt: (_ref18 = collectionPrompt !== null && collectionPrompt !== void 0 ? collectionPrompt : onboarding.collapsiblePrompt) !== null && _ref18 !== void 0 ? _ref18 : "Before we begin, I need to verify your details.",
60529
+ showPoweredBy: (_ref15 = showPoweredBy !== null && showPoweredBy !== void 0 ? showPoweredBy : branding.showPoweredBy) !== null && _ref15 !== void 0 ? _ref15 : true,
60530
+ onboardingQuestions: (_ref16 = onboardingQuestions !== null && onboardingQuestions !== void 0 ? onboardingQuestions : onboarding.questions) !== null && _ref16 !== void 0 ? _ref16 : [],
60531
+ collectionPrompt: (_ref17 = collectionPrompt !== null && collectionPrompt !== void 0 ? collectionPrompt : onboarding.collapsiblePrompt) !== null && _ref17 !== void 0 ? _ref17 : "Before we begin, I need to verify your details.",
60154
60532
  allowedDomains: finalAllowedDomains,
60155
60533
  launcherPosition: position !== null && position !== void 0 ? position : toLauncherPos(layout.launcherPosition),
60156
- onboardingEnabled: (_ref19 = onboardingEnabled !== null && onboardingEnabled !== void 0 ? onboardingEnabled : onboarding.enabled) !== null && _ref19 !== void 0 ? _ref19 : false,
60534
+ onboardingEnabled: (_ref18 = onboardingEnabled !== null && onboardingEnabled !== void 0 ? onboardingEnabled : onboarding.enabled) !== null && _ref18 !== void 0 ? _ref18 : false,
60157
60535
  inactivityTimeoutValue: (_inactivity$timeoutVa = inactivity.timeoutValue) !== null && _inactivity$timeoutVa !== void 0 ? _inactivity$timeoutVa : null,
60158
60536
  inactivityTimeoutUnit: (_inactivity$timeoutUn = inactivity.timeoutUnit) !== null && _inactivity$timeoutUn !== void 0 ? _inactivity$timeoutUn : null,
60159
- agentMessageBubbleFontFamily: (_ref20 = (_ref21 = agentMessageBubbleFontFamily !== null && agentMessageBubbleFontFamily !== void 0 ? agentMessageBubbleFontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref21 !== void 0 ? _ref21 : fontFamily) !== null && _ref20 !== void 0 ? _ref20 : "Inter",
60160
- agentMessageBubbleFontSize: (_ref22 = (_ref23 = agentMessageBubbleFontSize !== null && agentMessageBubbleFontSize !== void 0 ? agentMessageBubbleFontSize : typography.agentMessageBubbleFontSize) !== null && _ref23 !== void 0 ? _ref23 : fontSize) !== null && _ref22 !== void 0 ? _ref22 : "14px",
60161
- userMessageBubbleFontFamily: (_ref24 = (_ref25 = userMessageBubbleFontFamily !== null && userMessageBubbleFontFamily !== void 0 ? userMessageBubbleFontFamily : typography.userMessageBubbleFontFamily) !== null && _ref25 !== void 0 ? _ref25 : fontFamily) !== null && _ref24 !== void 0 ? _ref24 : "Inter",
60162
- userMessageBubbleFontSize: (_ref26 = (_ref27 = userMessageBubbleFontSize !== null && userMessageBubbleFontSize !== void 0 ? userMessageBubbleFontSize : typography.userMessageBubbleFontSize) !== null && _ref27 !== void 0 ? _ref27 : fontSize) !== null && _ref26 !== void 0 ? _ref26 : "14px",
60537
+ agentMessageBubbleFontFamily: (_ref19 = (_ref20 = agentMessageBubbleFontFamily !== null && agentMessageBubbleFontFamily !== void 0 ? agentMessageBubbleFontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref20 !== void 0 ? _ref20 : fontFamily) !== null && _ref19 !== void 0 ? _ref19 : "Inter",
60538
+ agentMessageBubbleFontSize: (_ref21 = (_ref22 = agentMessageBubbleFontSize !== null && agentMessageBubbleFontSize !== void 0 ? agentMessageBubbleFontSize : typography.agentMessageBubbleFontSize) !== null && _ref22 !== void 0 ? _ref22 : fontSize) !== null && _ref21 !== void 0 ? _ref21 : "14px",
60539
+ userMessageBubbleFontFamily: (_ref23 = (_ref24 = userMessageBubbleFontFamily !== null && userMessageBubbleFontFamily !== void 0 ? userMessageBubbleFontFamily : typography.userMessageBubbleFontFamily) !== null && _ref24 !== void 0 ? _ref24 : fontFamily) !== null && _ref23 !== void 0 ? _ref23 : "Inter",
60540
+ userMessageBubbleFontSize: (_ref25 = (_ref26 = userMessageBubbleFontSize !== null && userMessageBubbleFontSize !== void 0 ? userMessageBubbleFontSize : typography.userMessageBubbleFontSize) !== null && _ref26 !== void 0 ? _ref26 : fontSize) !== null && _ref25 !== void 0 ? _ref25 : "14px",
60163
60541
  csatEnabled: (_csat$enabled = csat.enabled) !== null && _csat$enabled !== void 0 ? _csat$enabled : false,
60164
60542
  csatFormat: (_csat$format = csat.format) !== null && _csat$format !== void 0 ? _csat$format : "EMOJI",
60165
60543
  csatPrompt: (_csat$prompt = csat.prompt) !== null && _csat$prompt !== void 0 ? _csat$prompt : "How was your experience?",
@@ -60250,7 +60628,13 @@
60250
60628
  onboardingEnabled: widgetConfig.onboardingEnabled,
60251
60629
  collectionPrompt: widgetConfig.collectionPrompt,
60252
60630
  inactivityTimeoutValue: widgetConfig.inactivityTimeoutValue,
60253
- inactivityTimeoutUnit: widgetConfig.inactivityTimeoutUnit
60631
+ inactivityTimeoutUnit: widgetConfig.inactivityTimeoutUnit,
60632
+ // Sliding-window cap for the localStorage transcript. Comes from
60633
+ // GET /deploy-agent/:orgId → agentConfig.messageLimits (single
60634
+ // source of truth across surfaces). When the field is missing
60635
+ // (older backend / fetch in flight) the hook falls back to its
60636
+ // module-level defaults so chat never blocks on this.
60637
+ messageLimits: agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.messageLimits
60254
60638
  } : defaultConfig);
60255
60639
 
60256
60640
  // Feedback handler — POST to /api/v1/support/feedback