@eshal-bot/chat-widget 0.1.64 → 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 +194 -132
- 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,16 +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
|
-
|
|
8022
|
-
|
|
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
|
+
}
|
|
8023
8028
|
}
|
|
8024
8029
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
|
|
8025
8030
|
method: 'POST',
|
|
@@ -8343,20 +8348,33 @@
|
|
|
8343
8348
|
// Handle text_chunk events
|
|
8344
8349
|
if (parsed.type === 'text_chunk') {
|
|
8345
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
|
+
}
|
|
8346
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;
|
|
8347
8377
|
if (content) {
|
|
8348
|
-
// Create skeleton on first chunk if not already created
|
|
8349
|
-
if (!hasReceivedFirstChunk.current) {
|
|
8350
|
-
hasReceivedFirstChunk.current = true;
|
|
8351
|
-
setMessages(prev => {
|
|
8352
|
-
const lastMessage = prev[prev.length - 1];
|
|
8353
|
-
if (!lastMessage || lastMessage.role !== 'assistant' || !lastMessage.isProcessing) {
|
|
8354
|
-
const skeletonMessage = createSkeletonMessage();
|
|
8355
|
-
return [...(prev || []), skeletonMessage];
|
|
8356
|
-
}
|
|
8357
|
-
return prev;
|
|
8358
|
-
});
|
|
8359
|
-
}
|
|
8360
8378
|
accumulatedText.current += content;
|
|
8361
8379
|
updateMessage(accumulatedText.current);
|
|
8362
8380
|
}
|
|
@@ -9129,6 +9147,7 @@
|
|
|
9129
9147
|
apiBaseUrl,
|
|
9130
9148
|
apiKey,
|
|
9131
9149
|
organizationId,
|
|
9150
|
+
getAuthToken,
|
|
9132
9151
|
autoOpen,
|
|
9133
9152
|
openDelay,
|
|
9134
9153
|
darkMode,
|
|
@@ -9765,7 +9784,7 @@
|
|
|
9765
9784
|
if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
|
|
9766
9785
|
// For onboarding fields, just consume the stream without processing.
|
|
9767
9786
|
// Single attempt — no retry, no user-visible partial state to clean up.
|
|
9768
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9787
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9769
9788
|
const reader = stream.getReader();
|
|
9770
9789
|
const decoder = new TextDecoder();
|
|
9771
9790
|
while (true) {
|
|
@@ -9784,7 +9803,7 @@
|
|
|
9784
9803
|
let lastErr;
|
|
9785
9804
|
for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
|
|
9786
9805
|
try {
|
|
9787
|
-
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9806
|
+
const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9788
9807
|
await processStream(stream, setMessages);
|
|
9789
9808
|
updateActivity(organizationId);
|
|
9790
9809
|
return {
|
|
@@ -9812,7 +9831,7 @@
|
|
|
9812
9831
|
|
|
9813
9832
|
// Non-chat, non-onboarding apiType (shouldn't currently happen):
|
|
9814
9833
|
// fire and forget, single attempt.
|
|
9815
|
-
await sendStreamingRequest(chatRequest, apiBaseUrl,
|
|
9834
|
+
await sendStreamingRequest(chatRequest, apiBaseUrl, getAuthToken);
|
|
9816
9835
|
updateActivity(organizationId);
|
|
9817
9836
|
return {
|
|
9818
9837
|
success: true
|
|
@@ -9848,7 +9867,7 @@
|
|
|
9848
9867
|
// UX-M09: release the concurrency guard so the next send can proceed.
|
|
9849
9868
|
isSendingRef.current = false;
|
|
9850
9869
|
}
|
|
9851
|
-
}, [apiBaseUrl, apiKey, getNextMessageId,
|
|
9870
|
+
}, [apiBaseUrl, apiKey, getNextMessageId, getAuthToken, messages, bidiSessionId, routingChoice, widgetFilter, provideCitations]);
|
|
9852
9871
|
const handleOnboardingFormSubmit = reactExports.useCallback(async answers => {
|
|
9853
9872
|
const questions = getOnboardingQuestions(onboardingQuestions);
|
|
9854
9873
|
if (questions.length === 0) return;
|
|
@@ -94366,7 +94385,7 @@
|
|
|
94366
94385
|
let {
|
|
94367
94386
|
sources = [],
|
|
94368
94387
|
apiBaseUrl,
|
|
94369
|
-
|
|
94388
|
+
getAuthToken,
|
|
94370
94389
|
isDark,
|
|
94371
94390
|
textColor,
|
|
94372
94391
|
fontFamily,
|
|
@@ -94392,7 +94411,7 @@
|
|
|
94392
94411
|
// corpora that have none of the above.
|
|
94393
94412
|
const _downloadKey = s => s.url || s.source_id || s.source_name || "";
|
|
94394
94413
|
const handleFileDownload = async source => {
|
|
94395
|
-
if (!apiBaseUrl
|
|
94414
|
+
if (!apiBaseUrl) return;
|
|
94396
94415
|
// SharePoint proxy URLs are GET endpoints that stream the file
|
|
94397
94416
|
// directly via Microsoft Graph using the stored OAuth credential.
|
|
94398
94417
|
// Bypass the legacy POST /file-download lookup (which expects an
|
|
@@ -94418,11 +94437,14 @@
|
|
|
94418
94437
|
// set, or future deployments that set it) pass through
|
|
94419
94438
|
// unchanged.
|
|
94420
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
|
+
}
|
|
94421
94445
|
res = await fetch(spUrl, {
|
|
94422
94446
|
method: "GET",
|
|
94423
|
-
headers:
|
|
94424
|
-
"x-eshal-org": organizationId
|
|
94425
|
-
},
|
|
94447
|
+
headers: spHeaders,
|
|
94426
94448
|
credentials: "include"
|
|
94427
94449
|
});
|
|
94428
94450
|
} else {
|
|
@@ -94438,15 +94460,18 @@
|
|
|
94438
94460
|
// - source.source_name (the user's original filename)
|
|
94439
94461
|
// Sending both lets the server skip the Milvus resolution
|
|
94440
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
|
+
}
|
|
94441
94470
|
res = await fetch(url, {
|
|
94442
94471
|
method: "POST",
|
|
94443
|
-
headers:
|
|
94444
|
-
"Content-Type": "application/json",
|
|
94445
|
-
"x-eshal-org": organizationId
|
|
94446
|
-
},
|
|
94472
|
+
headers: dlHeaders,
|
|
94447
94473
|
credentials: "include",
|
|
94448
94474
|
body: JSON.stringify({
|
|
94449
|
-
org_id: organizationId,
|
|
94450
94475
|
source_id: source.source_id,
|
|
94451
94476
|
file_name: source.url || "",
|
|
94452
94477
|
source_name: source.source_name || ""
|
|
@@ -94631,7 +94656,7 @@
|
|
|
94631
94656
|
conciergeName,
|
|
94632
94657
|
companyName,
|
|
94633
94658
|
apiBaseUrl,
|
|
94634
|
-
|
|
94659
|
+
getAuthToken,
|
|
94635
94660
|
onPromptSuggestionClick,
|
|
94636
94661
|
allowFeedback = true,
|
|
94637
94662
|
provideCitations = true,
|
|
@@ -94803,7 +94828,7 @@
|
|
|
94803
94828
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94804
94829
|
sources: message.sources,
|
|
94805
94830
|
apiBaseUrl: apiBaseUrl,
|
|
94806
|
-
|
|
94831
|
+
getAuthToken: getAuthToken,
|
|
94807
94832
|
isDark: isDark,
|
|
94808
94833
|
textColor: messageTextColor,
|
|
94809
94834
|
fontFamily: messageFontFamily,
|
|
@@ -94829,7 +94854,7 @@
|
|
|
94829
94854
|
}), /*#__PURE__*/jsxRuntimeExports.jsx(MessageSources, {
|
|
94830
94855
|
sources: message.sources,
|
|
94831
94856
|
apiBaseUrl: apiBaseUrl,
|
|
94832
|
-
|
|
94857
|
+
getAuthToken: getAuthToken,
|
|
94833
94858
|
isDark: isDark,
|
|
94834
94859
|
textColor: messageTextColor,
|
|
94835
94860
|
fontFamily: messageFontFamily,
|
|
@@ -95009,7 +95034,7 @@
|
|
|
95009
95034
|
conciergeName,
|
|
95010
95035
|
companyName,
|
|
95011
95036
|
apiBaseUrl,
|
|
95012
|
-
|
|
95037
|
+
getAuthToken,
|
|
95013
95038
|
onPromptSuggestionClick,
|
|
95014
95039
|
allowFeedback,
|
|
95015
95040
|
provideCitations,
|
|
@@ -95053,7 +95078,7 @@
|
|
|
95053
95078
|
conciergeName: conciergeName,
|
|
95054
95079
|
companyName: companyName,
|
|
95055
95080
|
apiBaseUrl: apiBaseUrl,
|
|
95056
|
-
|
|
95081
|
+
getAuthToken: getAuthToken,
|
|
95057
95082
|
onPromptSuggestionClick: onPromptSuggestionClick,
|
|
95058
95083
|
allowFeedback: allowFeedback,
|
|
95059
95084
|
provideCitations: provideCitations,
|
|
@@ -95898,7 +95923,7 @@
|
|
|
95898
95923
|
conciergeName,
|
|
95899
95924
|
companyName,
|
|
95900
95925
|
apiBaseUrl,
|
|
95901
|
-
|
|
95926
|
+
getAuthToken,
|
|
95902
95927
|
onPromptSuggestionClick,
|
|
95903
95928
|
conversationResetKey,
|
|
95904
95929
|
allowFeedback,
|
|
@@ -96080,7 +96105,7 @@
|
|
|
96080
96105
|
conciergeName: conciergeName,
|
|
96081
96106
|
companyName: companyName,
|
|
96082
96107
|
apiBaseUrl: apiBaseUrl,
|
|
96083
|
-
|
|
96108
|
+
getAuthToken: getAuthToken,
|
|
96084
96109
|
allowFeedback: allowFeedback,
|
|
96085
96110
|
provideCitations: provideCitations,
|
|
96086
96111
|
onFeedback: onFeedback,
|
|
@@ -96112,7 +96137,7 @@
|
|
|
96112
96137
|
conciergeName: conciergeName,
|
|
96113
96138
|
companyName: companyName,
|
|
96114
96139
|
apiBaseUrl: apiBaseUrl,
|
|
96115
|
-
|
|
96140
|
+
getAuthToken: getAuthToken,
|
|
96116
96141
|
headerTextColor: headerTextColor
|
|
96117
96142
|
}, "skeleton-waiting");
|
|
96118
96143
|
}
|
|
@@ -99627,7 +99652,7 @@
|
|
|
99627
99652
|
showPoweredBy = true,
|
|
99628
99653
|
apiBaseUrl,
|
|
99629
99654
|
apiKey,
|
|
99630
|
-
|
|
99655
|
+
getAuthToken,
|
|
99631
99656
|
conversationId,
|
|
99632
99657
|
conciergeId,
|
|
99633
99658
|
onboardingQuestions = [],
|
|
@@ -99775,7 +99800,7 @@
|
|
|
99775
99800
|
setCsatSubmitted(submitted);
|
|
99776
99801
|
if (submitted) setShowCsat(false);
|
|
99777
99802
|
} catch (_unused4) {}
|
|
99778
|
-
}, [conversationId
|
|
99803
|
+
}, [conversationId]);
|
|
99779
99804
|
|
|
99780
99805
|
// Auto-scroll hook
|
|
99781
99806
|
useAutoScroll(messagesContainerRef, [messages, isLoading], {
|
|
@@ -99913,14 +99938,15 @@
|
|
|
99913
99938
|
const handleCsatRating = async (rating, format) => {
|
|
99914
99939
|
setCsatSubmitted(true); // optimistic UI
|
|
99915
99940
|
setShowCsat(false);
|
|
99916
|
-
markCsatSubmitted(
|
|
99941
|
+
markCsatSubmitted(undefined); // persist so page refresh doesn't re-show CSAT
|
|
99917
99942
|
|
|
99918
99943
|
try {
|
|
99919
99944
|
const headers = {
|
|
99920
99945
|
"Content-Type": "application/json"
|
|
99921
99946
|
};
|
|
99922
|
-
if (
|
|
99923
|
-
|
|
99947
|
+
if (typeof getAuthToken === 'function') {
|
|
99948
|
+
const token = await getAuthToken();
|
|
99949
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99924
99950
|
}
|
|
99925
99951
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
99926
99952
|
method: "POST",
|
|
@@ -99930,7 +99956,6 @@
|
|
|
99930
99956
|
conversationId,
|
|
99931
99957
|
rating,
|
|
99932
99958
|
ratingFormat: format,
|
|
99933
|
-
organizationId,
|
|
99934
99959
|
conciergeId
|
|
99935
99960
|
})
|
|
99936
99961
|
});
|
|
@@ -99954,8 +99979,9 @@
|
|
|
99954
99979
|
const headers = {
|
|
99955
99980
|
"Content-Type": "application/json"
|
|
99956
99981
|
};
|
|
99957
|
-
if (
|
|
99958
|
-
|
|
99982
|
+
if (typeof getAuthToken === 'function') {
|
|
99983
|
+
const token = await getAuthToken();
|
|
99984
|
+
if (token) headers['Authorization'] = "Bearer ".concat(token);
|
|
99959
99985
|
}
|
|
99960
99986
|
const ratingFormat = normalizeCsatFormat(csatFormat);
|
|
99961
99987
|
const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/support/csat"), {
|
|
@@ -99966,7 +99992,6 @@
|
|
|
99966
99992
|
conversationId,
|
|
99967
99993
|
rating: String(resolveRating),
|
|
99968
99994
|
ratingFormat,
|
|
99969
|
-
organizationId,
|
|
99970
99995
|
conciergeId,
|
|
99971
99996
|
feedback: resolveFeedbackText || undefined
|
|
99972
99997
|
})
|
|
@@ -100000,7 +100025,7 @@
|
|
|
100000
100025
|
} finally {
|
|
100001
100026
|
setIsSubmittingResolve(false);
|
|
100002
100027
|
}
|
|
100003
|
-
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId,
|
|
100028
|
+
}, [isCsatEnabled, isSubmittingResolve, isConversationResolved, resolveRating, resolveFeedbackText, conversationId, getAuthToken, conciergeId, apiBaseUrl, csatFormat, onResetConversation, onRefreshChat, onWidgetFilterChange]);
|
|
100004
100029
|
|
|
100005
100030
|
// Handle X button: first click shows form, second click closes
|
|
100006
100031
|
const handleCloseClick = reactExports.useCallback(() => {
|
|
@@ -100338,7 +100363,7 @@
|
|
|
100338
100363
|
conciergeName: conciergeName,
|
|
100339
100364
|
companyName: companyName,
|
|
100340
100365
|
apiBaseUrl: apiBaseUrl,
|
|
100341
|
-
|
|
100366
|
+
getAuthToken: getAuthToken,
|
|
100342
100367
|
conversationResetKey: conversationId,
|
|
100343
100368
|
allowFeedback: allowFeedback,
|
|
100344
100369
|
provideCitations: provideCitations,
|
|
@@ -100423,7 +100448,7 @@
|
|
|
100423
100448
|
|
|
100424
100449
|
/**
|
|
100425
100450
|
* @param {string|undefined|null} apiBaseUrl
|
|
100426
|
-
* @param {
|
|
100451
|
+
* @param {Function|undefined|null} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined/null (Mode B proxy)
|
|
100427
100452
|
* @returns {{
|
|
100428
100453
|
* categories: Array<{id:string,label:string,count:number,tags:string[]}>,
|
|
100429
100454
|
* loading: boolean,
|
|
@@ -100431,14 +100456,15 @@
|
|
|
100431
100456
|
* hasFilters: boolean,
|
|
100432
100457
|
* }}
|
|
100433
100458
|
*/
|
|
100434
|
-
function useFilterCatalog(apiBaseUrl,
|
|
100459
|
+
function useFilterCatalog(apiBaseUrl, getAuthToken) {
|
|
100460
|
+
const hasAuth = !!apiBaseUrl;
|
|
100435
100461
|
const [state, setState] = reactExports.useState({
|
|
100436
100462
|
categories: [],
|
|
100437
|
-
loading:
|
|
100463
|
+
loading: hasAuth,
|
|
100438
100464
|
error: null
|
|
100439
100465
|
});
|
|
100440
100466
|
reactExports.useEffect(() => {
|
|
100441
|
-
if (!apiBaseUrl
|
|
100467
|
+
if (!apiBaseUrl) {
|
|
100442
100468
|
setState({
|
|
100443
100469
|
categories: [],
|
|
100444
100470
|
loading: false,
|
|
@@ -100446,8 +100472,8 @@
|
|
|
100446
100472
|
});
|
|
100447
100473
|
return;
|
|
100448
100474
|
}
|
|
100449
|
-
const
|
|
100450
|
-
const cached = _PROCESS_CACHE.get(
|
|
100475
|
+
const cacheKey = getAuthToken ? "".concat(apiBaseUrl, "|__session__") : "".concat(apiBaseUrl, "|__proxy__");
|
|
100476
|
+
const cached = _PROCESS_CACHE.get(cacheKey);
|
|
100451
100477
|
if (cached) {
|
|
100452
100478
|
setState({
|
|
100453
100479
|
categories: cached,
|
|
@@ -100461,43 +100487,55 @@
|
|
|
100461
100487
|
loading: true,
|
|
100462
100488
|
error: null
|
|
100463
100489
|
}));
|
|
100464
|
-
const
|
|
100465
|
-
|
|
100466
|
-
|
|
100467
|
-
|
|
100468
|
-
|
|
100469
|
-
|
|
100470
|
-
|
|
100471
|
-
|
|
100472
|
-
|
|
100490
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
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
|
+
});
|
|
100473
100533
|
}
|
|
100474
|
-
|
|
100475
|
-
return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
|
|
100476
|
-
}).then(categories => {
|
|
100477
|
-
if (cancelled) return;
|
|
100478
|
-
_PROCESS_CACHE.set(key, categories);
|
|
100479
|
-
setState({
|
|
100480
|
-
categories,
|
|
100481
|
-
loading: false,
|
|
100482
|
-
error: null
|
|
100483
|
-
});
|
|
100484
|
-
}).catch(err => {
|
|
100485
|
-
if (cancelled) return;
|
|
100486
|
-
// Fail-soft: log and present empty so the UI hides itself.
|
|
100487
|
-
// We do NOT bubble the error to the caller because chat
|
|
100488
|
-
// should keep working even if the catalog is unreachable.
|
|
100489
|
-
// eslint-disable-next-line no-console
|
|
100490
|
-
console.warn("[useFilterCatalog] catalog fetch failed:", err);
|
|
100491
|
-
setState({
|
|
100492
|
-
categories: [],
|
|
100493
|
-
loading: false,
|
|
100494
|
-
error: err
|
|
100495
|
-
});
|
|
100496
|
-
});
|
|
100534
|
+
})();
|
|
100497
100535
|
return () => {
|
|
100498
100536
|
cancelled = true;
|
|
100499
100537
|
};
|
|
100500
|
-
}, [apiBaseUrl,
|
|
100538
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100501
100539
|
const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
|
|
100502
100540
|
return _objectSpread2(_objectSpread2({}, state), {}, {
|
|
100503
100541
|
hasFilters
|
|
@@ -100576,20 +100614,31 @@
|
|
|
100576
100614
|
/**
|
|
100577
100615
|
* Fetches agent configuration from the deploy-agent endpoint
|
|
100578
100616
|
* @param {string} apiBaseUrl - Base URL for the API
|
|
100579
|
-
* @param {
|
|
100617
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Bearer token (in-app JWT path), or undefined (Mode B proxy)
|
|
100580
100618
|
* @returns {Promise<object>} Agent configuration object
|
|
100581
100619
|
*/
|
|
100582
|
-
const fetchAgentConfig = async (apiBaseUrl,
|
|
100583
|
-
if (!apiBaseUrl
|
|
100584
|
-
throw new Error("apiBaseUrl
|
|
100620
|
+
const fetchAgentConfig = async (apiBaseUrl, getAuthToken) => {
|
|
100621
|
+
if (!apiBaseUrl) {
|
|
100622
|
+
throw new Error("apiBaseUrl is required");
|
|
100585
100623
|
}
|
|
100586
100624
|
try {
|
|
100587
|
-
const
|
|
100625
|
+
const base = apiBaseUrl.replace(/\/$/, '');
|
|
100626
|
+
const url = "".concat(base, "/api/v1/deploy-agent");
|
|
100627
|
+
const headers = {
|
|
100628
|
+
"Content-Type": "application/json"
|
|
100629
|
+
};
|
|
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
|
+
}
|
|
100588
100639
|
const response = await fetch(url, {
|
|
100589
100640
|
method: "GET",
|
|
100590
|
-
headers
|
|
100591
|
-
"Content-Type": "application/json"
|
|
100592
|
-
},
|
|
100641
|
+
headers,
|
|
100593
100642
|
credentials: "include"
|
|
100594
100643
|
});
|
|
100595
100644
|
if (!response.ok) {
|
|
@@ -100606,8 +100655,10 @@
|
|
|
100606
100655
|
const ChatWidget = _ref => {
|
|
100607
100656
|
var _agentConfig$concierg;
|
|
100608
100657
|
let {
|
|
100609
|
-
|
|
100610
|
-
|
|
100658
|
+
/**
|
|
100659
|
+
* @param {Function|undefined} getAuthToken - async fn returning a Keycloak Bearer token (in-app JWT path)
|
|
100660
|
+
*/
|
|
100661
|
+
getAuthToken,
|
|
100611
100662
|
apiBaseUrl,
|
|
100612
100663
|
// Optional override props (will override fetched config if provided)
|
|
100613
100664
|
darkMode,
|
|
@@ -100658,14 +100709,14 @@
|
|
|
100658
100709
|
// Fetch agent configuration on mount
|
|
100659
100710
|
reactExports.useEffect(() => {
|
|
100660
100711
|
const loadAgentConfig = async () => {
|
|
100661
|
-
if (!
|
|
100662
|
-
setConfigError("
|
|
100712
|
+
if (!apiBaseUrl) {
|
|
100713
|
+
setConfigError("apiBaseUrl is required");
|
|
100663
100714
|
setIsLoadingConfig(false);
|
|
100664
100715
|
return;
|
|
100665
100716
|
}
|
|
100666
100717
|
try {
|
|
100667
100718
|
setIsLoadingConfig(true);
|
|
100668
|
-
const config = await fetchAgentConfig(apiBaseUrl,
|
|
100719
|
+
const config = await fetchAgentConfig(apiBaseUrl, getAuthToken);
|
|
100669
100720
|
setAgentConfig(config);
|
|
100670
100721
|
setConfigError(null);
|
|
100671
100722
|
} catch (error) {
|
|
@@ -100676,11 +100727,11 @@
|
|
|
100676
100727
|
}
|
|
100677
100728
|
};
|
|
100678
100729
|
loadAgentConfig();
|
|
100679
|
-
}, [
|
|
100730
|
+
}, [apiBaseUrl, getAuthToken]);
|
|
100680
100731
|
|
|
100681
100732
|
// Map API response to widget props (use props as overrides if provided)
|
|
100682
100733
|
const widgetConfig = reactExports.useMemo(() => {
|
|
100683
|
-
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;
|
|
100684
100735
|
if (!agentConfig) {
|
|
100685
100736
|
return null;
|
|
100686
100737
|
}
|
|
@@ -100739,7 +100790,6 @@
|
|
|
100739
100790
|
fontFamily: (_ref1 = fontFamily !== null && fontFamily !== void 0 ? fontFamily : typography.agentMessageBubbleFontFamily) !== null && _ref1 !== void 0 ? _ref1 : "Inter",
|
|
100740
100791
|
fontSize: (_ref10 = fontSize !== null && fontSize !== void 0 ? fontSize : typography.agentMessageBubbleFontSize) !== null && _ref10 !== void 0 ? _ref10 : "14px",
|
|
100741
100792
|
defaultLanguage: locale,
|
|
100742
|
-
organizationId: orgId,
|
|
100743
100793
|
headerTextBold: (_ref11 = headerTextBold !== null && headerTextBold !== void 0 ? headerTextBold : typography.headerTextBold) !== null && _ref11 !== void 0 ? _ref11 : false,
|
|
100744
100794
|
headerTextItalic: (_ref12 = headerTextItalic !== null && headerTextItalic !== void 0 ? headerTextItalic : typography.headerTextItalic) !== null && _ref12 !== void 0 ? _ref12 : false,
|
|
100745
100795
|
headerTextColor: (_typography$headerTex = typography.headerTextColor) !== null && _typography$headerTex !== void 0 ? _typography$headerTex : "#FFFFFF",
|
|
@@ -100776,9 +100826,12 @@
|
|
|
100776
100826
|
// every message and tell the backend to skip citation generation.
|
|
100777
100827
|
// Defaults to true so agents that haven't set the field keep showing
|
|
100778
100828
|
// citations (matches the testing-lab / chatbot-preview default).
|
|
100779
|
-
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
|
|
100780
100833
|
};
|
|
100781
|
-
}, [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
|
|
100782
100835
|
// CSAT dependencies
|
|
100783
100836
|
// (kept minimal because these come from agentConfig)
|
|
100784
100837
|
]);
|
|
@@ -100797,7 +100850,7 @@
|
|
|
100797
100850
|
quickQuestions: [],
|
|
100798
100851
|
apiBaseUrl: apiBaseUrl || "",
|
|
100799
100852
|
apiKey: apiKey || "",
|
|
100800
|
-
|
|
100853
|
+
getAuthToken,
|
|
100801
100854
|
autoOpen: false,
|
|
100802
100855
|
openDelay: 0,
|
|
100803
100856
|
darkMode: false,
|
|
@@ -100805,7 +100858,7 @@
|
|
|
100805
100858
|
onboardingQuestions: [],
|
|
100806
100859
|
onboardingEnabled: false,
|
|
100807
100860
|
collectionPrompt: undefined
|
|
100808
|
-
}), [apiBaseUrl, apiKey,
|
|
100861
|
+
}), [apiBaseUrl, apiKey, getAuthToken]);
|
|
100809
100862
|
|
|
100810
100863
|
// ALWAYS call hooks before any early returns (Rules of Hooks)
|
|
100811
100864
|
const {
|
|
@@ -100854,6 +100907,7 @@
|
|
|
100854
100907
|
// knowledge-api LoadBalancer.
|
|
100855
100908
|
apiKey,
|
|
100856
100909
|
organizationId: widgetConfig.organizationId,
|
|
100910
|
+
getAuthToken,
|
|
100857
100911
|
autoOpen,
|
|
100858
100912
|
openDelay,
|
|
100859
100913
|
darkMode: widgetConfig.darkMode,
|
|
@@ -100880,24 +100934,33 @@
|
|
|
100880
100934
|
// navigation (but not a page refresh — feedback is not written to
|
|
100881
100935
|
// localStorage so it resets on reload as intended).
|
|
100882
100936
|
updateMessageFeedback(messageContent, feedback);
|
|
100883
|
-
if (!
|
|
100937
|
+
if (!conversationId || !apiBaseUrl) return;
|
|
100884
100938
|
const base = apiBaseUrl.replace(/\/$/, "");
|
|
100885
|
-
|
|
100886
|
-
|
|
100887
|
-
|
|
100888
|
-
|
|
100889
|
-
|
|
100890
|
-
|
|
100891
|
-
|
|
100892
|
-
|
|
100893
|
-
|
|
100894
|
-
|
|
100895
|
-
|
|
100896
|
-
|
|
100897
|
-
|
|
100898
|
-
|
|
100899
|
-
|
|
100900
|
-
|
|
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]);
|
|
100901
100964
|
|
|
100902
100965
|
// Refresh chat handler — reset conversation
|
|
100903
100966
|
const handleRefreshChat = reactExports.useCallback(() => {
|
|
@@ -100913,7 +100976,7 @@
|
|
|
100913
100976
|
const {
|
|
100914
100977
|
categories: filterCategories,
|
|
100915
100978
|
hasFilters: showFilterStep
|
|
100916
|
-
} = useFilterCatalog(apiBaseUrl,
|
|
100979
|
+
} = useFilterCatalog(apiBaseUrl, getAuthToken);
|
|
100917
100980
|
|
|
100918
100981
|
// Copy chat handler — copy all messages to clipboard
|
|
100919
100982
|
const handleCopyChat = reactExports.useCallback(() => {
|
|
@@ -101018,7 +101081,7 @@
|
|
|
101018
101081
|
showPoweredBy: widgetConfig.showPoweredBy,
|
|
101019
101082
|
apiBaseUrl: apiBaseUrl,
|
|
101020
101083
|
apiKey: apiKey,
|
|
101021
|
-
|
|
101084
|
+
getAuthToken: getAuthToken,
|
|
101022
101085
|
conversationId: conversationId,
|
|
101023
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,
|
|
101024
101087
|
onboardingQuestions: widgetConfig.onboardingQuestions,
|
|
@@ -101166,11 +101229,10 @@
|
|
|
101166
101229
|
}
|
|
101167
101230
|
|
|
101168
101231
|
// Render component
|
|
101169
|
-
//
|
|
101170
|
-
//
|
|
101171
|
-
const
|
|
101172
|
-
|
|
101173
|
-
orgId: config.orgId,
|
|
101232
|
+
// Mode B proxy: apiBaseUrl alone is enough — no key or orgId in browser
|
|
101233
|
+
// In-app JWT: getAuthToken fn passed via config
|
|
101234
|
+
const hasNewAuth = config.apiBaseUrl;
|
|
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({
|
|
101174
101236
|
apiBaseUrl: config.apiBaseUrl
|
|
101175
101237
|
}, options.wsBaseUrl !== undefined && {
|
|
101176
101238
|
wsBaseUrl: config.wsBaseUrl
|