@eshal-bot/chat-widget 0.2.15 → 0.2.17

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.
@@ -1 +1 @@
1
- export{S as default}from"./chat-widget-index-CnsqQX_h.esm.js";import"react";import"react-dom/client";import"react/jsx-runtime";import"react-dom";
1
+ export{e as default}from"./chat-widget-index-CNSzbZUh.esm.js";import"react";import"react-dom/client";import"react/jsx-runtime";import"react-dom";
@@ -12197,6 +12197,28 @@
12197
12197
  const speechGateRef = reactExports.useRef(null);
12198
12198
  // The single pending voice reconnect, so stop/unmount can cancel it.
12199
12199
  const reconnectTimerRef = reactExports.useRef(null);
12200
+ // EV-4552 (19): the org's fallback message from an out-of-credits refusal
12201
+ // frame, shown to the customer when the socket then closes 1008.
12202
+ const voiceFallbackRef = reactExports.useRef(null);
12203
+ // EV-4552 (19): show the org's fallback message from an out-of-credits
12204
+ // refusal in the chat, once. A mid-session refusal already spoke and
12205
+ // streamed it, so skip it when it is already the last assistant message.
12206
+ const showVoiceFallback = () => {
12207
+ const fallbackText = voiceFallbackRef.current;
12208
+ voiceFallbackRef.current = null;
12209
+ if (!fallbackText) return;
12210
+ setMessages(prev => {
12211
+ const last = [...prev].reverse().find(m => m.role === "assistant");
12212
+ if (last && typeof last.content === "string" && last.content.trim() === fallbackText) {
12213
+ return prev;
12214
+ }
12215
+ return [...prev, createMessage({
12216
+ id: getNextMessageId(),
12217
+ role: "assistant",
12218
+ content: fallbackText
12219
+ })];
12220
+ });
12221
+ };
12200
12222
  const currentInputMessageIdRef = reactExports.useRef(null);
12201
12223
  const currentAssistantMessageIdRef = reactExports.useRef(null);
12202
12224
  const isAudioReadyRef = reactExports.useRef(false);
@@ -14280,6 +14302,9 @@
14280
14302
  hasContentParts: !!payload.content?.parts,
14281
14303
  payload
14282
14304
  });
14305
+ if (payload && payload.code === "AI_CREDITS_EXHAUSTED" && typeof payload.fallbackMessage === "string" && payload.fallbackMessage.trim()) {
14306
+ voiceFallbackRef.current = payload.fallbackMessage.trim();
14307
+ }
14283
14308
  handleBidiEvent(payload);
14284
14309
  } catch (error) {
14285
14310
  console.error("[BIDI] Failed to parse WebSocket event", {
@@ -14315,6 +14340,8 @@
14315
14340
  setIsVoiceSessionActive(false);
14316
14341
  setVoiceStatus("error");
14317
14342
  setVoiceError("voiceUnavailable");
14343
+ // EV-4552 (19): show the org's fallback message in the chat, once.
14344
+ showVoiceFallback();
14318
14345
  return;
14319
14346
  }
14320
14347
  if (isVoiceSessionActiveRef.current) {
@@ -16891,6 +16918,10 @@
16891
16918
  });
16892
16919
  }
16893
16920
 
16921
+ // Vertical pills: the text takes the agent bubble's own text colour (same
16922
+ // fallback chain MessageBubble uses), the border keeps the header colour.
16923
+ const chipTextColor = questionTextColor ?? textColor ?? (isDark ? "#f3f4f6" : "#1A1A1A");
16924
+
16894
16925
  // Vertical layout (default) - pill style matching chat-preview
16895
16926
  return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
16896
16927
  style: {
@@ -16915,7 +16946,7 @@
16915
16946
  direction: questionIsRtl ? "rtl" : "ltr",
16916
16947
  fontFamily,
16917
16948
  fontSize,
16918
- color: primaryColor,
16949
+ color: chipTextColor,
16919
16950
  backgroundColor: isDark ? "transparent" : "#ffffff",
16920
16951
  border: `1.5px solid ${primaryColor}`,
16921
16952
  borderRadius: "9999px",
@@ -21625,7 +21656,7 @@
21625
21656
  * value by its own test (that constant sat at 0.1.113 while this package was
21626
21657
  * at 0.2.2 precisely because nothing enforced it - EV-2431).
21627
21658
  */
21628
- const WIDGET_VERSION = '0.2.15';
21659
+ const WIDGET_VERSION = '0.2.17';
21629
21660
 
21630
21661
  /**
21631
21662
  * API utility functions
@@ -21712,6 +21743,24 @@
21712
21743
  }
21713
21744
  };
21714
21745
 
21746
+ /** Called once with the fetched agent config; no policy means off. */
21747
+ function setChatImagePolicy(policy, apiBaseUrl) {
21748
+ if (!policy || policy.mode !== "report" && policy.mode !== "enforce") {
21749
+ return;
21750
+ }
21751
+ let apiOrigin = "";
21752
+ try {
21753
+ apiOrigin = new URL(apiBaseUrl).origin;
21754
+ } catch {
21755
+ apiOrigin = "";
21756
+ }
21757
+ ({
21758
+ mode: policy.mode,
21759
+ hosts: new Set((Array.isArray(policy.hosts) ? policy.hosts : []).map(h => String(h).toLowerCase())),
21760
+ apiOrigin
21761
+ });
21762
+ }
21763
+
21715
21764
  const ChatWidget = ({
21716
21765
  /**
21717
21766
  * @param {Function|undefined} getAuthToken - async fn returning a Keycloak Bearer token (in-app JWT path)
@@ -21801,6 +21850,8 @@
21801
21850
  setConfigError(null);
21802
21851
  return;
21803
21852
  }
21853
+ // Absent unless WIDGET_IMAGE_POLICY is on server-side: then a no-op.
21854
+ setChatImagePolicy(config.chatImagePolicy, apiBaseUrl);
21804
21855
  setAgentConfig(config);
21805
21856
  setConfigError(null);
21806
21857
  } catch (error) {