@eshal-bot/chat-widget 0.1.65 → 0.1.66
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.
- package/dist/chat-widget.esm.js +2 -2
- package/dist/chat-widget.js +180 -152
- package/dist/chat-widget.min.js +2 -2
- package/dist/chat-widget.umd.js +2 -2
- package/package.json +1 -1
package/dist/chat-widget.js
CHANGED
|
@@ -8010,20 +8010,21 @@
|
|
|
8010
8010
|
* Sends a streaming chat request using fetch
|
|
8011
8011
|
* @param {object} request - Chat request object
|
|
8012
8012
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
8013
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
|
|
8013
8014
|
*/
|
|
8014
|
-
const sendStreamingRequest = async (request, apiBaseUrl,
|
|
8015
|
+
const sendStreamingRequest = async (request, apiBaseUrl, getAuthToken) => {
|
|
8015
8016
|
const headers = {
|
|
8016
8017
|
'Content-Type': 'application/json',
|
|
8017
8018
|
Accept: 'text/event-stream'
|
|
8018
8019
|
};
|
|
8019
8020
|
|
|
8020
|
-
//
|
|
8021
|
-
// Mode B
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8021
|
+
// In-app JWT path: call getAuthToken() and set Authorization header
|
|
8022
|
+
// Mode B proxy: no auth header from widget; proxy injects x-eshal-sig + x-eshal-org server-side
|
|
8023
|
+
if (typeof getAuthToken === 'function') {
|
|
8024
|
+
const token = await getAuthToken();
|
|
8025
|
+
if (token) {
|
|
8026
|
+
headers['Authorization'] = "Bearer ".concat(token);
|
|
8027
|
+
}
|
|
8027
8028
|
}
|
|
8028
8029
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
|
|
8029
8030
|
method: 'POST',
|
|
@@ -8347,20 +8348,33 @@
|
|
|
8347
8348
|
// Handle text_chunk events
|
|
8348
8349
|
if (parsed.type === 'text_chunk') {
|
|
8349
8350
|
var _parsed$choices;
|
|
8351
|
+
// ALWAYS create the skeleton on the first text_chunk, even when it
|
|
8352
|
+
// carries no content (a role-only "skeleton" chunk — delta.role
|
|
8353
|
+
// without delta.content — is the backend's "an assistant turn is
|
|
8354
|
+
// starting" signal used by the input/source-clarification short-
|
|
8355
|
+
// circuits in chat_utils.py). Without this, a clarification turn
|
|
8356
|
+
// that emits [text_chunk role-only] -> [complete content+
|
|
8357
|
+
// finalContent] never created the assistant bubble (this branch
|
|
8358
|
+
// skipped on empty content; the COMPLETE branch's updateFinalMessage
|
|
8359
|
+
// requires lastMessage.role === 'assistant' to render — see
|
|
8360
|
+
// updateFinalMessage line ~189) → the follow-up question was
|
|
8361
|
+
// silently dropped, even though the wire carried everything
|
|
8362
|
+
// correctly. Mirrors handleStreamEvent (line ~257) and packages/
|
|
8363
|
+
// frontend ChatService.ts (line ~213) which both create the
|
|
8364
|
+
// skeleton unconditionally on the first text_chunk.
|
|
8365
|
+
if (!hasReceivedFirstChunk.current) {
|
|
8366
|
+
hasReceivedFirstChunk.current = true;
|
|
8367
|
+
setMessages(prev => {
|
|
8368
|
+
const lastMessage = prev[prev.length - 1];
|
|
8369
|
+
if (!lastMessage || lastMessage.role !== 'assistant' || !lastMessage.isProcessing) {
|
|
8370
|
+
const skeletonMessage = createSkeletonMessage();
|
|
8371
|
+
return [...(prev || []), skeletonMessage];
|
|
8372
|
+
}
|
|
8373
|
+
return prev;
|
|
8374
|
+
});
|
|
8375
|
+
}
|
|
8350
8376
|
const content = ((_parsed$choices = parsed.choices) === null || _parsed$choices === void 0 || (_parsed$choices = _parsed$choices[0]) === null || _parsed$choices === void 0 || (_parsed$choices = _parsed$choices.delta) === null || _parsed$choices === void 0 ? void 0 : _parsed$choices.content) || parsed.content;
|
|
8351
8377
|
if (content) {
|
|
8352
|
-
// Create skeleton on first chunk if not already created
|
|
8353
|
-
if (!hasReceivedFirstChunk.current) {
|
|
8354
|
-
hasReceivedFirstChunk.current = true;
|
|
8355
|
-
setMessages(prev => {
|
|
8356
|
-
const lastMessage = prev[prev.length - 1];
|
|
8357
|
-
if (!lastMessage || lastMessage.role !== 'assistant' || !lastMessage.isProcessing) {
|
|
8358
|
-
const skeletonMessage = createSkeletonMessage();
|
|
8359
|
-
return [...(prev || []), skeletonMessage];
|
|
8360
|
-
}
|
|
8361
|
-
return prev;
|
|
8362
|
-
});
|
|
8363
|
-
}
|
|
8364
8378
|
accumulatedText.current += content;
|
|
8365
8379
|
updateMessage(accumulatedText.current);
|
|
8366
8380
|
}
|
|
@@ -9133,7 +9147,7 @@
|
|
|
9133
9147
|
apiBaseUrl,
|
|
9134
9148
|
apiKey,
|
|
9135
9149
|
organizationId,
|
|
9136
|
-
|
|
9150
|
+
getAuthToken,
|
|
9137
9151
|
autoOpen,
|
|
9138
9152
|
openDelay,
|
|
9139
9153
|
darkMode,
|
|
@@ -9770,7 +9784,7 @@
|
|
|
9770
9784
|
if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
|
|
9771
9785
|
// For onboarding fields, just consume the stream without processing.
|
|
9772
9786
|
// Single attempt — no retry, no user-visible partial state to clean up.
|
|
9773
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9787
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9774
9788
|
const reader = stream.getReader();
|
|
9775
9789
|
const decoder = new TextDecoder();
|
|
9776
9790
|
while (true) {
|
|
@@ -9789,7 +9803,7 @@
|
|
|
9789
9803
|
let lastErr;
|
|
9790
9804
|
for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
9791
9805
|
try {
|
|
9792
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9806
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9793
9807
|
await processStream(stream, setMessages);
|
|
9794
9808
|
updateActivity(organizationId);
|
|
9795
9809
|
return {
|
|
@@ -9817,7 +9831,7 @@
|
|
|
9817
9831
|
|
|
9818
9832
|
// Non-chat, non-onboarding apiType (shouldn't currently happen):
|
|
9819
9833
|
// fire and forget, single attempt.
|
|
9820
|
-
await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9834
|
+
await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9821
9835
|
updateActivity(organizationId);
|
|
9822
9836
|
return {
|
|
9823
9837
|
success: true
|
|
@@ -9853,7 +9867,7 @@
|
|
|
9853
9867
|
// UX-M09: release the concurrency guard so the next send can proceed.
|
|
9854
9868
|
isSendingRef.current = false;
|
|
9855
9869
|
}
|
|
9856
|
-
}, [apiBaseUrl, apiKey, getNextMessageId,
|
|
9870
|
+
}, [apiBaseUrl, apiKey, getNextMessageId, getAuthToken, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
|
|
9857
9871
|
const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
|
|
9858
9872
|
const questions = getOnboardingQuestions(onboardingQuestions);
|
|
9859
9873
|
if (questions.length === 0) return;
|
|
@@ -94371,7 +94385,7 @@
|
|
|
94371
94385
|
let {
|
|
94372
94386
|
sources = [],
|
|
94373
94387
|
apiBaseUrl,
|
|
94374
|
-
|
|
94388
|
+
getAuthToken,
|
|
94375
94389
|
isDark,
|
|
94376
94390
|
textColor,
|
|
94377
94391
|
fontFamily,
|
|
@@ -94397,7 +94411,7 @@
|
|
|
94397
94411
|
// corpora that have none of the above.
|
|
94398
94412
|
const _downloadKey = s => s.url || s.source_id || s.source_name || "";
|
|
94399
94413
|
const handleFileDownload = async source => {
|
|
94400
|
-
if (!apiBaseUrl
|
|
94414
|
+
if (!apiBaseUrl) return;
|
|
94401
94415
|
// SharePoint proxy URLs are GET endpoints that stream the file
|
|
94402
94416
|
// directly via Microsoft Graph using the stored OAuth credential.
|
|
94403
94417
|
// Bypass the legacy POST /file-download lookup (which expects an
|
|
@@ -94423,11 +94437,14 @@
|
|
|
94423
94437
|
// set, or future deployments that set it) pass through
|
|
94424
94438
|
// unchanged.
|
|
94425
94439
|
const spUrl = source.url.startsWith("http") ? source.url : "".concat(apiBaseUrl.replace(/\/$/, "")).concat(source.url);
|
|
94440
|
+
const spHeaders = {};
|
|
94441
|
+
if (typeof getAuthToken === 'function') {
|
|
94442
|
+
const token = await getAuthToken();
|
|
94443
|
+
if (token) spHeaders['Authorization'] = "Bearer ".concat(token);
|
|
94444
|
+
}
|
|
94426
94445
|
res = await fetch(spUrl, {
|
|
94427
94446
|
method: "GET",
|
|
94428
|
-
headers:
|
|
94429
|
-
"x-eshal-org": organizationId
|
|
94430
|
-
},
|
|
94447
|
+
headers: spHeaders,
|
|
94431
94448
|
credentials: "include"
|
|
94432
94449
|
});
|
|
94433
94450
|
} else {
|
|
@@ -94443,15 +94460,18 @@
|
|
|
94443
94460
|
// - source.source_name (the user's original filename)
|
|
94444
94461
|
// Sending both lets the server skip the Milvus resolution
|
|
94445
94462
|
// AND advertise the readable name in Content-Disposition.
|
|
94463
|
+
const dlHeaders = {
|
|
94464
|
+
"Content-Type": "application/json"
|
|
94465
|
+
};
|
|
94466
|
+
if (typeof getAuthToken === 'function') {
|
|
94467
|
+
const token = await getAuthToken();
|
|
94468
|
+
if (token) dlHeaders['Authorization'] = "Bearer ".concat(token);
|
|
94469
|
+
}
|
|
94446
94470
|
res = await fetch(url, {
|
|
94447
94471
|
method: "POST",
|
|
94448
|
-
headers:
|
|
94449
|
-
"Content-Type": "application/json",
|
|
94450
|
-
"x-eshal-org": organizationId
|
|
94451
|
-
},
|
|
94472
|
+
headers: dlHeaders,
|
|
94452
94473
|
credentials: "include",
|
|
94453
94474
|
body: JSON.stringify({
|
|
94454
|
-
org_id: organizationId,
|
|
94455
94475
|
source_id: source.source_id,
|
|
94456
94476
|
file_name: source.url || "",
|
|
94457
94477
|
source_name: source.source_name || ""
|
|
@@ -94636,7 +94656,7 @@
|
|
|
94636
94656
|
conciergeName,
|
|
94637
94657
|
companyName,
|
|
94638
94658
|
apiBaseUrl,
|
|
94639
|
-
|
|
94659
|
+
getAuthToken,
|
|
94640
94660
|
onPromptSuggestionClick,
|
|
94641
94661
|
allowFeedback = true,
|
|
94642
94662
|
provideCitations = true,
|
|
@@ -94808,7 +94828,7 @@
|
|
|
94808
94828
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94809
94829
|
sources: message.sources,
|
|
94810
94830
|
apiBaseUrl: apiBaseUrl,
|
|
94811
|
-
|
|
94831
|
+
getAuthToken: getAuthToken,
|
|
94812
94832
|
isDark: isDark,
|
|
94813
94833
|
textColor: messageTextColor,
|
|
94814
94834
|
fontFamily: messageFontFamily,
|
|
@@ -94834,7 +94854,7 @@
|
|
|
94834
94854
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94835
94855
|
sources: message.sources,
|
|
94836
94856
|
apiBaseUrl: apiBaseUrl,
|
|
94837
|
-
|
|
94857
|
+
getAuthToken: getAuthToken,
|
|
94838
94858
|
isDark: isDark,
|
|
94839
94859
|
textColor: messageTextColor,
|
|
94840
94860
|
fontFamily: messageFontFamily,
|
|
@@ -95014,7 +95034,7 @@
|
|
|
95014
95034
|
conciergeName,
|
|
95015
95035
|
companyName,
|
|
95016
95036
|
apiBaseUrl,
|
|
95017
|
-
|
|
95037
|
+
getAuthToken,
|
|
95018
95038
|
onPromptSuggestionClick,
|
|
95019
95039
|
allowFeedback,
|
|
95020
95040
|
provideCitations,
|
|
@@ -95058,7 +95078,7 @@
|
|
|
95058
95078
|
conciergeName: conciergeName,
|
|
95059
95079
|
companyName: companyName,
|
|
95060
95080
|
apiBaseUrl: apiBaseUrl,
|
|
95061
|
-
|
|
95081
|
+
getAuthToken: getAuthToken,
|
|
95062
95082
|
onPromptSuggestionClick: onPromptSuggestionClick,
|
|
95063
95083
|
allowFeedback: allowFeedback,
|
|
95064
95084
|
provideCitations: provideCitations,
|
|
@@ -95903,7 +95923,7 @@
|
|
|
95903
95923
|
conciergeName,
|
|
95904
95924
|
companyName,
|
|
95905
95925
|
apiBaseUrl,
|
|
95906
|
-
|
|
95926
|
+
getAuthToken,
|
|
95907
95927
|
onPromptSuggestionClick,
|
|
95908
95928
|
conversationResetKey,
|
|
95909
95929
|
allowFeedback,
|
|
@@ -96085,7 +96105,7 @@
|
|
|
96085
96105
|
conciergeName: conciergeName,
|
|
96086
96106
|
companyName: companyName,
|
|
96087
96107
|
apiBaseUrl: apiBaseUrl,
|
|
96088
|
-
|
|
96108
|
+
getAuthToken: getAuthToken,
|
|
96089
96109
|
allowFeedback: allowFeedback,
|
|
96090
96110
|
provideCitations: provideCitations,
|
|
96091
96111
|
onFeedback: onFeedback,
|
|
@@ -96117,7 +96137,7 @@
|
|
|
96117
96137
|
conciergeName: conciergeName,
|
|
96118
96138
|
companyName: companyName,
|
|
96119
96139
|
apiBaseUrl: apiBaseUrl,
|
|
96120
|
-
|
|
96140
|
+
getAuthToken: getAuthToken,
|
|
96121
96141
|
headerTextColor: headerTextColor
|
|
96122
96142
|
}, "skeleton-waiting");
|
|
96123
96143
|
}
|
|
@@ -99632,8 +99652,7 @@
|
|
|
99632
99652
|
showPoweredBy = true,
|
|
99633
99653
|
apiBaseUrl,
|
|
99634
99654
|
apiKey,
|
|
99635
|
-
|
|
99636
|
-
secretKey,
|
|
99655
|
+
getAuthToken,
|
|
99637
99656
|
conversationId,
|
|
99638
99657
|
conciergeId,
|
|
99639
99658
|
onboardingQuestions = [],
|
|
@@ -99781,7 +99800,7 @@
|
|
|
99781
99800
|
setCsatSubmitted(submitted);
|
|
99782
99801
|
if (submitted) setShowCsat(false);
|
|
99783
99802
|
} catch (_unused4) {}
|
|
99784
|
-
}, [conversationId
|
|
99803
|
+
}, [conversationId]);
|
|
99785
99804
|
|
|
99786
99805
|
// Auto-scroll hook
|
|
99787
99806
|
useAutoScroll(messagesContainerRef, [messages, isLoading], {
|
|
@@ -99919,14 +99938,15 @@
|
|
|
99919
99938
|
const handleCsatRating = async (rating, format) => {
|
|
99920
99939
|
setCsatSubmitted(true); // optimistic UI
|
|
99921
99940
|
setShowCsat(false);
|
|
99922
|
-
markCsatSubmitted(
|
|
99941
|
+
markCsatSubmitted(undefined); // persist so page refresh doesn't re-show CSAT
|
|
99923
99942
|
|
|
99924
99943
|
try {
|
|
99925
99944
|
const headers = {
|
|
99926
99945
|
"Content-Type": "application/json"
|
|
99927
99946
|
};
|
|
99928
|
-
if (
|
|
99929
|
-
|
|
99947
|
+
if (typeof getAuthToken === 'function') {
|
|
99948
|
+
const token = await getAuthToken();
|
|
99949
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99930
99950
|
}
|
|
99931
99951
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
99932
99952
|
method: "POST",
|
|
@@ -99936,7 +99956,6 @@
|
|
|
99936
99956
|
conversationId,
|
|
99937
99957
|
rating,
|
|
99938
99958
|
ratingFormat: format,
|
|
99939
|
-
organizationId,
|
|
99940
99959
|
conciergeId
|
|
99941
99960
|
})
|
|
99942
99961
|
});
|
|
@@ -99960,8 +99979,9 @@
|
|
|
99960
99979
|
const headers = {
|
|
99961
99980
|
"Content-Type": "application/json"
|
|
99962
99981
|
};
|
|
99963
|
-
if (
|
|
99964
|
-
|
|
99982
|
+
if (typeof getAuthToken === 'function') {
|
|
99983
|
+
const token = await getAuthToken();
|
|
99984
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99965
99985
|
}
|
|
99966
99986
|
const ratingFormat = normalizeCsatFormat(csatFormat);
|
|
99967
99987
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
@@ -99972,7 +99992,6 @@
|
|
|
99972
99992
|
conversationId,
|
|
99973
99993
|
rating: String(resolveRating),
|
|
99974
99994
|
ratingFormat,
|
|
99975
|
-
organizationId,
|
|
99976
99995
|
conciergeId,
|
|
99977
99996
|
feedback: resolveFeedbackText || undefined
|
|
99978
99997
|
})
|
|
@@ -100006,7 +100025,7 @@
|
|
|
100006
100025
|
} finally {
|
|
100007
100026
|
setIsSubmittingResolve(false);
|
|
100008
100027
|
}
|
|
100009
|
-
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId,
|
|
100028
|
+
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, getAuthToken, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
|
|
100010
100029
|
|
|
100011
100030
|
// Handle X button: first click shows form, second click closes
|
|
100012
100031
|
const handleCloseClick = reactExports.useCallback(() => {
|
|
@@ -100344,7 +100363,7 @@
|
|
|
100344
100363
|
conciergeName: conciergeName,
|
|
100345
100364
|
companyName: companyName,
|
|
100346
100365
|
apiBaseUrl: apiBaseUrl,
|
|
100347
|
-
|
|
100366
|
+
getAuthToken: getAuthToken,
|
|
100348
100367
|
conversationResetKey: conversationId,
|
|
100349
100368
|
allowFeedback: allowFeedback,
|
|
100350
100369
|
provideCitations: provideCitations,
|
|
@@ -100429,8 +100448,7 @@
|
|
|
100429
100448
|
|
|
100430
100449
|
/**
|
|
100431
100450
|
* @param {string|undefined|null} apiBaseUrl
|
|
100432
|
-
* @param {
|
|
100433
|
-
* @param {string|undefined|null} secretKey — Mode A/B: orgId resolved server-side
|
|
100451
|
+
* @param {Function|undefined|null} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined/null (Mode B proxy)
|
|
100434
100452
|
* @returns {{
|
|
100435
100453
|
* categories: Array<{id:string,label:string,count:number,tags:string[]}>,
|
|
100436
100454
|
* loading: boolean,
|
|
@@ -100438,17 +100456,15 @@
|
|
|
100438
100456
|
* hasFilters: boolean,
|
|
100439
100457
|
* }}
|
|
100440
100458
|
*/
|
|
100441
|
-
function useFilterCatalog(apiBaseUrl,
|
|
100442
|
-
|
|
100443
|
-
// Mode C: orgId present (legacy) → orgId-in-path endpoint
|
|
100444
|
-
const hasAuth = !!(apiBaseUrl && (orgId || secretKey));
|
|
100459
|
+
function useFilterCatalog(apiBaseUrl, getAuthToken) {
|
|
100460
|
+
const hasAuth = !!apiBaseUrl;
|
|
100445
100461
|
const [state, setState] = reactExports.useState({
|
|
100446
100462
|
categories: [],
|
|
100447
100463
|
loading: hasAuth,
|
|
100448
100464
|
error: null
|
|
100449
100465
|
});
|
|
100450
100466
|
reactExports.useEffect(() => {
|
|
100451
|
-
if (!apiBaseUrl
|
|
100467
|
+
if (!apiBaseUrl) {
|
|
100452
100468
|
setState({
|
|
100453
100469
|
categories: [],
|
|
100454
100470
|
loading: false,
|
|
@@ -100456,7 +100472,7 @@
|
|
|
100456
100472
|
});
|
|
100457
100473
|
return;
|
|
100458
100474
|
}
|
|
100459
|
-
const cacheKey =
|
|
100475
|
+
const cacheKey = getAuthToken ? "".concat(apiBaseUrl, "|__session__") : "".concat(apiBaseUrl, "|__proxy__");
|
|
100460
100476
|
const cached = _PROCESS_CACHE.get(cacheKey);
|
|
100461
100477
|
if (cached) {
|
|
100462
100478
|
setState({
|
|
@@ -100472,45 +100488,54 @@
|
|
|
100472
100488
|
error: null
|
|
100473
100489
|
}));
|
|
100474
100490
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100475
|
-
const url =
|
|
100476
|
-
|
|
100477
|
-
|
|
100478
|
-
|
|
100479
|
-
|
|
100480
|
-
|
|
100481
|
-
|
|
100482
|
-
|
|
100483
|
-
|
|
100484
|
-
|
|
100485
|
-
|
|
100491
|
+
const url = "".concat(base, "/api/v1/widget/filters");
|
|
100492
|
+
(async () => {
|
|
100493
|
+
try {
|
|
100494
|
+
const headers = {
|
|
100495
|
+
Accept: "application/json"
|
|
100496
|
+
};
|
|
100497
|
+
// In-app JWT path: set Authorization header
|
|
100498
|
+
// Mode B proxy: no auth header; proxy handles org resolution
|
|
100499
|
+
if (typeof getAuthToken === 'function') {
|
|
100500
|
+
const token = await getAuthToken();
|
|
100501
|
+
if (token) {
|
|
100502
|
+
headers['Authorization'] = "Bearer ".concat(token);
|
|
100503
|
+
}
|
|
100504
|
+
}
|
|
100505
|
+
const res = await fetch(url, {
|
|
100506
|
+
method: "GET",
|
|
100507
|
+
headers
|
|
100508
|
+
});
|
|
100509
|
+
if (!res.ok) {
|
|
100510
|
+
throw new Error("filter catalog HTTP ".concat(res.status));
|
|
100511
|
+
}
|
|
100512
|
+
const body = await res.json();
|
|
100513
|
+
const categories = Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100514
|
+
if (cancelled) return;
|
|
100515
|
+
_PROCESS_CACHE.set(cacheKey, categories);
|
|
100516
|
+
setState({
|
|
100517
|
+
categories,
|
|
100518
|
+
loading: false,
|
|
100519
|
+
error: null
|
|
100520
|
+
});
|
|
100521
|
+
} catch (err) {
|
|
100522
|
+
if (cancelled) return;
|
|
100523
|
+
// Fail-soft: log and present empty so the UI hides itself.
|
|
100524
|
+
// We do NOT bubble the error to the caller because chat
|
|
100525
|
+
// should keep working even if the catalog is unreachable.
|
|
100526
|
+
// eslint-disable-next-line no-console
|
|
100527
|
+
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100528
|
+
setState({
|
|
100529
|
+
categories: [],
|
|
100530
|
+
loading: false,
|
|
100531
|
+
error: err
|
|
100532
|
+
});
|
|
100486
100533
|
}
|
|
100487
|
-
|
|
100488
|
-
return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100489
|
-
}).then(categories => {
|
|
100490
|
-
if (cancelled) return;
|
|
100491
|
-
_PROCESS_CACHE.set(cacheKey, categories);
|
|
100492
|
-
setState({
|
|
100493
|
-
categories,
|
|
100494
|
-
loading: false,
|
|
100495
|
-
error: null
|
|
100496
|
-
});
|
|
100497
|
-
}).catch(err => {
|
|
100498
|
-
if (cancelled) return;
|
|
100499
|
-
// Fail-soft: log and present empty so the UI hides itself.
|
|
100500
|
-
// We do NOT bubble the error to the caller because chat
|
|
100501
|
-
// should keep working even if the catalog is unreachable.
|
|
100502
|
-
// eslint-disable-next-line no-console
|
|
100503
|
-
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100504
|
-
setState({
|
|
100505
|
-
categories: [],
|
|
100506
|
-
loading: false,
|
|
100507
|
-
error: err
|
|
100508
|
-
});
|
|
100509
|
-
});
|
|
100534
|
+
})();
|
|
100510
100535
|
return () => {
|
|
100511
100536
|
cancelled = true;
|
|
100512
100537
|
};
|
|
100513
|
-
}, [apiBaseUrl,
|
|
100538
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100514
100539
|
const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
|
|
100515
100540
|
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
100516
100541
|
hasFilters
|
|
@@ -100589,24 +100614,28 @@
|
|
|
100589
100614
|
/**
|
|
100590
100615
|
* Fetches agent configuration from the deploy-agent endpoint
|
|
100591
100616
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
100592
|
-
* @param {
|
|
100617
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
|
|
100593
100618
|
* @returns {Promise<object>} Agent configuration object
|
|
100594
100619
|
*/
|
|
100595
|
-
const fetchAgentConfig = async (apiBaseUrl,
|
|
100620
|
+
const fetchAgentConfig = async (apiBaseUrl, getAuthToken) => {
|
|
100596
100621
|
if (!apiBaseUrl) {
|
|
100597
100622
|
throw new Error("apiBaseUrl is required");
|
|
100598
100623
|
}
|
|
100599
|
-
// Mode A: secretKey in header, no orgId in URL
|
|
100600
|
-
// Mode B (proxy): no secretKey, no orgId — proxy intercepts and signs the request
|
|
100601
|
-
// Mode C legacy: orgId in URL path
|
|
100602
|
-
|
|
100603
100624
|
try {
|
|
100604
100625
|
const base = apiBaseUrl.replace(/\/$/, '');
|
|
100605
|
-
const url =
|
|
100626
|
+
const url = "".concat(base, "/api/v1/deploy-agent");
|
|
100606
100627
|
const headers = {
|
|
100607
100628
|
"Content-Type": "application/json"
|
|
100608
100629
|
};
|
|
100609
|
-
|
|
100630
|
+
|
|
100631
|
+
// In-app JWT path: set Authorization header
|
|
100632
|
+
// Mode B proxy: no auth header; proxy handles org resolution
|
|
100633
|
+
if (typeof getAuthToken === 'function') {
|
|
100634
|
+
const token = await getAuthToken();
|
|
100635
|
+
if (token) {
|
|
100636
|
+
headers['Authorization'] = "Bearer ".concat(token);
|
|
100637
|
+
}
|
|
100638
|
+
}
|
|
100610
100639
|
const response = await fetch(url, {
|
|
100611
100640
|
method: "GET",
|
|
100612
100641
|
headers,
|
|
@@ -100626,11 +100655,10 @@
|
|
|
100626
100655
|
const ChatWidget = _ref => {
|
|
100627
100656
|
var _agentConfig$concierg;
|
|
100628
100657
|
let {
|
|
100629
|
-
|
|
100630
|
-
|
|
100631
|
-
|
|
100632
|
-
|
|
100633
|
-
// legacy — kept for backward compatibility
|
|
100658
|
+
/**
|
|
100659
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Keycloak Bearer token (in-app JWT path)
|
|
100660
|
+
*/
|
|
100661
|
+
getAuthToken,
|
|
100634
100662
|
apiBaseUrl,
|
|
100635
100663
|
// Optional override props (will override fetched config if provided)
|
|
100636
100664
|
darkMode,
|
|
@@ -100686,13 +100714,9 @@
|
|
|
100686
100714
|
setIsLoadingConfig(false);
|
|
100687
100715
|
return;
|
|
100688
100716
|
}
|
|
100689
|
-
// Mode B proxy: neither secretKey nor orgId in browser — proxy handles auth
|
|
100690
|
-
|
|
100691
100717
|
try {
|
|
100692
100718
|
setIsLoadingConfig(true);
|
|
100693
|
-
|
|
100694
|
-
// header and omit the orgId path segment. Pass orgId for Mode C legacy.
|
|
100695
|
-
const config = await fetchAgentConfig(apiBaseUrl, orgId, secretKey);
|
|
100719
|
+
const config = await fetchAgentConfig(apiBaseUrl, getAuthToken);
|
|
100696
100720
|
setAgentConfig(config);
|
|
100697
100721
|
setConfigError(null);
|
|
100698
100722
|
} catch (error) {
|
|
@@ -100703,11 +100727,11 @@
|
|
|
100703
100727
|
}
|
|
100704
100728
|
};
|
|
100705
100729
|
loadAgentConfig();
|
|
100706
|
-
}, [
|
|
100730
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100707
100731
|
|
|
100708
100732
|
// Map API response to widget props (use props as overrides if provided)
|
|
100709
100733
|
const widgetConfig = reactExports.useMemo(() => {
|
|
100710
|
-
var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _typography$headerTex, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita;
|
|
100734
|
+
var _agentConfig$config, _cfg$identity, _cfg$branding, _cfg$typography, _cfg$messages, _cfg$voice, _cfg$onboarding, _cfg$disclaimer, _cfg$csat, _cfg$inactivity, _cfg$behavior, _cfg$security, _cfg$layout, _ref2, _messages$bundles$loc, _messages$bundles, _messages$bundles$en, _messages$bundles2, _ref3, _ref4, _ref5, _ref6, _branding$chatbotAvat, _branding$chatbotAvat2, _ref7, _ref8, _ref9, _ref0, _ref1, _ref10, _ref11, _ref12, _typography$headerTex, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _inactivity$timeoutVa, _inactivity$timeoutUn, _ref19, _ref20, _ref21, _ref22, _ref23, _ref24, _ref25, _ref26, _csat$enabled, _csat$format, _csat$prompt, _csat$triggerType, _csat$idleTimeoutMins, _csat$followUpEnabled, _csat$followUpPrompt, _behavior$autoPromptE, _behavior$allowFeedba, _behavior$provideCita, _agentConfig$organiza;
|
|
100711
100735
|
if (!agentConfig) {
|
|
100712
100736
|
return null;
|
|
100713
100737
|
}
|
|
@@ -100766,7 +100790,6 @@
|
|
|
100766
100790
|
fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
|
|
100767
100791
|
fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
|
|
100768
100792
|
defaultLanguage: locale,
|
|
100769
|
-
organizationId: orgId,
|
|
100770
100793
|
headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
|
|
100771
100794
|
headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
|
|
100772
100795
|
headerTextColor: (_typography$headerTex = typography.headerTextColor) !== null && _typography$headerTex !== void 0 ? _typography$headerTex : "#FFFFFF",
|
|
@@ -100803,9 +100826,12 @@
|
|
|
100803
100826
|
// every message and tell the backend to skip citation generation.
|
|
100804
100827
|
// Defaults to true so agents that haven't set the field keep showing
|
|
100805
100828
|
// citations (matches the testing-lab / chatbot-preview default).
|
|
100806
|
-
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true
|
|
100829
|
+
provideCitations: (_behavior$provideCita = behavior.provideCitations) !== null && _behavior$provideCita !== void 0 ? _behavior$provideCita : true,
|
|
100830
|
+
// organizationId from the backend response — used internally for
|
|
100831
|
+
// session isolation (localStorage keys) and WS paths. Not a user prop.
|
|
100832
|
+
organizationId: (_agentConfig$organiza = agentConfig.organizationId) !== null && _agentConfig$organiza !== void 0 ? _agentConfig$organiza : null
|
|
100807
100833
|
};
|
|
100808
|
-
}, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage,
|
|
100834
|
+
}, [agentConfig, darkMode, primaryColor, position, companyName, conciergeName, companyLogo, welcomeMessage, quickQuestions, quickQuestionsEnabled, textColor, agentMessageBubbleColor, userMessageBoxColor, assistantTextColor, userTextColor, fontFamily, fontSize, defaultLanguage, headerTextBold, headerTextItalic, enableVoiceInteraction, disclaimerEnabled, disclaimerText, disclaimerPosition, disclaimerLinkLabel, disclaimerLinkUrl, showPoweredBy, onboardingQuestions, onboardingEnabled, collectionPrompt, allowedDomains, agentMessageBubbleFontFamily, agentMessageBubbleFontSize, userMessageBubbleFontFamily, userMessageBubbleFontSize
|
|
100809
100835
|
// CSAT dependencies
|
|
100810
100836
|
// (kept minimal because these come from agentConfig)
|
|
100811
100837
|
]);
|
|
@@ -100824,7 +100850,7 @@
|
|
|
100824
100850
|
quickQuestions: [],
|
|
100825
100851
|
apiBaseUrl: apiBaseUrl || "",
|
|
100826
100852
|
apiKey: apiKey || "",
|
|
100827
|
-
|
|
100853
|
+
getAuthToken,
|
|
100828
100854
|
autoOpen: false,
|
|
100829
100855
|
openDelay: 0,
|
|
100830
100856
|
darkMode: false,
|
|
@@ -100832,7 +100858,7 @@
|
|
|
100832
100858
|
onboardingQuestions: [],
|
|
100833
100859
|
onboardingEnabled: false,
|
|
100834
100860
|
collectionPrompt: undefined
|
|
100835
|
-
}), [apiBaseUrl, apiKey,
|
|
100861
|
+
}), [apiBaseUrl, apiKey, getAuthToken]);
|
|
100836
100862
|
|
|
100837
100863
|
// ALWAYS call hooks before any early returns (Rules of Hooks)
|
|
100838
100864
|
const {
|
|
@@ -100881,7 +100907,7 @@
|
|
|
100881
100907
|
// knowledge-api LoadBalancer.
|
|
100882
100908
|
apiKey,
|
|
100883
100909
|
organizationId: widgetConfig.organizationId,
|
|
100884
|
-
|
|
100910
|
+
getAuthToken,
|
|
100885
100911
|
autoOpen,
|
|
100886
100912
|
openDelay,
|
|
100887
100913
|
darkMode: widgetConfig.darkMode,
|
|
@@ -100908,25 +100934,33 @@
|
|
|
100908
100934
|
// navigation (but not a page refresh — feedback is not written to
|
|
100909
100935
|
// localStorage so it resets on reload as intended).
|
|
100910
100936
|
updateMessageFeedback(messageContent, feedback);
|
|
100911
|
-
if (!
|
|
100937
|
+
if (!conversationId || !apiBaseUrl) return;
|
|
100912
100938
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100913
|
-
|
|
100914
|
-
|
|
100915
|
-
|
|
100916
|
-
|
|
100917
|
-
|
|
100918
|
-
|
|
100919
|
-
|
|
100920
|
-
|
|
100921
|
-
|
|
100922
|
-
|
|
100923
|
-
|
|
100924
|
-
|
|
100925
|
-
|
|
100926
|
-
|
|
100927
|
-
|
|
100928
|
-
|
|
100929
|
-
|
|
100939
|
+
(async () => {
|
|
100940
|
+
try {
|
|
100941
|
+
const feedbackHeaders = {
|
|
100942
|
+
"Content-Type": "application/json"
|
|
100943
|
+
};
|
|
100944
|
+
if (typeof getAuthToken === 'function') {
|
|
100945
|
+
const token = await getAuthToken();
|
|
100946
|
+
if (token) feedbackHeaders['Authorization'] = "Bearer ".concat(token);
|
|
100947
|
+
}
|
|
100948
|
+
await fetch("".concat(base, "/api/v1/support/feedback"), {
|
|
100949
|
+
method: "POST",
|
|
100950
|
+
headers: feedbackHeaders,
|
|
100951
|
+
credentials: "include",
|
|
100952
|
+
body: JSON.stringify({
|
|
100953
|
+
messageContent,
|
|
100954
|
+
conversationId,
|
|
100955
|
+
feedback,
|
|
100956
|
+
feedbackText
|
|
100957
|
+
})
|
|
100958
|
+
});
|
|
100959
|
+
} catch (err) {
|
|
100960
|
+
console.error("Feedback submission error:", err);
|
|
100961
|
+
}
|
|
100962
|
+
})();
|
|
100963
|
+
}, [conversationId, apiBaseUrl, getAuthToken, updateMessageFeedback]);
|
|
100930
100964
|
|
|
100931
100965
|
// Refresh chat handler — reset conversation
|
|
100932
100966
|
const handleRefreshChat = reactExports.useCallback(() => {
|
|
@@ -100942,7 +100976,7 @@
|
|
|
100942
100976
|
const {
|
|
100943
100977
|
categories: filterCategories,
|
|
100944
100978
|
hasFilters: showFilterStep
|
|
100945
|
-
} = useFilterCatalog(apiBaseUrl,
|
|
100979
|
+
} = useFilterCatalog(apiBaseUrl, getAuthToken);
|
|
100946
100980
|
|
|
100947
100981
|
// Copy chat handler — copy all messages to clipboard
|
|
100948
100982
|
const handleCopyChat = reactExports.useCallback(() => {
|
|
@@ -101047,8 +101081,7 @@
|
|
|
101047
101081
|
showPoweredBy: widgetConfig.showPoweredBy,
|
|
101048
101082
|
apiBaseUrl: apiBaseUrl,
|
|
101049
101083
|
apiKey: apiKey,
|
|
101050
|
-
|
|
101051
|
-
secretKey: secretKey,
|
|
101084
|
+
getAuthToken: getAuthToken,
|
|
101052
101085
|
conversationId: conversationId,
|
|
101053
101086
|
conciergeId: (_agentConfig$concierg = agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.conciergeId) !== null && _agentConfig$concierg !== void 0 ? _agentConfig$concierg : agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.id,
|
|
101054
101087
|
onboardingQuestions: widgetConfig.onboardingQuestions,
|
|
@@ -101196,15 +101229,10 @@
|
|
|
101196
101229
|
}
|
|
101197
101230
|
|
|
101198
101231
|
// Render component
|
|
101199
|
-
// Mode
|
|
101200
|
-
//
|
|
101201
|
-
// Both: apiBaseUrl required
|
|
101202
|
-
// Mode B proxy: apiBaseUrl alone is enough — no secretKey or orgId in browser
|
|
101232
|
+
// Mode B proxy: apiBaseUrl alone is enough — no key or orgId in browser
|
|
101233
|
+
// In-app JWT: getAuthToken fn passed via config
|
|
101203
101234
|
const hasNewAuth = config.apiBaseUrl;
|
|
101204
101235
|
const widgetProps = hasNewAuth ? _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
|
|
101205
|
-
// Auth props — secretKey replaces orgId in Mode A/B
|
|
101206
|
-
secretKey: config.secretKey || null,
|
|
101207
|
-
orgId: config.orgId || null,
|
|
101208
101236
|
apiBaseUrl: config.apiBaseUrl
|
|
101209
101237
|
}, options.wsBaseUrl !== undefined && {
|
|
101210
101238
|
wsBaseUrl: config.wsBaseUrl
|