@eshal-bot/chat-widget 0.1.42 → 0.1.44

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.
@@ -8537,6 +8537,144 @@
8537
8537
  return pcm16Array.buffer;
8538
8538
  }
8539
8539
 
8540
+ /**
8541
+ * Client-side Voice-Activity gate for the widget's voice (Bidi) session.
8542
+ *
8543
+ * Ported from packages/agent-sdk/src/voice/audio/vad.ts (`createSpeechGate`),
8544
+ * which was itself originally derived from this widget. The gate sits between
8545
+ * the mic worklet and the WebSocket: it forwards PCM ONLY inside a confirmed
8546
+ * speech turn and fires `onSpeechEnd` when the turn closes, so the caller can
8547
+ * tell the backend the utterance is done (an `audio_end` signal).
8548
+ *
8549
+ * Why the widget needs this: knowledge-api's non-streaming voice path only
8550
+ * transcribes a turn when it receives `audio_end` (or its flaky energy-VAD
8551
+ * backstop trips). Without a client gate the widget streamed mic audio forever
8552
+ * and the backend just buffered it ("Collecting Audio…") without ever
8553
+ * transcribing — so voice connected but never replied. The agent-sdk already
8554
+ * gates this way; this brings the widget to parity.
8555
+ *
8556
+ * Framework-free and side-effect-free (only calls the two callbacks) so it can
8557
+ * be reasoned about / tested in isolation.
8558
+ */
8559
+
8560
+ const DEFAULT_SPEECH_GATE_CONFIG = {
8561
+ // RMS energy (0..1) at/above which a frame counts as voiced. Speech is
8562
+ // typically 0.05–0.3; quiet room tone sits well under 0.02.
8563
+ energyThreshold: 0.025,
8564
+ // Continuous voiced audio (ms) required to OPEN a turn — rejects coughs/clicks.
8565
+ speechStartMs: 200,
8566
+ // Continuous silence (ms) after speech that CLOSES the turn. Long enough to
8567
+ // ride over natural mid-sentence pauses, short enough to feel responsive.
8568
+ hangoverMs: 1000,
8569
+ // How much audio (ms) preceding turn-open to retain + flush so the leading
8570
+ // phoneme of the first word survives the start-detection delay.
8571
+ preRollMs: 300
8572
+ };
8573
+
8574
+ /** RMS amplitude of an Int16 PCM frame (ArrayBuffer), normalised to 0..1. */
8575
+ function computeRms(pcm) {
8576
+ const samples = new Int16Array(pcm);
8577
+ if (samples.length === 0) return 0;
8578
+ let sumSquares = 0;
8579
+ for (let i = 0; i < samples.length; i += 1) {
8580
+ const v = samples[i] / 0x8000; // normalise Int16 → -1..1
8581
+ sumSquares += v * v;
8582
+ }
8583
+ return Math.sqrt(sumSquares / samples.length);
8584
+ }
8585
+
8586
+ /**
8587
+ * Create a stateful speech gate. Returns `{ process, reset, isSpeaking }`.
8588
+ * `config.sampleRate` is required; everything else falls back to
8589
+ * DEFAULT_SPEECH_GATE_CONFIG.
8590
+ *
8591
+ * @param {{ sampleRate: number, energyThreshold?: number, speechStartMs?: number, hangoverMs?: number, preRollMs?: number }} config
8592
+ * @param {{ onVoiceFrame: (pcm: ArrayBuffer) => void, onSpeechEnd: () => void }} callbacks
8593
+ */
8594
+ function createSpeechGate(config, callbacks) {
8595
+ var _config$energyThresho, _config$speechStartMs, _config$hangoverMs, _config$preRollMs;
8596
+ const cfg = {
8597
+ sampleRate: config.sampleRate,
8598
+ energyThreshold: (_config$energyThresho = config.energyThreshold) !== null && _config$energyThresho !== void 0 ? _config$energyThresho : DEFAULT_SPEECH_GATE_CONFIG.energyThreshold,
8599
+ speechStartMs: (_config$speechStartMs = config.speechStartMs) !== null && _config$speechStartMs !== void 0 ? _config$speechStartMs : DEFAULT_SPEECH_GATE_CONFIG.speechStartMs,
8600
+ hangoverMs: (_config$hangoverMs = config.hangoverMs) !== null && _config$hangoverMs !== void 0 ? _config$hangoverMs : DEFAULT_SPEECH_GATE_CONFIG.hangoverMs,
8601
+ preRollMs: (_config$preRollMs = config.preRollMs) !== null && _config$preRollMs !== void 0 ? _config$preRollMs : DEFAULT_SPEECH_GATE_CONFIG.preRollMs
8602
+ };
8603
+ let speaking = false;
8604
+ // While idle: how much consecutive voiced audio we've seen (to open).
8605
+ let voicedRunMs = 0;
8606
+ // While speaking: how much consecutive silence we've seen (to close).
8607
+ let silenceRunMs = 0;
8608
+ // Ring of recent frames kept only while idle, for pre-roll on open.
8609
+ let preRoll = [];
8610
+ let preRollMsBuffered = 0;
8611
+
8612
+ // Int16 → 2 bytes/sample; ms = samples / sampleRate * 1000.
8613
+ const frameDurationMs = pcm => pcm.byteLength / 2 / cfg.sampleRate * 1000;
8614
+ const reset = () => {
8615
+ speaking = false;
8616
+ voicedRunMs = 0;
8617
+ silenceRunMs = 0;
8618
+ preRoll = [];
8619
+ preRollMsBuffered = 0;
8620
+ };
8621
+ const process = pcm => {
8622
+ const durationMs = frameDurationMs(pcm);
8623
+ const voiced = computeRms(pcm) >= cfg.energyThreshold;
8624
+ if (!speaking) {
8625
+ // Keep a bounded pre-roll of the most recent audio so we don't clip the
8626
+ // onset once we decide this is speech.
8627
+ preRoll.push({
8628
+ pcm,
8629
+ durationMs
8630
+ });
8631
+ preRollMsBuffered += durationMs;
8632
+ while (preRollMsBuffered > cfg.preRollMs && preRoll.length > 1) {
8633
+ var _dropped$durationMs;
8634
+ const dropped = preRoll.shift();
8635
+ preRollMsBuffered -= (_dropped$durationMs = dropped === null || dropped === void 0 ? void 0 : dropped.durationMs) !== null && _dropped$durationMs !== void 0 ? _dropped$durationMs : 0;
8636
+ }
8637
+ if (voiced) {
8638
+ voicedRunMs += durationMs;
8639
+ if (voicedRunMs >= cfg.speechStartMs) {
8640
+ // Confirmed speech — open the turn and flush the pre-roll (which
8641
+ // contains the voiced onset) before going live.
8642
+ speaking = true;
8643
+ silenceRunMs = 0;
8644
+ for (const frame of preRoll) callbacks.onVoiceFrame(frame.pcm);
8645
+ preRoll = [];
8646
+ preRollMsBuffered = 0;
8647
+ }
8648
+ } else {
8649
+ // Need *sustained* energy — reset on any silent frame so a brief
8650
+ // transient can't accumulate its way over the threshold.
8651
+ voicedRunMs = 0;
8652
+ }
8653
+ return;
8654
+ }
8655
+
8656
+ // Inside a confirmed turn: forward everything, including short internal
8657
+ // pauses, until the hangover elapses.
8658
+ callbacks.onVoiceFrame(pcm);
8659
+ if (voiced) {
8660
+ silenceRunMs = 0;
8661
+ } else {
8662
+ silenceRunMs += durationMs;
8663
+ if (silenceRunMs >= cfg.hangoverMs) {
8664
+ speaking = false;
8665
+ voicedRunMs = 0;
8666
+ silenceRunMs = 0;
8667
+ callbacks.onSpeechEnd();
8668
+ }
8669
+ }
8670
+ };
8671
+ return {
8672
+ process,
8673
+ reset,
8674
+ isSpeaking: () => speaking
8675
+ };
8676
+ }
8677
+
8540
8678
  /**
8541
8679
  * Clean CJK (Chinese, Japanese, Korean) spaces
8542
8680
  */
@@ -8935,6 +9073,11 @@
8935
9073
  openDelay,
8936
9074
  darkMode,
8937
9075
  enableVoiceInteraction = false,
9076
+ // behavior.provideCitations — forwarded as enable_citation on the chat
9077
+ // request so the backend skips citation generation when disabled. The
9078
+ // Sources render is also gated in MessageBubble (defence-in-depth, same
9079
+ // as the dashboard testing lab). Defaults to true.
9080
+ provideCitations = true,
8938
9081
  onboardingQuestions = [],
8939
9082
  onboardingEnabled = false,
8940
9083
  collectionPrompt,
@@ -9054,6 +9197,11 @@
9054
9197
  const audioRecorderNodeRef = reactExports.useRef(null);
9055
9198
  const audioRecorderContextRef = reactExports.useRef(null);
9056
9199
  const micStreamRef = reactExports.useRef(null);
9200
+ // Client-side VAD gate. Forwards only voiced PCM upstream and fires
9201
+ // `audio_end` on end-of-speech so the backend's non-streaming voice path
9202
+ // transcribes the turn (mirrors the agent-sdk). Without it the backend
9203
+ // buffers audio forever and never responds.
9204
+ const speechGateRef = reactExports.useRef(null);
9057
9205
  const currentInputMessageIdRef = reactExports.useRef(null);
9058
9206
  const currentAssistantMessageIdRef = reactExports.useRef(null);
9059
9207
  const isAudioReadyRef = reactExports.useRef(false);
@@ -9458,6 +9606,11 @@
9458
9606
  message: trimmed,
9459
9607
  type: apiType,
9460
9608
  conversationId: bidiSessionId,
9609
+ // Citation toggle (behavior.provideCitations). Mirrors the testing
9610
+ // lab's useChat.ts so knowledge-api skips citation generation when
9611
+ // the admin disabled them. The Sources panel is also gated client-
9612
+ // side in MessageBubble (defence-in-depth). Defaults to on.
9613
+ enable_citation: provideCitations !== null && provideCitations !== void 0 ? provideCitations : true,
9461
9614
  // Identify this as a widget call so the knowledge-api side
9462
9615
  // (chat_utils.EXTERNAL_CHAT_CHANNELS in services/
9463
9616
  // knowledge-api) can enforce the visibility ACL — private
@@ -9562,7 +9715,7 @@
9562
9715
  setIsLoading(false);
9563
9716
  }
9564
9717
  }
9565
- }, [apiBaseUrl, apiKey, getNextMessageId, organizationId, messages, bidiSessionId, routingChoice, widgetFilter]);
9718
+ }, [apiBaseUrl, apiKey, getNextMessageId, organizationId, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
9566
9719
  const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
9567
9720
  const questions = getOnboardingQuestions(onboardingQuestions);
9568
9721
  if (questions.length === 0) return;
@@ -10183,7 +10336,7 @@
10183
10336
  }));
10184
10337
  }, []);
10185
10338
  const stopVoiceSession = reactExports.useCallback(() => {
10186
- var _audioPlayerNodeRef$c, _audioPlayerContextRe, _audioRecorderNodeRef, _audioRecorderContext;
10339
+ var _audioPlayerNodeRef$c, _audioPlayerContextRe, _audioRecorderNodeRef, _audioRecorderContext, _speechGateRef$curren;
10187
10340
  setIsVoiceSessionActive(false);
10188
10341
  setIsLoading(false);
10189
10342
  isVoiceSessionActiveRef.current = false;
@@ -10207,6 +10360,8 @@
10207
10360
  audioRecorderContextRef.current = null;
10208
10361
  stopMicrophone(micStreamRef.current);
10209
10362
  micStreamRef.current = null;
10363
+ (_speechGateRef$curren = speechGateRef.current) === null || _speechGateRef$curren === void 0 || _speechGateRef$curren.reset();
10364
+ speechGateRef.current = null;
10210
10365
  isAudioReadyRef.current = false;
10211
10366
  }, []);
10212
10367
  const handleBidiEvent = reactExports.useCallback(event => {
@@ -10386,11 +10541,22 @@
10386
10541
  }
10387
10542
  }, [appendAssistantContent, appendUserTranscription, handleContentParts, handleInterrupted, handleTurnComplete, stopVoiceSession]);
10388
10543
  const handleRecorderAudio = reactExports.useCallback(pcmData => {
10389
- const ws = websocketRef.current;
10390
- if (!isVoiceSessionActiveRef.current || !ws || ws.readyState !== WebSocket.OPEN) {
10544
+ if (!isVoiceSessionActiveRef.current) {
10545
+ return;
10546
+ }
10547
+ // Route mic frames through the VAD gate: it forwards only voiced frames
10548
+ // (onVoiceFrame → ws.send) and fires onSpeechEnd → audio_end so the
10549
+ // backend finalizes the turn. Fall back to raw streaming only if the
10550
+ // gate somehow wasn't built.
10551
+ const gate = speechGateRef.current;
10552
+ if (gate) {
10553
+ gate.process(pcmData);
10391
10554
  return;
10392
10555
  }
10393
- ws.send(pcmData);
10556
+ const ws = websocketRef.current;
10557
+ if (ws && ws.readyState === WebSocket.OPEN) {
10558
+ ws.send(pcmData);
10559
+ }
10394
10560
  }, []);
10395
10561
  const initializeAudioPipeline = reactExports.useCallback(async () => {
10396
10562
  if (isAudioReadyRef.current || typeof window === "undefined") {
@@ -10401,6 +10567,30 @@
10401
10567
  audioPlayerNodeRef.current = playerNode;
10402
10568
  audioPlayerContextRef.current = playerContext;
10403
10569
  await playerContext.resume();
10570
+
10571
+ // Build the VAD gate before the recorder starts producing frames.
10572
+ // onVoiceFrame streams voiced PCM upstream; onSpeechEnd tells the backend
10573
+ // the utterance is complete so it transcribes + responds. The recorder
10574
+ // runs at 16 kHz (see audio-recorder.js).
10575
+ const sendVoicePcm = pcm => {
10576
+ const ws = websocketRef.current;
10577
+ if (ws && ws.readyState === WebSocket.OPEN) {
10578
+ ws.send(pcm);
10579
+ }
10580
+ };
10581
+ speechGateRef.current = createSpeechGate({
10582
+ sampleRate: 16000
10583
+ }, {
10584
+ onVoiceFrame: sendVoicePcm,
10585
+ onSpeechEnd: () => {
10586
+ const ws = websocketRef.current;
10587
+ if (ws && ws.readyState === WebSocket.OPEN) {
10588
+ ws.send(JSON.stringify({
10589
+ type: "audio_end"
10590
+ }));
10591
+ }
10592
+ }
10593
+ });
10404
10594
  const [recorderNode, recorderContext, micStream] = await startAudioRecorderWorklet(handleRecorderAudio);
10405
10595
  audioRecorderNodeRef.current = recorderNode;
10406
10596
  audioRecorderContextRef.current = recorderContext;
@@ -54632,6 +54822,7 @@
54632
54822
  organizationId,
54633
54823
  onPromptSuggestionClick,
54634
54824
  allowFeedback = true,
54825
+ provideCitations = true,
54635
54826
  onFeedback,
54636
54827
  headerTextColor
54637
54828
  } = _ref17;
@@ -54740,7 +54931,7 @@
54740
54931
  }), message.decisionResponse && /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
54741
54932
  className: "mt-2 text-xs ".concat(isDark ? "text-gray-400" : "text-gray-500"),
54742
54933
  children: ["Selected: ", message.decisionResponse.selectedOption]
54743
- }), ((_message$sources = message.sources) === null || _message$sources === void 0 ? void 0 : _message$sources.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54934
+ }), provideCitations !== false && ((_message$sources = message.sources) === null || _message$sources === void 0 ? void 0 : _message$sources.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54744
54935
  children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
54745
54936
  className: "mt-3",
54746
54937
  style: {
@@ -54765,7 +54956,7 @@
54765
54956
  textColor: messageTextColor,
54766
54957
  fontFamily: messageFontFamily,
54767
54958
  fontSize: messageFontSize
54768
- }), ((_message$sources2 = message.sources) === null || _message$sources2 === void 0 ? void 0 : _message$sources2.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54959
+ }), provideCitations !== false && ((_message$sources2 = message.sources) === null || _message$sources2 === void 0 ? void 0 : _message$sources2.length) > 0 && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
54769
54960
  children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
54770
54961
  className: "mt-3",
54771
54962
  style: {
@@ -54908,6 +55099,7 @@
54908
55099
  organizationId,
54909
55100
  onPromptSuggestionClick,
54910
55101
  allowFeedback,
55102
+ provideCitations,
54911
55103
  onFeedback,
54912
55104
  headerTextColor
54913
55105
  } = _ref19;
@@ -54951,6 +55143,7 @@
54951
55143
  organizationId: organizationId,
54952
55144
  onPromptSuggestionClick: onPromptSuggestionClick,
54953
55145
  allowFeedback: allowFeedback,
55146
+ provideCitations: provideCitations,
54954
55147
  onFeedback: onFeedback,
54955
55148
  headerTextColor: headerTextColor
54956
55149
  });
@@ -55806,6 +55999,7 @@
55806
55999
  onPromptSuggestionClick,
55807
56000
  conversationResetKey,
55808
56001
  allowFeedback,
56002
+ provideCitations,
55809
56003
  onFeedback,
55810
56004
  headerTextColor,
55811
56005
  // Inline filter slots — rendered in the initial-state region
@@ -55961,6 +56155,7 @@
55961
56155
  apiBaseUrl: apiBaseUrl,
55962
56156
  organizationId: organizationId,
55963
56157
  allowFeedback: allowFeedback,
56158
+ provideCitations: provideCitations,
55964
56159
  onFeedback: onFeedback,
55965
56160
  headerTextColor: headerTextColor
55966
56161
  }, message.id)), (() => {
@@ -59582,6 +59777,7 @@
59582
59777
  onResetConversation,
59583
59778
  autoPromptEnabled = true,
59584
59779
  allowFeedback = true,
59780
+ provideCitations = true,
59585
59781
  onFeedback,
59586
59782
  onRefreshChat,
59587
59783
  onCopyChat,
@@ -60120,6 +60316,7 @@
60120
60316
  organizationId: organizationId,
60121
60317
  conversationResetKey: conversationId,
60122
60318
  allowFeedback: allowFeedback,
60319
+ provideCitations: provideCitations,
60123
60320
  onFeedback: onFeedback,
60124
60321
  headerTextColor: headerTextColor,
60125
60322
  beforePromptsSlot: showInlineFilter ? /*#__PURE__*/jsxRuntimeExports.jsx(CategoryFilterInline, {
@@ -60457,7 +60654,7 @@
60457
60654
 
60458
60655
  // Map API response to widget props (use props as overrides if provided)
60459
60656
  const widgetConfig = reactExports.useMemo(() => {
60460
- 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;
60657
+ var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita;
60461
60658
  if (!agentConfig) {
60462
60659
  return null;
60463
60660
  }
@@ -60548,7 +60745,12 @@
60548
60745
  csatFollowUpEnabled: (_csat$followUpEnabled = csat.followUpEnabled) !== null && _csat$followUpEnabled !== void 0 ? _csat$followUpEnabled : false,
60549
60746
  csatFollowUpPrompt: (_csat$followUpPrompt = csat.followUpPrompt) !== null && _csat$followUpPrompt !== void 0 ? _csat$followUpPrompt : null,
60550
60747
  autoPromptEnabled: (_behavior$autoPromptE = behavior.autoPromptEnabled) !== null && _behavior$autoPromptE !== void 0 ? _behavior$autoPromptE : true,
60551
- allowFeedback: (_behavior$allowFeedba = behavior.allowFeedback) !== null && _behavior$allowFeedba !== void 0 ? _behavior$allowFeedba : true
60748
+ allowFeedback: (_behavior$allowFeedba = behavior.allowFeedback) !== null && _behavior$allowFeedba !== void 0 ? _behavior$allowFeedba : true,
60749
+ // behavior.provideCitations — when false, hide the Sources panel on
60750
+ // every message and tell the backend to skip citation generation.
60751
+ // Defaults to true so agents that haven't set the field keep showing
60752
+ // citations (matches the testing-lab / chatbot-preview default).
60753
+ provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
60552
60754
  };
60553
60755
  }, [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
60554
60756
  // CSAT dependencies
@@ -60632,6 +60834,9 @@
60632
60834
  onboardingQuestions: widgetConfig.onboardingQuestions,
60633
60835
  onboardingEnabled: widgetConfig.onboardingEnabled,
60634
60836
  collectionPrompt: widgetConfig.collectionPrompt,
60837
+ // Gates the chat request's enable_citation flag (text path). Voice
60838
+ // already reads provideCitations from the DB in voice_chat.py.
60839
+ provideCitations: widgetConfig.provideCitations,
60635
60840
  inactivityTimeoutValue: widgetConfig.inactivityTimeoutValue,
60636
60841
  inactivityTimeoutUnit: widgetConfig.inactivityTimeoutUnit,
60637
60842
  // Sliding-window cap for the localStorage transcript. Comes from
@@ -60798,6 +61003,7 @@
60798
61003
  onResetConversation: resetConversation,
60799
61004
  autoPromptEnabled: widgetConfig.autoPromptEnabled,
60800
61005
  allowFeedback: widgetConfig.allowFeedback,
61006
+ provideCitations: widgetConfig.provideCitations,
60801
61007
  onFeedback: handleMessageFeedback,
60802
61008
  onRefreshChat: handleRefreshChat,
60803
61009
  onCopyChat: handleCopyChat